WPF 的ListBox不支持很多常见的用户习惯,如在Explorer中用鼠标可以选择多项Item,并且点击已经选择的Item,按住鼠标左键可以将所有已选择Item拖拽到指定的位置。本文简单的实现了这一功能。

效果图:

拖拽1个Item

拖拽多个Item

说明:

代码下载地址:http://download.csdn.net/download/u012566751/6452323

代码中使用了两个类:

1.DragDropAdorner,用于拖拽过程中显示预览图,代码来自CSDN

2.ListBoxSelectionHelper,用于通过鼠标拖拽框选ListBoxItem,代码来自Codeproject,作者略作修改

具体操作

1.创建一个WPF工程,WpfDragMultiSelect,主界面代码如下:

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="5"/>
<RowDefinition Height="*"/>
<RowDefinition Height="5"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="5"/>
</Grid.ColumnDefinitions>
<Grid Grid.Row="1" Grid.Column="1" Background="Black"/>
<GridSplitter Grid.Row="1"
Grid.Column="2"
ShowsPreview="True"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
<Grid Grid.Row="1" Grid.Column="3" Background="Gray">
</Grid>
</Grid>

2.创建一个列表,使用数据绑定方式,数据类如下:

class ListData
{
public int Number
{
get;
set;
} public ListData(int nNum)
{
Number = nNum;
}
}

数据类只有一个公共属性Number,类型为int

主类添加代码:

List<ListData> _list = new List<ListData>();
AdornerLayer mAdornerLayer = null;
bool bIsDraging = false; public MainWindow()
{
InitializeComponent(); for (int n = ; n < ; n++ )
{
this._list.Add(new ListData(n));
} this.DataContext = _list; this.list.AllowDrop = true; this.list.QueryContinueDrag += delegate(object sender, QueryContinueDragEventArgs e)
{
//_adornerLayer.Update();
//this.list.Cursor = Cursors.Arrow;
mAdornerLayer.Update(); };
}

_list作为列表数据源

bIsDraging表示数据拖拽状态

列表数据模板如下:

<DataTemplate x:Key="dt_Rectangle">
<Grid Margin="10" >
<Rectangle Width="50"
Height="50"
Fill="LightBlue" RadiusX="3" RadiusY="3" />
<TextBlock Text="{Binding Path=Number}"
Foreground="White"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
<Rectangle Width="50"
Height="50"
Fill="Transparent"
PreviewMouseDown="Rectangle_PreviewMouseDown" PreviewMouseMove="Rectangle_PreviewMouseMove" PreviewMouseUp="Rectangle_PreviewMouseUp" />
</Grid>
</DataTemplate>

每个列表显示为一个亮蓝色(LightBlue)的正方形,在每个正方形中显示该项绑定ListData对象的Number属性。

最后一个Rectangle专门用于响应鼠标事件

在主界面中添加列表:

<ListBox x:Name="list"
Background="Transparent"
ItemsSource="{Binding}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" loc:ListBoxSelectionHelper.MultiSelect="True"
loc:ListBoxSelectionHelper.PreviewDrag="True" PreviewDragEnter="list_PreviewDragEnter">
<ListBox.ItemTemplate>
<DynamicResource ResourceKey="dt_Rectangle"/>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

代码:loc:ListBoxSelectionHelper.MultiSelect="True"  loc:ListBoxSelectionHelper.PreviewDrag="True" ,使用ListBoxSelectionHelper类实现鼠标拖拽框选功能

3.添加一个Grid,用于在拖拽过程中显示预览

<!--拖拽预览-->
<Grid Width="100"
Height="100">
<Grid x:Name="gridAdorner" Visibility="Hidden">
<Rectangle Width="50"
Height="50"
Fill="LightGray"/>
<TextBlock x:Name="textAdorner"
Text="0"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Foreground="Red"
FontSize="20">
</TextBlock>
</Grid>
</Grid>

