开发者

WPF中实现DataGrid行拖拽功能的完整方案

目录
  • 前言
  • 实现效果
  • 核心实现
    • 1、定义拖拽行为类
    • 2、UI工具类
    • 3、XAML中使用
  • 实现原理
    • 关键特性
      • 总结

        前言

        在wpF应用开发中,DataGrid是一个常用的数据展示控件。然而,原生的DataGrid并不支持行拖拽功能,这在需要调整行顺序的场景中显得尤为不便。

        本文将介绍如何实现一个优雅的DataGrid行拖拽功能,使用户可以通过拖拽操作轻松调整行顺序。该方案基于开源项目实现,使用MIT协议的HandyControl样式库,具有良好的可扩展性和实用性。

        实现效果

        WPF中实现DataGrid行拖拽功能的完整方案

        核心实现

        1、定义拖拽行为类

        public static class DragDropRowBehavior
        {
            private static DataGrid dataGrid;
            private static Popup popup;
            private static bool enable;
            private static object draggedItem;
        
            public static object DraggedItem
            {
                get { return DragDropRowBehavior.draggedItem; }
                set { DragDropRowBehavior.draggedItem = value; }
            }
        
            // 定义PopupControl附加属性
            public static readonly Depend编程客栈encyProperty PopupControlProperty =
                DependencyProperty.RegisterAttached("PopupControl", typeof(Popup), 
                    typeof(DragDropRowBehavior), new UIPropertyMetadata(null, OnPopupControlChanged));
        
            public static Popup GetPopupControl(DependencyObject obj)
            {
                return (Popup)obj.GetValue(PopupControlProperty);
            }
        
            public static void SetPopupControl(DependencyObject obj, Popup value)
            {
                obj.SetValue(PopupControlProperty, value);
            }
        
            private static void OnPopupControlChanged(DependencyObject depObject, DependencyPropertyChangedEventArgs e)
            {
                if (e.NewValue == null || !(e.NewValue is Popup))
                {
                    throw new ArgumentException("Popup Control should be set", "PopupControl");
                }
                popup = e.NewValue as Popup;
        
                dataGrid = depObject as DataGrid;
                if (dataGrid == null) return;
        
                if (enable && popup != null)
                {
                    dataGrid.BeginningEdit += OnBeginEdit;
                    dataGrid.CellEditEnding += OnEndEdit;
                    dataGrid.MouseLeftButtonUp += OnMouseLeftButtonUp;
                    dataGrid.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
                    dataGrid.MouseMove += OnMouseMove;
                }
                else
                {
               编程客栈     dataGrid.BeginningEdit -= OnBeginEdit;
                    dataGrid.CellEditEnding -= OnEndEdit;
                    dataGrid.MouseLeftButtonUp -= OnMouseLeftButtonUp;
                    dataGrid.MouseLeftButtonDown -= OnMouseLeftButtonDown;
                    dataGrid.MouseMove -= OnMouseMove;
        
                    dataGrid = null;
                    popup = null;
                    draggedItem = null;
                    IsEditing = false;
                    IsDragging = false;
                }
            }
        
            // 定义Enabled附加属性
            public static readonly DependencyProperty EnabledProperty =
                DependencyProperty.RegisterAttpythonached("Enabled", typeof(bool), 
                    typeof(DragDropRowBehavior), new UIPropertyMetadata(false, OnEnabledChanged));
        
            public static bool GetEnabled(DependencyObject obj)
            {
                return (bool)obj.GetValue(EnabledProperty);
            }
        
            public static void SetEnabled(DependencyObject obj, bool value)
            {
                obj.SetValue(EnabledProperty, value);
            }
        
            private static void OnEnabledChanged(DependencyObject depObject, DependencyPropertyChangedEventwww.devze.comArgs e)
            {
                if (e.NewValue is bool == false)
                    throw new ArgumentException("Value should be of bool type", "Enabled");
        
                enable = (bool)e.NewValue;
            }
        
            public static bool IsEditing { get; set; }
            public static bool IsDragging { get; set; }
        
            private static void OnBeginEdit(object sender, DataGridBeginningEditEventArgs e)
            {
                IsEditing = true;
                if (IsDragging) ResetDragDrop();
            }
        
            private static void OnEndEdit(object sender, DataGridCellEditEndingEventArgs e)
            {
                IsEditing = false;
            }
        
            private static void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                if (IsEditing) return;
                
                var row = UIHelpers.TryFindFromPoint<DataGridRow>((UIElement)sender, e.GetPosition(dataGrid));
                if (row == null || row.IsEditing) return;
        
                IsDragging = true;
                DraggedItem = row.Item;
            }
        
            private static void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
            {
                if (!IsDragging || IsEditing) return;
                
                dataGrid.Cursor = Cursors.Arrow;
                var targetItem = dataGrid.SelectedItem;
        
                if (targetItem == null || !ReferenceEquals(DraggedItem, targetItem))
                {
                    var targetIndex = ((dataGrid).ItemsSource as IList).IndexOf(targetItem);
                    ((dataGrid).ItemsSource as IList).Remove(DraggedItem);
                    ((dataGrid).ItemsSource as IList).Insert(targetIndex, DraggedItem);
                    dataGrid.SelectedItem = DraggedItem;
                }
        
                ResetDragDrop();
            }
        
            private static void ResetDragDrop()
            {
                IsDragging = false;
                popup.IsOpen = false;
                dataGrid.IsReadOnly = false;
            }
        
            private static void OnMouseMove(object sender, MouseEventArgs e)
            {
                if (!IsDragging || e.LeftButton != MouseButtonState.Pressed) return;
                
                if (dataGrid.Cursor != Cursors.SizeAll) dataGrid.Cursor = Cursors.SizeAll;
                
                popup.DataContext = DraggedItem;
                
                if (!popup.IsOpen)
                {
                    dataGrid.IsReadOnly = true;
                    popup.IsOpen = true;
                }
        
                Size popupSize = new Size(popup.ActualWidth, popup.ActualHeight);
                popup.PlacementRectangle = new Rect(e.GetPosition(dataGrid), popupSize);
        
                Point position = e.GetPosition(dataGrid);
                var row = UIHelpers.TryFindFromPoint<DataGridRow>(dataGrid, position);
                if (row != null) dataGrid.SelectedItem = row.Item;
            }
        }
        

        2、UI工具类

        public static class UIHelpers
        {
            // 查找父元素
            public static T TryFindParent<T>(DependencyObject child) where T : DependencyObject
            {
                DependencyObject parentObject = GetParentObject(child);
                if (parentObject == null) return null;
                
                T parent = parentObject as T;
                if (parent != null) return parent;
                else return TryFindParent<T>(parentObject);
            }
        
            public static DependencyObject GetParentObject(DependencyObject child)
            {
                if (child == null) return null;
                
                if (child is ContentElement contentElement)
                {
                    DependencyObject parent = ContentOperations.GetParent(contentElement);
                    if (parent != null) return parent;
        
                    if (contentElement is FrameworkContentElement fce) 
                        return fce.Parent;
                }
                
                return VisualTreeHelper.GetParent(child);
            }
        
            // 更新绑定源
            public static void UpdateBindingSources(DependencyObject obj, params DependencyProperty[] propepythonrties)
            {
                foreach (DependencyProperty depProperty in properties)
                {
                    BindingExpression be = BindingOperations.GetBindingExpression(obj, depProperty);
                    if (be != null) be.UpdateSource();
                }
        
                int count = VisualTreeHelper.GetChildrenCount(obj);
                for (int i = 0; i < count; i++)
                {
                    DependencyObject childObject = VisualTreeHelper.GetChild(obj, i);
                    UpdateBindingSources(childObject, properties);
                }
            }
        
            // 从指定点查找元素
            public static T TryFindFromPoint<T>(UIElement reference, Point point) where T : DependencyObject
            {
                DependencyObject element = reference.InputHitTest(point) as DependencyObject;
                if (element == null) return null;
                else if (element is T) return (T)element;
                else return TryFindParent<T>(element);
            }
        }
        

        3、XAML中使用

        <Grid Grid.Row="1" Margin="5">
            <!-- 拖拽提示Popup -->
            <Popup x:Name="popup1" AllowsTransparency="True" 
                   IsHitTestVisible="False" Placement="RelativePoint" 
                   PlacementTarget="{Binding ElementName=dataGrid1}">
                <Textblock Margin="8,0,0,0" VerticalAlignment="Center" 
                           FontSize="14" FontWeight="Bold" Text="Dragging..." />
            </Popup>
            
            <!-- 启用拖拽行为的DataGrid -->
            <DataGrid x:Name="dataGrid1" 
                      controlEx:DragDropRowBehavior.Enabled="True"
                      controlEx:DragDropRowBehavior.PopupControl="{Binding ElementName=popup1}"
                      AutoGenerateColumns="False" 
                      ItemsSource="{Binding ListItem}" 
                      RowHeaderWidth="60">
                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding Column1}" Header="column1" />
                    <DataGridTextColumn Binding="{Binding Column2}" Header="column2" />
                    <DataGridTextColumn Binding="{Binding Column3}" Header="column3" />
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
        

        实现原理

        1、拖拽开始:用户按下鼠标左键时,记录被拖拽的行(OnMouseLeftButtonDown

        2、拖拽过程:鼠标移动时显示提示Popup并更新位置(OnMouseMove

        3、拖拽结束:释放鼠标时交换行位置(OnMouseLeftButtonUp

        4、状态管理:使用IsEditingIsDragging标志管理编辑与拖拽状态

        5、UI辅助:通过UIHelpers类在可视化树中查找元素

        关键特性

        可视化反馈:拖拽时显示"Dragging..."提示

        编辑支持:编辑状态下自动禁用拖拽功能

        平滑交互:拖拽过程中光标自动切换为"SizeAll"样式

        行定位:自动定位鼠标悬停位置的行

        总结

        本文介绍了WPF DataGrid行拖拽功能的完整实现方案。该方案通过附加行为的方式扩展了DataGrid的功能,使其支持行拖拽操作。

        关键点包括:

        1、使用附加属性实现功能解耦

        2、通过Popup提供拖拽视觉反馈

        3、利用UIHelpers工具类简化可视化树操作

        4、正确处理编辑与拖拽的冲突

        该实现方案具有较好的可复用性,只需在DataGrid上设置附加属性即可启用拖拽功能,无需修改原有业务逻辑。

        以上就是WPF中实现DataGrid行拖拽功能的完整方案的详细内容,更多关于WPF DataGrid行拖拽功能的资料请关注编程客栈(www.devze.com)其它相关文章!

        0

        上一篇:

        下一篇:

        精彩评论

        暂无评论...
        验证码 换一张
        取 消

        最新开发

        开发排行榜