65.9K
CodeProject 正在变化。 阅读更多。
Home

C# WPF ListView 拖放自定义项

2018年3月26日

CPOL

2分钟阅读

viewsIcon

42207

本文档解释了如何在 WPF 技术中,在 ListView 控件内实现自定义项目的拖放功能。

引言

本文档将逐步展示如何在 ListView 对象中实现拖放功能,以改变当前选中项目的呈现顺序。行项目使用自定义类定义。还将展示如何使用两个按钮移动项目。

图 1 - “行 3” 已从最后一个位置移动到第一个位置

Using the Code

创建一个新项目,并在定义主窗口的 XAML 代码编辑器中插入此代码,以添加一对按钮和一个 ListView 对象。在 ListView 对象中,重要的是设置此属性 AllowDrop = "True" 否则拖放功能将不会启用。

<Button x:Name="btnUp" Content="Move Up" HorizontalAlignment="Left" Height="28" 
Margin="10,12,0,0" VerticalAlignment="Top" Width="68" Click="btnUp_Click"/>
<Button x:Name="btnDown" Content="Move Dn" HorizontalAlignment="Left" Height="28" 
Margin="83,12,0,0" VerticalAlignment="Top" Width="68" Click="btnDown_Click"/>
<ListView Margin="10,50,10,10" Name="lstView" BorderBrush="WhiteSmoke" 
AllowDrop="True" PreviewMouseLeftButtonDown="lstView_PreviewMouseLeftButtonDown" 
MouseMove="lstView_MouseMove" DragEnter="lstView_DragEnter" Drop="lstView_Drop">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Sel." Width="32">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <CheckBox IsChecked="{Binding IsSelected}"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
            <GridViewColumn Header="Title" Width="120" DisplayMemberBinding="{Binding Title}" />
            <GridViewColumn Header="Note" Width="150" DisplayMemberBinding="{Binding Note}" />
        </GridView>
    </ListView.View>
</ListView>
代码 1 - 此 XAML 代码必须插入在 <Grid></Grid> 数据块之间。

创建一个新类并添加以下代码

public class WorkItem
{
    public bool IsSelected { get; set; }
    public string Title { get; set; }
    public string Note { get; set; }

    public WorkItem(bool isSelected, string title, string note)
    {
        this.IsSelected = isSelected;
        this.Title = title;
        this.Note = note;
    }
}

这个类是代表 ListView 对象的数据项的模型。要显示你的自定义项目,你必须修改 WorkItem 类的属性和 XAML 代码,以定义你的数据列以及正确的数据绑定到你的自定义类。

打开主窗口的代码编辑器,并在 using 部分添加以下代码

using System.Collections.ObjectModel;       // ObservableCollection class

项目列表将通过 ObservableCollection 对象进行管理,因为该对象暴露了使在集合中移动项目更容易的方法。

在窗口类级别添加以下 private 变量。

private Point startPoint = new Point();
private ObservableCollection<WorkItem> Items = new ObservableCollection<WorkItem>();
private int startIndex = -1;

为了在 ListView 控件中插入测试数据,请从类构造函数中调用 InitializeListView(); 函数,例如在调用标准函数 InitializeComponent(); 之后。

private void InitializeListView()
{
    // Clear data
    lstView.Items.Clear();
    Items.Clear();
    // Add rows
    Items.Add(new WorkItem(true, "Row 1", "First orw"));
    Items.Add(new WorkItem(false, "Row 2", "Second row"));
    Items.Add(new WorkItem(true, "Row 3", "Third row"));
    lstView.ItemsSource = Items;
}

添加以下代码以实现与屏幕对象关联的事件。使用 btnUpbtnDown 按钮,你可以移动 ListView 控件中的选中项目,而无需使用拖放功能。

private void lstView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    // Get current mouse position
    startPoint = e.GetPosition(null);
}

// Helper to search up the VisualTree
private static T FindAnchestor<T>(DependencyObject current)
    where T : DependencyObject
{
    do
    {
        if (current is T)
        {
            return (T)current;
        }
        current = VisualTreeHelper.GetParent(current);
    }
    while (current != null);
    return null;
}

private void lstView_MouseMove(object sender, MouseEventArgs e)
{
    // Get the current mouse position
    Point mousePos = e.GetPosition(null);
    Vector diff = startPoint - mousePos;

    if (e.LeftButton == MouseButtonState.Pressed && 
        (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance || 
               Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
    {
        // Get the dragged ListViewItem
        ListView listView = sender as ListView;
        ListViewItem listViewItem = FindAnchestor<ListViewItem>((DependencyObject)e.OriginalSource);
        if (listViewItem == null) return;           // Abort
        // Find the data behind the ListViewItem
        WorkItem item = (WorkItem)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
        if (item == null) return;                   // Abort
        // Initialize the drag & drop operation
        startIndex = lstView.SelectedIndex;
        DataObject dragData = new DataObject("WorkItem", item);
        DragDrop.DoDragDrop(listViewItem, dragData, DragDropEffects.Copy | DragDropEffects.Move);
    }
}

private void lstView_DragEnter(object sender, DragEventArgs e)
{
    if (!e.Data.GetDataPresent("WorkItem") || sender != e.Source)
    {
        e.Effects = DragDropEffects.None;
    }
}

private void lstView_Drop(object sender, DragEventArgs e)
{
    int index = -1;

    if (e.Data.GetDataPresent("WorkItem") && sender == e.Source)
    {
        // Get the drop ListViewItem destination
        ListView listView = sender as ListView;
        ListViewItem listViewItem = FindAnchestor<ListViewItem>((DependencyObject)e.OriginalSource);
        if (listViewItem == null)
        {
            // Abort
            e.Effects = DragDropEffects.None;
            return;
        }
        // Find the data behind the ListViewItem
        WorkItem item = (WorkItem)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
        // Move item into observable collection 
        // (this will be automatically reflected to lstView.ItemsSource)
        e.Effects = DragDropEffects.Move;
        index = Items.IndexOf(item);
        if (startIndex >=0 && index >= 0)
        {
            Items.Move(startIndex, index);
        }
        startIndex = -1;        // Done!
    }
}

private void btnUp_Click(object sender, RoutedEventArgs e)
{            
    WorkItem item = null;
    int index = -1;

    if (lstView.SelectedItems.Count != 1) return;
    item = (WorkItem)lstView.SelectedItems[0];
    index = Items.IndexOf(item);
    if (index > 0)
    {
        Items.Move(index, index - 1);
    }
}

private void btnDown_Click(object sender, RoutedEventArgs e)
{
    WorkItem item = null;
    int index = -1;

    if (lstView.SelectedItems.Count != 1) return;
    item = (WorkItem)lstView.SelectedItems[0];
    index = Items.IndexOf(item);
    if (index < Items.Count - 1)
    {
        Items.Move(index, index + 1);
    }
}

就这样了!感谢您抽出时间阅读本文。如果您觉得它有用,请对其进行评分。

历史

  • 版本 1.0.0 - 2018/03/26
© . All rights reserved.