4.实现数据模板dt_Rectangle中声明的事件,用以支持拖拽功能

private void Rectangle_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var rectangle = (FrameworkElement)sender;
var rectangleViewModel = (ListData)rectangle.DataContext;
ListBoxItem lstitem = this.list.ItemContainerGenerator.ContainerFromItem(rectangleViewModel) as ListBoxItem;
if (lstitem.IsSelected == true)
{ bIsDraging = true;
e.Handled = true;
}
else
{
bIsDraging = false;
}
} private void Rectangle_PreviewMouseMove(object sender, MouseEventArgs e)
{
if(bIsDraging)
{
if (Mouse.LeftButton == MouseButtonState.Pressed)
{ if (this.list.SelectedItems.Count>)
{
//ListBoxItem lstitem = this.list.ItemContainerGenerator.ContainerFromItem(pr) as ListBoxItem; //更新数据
//MAx 2013-10-23 16:19:44
this.textAdorner.Text = this.list.SelectedItems.Count.ToString();
this.gridAdorner.Visibility = Visibility.Visible; DragDropAdorner adorner = new DragDropAdorner(this.gridAdorner);
mAdornerLayer = AdornerLayer.GetAdornerLayer(this.list); // Window class do not have AdornerLayer
mAdornerLayer.Add(adorner); this.list.Cursor = Cursors.Arrow;
string[] files = new string[]; DragDrop.DoDragDrop(list, new DataObject(DataFormats.FileDrop, files), DragDropEffects.Copy | DragDropEffects.Move /* | DragDropEffects.Link */); //DataObject dataObject = new DataObject(files);
//System.Windows.DragDrop.DoDragDrop(this.list, dataObject, DragDropEffects.Copy); mAdornerLayer.Remove(adorner);
mAdornerLayer = null;
this.gridAdorner.Visibility = Visibility.Hidden;
}
}
}
} private void Rectangle_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
bIsDraging = false;
}

分别响应PreviewMouseDown,PreviewMouseMove,PreviewMouseUp三个事件
PreviewMouseDown:判断Item是否选为已选择的Item,如果是则进入拖拽状态,标记bIsDraging,并且截断事件e.Handled = true;如果不截断,ListBox会响应点击事件,默认选择当前Item

Rectangle_PreviewMouseMove:如果当前为拖拽状态且鼠标左键按下,则开始拖拽。将gridAdorner作为拖拽预览

Rectangle_PreviewMouseUp:关闭拖拽状态

至此,已经完全实现拖拽多个Item功能。

