diff --git a/DiffPlex.Wpf.Demo/MainWindow.xaml b/DiffPlex.Wpf.Demo/MainWindow.xaml
index bf0dd294..c6e5f360 100644
--- a/DiffPlex.Wpf.Demo/MainWindow.xaml
+++ b/DiffPlex.Wpf.Demo/MainWindow.xaml
@@ -19,6 +19,7 @@
+
diff --git a/DiffPlex.Wpf/Controls/BooleanToScrollBarVisibilityConverter.cs b/DiffPlex.Wpf/Controls/BooleanToScrollBarVisibilityConverter.cs
new file mode 100644
index 00000000..cfc1be80
--- /dev/null
+++ b/DiffPlex.Wpf/Controls/BooleanToScrollBarVisibilityConverter.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Globalization;
+using System.Windows.Controls;
+using System.Windows.Data;
+
+namespace DiffPlex.Wpf.Controls
+{
+ public class BooleanToScrollBarVisibilityConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is bool isTextWrapEnabled)
+ {
+ return isTextWrapEnabled ? ScrollBarVisibility.Hidden : ScrollBarVisibility.Auto;
+ }
+ return ScrollBarVisibility.Auto;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/DiffPlex.Wpf/Controls/DiffViewer.xaml b/DiffPlex.Wpf/Controls/DiffViewer.xaml
index 2dbeb933..84950593 100644
--- a/DiffPlex.Wpf/Controls/DiffViewer.xaml
+++ b/DiffPlex.Wpf/Controls/DiffViewer.xaml
@@ -129,4 +129,4 @@
-
+
\ No newline at end of file
diff --git a/DiffPlex.Wpf/Controls/DiffViewer.xaml.cs b/DiffPlex.Wpf/Controls/DiffViewer.xaml.cs
index eda23517..1272c897 100644
--- a/DiffPlex.Wpf/Controls/DiffViewer.xaml.cs
+++ b/DiffPlex.Wpf/Controls/DiffViewer.xaml.cs
@@ -27,7 +27,7 @@ namespace DiffPlex.Wpf.Controls;
///
/// The diff control for text.
///
-public partial class DiffViewer : UserControl
+public partial class DiffViewer : UserControl, IDiffViewer
{
///
/// The event arguments of view mode changed.
@@ -80,6 +80,11 @@ public ViewModeChangedEventArgs(bool isSideBySide)
///
public static readonly DependencyProperty LineNumberForegroundProperty = RegisterDependencyProperty(nameof(LineNumberForeground), new SolidColorBrush(Color.FromArgb(255, 64, 128, 160)));
+ ///
+ /// The property of text wrapping state.
+ ///
+ public static readonly DependencyProperty IsTextWrapEnabledProperty = RegisterRefreshDependencyProperty(nameof(IsTextWrapEnabled), false);
+
///
/// The property of line number width.
///
@@ -651,6 +656,17 @@ public int LinesContext
set => SetValue(LinesContextProperty, value);
}
+ ///
+ /// Gets or sets the value indicating whether is wrap text.
+ ///
+ [Bindable(true)]
+ [Category("Appearance")]
+ public bool IsTextWrapEnabled
+ {
+ get => (bool)GetValue(IsTextWrapEnabledProperty);
+ set => SetValue(IsTextWrapEnabledProperty, value);
+ }
+
///
/// Gets or sets the display name of inline mode toggle.
///
@@ -1142,7 +1158,7 @@ private void RenderSideBySideDiffs()
var m = sideBySideResult;
CollapseUnchangedSectionsToggle.IsChecked = IgnoreUnchanged;
if (m == null) return;
- var contextLineCount = IgnoreUnchanged ? LinesContext: -1;
+ var contextLineCount = IgnoreUnchanged ? LinesContext : -1;
Helper.InsertLines(LeftContentPanel, m.OldText?.Lines, true, this, contextLineCount);
Helper.InsertLines(RightContentPanel, m.NewText.Lines, false, this, contextLineCount);
}
@@ -1306,6 +1322,13 @@ public DiffPiece NextDiff()
return line;
}
+ public void ToggleTextWrapping()
+ {
+ IsTextWrapEnabled = !IsTextWrapEnabled;
+
+ this.Refresh();
+ }
+
private string GenerateHeader(FileInfo file)
{
if (file == null || !file.Exists) return Resource.Empty;
@@ -1488,4 +1511,4 @@ private static DependencyProperty RegisterRefreshDependencyProperty(string na
c.Refresh();
}));
}
-}
+}
\ No newline at end of file
diff --git a/DiffPlex.Wpf/Controls/Helper.cs b/DiffPlex.Wpf/Controls/Helper.cs
index 47723981..14f0ac5f 100644
--- a/DiffPlex.Wpf/Controls/Helper.cs
+++ b/DiffPlex.Wpf/Controls/Helper.cs
@@ -38,9 +38,11 @@ internal static class Helper
///
/// Updates the inline diffs view.
///
- internal static void RenderInlineDiffs(InternalLinesViewer viewer, ICollection lines, UIElement source, int contextLineCount)
+ internal static void RenderInlineDiffs(InternalLinesViewer viewer, ICollection lines, IDiffViewer source, int contextLineCount)
{
viewer.Clear();
+ var diffViewer = source as DiffViewer;
+
if (lines == null) return;
if (lines.Any() == false) return;
var disableSubPieces = lines.Count > MaxCount; // For performance.
@@ -50,6 +52,7 @@ internal static void RenderInlineDiffs(InternalLinesViewer viewer, ICollection 1 && !disableSubPieces)
{
var details = GetSubPiecesInfo(line, true);
+
var c = viewer.Add(line.Position, "+", details, changeType.ToString(), source);
c.Tag = line;
+
hasAdded = true;
}
@@ -75,8 +80,10 @@ internal static void RenderInlineDiffs(InternalLinesViewer viewer, ICollection 1 && !disableSubPieces)
{
var details = GetSubPiecesInfo(line, false);
+
var c = viewer.Add(line.Position, "-", details, changeType.ToString(), source);
c.Tag = line;
+
hasAdded = true;
}
@@ -105,7 +112,7 @@ internal static void RenderInlineDiffs(InternalLinesViewer viewer, ICollection lines, bool isOld, UIElement source, int contextLineCount)
+ internal static void InsertLines(InternalLinesViewer panel, List lines, bool isOld, IDiffViewer source, int contextLineCount)
{
if (lines == null || panel == null) return;
var guid = panel.TrackingId = Guid.NewGuid();
@@ -119,7 +126,7 @@ internal static void InsertLines(InternalLinesViewer panel, List line
_ = InsertLinesAsync(guid, panel, lines, isOld, source, contextLineCount);
}
- private static async Task InsertLinesAsync(Guid guid, InternalLinesViewer panel, List lines, bool isOld, UIElement source, int contextLineCount)
+ private static async Task InsertLinesAsync(Guid guid, InternalLinesViewer panel, List lines, bool isOld, IDiffViewer source, int contextLineCount)
{ // For performance.
if (lines == null || panel == null) return;
var disablePieces = lines.Count > MaxCount;
@@ -231,7 +238,7 @@ internal static bool GoTo(InternalLinesViewer panel, int lineIndex)
catch (InvalidOperationException)
{
}
-
+
return false;
}
@@ -476,14 +483,17 @@ private static List> GetSubPiecesInfo(DiffPiece lin
return details;
}
- private static void InsertLinesInteral(InternalLinesViewer panel, List lines, bool isOld, UIElement source, bool disableSubPieces = false)
+ private static void InsertLinesInteral(InternalLinesViewer panel, List lines, bool isOld, IDiffViewer source, bool disableSubPieces = false)
{
+ var diffViewer = source as DiffViewer;
+
foreach (var line in lines)
{
if (line == null)
{
var c = panel.Add(null, null, null as string, ChangeType.Unchanged.ToString(), source);
c.Tag = line;
+
continue;
}
@@ -497,8 +507,10 @@ private static void InsertLinesInteral(InternalLinesViewer panel, List 1 && !disableSubPieces)
{
var details = GetSubPiecesInfo(line, isOld);
+
var c = panel.Add(line.Position, isOld ? "-" : "+", details, changeType.ToString(), source);
c.Tag = line;
+
hasAdded = true;
}
@@ -527,4 +539,4 @@ private static void InsertLinesInteral(InternalLinesViewer panel, List
-public partial class InlineDiffViewer : UserControl
+public partial class InlineDiffViewer : UserControl, IDiffViewer
{
///
/// The property of diff model.
@@ -58,6 +58,11 @@ public partial class InlineDiffViewer : UserControl
c.ContentPanel.LineNumberWidth = n;
});
+ ///
+ /// The property of text wrapping state.
+ ///
+ public static readonly DependencyProperty IsTextWrapEnabledProperty = RegisterDependencyProperty(nameof(IsTextWrapEnabled), false);
+
///
/// The property of change type symbol foreground brush.
///
@@ -339,6 +344,14 @@ public int LinesContext
set => SetValue(LinesContextProperty, value);
}
+ [Bindable(true)]
+ [Category("Appearance")]
+ public bool IsTextWrapEnabled
+ {
+ get => (bool)GetValue(IsTextWrapEnabledProperty);
+ set => SetValue(IsTextWrapEnabledProperty, value);
+ }
+
///
/// Sets a new diff model.
///
diff --git a/DiffPlex.Wpf/Controls/InternalLinesViewer.xaml b/DiffPlex.Wpf/Controls/InternalLinesViewer.xaml
index 440f5f3d..f844d0f8 100644
--- a/DiffPlex.Wpf/Controls/InternalLinesViewer.xaml
+++ b/DiffPlex.Wpf/Controls/InternalLinesViewer.xaml
@@ -1,126 +1,199 @@
-
+
+
-
-
-
+
+
+
-
+
-
-
-
+
+
+
-
-
+
+
-
-
+
+
-
-
+
+
diff --git a/DiffPlex.Wpf/Controls/InternalLinesViewer.xaml.cs b/DiffPlex.Wpf/Controls/InternalLinesViewer.xaml.cs
index 2e35e263..b758d4fb 100644
--- a/DiffPlex.Wpf/Controls/InternalLinesViewer.xaml.cs
+++ b/DiffPlex.Wpf/Controls/InternalLinesViewer.xaml.cs
@@ -42,6 +42,8 @@ public ScrollBarVisibility VerticalScrollBarVisibility
public ContextMenu LineContextMenu { get; set; }
+ public bool IsTextWrapEnabled { get; set; }
+
public double VerticalOffset => ValueScrollViewer.VerticalOffset;
public int LineNumberWidth
@@ -67,88 +69,138 @@ public void Clear()
OperationPanel.Children.Clear();
ValuePanel.Children.Clear();
}
+
+
+ public Panel Add(int? number, string operation, string value, string changeType, IDiffViewer diffViewer)
+ {
+ IsTextWrapEnabled = diffViewer.IsTextWrapEnabled;
+
+ Panel panel = IsTextWrapEnabled ? new WrapPanel { Orientation = Orientation.Horizontal }
+ : new StackPanel { Orientation = Orientation.Horizontal };
+
+ var grid = new Grid();
+ grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
+ grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
+ grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
- public StackPanel Add(int? number, string operation, string value, string changeType, UIElement source)
- {
var index = new TextBlock
{
Text = number.HasValue ? number.ToString() : string.Empty,
- TextAlignment = TextAlignment.Right
+ TextAlignment = TextAlignment.Right,
+ VerticalAlignment = IsTextWrapEnabled ? VerticalAlignment.Top : VerticalAlignment.Stretch,
+ Margin = IsTextWrapEnabled ? new Thickness(2) : new Thickness(0)
};
- index.SetBinding(TextBlock.ForegroundProperty, GetBindings("LineNumberForeground", source, Foreground));
- index.SetBinding(TextBlock.BackgroundProperty, GetBindings(changeType + "Background", source));
- ApplyTextBlockProperties(index, source);
- NumberPanel.Children.Add(index);
+ index.SetBinding(TextBlock.ForegroundProperty, GetBindings("LineNumberForeground", diffViewer, Foreground));
+ index.SetBinding(TextBlock.BackgroundProperty, GetBindings(changeType + "Background", diffViewer));
+ ApplyTextBlockProperties(index, diffViewer);
+ if(!IsTextWrapEnabled)
+ NumberPanel.Children.Add(index);
var op = new TextBlock
{
Text = operation,
- TextAlignment = TextAlignment.Center
+ TextAlignment = TextAlignment.Center,
+ VerticalAlignment = IsTextWrapEnabled ? VerticalAlignment.Top : VerticalAlignment.Stretch,
+ Margin = IsTextWrapEnabled ? new Thickness(2) : new Thickness(0)
};
- op.SetBinding(TextBlock.ForegroundProperty, GetBindings("ChangeTypeForeground", source, Foreground));
- op.SetBinding(TextBlock.BackgroundProperty, GetBindings(changeType + "Background", source));
- ApplyTextBlockProperties(op, source);
- OperationPanel.Children.Add(op);
+ op.SetBinding(TextBlock.ForegroundProperty, GetBindings("ChangeTypeForeground", diffViewer, Foreground));
+ op.SetBinding(TextBlock.BackgroundProperty, GetBindings(changeType + "Background", diffViewer));
+ ApplyTextBlockProperties(op, diffViewer);
+ if (!IsTextWrapEnabled)
+ OperationPanel.Children.Add(op);
+
+ if(IsTextWrapEnabled)
+ {
+ Grid.SetColumn(index, 0);
+ Grid.SetColumn(op, 1);
+ grid.Children.Add(index);
+ grid.Children.Add(op);
+ }
- var panel = new StackPanel { Orientation = Orientation.Horizontal };
- panel.SetBinding(BackgroundProperty, GetBindings(changeType + "Background", source));
var text = new TextBlock
{
- Text = value
+ Text = value,
+ TextWrapping = IsTextWrapEnabled ? TextWrapping.Wrap : TextWrapping.NoWrap,
+ VerticalAlignment = IsTextWrapEnabled ? VerticalAlignment.Top : VerticalAlignment.Stretch,
+ Margin = IsTextWrapEnabled ? new Thickness(5, 0, 5, 0) : new Thickness(0)
};
if (!string.IsNullOrEmpty(value))
{
- text.SetBinding(TextBlock.ForegroundProperty, GetBindings(changeType + "Foreground", source, Foreground));
- text.SetBinding(TextBlock.BackgroundProperty, GetBindings(changeType + "Background", source));
- ApplyTextBlockProperties(text, source);
- panel.ContextMenu = LineContextMenu;
+ text.SetBinding(TextBlock.ForegroundProperty, GetBindings(changeType + "Foreground", diffViewer, Foreground));
+ text.SetBinding(TextBlock.BackgroundProperty, GetBindings(changeType + "Background", diffViewer));
+ ApplyTextBlockProperties(text, diffViewer);
+ if (IsTextWrapEnabled)
+ grid.ContextMenu = LineContextMenu;
+ else
+ panel.ContextMenu = LineContextMenu;
}
- panel.Children.Add(text);
+ if(IsTextWrapEnabled)
+ {
+ Grid.SetColumn(text, 2);
+ grid.Children.Add(text);
+ }
+
+ if (IsTextWrapEnabled)
+ panel.Children.Add(grid);
+ else
+ panel.Children.Add(text);
+
ValuePanel.Children.Add(panel);
+ ValuePanel.CanHorizontallyScroll = false;
+ ValueScrollViewer.HorizontalScrollBarVisibility = IsTextWrapEnabled ? ScrollBarVisibility.Disabled : ScrollBarVisibility.Visible;
+
return panel;
}
- public StackPanel Add(int? number, string operation, List> value, string changeType, UIElement source)
+ public Panel Add(int? number, string operation, List> value, string changeType, IDiffViewer diffViewer)
{
+ IsTextWrapEnabled = diffViewer.IsTextWrapEnabled;
+
var index = new TextBlock
{
Text = number.HasValue ? number.ToString() : string.Empty,
- TextAlignment = TextAlignment.Right
+ TextAlignment = IsTextWrapEnabled ? TextAlignment.Center : TextAlignment.Right,
+ VerticalAlignment = VerticalAlignment.Top
};
- index.SetBinding(TextBlock.ForegroundProperty, GetBindings("LineNumberForeground", source, Foreground));
- index.SetBinding(TextBlock.BackgroundProperty, GetBindings(changeType + "Background", source));
- ApplyTextBlockProperties(index, source);
+ index.SetBinding(TextBlock.ForegroundProperty, GetBindings("LineNumberForeground", diffViewer, Foreground));
+ index.SetBinding(TextBlock.BackgroundProperty, GetBindings(changeType + "Background", diffViewer));
+ ApplyTextBlockProperties(index, diffViewer);
NumberPanel.Children.Add(index);
var op = new TextBlock
{
Text = operation,
- TextAlignment = TextAlignment.Center
+ TextAlignment = IsTextWrapEnabled ? TextAlignment.Center : TextAlignment.Right,
+ VerticalAlignment = VerticalAlignment.Top
};
- op.SetBinding(TextBlock.ForegroundProperty, GetBindings("ChangeTypeForeground", source, Foreground));
- op.SetBinding(TextBlock.BackgroundProperty, GetBindings(changeType + "Background", source));
- ApplyTextBlockProperties(op, source);
+ op.SetBinding(TextBlock.ForegroundProperty, GetBindings("ChangeTypeForeground", diffViewer, Foreground));
+ op.SetBinding(TextBlock.BackgroundProperty, GetBindings(changeType + "Background", diffViewer));
+ ApplyTextBlockProperties(op, diffViewer);
OperationPanel.Children.Add(op);
- var panel = new StackPanel { Orientation = Orientation.Horizontal };
- panel.SetBinding(BackgroundProperty, GetBindings(changeType + "Background", source));
+ Panel panel = IsTextWrapEnabled ? new WrapPanel { Orientation = Orientation.Horizontal }
+ : new StackPanel { Orientation = Orientation.Horizontal };
+
+ panel.SetBinding(BackgroundProperty, GetBindings(changeType + "Background", diffViewer));
value ??= new List>();
foreach (var ele in value)
{
if (string.IsNullOrEmpty(ele.Key)) continue;
var text = new TextBlock
{
- Text = ele.Key
+ Text = ele.Key,
+ VerticalAlignment = VerticalAlignment.Top,
+ TextWrapping = IsTextWrapEnabled ? TextWrapping.Wrap : TextWrapping.NoWrap
};
if (!string.IsNullOrEmpty(ele.Value))
{
if (!string.IsNullOrEmpty(ele.Key))
- text.SetBinding(TextBlock.ForegroundProperty, GetBindings(ele.Value + "Foreground", source, Foreground));
- text.SetBinding(TextBlock.BackgroundProperty, GetBindings(ele.Value + "Background", source));
+ text.SetBinding(TextBlock.ForegroundProperty, GetBindings(ele.Value + "Foreground", diffViewer, Foreground));
+ text.SetBinding(TextBlock.BackgroundProperty, GetBindings(ele.Value + "Background", diffViewer));
}
- ApplyTextBlockProperties(text, source);
+ ApplyTextBlockProperties(text, diffViewer);
panel.Children.Add(text);
}
@@ -162,6 +214,9 @@ public StackPanel Add(int? number, string operation, List GetTagsOfEachLine()
}
}
- private Binding GetBindings(string key, UIElement source)
+ private Binding GetBindings(string key, IDiffViewer source)
{
if (bindings.TryGetValue(key, out var r) && r.Source == source) return r;
return bindings[key] = new Binding(key) { Source = source, Mode = BindingMode.OneWay };
}
- private Binding GetBindings(string key, UIElement source, object defaultValue)
+ private Binding GetBindings(string key, IDiffViewer source, object defaultValue)
{
if (bindings.TryGetValue(key, out var r) && r.Source == source) return r;
return bindings[key] = new Binding(key) { Source = source, Mode = BindingMode.OneWay, TargetNullValue = defaultValue };
@@ -218,10 +273,10 @@ internal void AdjustScrollView()
}
}
- private void ApplyTextBlockProperties(TextBlock text, UIElement source)
+ private void ApplyTextBlockProperties(TextBlock text, IDiffViewer source)
{
text.SetBinding(TextBlock.FontSizeProperty, GetBindings("FontSize", source));
- text.SetBinding(TextBlock.FontFamilyProperty, GetBindings("FontFamily", source, Helper.FontFamily ));
+ text.SetBinding(TextBlock.FontFamilyProperty, GetBindings("FontFamily", source, Helper.FontFamily));
text.SetBinding(TextBlock.FontWeightProperty, GetBindings("FontWeight", source));
text.SetBinding(TextBlock.FontStretchProperty, GetBindings("FontStretch", source));
text.SetBinding(TextBlock.FontStyleProperty, GetBindings("FontStyle", source));
diff --git a/DiffPlex.Wpf/Controls/SideBySideDiffViewer.xaml.cs b/DiffPlex.Wpf/Controls/SideBySideDiffViewer.xaml.cs
index 4bf069c4..eb6c4972 100644
--- a/DiffPlex.Wpf/Controls/SideBySideDiffViewer.xaml.cs
+++ b/DiffPlex.Wpf/Controls/SideBySideDiffViewer.xaml.cs
@@ -24,7 +24,7 @@ namespace DiffPlex.Wpf.Controls;
/// The side by side diff control for text.
/// Interaction logic for SideBySideDiffViewer.xaml
///
-public partial class SideBySideDiffViewer : UserControl
+public partial class SideBySideDiffViewer : UserControl, IDiffViewer
{
///
/// The property of diff model.
@@ -58,6 +58,11 @@ public partial class SideBySideDiffViewer : UserControl
c.LeftContentPanel.LineNumberWidth = c.RightContentPanel.LineNumberWidth = n;
});
+ ///
+ /// The property of text wrapping state.
+ ///
+ public static readonly DependencyProperty IsTextWrapEnabledProperty = RegisterDependencyProperty(nameof(IsTextWrapEnabled), false);
+
///
/// The property of change type symbol foreground brush.
///
@@ -398,6 +403,7 @@ public int LinesContext
get => (int)GetValue(LinesContextProperty);
set => SetValue(LinesContextProperty, value);
}
+ public bool IsTextWrapEnabled { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
///
/// Sets a new diff model.
diff --git a/DiffPlex.Wpf/DiffWindow.xaml.cs b/DiffPlex.Wpf/DiffWindow.xaml.cs
index 06e67744..fe2807ed 100644
--- a/DiffPlex.Wpf/DiffWindow.xaml.cs
+++ b/DiffPlex.Wpf/DiffWindow.xaml.cs
@@ -296,5 +296,11 @@ public void SetMenuTextBoxStyle(Style style)
/// The control template to set.
public void SetMenuTextBoxTemlate(ControlTemplate template)
=> DiffView.SetMenuTextBoxTemlate(template);
+
+ ///
+ /// Enables text wrapping for the text blocks in the diff viewer.
+ ///
+ public void ToggleTextWrapping()
+ => DiffView.ToggleTextWrapping();
}
}