WPF DataGrid列调整事件:如何检测列缩放取消滚动条靠右吸附实现Excel预览效果
WPF DataGrid 列宽调整禁用滚动条吸附及Excel式预览实现方案
基础方案:禁用滚动条自动靠右吸附
核心原理是在列宽调整开始时记录当前水平滚动偏移量,调整过程中拦截默认的自动滚动逻辑,调整完成后恢复原有偏移量,即可解决吸附问题。
实现代码
首先创建自定义DataGrid类,重写列宽调整相关逻辑:
using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Media; public class CustomDataGrid : DataGrid { private double _savedHorizontalOffset; private bool _isColumnResizing; public CustomDataGrid() { AddHandler(Thumb.DragStartedEvent, new DragStartedEventHandler(OnColumnResizeStarted)); AddHandler(Thumb.DragCompletedEvent, new DragCompletedEventHandler(OnColumnResizeCompleted)); } private void OnColumnResizeStarted(object sender, DragStartedEventArgs e) { if (e.OriginalSource is Thumb thumb && thumb.TemplatedParent is DataGridColumnHeader) { _isColumnResizing = true; _savedHorizontalOffset = ScrollViewer.GetHorizontalOffset(this); } } private void OnColumnResizeCompleted(object sender, DragCompletedEventArgs e) { if (_isColumnResizing) { _isColumnResizing = false; var scrollViewer = GetVisualChild<ScrollViewer>(this); if (scrollViewer != null) { scrollViewer.ScrollToHorizontalOffset(_savedHorizontalOffset); } } } private static T GetVisualChild<T>(DependencyObject parent) where T : Visual { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) { var child = VisualTreeHelper.GetChild(parent, i); if (child is T typedChild) return typedChild; var result = GetVisualChild<T>(child); if (result != null) return result; } return null; } protected override void OnColumnWidthChanged(DataGridColumnEventArgs e) { if (!_isColumnResizing) base.OnColumnWidthChanged(e); } }
XAML 替换使用
把你原有代码中的DataGrid标签替换为本地的CustomDataGrid即可:
<local:CustomDataGrid Grid.Column="0" ItemsSource="{Binding Items}" AutoGenerateColumns="False" SelectionMode="Extended" local:MultiSelect.IsEnabled="True" HorizontalScrollBarVisibility="Auto"> <DataGrid.Columns> <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="100"/> <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="100"/> <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="100"/> </DataGrid.Columns> </local:CustomDataGrid>
进阶方案:实现Excel式列宽调整预览效果
如果需要和Excel完全一致的拖动时显示虚线预览、松开后才修改列宽的效果,可以通过重写列头Thumb的拖动逻辑实现,拖动过程中仅绘制预览线,不实际修改列宽,完全避免滚动条实时调整的问题。
实现代码
using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Documents; using System.Windows.Media; public static class ExcelStyleColumnResizeBehavior { public static readonly DependencyProperty EnableExcelStyleResizeProperty = DependencyProperty.RegisterAttached("EnableExcelStyleResize", typeof(bool), typeof(ExcelStyleColumnResizeBehavior), new PropertyMetadata(false, OnEnableExcelStyleResizeChanged)); public static bool GetEnableExcelStyleResize(DependencyObject obj) { return (bool)obj.GetValue(EnableExcelStyleResizeProperty); } public static void SetEnableExcelStyleResize(DependencyObject obj, bool value) { obj.SetValue(EnableExcelStyleResizeProperty, value); } private static AdornerLayer _adornerLayer; private static ResizeAdorner _currentAdorner; private static void OnEnableExcelStyleResizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is DataGrid dataGrid && (bool)e.NewValue) { dataGrid.Loaded += (s, args) => _adornerLayer = AdornerLayer.GetAdornerLayer(dataGrid); dataGrid.AddHandler(Thumb.DragStartedEvent, new DragStartedEventHandler((sender, args) => { if (args.OriginalSource is Thumb thumb && thumb.TemplatedParent is DataGridColumnHeader header) { thumb.Cursor = Cursors.SizeWE; header.Tag = header.Column.Width; _currentAdorner = new ResizeAdorner(header); _adornerLayer.Add(_currentAdorner); args.Handled = true; } })); dataGrid.AddHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler((sender, args) => { if (args.OriginalSource is Thumb thumb && thumb.TemplatedParent is DataGridColumnHeader header && header.Tag is DataGridLength originalWidth) { _currentAdorner.OffsetX = args.HorizontalChange; args.Handled = true; } })); dataGrid.AddHandler(Thumb.DragCompletedEvent, new DragCompletedEventHandler((sender, args) => { if (args.OriginalSource is Thumb thumb && thumb.TemplatedParent is DataGridColumnHeader header && header.Tag is DataGridLength originalWidth) { var newWidth = Math.Max(0, originalWidth.Value + args.HorizontalChange); header.Column.Width = new DataGridLength(newWidth, originalWidth.UnitType); header.Tag = null; _adornerLayer.Remove(_currentAdorner); _currentAdorner = null; args.Handled = true; } })); } } private class ResizeAdorner : Adorner { public double OffsetX { get; set; } private static readonly Pen _previewPen = new Pen(Brushes.Black, 1) { DashStyle = DashStyles.Dash }; public ResizeAdorner(UIElement adornedElement) : base(adornedElement) { } protected override void OnRender(DrawingContext drawingContext) { var rect = new Rect(AdornedElement.RenderSize); var lineX = rect.Right + OffsetX; drawingContext.DrawLine(_previewPen, new Point(lineX, 0), new Point(lineX, ((DataGrid)VisualTreeHelper.GetParent(AdornedElement)).ActualHeight)); } } }
XAML 使用方式
直接在原有DataGrid上附加属性即可,无需修改其他逻辑:
<DataGrid Grid.Column="0" ItemsSource="{Binding Items}" AutoGenerateColumns="False" SelectionMode="Extended" local:MultiSelect.IsEnabled="True" HorizontalScrollBarVisibility="Auto" local:ExcelStyleColumnResizeBehavior.EnableExcelStyleResize="True"> <DataGrid.Columns> <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="100"/> <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="100"/> <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="100"/> </DataGrid.Columns> </DataGrid>
内容的提问来源于stack exchange,提问作者Luy
相关产品推荐
相关产品推荐