WPF MultiSelect模式下ListBox 实现多个ListBoxItem拖拽的更多相关文章

  1. wpf mvvm模式下CommandParameter传递多参

    原文:wpf mvvm模式下CommandParameter传递多参 CommandParameter一般只允许设置一次,所以如果要传递多参数,就要稍微处理一下.我暂时还没找到更好的方案,下面介绍的这 ...

  2. 告诉你吧,一套皮肤在winform与wpf开发模式下实现的界面效果同样精彩,winform界面和wpf界面。

    一.同一资源: 二.先上软件界面: (1)wpf界面: 在wpf中实现这样类似web风格的软件界面就不用我多说了,在wpf实现这样的风格是很简单的,完全像网页设计一样的. (2)winform界面 在 ...

  3. WPF MVVM模式下的无阻塞刷新探讨

    很多时候我们需要做一个工作,在一个方法体里面,读取大数据绑定到UI界面,由于长时间的读取,读取独占了线程域,导致界面一直处于假死状态.例如,当应用程序开始读取Web资源时,读取的时效是由网络链路的速度 ...

  4. WPF MVVM模式下ComboBox级联效果 选择第一项

    MVVM模式下做的省市区的级联效果.通过改变ComboBox执行命令改变市,区. 解决主要问题就是默认选中第一项 1.首先要定义一个属性,继承自INotifyPropertyChanged接口.我这里 ...

  5. wpf图片查看器,支持鼠标滚动缩放拖拽

    最近项目需要,要用到一个图片查看器,类似于windows自带的图片查看器那样,鼠标滚动可以缩放,可以拖拽图片,于是就写了这个简单的图片查看器. 前台代码: <Window x:Class=&qu ...

  6. WPF MVVM模式下路由事件

    一,路由事件下三种路由策略: 1 冒泡:由事件源向上传递一直到根元素.2直接:只有事件源才有机会响应事件.3隧道:从元素树的根部调用事件处理程序并依次向下深入直到事件源.一般情况下,WPF提供的输入事 ...

  7. WPF MVVM模式下实现ListView下拉显示更多内容

    在手机App中,如果有一个展示信息的列表,通常会展示很少一部分,当用户滑动到列表底部时,再加载更多内容.这样有两个好处,提高程序性能,减少网络流量.这篇博客中,将介绍如何在WPF ListView中实 ...

  8. wpf mvvm模式下的image绑定

    view文件 <Image Grid.Column="2" Width="48" Height="64" Stretch=" ...

  9. wpf mvvm模式下 在ViewModel关闭view

    本文只是博主用来记录笔记,误喷 使用到到了MVVM中消息通知功能 第一步:在需要关闭窗体中注册消息 public UserView() { this.DataContext = new UserVie ...

随机推荐

  1. java中关于length的真确理解~~~~有补充的请跟帖~~~

    java中的length到底是个什么东西呢?有人说它是一个类,一个方法,如果是方法,它没有括号, 我只知道它可以得出数组的长度,但是今天遇到一段代码,怎么都解释不通,那就是绝对不会把循环的次数与数组的 ...

  2. juc线程池原理(一):总体介绍

    概要 线程池类图 线程池的类图如下: 1. Executor 它是"执行者"接口,它是来执行任务的.准确的说,Executor提供了execute()接口来执行已提交的 Runna ...

  3. 各种Java项目环境搭建-文档引用汇总记录

    springmvc环境搭建 1.如何用Maven创建web项目(具体步骤) 2.springmvc环境搭建,一步一步超简单

  4. 第五章 深入class文件结构

    一次编译好的class文件是如何到处运行的 5.1 JVM指令集简介 5.1.1 与类相关的指令 5.1.2 方法的定义 5.1.3 属性的定义 5.1.4 其他指令集 5.2 class文件头的表示 ...

  5. python学习(二) 列表和元组

    第2章  列表和元组 2.1 序列概论 python有六种内建的序列,本章讨论最常用的两种类型:列表和元组.其他的内建序列有字符串.Unicode字符串.buffer对象和xragne对象. 列表和元 ...

  6. linux输入子系统概念介绍

    在此文章之前,我们讲解的都是简单的字符驱动,涉及的内容有字符驱动的框架.自动创建设备节点.linux中断.poll机制.异步通知.同步互斥.非阻塞.定时器去抖动. 上一节文章链接:http://blo ...

  7. JavaScript笔记——事件

    事件一般是用于浏览器和用户操作进行交互.最早是 IE 和 Netscape Navigator 中出现, 作为分担服务器端运算负载的一种手段.直到几乎所有的浏览器都支持事件处理.而 DOM2 级规范开 ...

  8. python's twenty-third day for me 面向对象进阶

    普通方法:对象和类绑定的过程. class A: def func1(self):pass def func2(self):pass def func3(self):pass def func4(se ...

  9. Python技巧(一)

    一  if..else的多种写法 a, b, c = 1, 2, 3 1.常规 if a > b: c = a else: c = b 2.表达式 c = a if a > b else ...

  10. ffmpeg-URL(转)

    ffmpeg中为方便对资源进行访问,定义了两个结构体,URLContext中是对具体资源文件进行操作的上下文,URLProtocol则是在将资源进行分类的基础上,对某一类资源操作的函数集,熟悉Linu ...