• WPF DataGrid行拖拽


    效果

    image

    我在GitHub开源地址:https://github.com/adsf130162/WPF-DataGrdDragRow
    使用MIT开源HandyControl样式库:https://github.com/HandyOrg/HandyControl

    首先定义一个行为

    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; }
            }
    
            public static Popup GetPopupControl(DependencyObject obj)
            {
                return (Popup)obj.GetValue(PopupControlProperty);
            }
    
            public static void SetPopupControl(DependencyObject obj, Popup value)
            {
                obj.SetValue(PopupControlProperty, value);
            }
    
            // Using a DependencyProperty as the backing store for PopupControl.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty PopupControlProperty =
                DependencyProperty.RegisterAttached("PopupControl", typeof(Popup), typeof(DragDropRowBehavior), new UIPropertyMetadata(null, OnPopupControlChanged));
    
            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;
                // Check if DataGrid
                if (dataGrid == null)
                    return;
    
    
                if (enable && popup != null)
                {
                    dataGrid.BeginningEdit += new EventHandler<DataGridBeginningEditEventArgs>(OnBeginEdit);
                    dataGrid.CellEditEnding += new EventHandler<DataGridCellEditEndingEventArgs>(OnEndEdit);
                    dataGrid.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(OnMouseLeftButtonUp);
                    dataGrid.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(OnMouseLeftButtonDown);
                    dataGrid.MouseMove += new MouseEventHandler(OnMouseMove);
                }
                else
                {
                    dataGrid.BeginningEdit -= new EventHandler<DataGridBeginningEditEventArgs>(OnBeginEdit);
                    dataGrid.CellEditEnding -= new EventHandler<DataGridCellEditEndingEventArgs>(OnEndEdit);
                    dataGrid.MouseLeftButtonUp -= new System.Windows.Input.MouseButtonEventHandler(OnMouseLeftButtonUp);
                    dataGrid.MouseLeftButtonDown -= new MouseButtonEventHandler(OnMouseLeftButtonDown);
                    dataGrid.MouseMove -= new MouseEventHandler(OnMouseMove);
    
                    dataGrid = null;
                    popup = null;
                    draggedItem = null;
                    IsEditing = false;
                    IsDragging = false;
                }
            }
    
            public static bool GetEnabled(DependencyObject obj)
            {
                return (bool)obj.GetValue(EnabledProperty);
            }
    
            public static void SetEnabled(DependencyObject obj, bool value)
            {
                obj.SetValue(EnabledProperty, value);
            }
    
            // Using a DependencyProperty as the backing store for Enabled.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty EnabledProperty =
                DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(DragDropRowBehavior), new UIPropertyMetadata(false, OnEnabledChanged));
    
            private static void OnEnabledChanged(DependencyObject depObject, DependencyPropertyChangedEventArgs e)
            {
                //Check if value is a Boolean Type
                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;
                //in case we are in the middle of a drag/drop operation, cancel it...
                if (IsDragging) ResetDragDrop();
            }
    
            private static void OnEndEdit(object sender, DataGridCellEditEndingEventArgs e)
            {
                IsEditing = false;
            }
    
    
            /// <summary>
            /// Initiates a drag action if the grid is not in edit mode.
            /// </summary>
            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;
    
                //set flag that indicates we're capturing mouse movements
                IsDragging = true;
                DraggedItem = row.Item;
            }
    
            /// <summary>
            /// Completes a drag/drop operation.
            /// </summary>
            private static void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
            {
                if (!IsDragging || IsEditing)
                {
                    return;
                }
                dataGrid.Cursor = Cursors.Arrow;
    
                //get the target item
                var targetItem = dataGrid.SelectedItem;
    
                if (targetItem == null || !ReferenceEquals(DraggedItem, targetItem))
                {
                    //get target index
                    var targetIndex = ((dataGrid).ItemsSource as IList).IndexOf(targetItem);
    
                    //remove the source from the list
                    ((dataGrid).ItemsSource as IList).Remove(DraggedItem);
    
                    //move source at the target's location
                    ((dataGrid).ItemsSource as IList).Insert(targetIndex, DraggedItem);
    
                    //select the dropped item
                    dataGrid.SelectedItem = DraggedItem;
                }
    
                //reset
                ResetDragDrop();
            }
    
            /// <summary>
            /// Closes the popup and resets the
            /// grid to read-enabled mode.
            /// </summary>
            private static void ResetDragDrop()
            {
                IsDragging = false;
                popup.IsOpen = false;
                dataGrid.IsReadOnly = false;
            }
    
            /// <summary>
            /// Updates the popup's position in case of a drag/drop operation.
            /// </summary>
            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;
                //display the popup if it hasn't been opened yet
                if (!popup.IsOpen)
                {
                    //switch to read-only mode
                    dataGrid.IsReadOnly = true;
    
                    //make sure the popup is visible
                    popup.IsOpen = true;
                }
    
    
                Size popupSize = new Size(popup.ActualWidth, popup.ActualHeight);
                popup.PlacementRectangle = new Rect(e.GetPosition(dataGrid), popupSize);
    
                //make sure the row under the grid is being selected
                Point position = e.GetPosition(dataGrid);
                var row = UIHelpers.TryFindFromPoint<DataGridRow>(dataGrid, position);
                if (row != null) dataGrid.SelectedItem = row.Item;
            }
    
        }
    

    供行为使用的UI工具类

    public static class UIHelpers
        {
            #region find parent
    
            /// <summary>
            /// Finds a parent of a given item on the visual tree.
            /// </summary>
            /// <typeparam name="T">The type of the queried item.</typeparam>
            /// <param name="child">A direct or indirect child of the
            /// queried item.</param>
            /// <returns>The first parent item that matches the submitted
            /// type parameter. If not matching item can be found, a null
            /// reference is being returned.</returns>
            public static T TryFindParent<T>(DependencyObject child)
              where T : DependencyObject
            {
                //get parent item
                DependencyObject parentObject = GetParentObject(child);
    
                //we've reached the end of the tree
                if (parentObject == null) return null;
    
                //check if the parent matches the type we're looking for
                T parent = parentObject as T;
                if (parent != null)
                {
                    return parent;
                }
                else
                {
                    //use recursion to proceed with next level
                    return TryFindParent<T>(parentObject);
                }
            }
    
    
            /// <summary>
            /// This method is an alternative to WPF's
            /// <see cref="VisualTreeHelper.GetParent"/> method, which also
            /// supports content elements. Do note, that for content element,
            /// this method falls back to the logical tree of the element.
            /// </summary>
            /// <param name="child">The item to be processed.</param>
            /// <returns>The submitted item's parent, if available. Otherwise
            /// null.</returns>
            public static DependencyObject GetParentObject(DependencyObject child)
            {
                if (child == null) return null;
                ContentElement contentElement = child as ContentElement;
    
                if (contentElement != null)
                {
                    DependencyObject parent = ContentOperations.GetParent(contentElement);
                    if (parent != null) return parent;
    
                    FrameworkContentElement fce = contentElement as FrameworkContentElement;
                    return fce != null ? fce.Parent : null;
                }
    
                //if it's not a ContentElement, rely on VisualTreeHelper
                return VisualTreeHelper.GetParent(child);
            }
    
            #endregion
    
    
            #region update binding sources
    
            /// <summary>
            /// Recursively processes a given dependency object and all its
            /// children, and updates sources of all objects that use a
            /// binding expression on a given property.
            /// </summary>
            /// <param name="obj">The dependency object that marks a starting
            /// point. This could be a dialog window or a panel control that
            /// hosts bound controls.</param>
            /// <param name="properties">The properties to be updated if
            /// <paramref name="obj"/> or one of its childs provide it along
            /// with a binding expression.</param>
            public static void UpdateBindingSources(DependencyObject obj,
                                      params DependencyProperty[] properties)
            {
                foreach (DependencyProperty depProperty in properties)
                {
                    //check whether the submitted object provides a bound property
                    //that matches the property parameters
                    BindingExpression be = BindingOperations.GetBindingExpression(obj, depProperty);
                    if (be != null) be.UpdateSource();
                }
    
                int count = VisualTreeHelper.GetChildrenCount(obj);
                for (int i = 0; i < count; i++)
                {
                    //process child items recursively
                    DependencyObject childObject = VisualTreeHelper.GetChild(obj, i);
                    UpdateBindingSources(childObject, properties);
                }
            }
    
            #endregion
    
    
            /// <summary>
            /// Tries to locate a given item within the visual tree,
            /// starting with the dependency object at a given position. 
            /// </summary>
            /// <typeparam name="T">The type of the element to be found
            /// on the visual tree of the element at the given location.</typeparam>
            /// <param name="reference">The main element which is used to perform
            /// hit testing.</param>
            /// <param name="point">The position to be evaluated on the origin.</param>
            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);
            }
        }
    

    XAML中加入命名空间,后使用

    <Grid Grid.Row="1" Margin="5">
                <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 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>
    
  • 相关阅读:
    4年Java程序员十面阿里终拿下offer,评级P6+年薪30-40w无股票
    真香警告!手绘172张图解HTTP协议+703页TCP/IP协议笔记
    Git官方和创始人都推荐的Git权威指南,广度深度和实战性史无前例
    阿里“教授”总结整理手写大型网站技术架构:核心原理与案例分析
    GitHub上120K Stars国内第一的Java多线程PDF到底有什么魅力?
    霸榜GitHub必读书籍:编写高质量代码改善Java程序员的151个建议
    GitHub上260K Stars的P8架构师纯手写的Java高并发编程详解
    LeetCode每日一题:802 找到最终安全状态
    LeetCode每日一题:662二叉树最大宽度
    Springboot之security框架 登录安全验证授权流程
  • 原文地址:https://www.cnblogs.com/Stay627/p/15023454.html
Copyright © 2020-2023  润新知