当前位置: 欣欣网 > 码农

WPF 实现 ListBox 拖动子项

2024-04-02码农

WPF 实现 ListBox 拖动子项

控件名:WPFListBoxItemDrag

作 者:WPFDevelopersOrg - 驚鏵

源码:如下

  • 框架支持 .NET4 至 .NET8

  • Visual Studio 2022 ;

  • XAML 部分:

    1)新增 MainWindow.xaml 代码如下:

  • Grid 定义两列。

  • 第一列 ListBox 控件,命名 ListBoxStart ,原数据被拖动者。

  • Canvas 画布,用于在拖动过程中呈献拖动项。

  • 第二列 ListBox 控件,命名 ListBoxEnd ,用于接收拖动者。

  • <wd:Window
    x: class="WPFListBoxItemDrag.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:WPFListBoxItemDrag"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers"
    Title="WPF开发者 - ListBoxItemDrag"
    Width="800"
    Height="450"
    WindowStartupLocation="CenterScreen"
    mc:Ignorable="d">

    <Grid>
    <Grid.ColumnDefinitions>
    <ColumnDefinition />
    <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <ListBox
    x:Name="ListBoxStart"
    AllowDrop="True"
    BorderThickness="1"
    ItemsSource="{Binding ItemsA}"
    PreviewMouseLeftButtonDown="ListBoxStart_PreviewMouseLeftButtonDown"
    PreviewMouseLeftButtonUp="ListBoxStart_PreviewMouseLeftButtonUp"
    PreviewMouseMove="ListBoxStart_PreviewMouseMove" />

    <Canvas
    x:Name="DragCanvas"
    Grid.ColumnSpan="2"
    Panel.ZIndex="1000" />

    <ListBox
    x:Name="ListBoxEnd"
    Grid.Column="1"
    AllowDrop="True"
    Drop="ListBoxEnd_Drop"
    ItemsSource="{Binding ItemsB}" />

    </Grid>
    </wd:Window>

    CSharp 部分:

    2)新增 MainWindow.xaml.cs 代码如下:

  • ItemsA ItemsB ObservableCollection<string> ,分别用于存储 ListBoxStart ListBoxEnd 中的项。

  • ListBoxStart_PreviewMouseLeftButtonDown 方法处理当在 ListBoxStart 按下鼠标左键时的 Item 数据,标记拖放操作的开始。

  • FindVisualParent 在可视树中查找元素。

  • GetListBoxItemData 获取选中项 ListBoxItem 的数据。

  • ListBoxStart_PreviewMouseLeftButtonUp 处理当在 ListBoxStart 释放鼠标左键的事件执行实际的拖放操作。

  • ListBoxStart_PreviewMouseMove 处理当在 ListBoxStart 移动鼠标时的事件在拖动过程中更新拖动的位置。

  • ListBoxEnd_Drop 处理当将 ListBoxStart 拖动项放到 ListBoxEnd 的事件,将拖动项添加到 ListBoxEnd 的数据源中。

  • using System.Collections.ObjectModel;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Media;
    namespaceWPFListBoxItemDrag
    {
    ///<summary>
    /// Interaction logic for MainWindow.xaml
    ///</summary>
    publicpartial classMainWindow
    {
    privatebool isDragging;
    private ListBoxItem item;
    private ListBoxItem dragItem;
    privateobject data;
    public ObservableCollection<string> ItemsA { getset; }
    public ObservableCollection<string> ItemsB { getset; }
    publicMainWindow()
    {
    InitializeComponent();
    DataContext = this;
    ItemsA = new() { "WPFDevelopersOrg""WPFDevelopers""WPF开发者""ListBox""ListBoxItem" };
    ItemsB = new ObservableCollection<string>();
    }
    privatevoidListBoxStart_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
    data = GetListBoxItemData(ListBoxStart, e.GetPosition(ListBoxStart));
    item = FindVisualParent<ListBoxItem>((DependencyObject)e.OriginalSource);
    if (item != null)
    isDragging = true;
    }
    private T FindVisualParent<T>(DependencyObject obj) where T : DependencyObject
    {
    while (obj != null)
    {
    if (obj is T)
    return (T)obj;
    obj = VisualTreeHelper.GetParent(obj);
    }
    returnnull;
    }
    privateobjectGetListBoxItemData(ListBox source, Point point)
    {
    var element = source.InputHitTest(point) as UIElement;
    if (element != null)
    {
    var data = DependencyProperty.UnsetValue;
    while (data == DependencyProperty.UnsetValue)
    {
    data = source.ItemContainerGenerator.ItemFromContainer(element);
    if (data == DependencyProperty.UnsetValue)
    element = VisualTreeHelper.GetParent(element) as UIElement;
    if (element == source)
    returnnull;
    }
    if (data != DependencyProperty.UnsetValue)
    return data;
    }
    returnnull;
    }
    privatevoidListBoxStart_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
    if (data != null)
    DragDrop.DoDragDrop(ListBoxStart, data, DragDropEffects.Move);
    isDragging = false;
    if (dragItem != null)
    {
    DragCanvas.Children.Remove(dragItem);
    dragItem = null;
    }
    }
    privatevoidListBoxStart_PreviewMouseMove(object sender, MouseEventArgs e)
    {
    if (isDragging)
    {
    if (dragItem == null)
    {
    dragItem = new ListBoxItem
    {
    Content = item.Content,
    Width = item.ActualWidth,
    Height = item.ActualHeight,
    Background = Brushes.Gray,
    ContentTemplate = item.ContentTemplate,
    ContentTemplateSelector = item.ContentTemplateSelector,
    style = item. style,
    Padding = item.Padding,
    Opacity = .5,
    IsHitTestVisible = false,
    };
    DragCanvas.Children.Add(dragItem);
    }
    var mousePos = e.GetPosition(DragCanvas);
    Canvas.SetLeft(dragItem, mousePos.X - dragItem.ActualWidth / 2);
    Canvas.SetTop(dragItem, mousePos.Y - dragItem.ActualHeight / 2);
    }
    }
    privatevoidListBoxEnd_Drop(object sender, DragEventArgs e)
    {
    if (e.Data.GetDataPresent(typeof(string)))
    {
    var data = e.Data.GetData(typeof(string)).ToString();
    ItemsB.Add(data);
    ItemsA.Remove(data.ToString());
    }
    }
    }
    }