欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

WPF中實(shí)現(xiàn)DataGrid行拖拽功能的完整方案

 更新時(shí)間:2025年06月11日 09:09:28   作者:小碼編匠  
在WPF應(yīng)用開發(fā)中,DataGrid是一個(gè)常用的數(shù)據(jù)展示控件,然而,原生的DataGrid并不支持行拖拽功能,這在需要調(diào)整行順序的場(chǎng)景中顯得尤為不便,所以本文將介紹如何實(shí)現(xiàn)一個(gè)優(yōu)雅的DataGrid行拖拽功能,需要的朋友可以參考下

前言

在WPF應(yīng)用開發(fā)中,DataGrid是一個(gè)常用的數(shù)據(jù)展示控件。然而,原生的DataGrid并不支持行拖拽功能,這在需要調(diào)整行順序的場(chǎng)景中顯得尤為不便。

本文將介紹如何實(shí)現(xiàn)一個(gè)優(yōu)雅的DataGrid行拖拽功能,使用戶可以通過(guò)拖拽操作輕松調(diào)整行順序。該方案基于開源項(xiàng)目實(shí)現(xiàn),使用MIT協(xié)議的HandyControl樣式庫(kù),具有良好的可擴(kuò)展性和實(shí)用性。

實(shí)現(xiàn)效果

核心實(shí)現(xiàn)

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 DependencyProperty 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.RegisterAttached("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, DependencyPropertyChangedEventArgs 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[] properties)
    {
        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);
        }
    }

    // 從指定點(diǎn)查找元素
    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>

實(shí)現(xiàn)原理

1、拖拽開始:用戶按下鼠標(biāo)左鍵時(shí),記錄被拖拽的行(OnMouseLeftButtonDown

2、拖拽過(guò)程:鼠標(biāo)移動(dòng)時(shí)顯示提示Popup并更新位置(OnMouseMove

3、拖拽結(jié)束:釋放鼠標(biāo)時(shí)交換行位置(OnMouseLeftButtonUp

4、狀態(tài)管理:使用IsEditingIsDragging標(biāo)志管理編輯與拖拽狀態(tài)

5、UI輔助:通過(guò)UIHelpers類在可視化樹中查找元素

關(guān)鍵特性

可視化反饋:拖拽時(shí)顯示"Dragging..."提示

編輯支持:編輯狀態(tài)下自動(dòng)禁用拖拽功能

平滑交互:拖拽過(guò)程中光標(biāo)自動(dòng)切換為"SizeAll"樣式

行定位:自動(dòng)定位鼠標(biāo)懸停位置的行

總結(jié)

本文介紹了WPF DataGrid行拖拽功能的完整實(shí)現(xiàn)方案。該方案通過(guò)附加行為的方式擴(kuò)展了DataGrid的功能,使其支持行拖拽操作。

關(guān)鍵點(diǎn)包括:

1、使用附加屬性實(shí)現(xiàn)功能解耦

2、通過(guò)Popup提供拖拽視覺(jué)反饋

3、利用UIHelpers工具類簡(jiǎn)化可視化樹操作

4、正確處理編輯與拖拽的沖突

該實(shí)現(xiàn)方案具有較好的可復(fù)用性,只需在DataGrid上設(shè)置附加屬性即可啟用拖拽功能,無(wú)需修改原有業(yè)務(wù)邏輯。

以上就是WPF中實(shí)現(xiàn)DataGrid行拖拽功能的完整方案的詳細(xì)內(nèi)容,更多關(guān)于WPF DataGrid行拖拽功能的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C#數(shù)據(jù)庫(kù)操作的示例詳解

    C#數(shù)據(jù)庫(kù)操作的示例詳解

    這篇文章主要通過(guò)一些示例為大家詳細(xì)介紹了C#中數(shù)據(jù)庫(kù)操作用法,文中的示例代碼講解詳細(xì),具有有一定的借鑒價(jià)值,需要的可以參考一下
    2022-07-07
  • C#中重載重寫和覆蓋的定義與區(qū)別

    C#中重載重寫和覆蓋的定義與區(qū)別

    今天小編就為大家分享一篇關(guān)于C#中重載重寫和覆蓋的定義與區(qū)別,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-10-10
  • C#實(shí)現(xiàn)給Word每一頁(yè)設(shè)置不同文字水印的方法詳解

    C#實(shí)現(xiàn)給Word每一頁(yè)設(shè)置不同文字水印的方法詳解

    Word中設(shè)置水印時(shí),可使用預(yù)設(shè)的文字或自定義文字設(shè)置為水印效果,但通常添加水印效果時(shí),會(huì)對(duì)所有頁(yè)面都設(shè)置成統(tǒng)一效果。本文以C#?代碼為例,對(duì)Word每一頁(yè)設(shè)置不同的文字水印效果作詳細(xì)介紹,感興趣的可以了解一下
    2022-07-07
  • .NET/C#實(shí)現(xiàn)識(shí)別用戶訪問(wèn)設(shè)備的方法

    .NET/C#實(shí)現(xiàn)識(shí)別用戶訪問(wèn)設(shè)備的方法

    這篇文章主要介紹了.NET/C#實(shí)現(xiàn)識(shí)別用戶訪問(wèn)設(shè)備的方法,結(jié)合實(shí)例形式分析了C#識(shí)別用戶訪問(wèn)設(shè)備的操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2017-02-02
  • C#使用Socket實(shí)現(xiàn)分布式事件總線的示例代碼

    C#使用Socket實(shí)現(xiàn)分布式事件總線的示例代碼

    這篇文章主要介紹了C#使用Socket實(shí)現(xiàn)分布式事件總線,文中通過(guò)代碼示例給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-10-10
  • C# 下載文件 刪除文件 寫入文本的實(shí)例

    C# 下載文件 刪除文件 寫入文本的實(shí)例

    下面小編就為大家分享一篇C# 下載文件 刪除文件 寫入文本的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • C#中使用FilleStream實(shí)現(xiàn)視頻文件的復(fù)制功能

    C#中使用FilleStream實(shí)現(xiàn)視頻文件的復(fù)制功能

    這篇文章主要介紹了C#中使用FilleStream實(shí)現(xiàn)視頻文件的復(fù)制功能,本文通過(guò)實(shí)例代碼給大家介紹的非常想詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-09-09
  • 基于多態(tài)之虛方法、抽象類、接口詳解

    基于多態(tài)之虛方法、抽象類、接口詳解

    下面小編就為大家分享一篇基于多態(tài)之虛方法、抽象類、接口詳解,具有很好的參考價(jià)值。希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • C#?窗口過(guò)程消息處理?WndProc的方法詳解

    C#?窗口過(guò)程消息處理?WndProc的方法詳解

    在WinForm中一般采用重寫WndProc的方法對(duì)窗口或控件接受到的指定消息進(jìn)行處理,本文給大家介紹C#窗口過(guò)程消息處理WndProc的方法詳解,感興趣的朋友一起看看吧
    2025-04-04
  • C#用timer實(shí)現(xiàn)背單詞小程序

    C#用timer實(shí)現(xiàn)背單詞小程序

    這篇文章主要為大家詳細(xì)介紹了C#用timer實(shí)現(xiàn)背單詞小程序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-07-07

最新評(píng)論