WPF MultiSelect模式下ListBox 实现多个ListBoxItem拖拽
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拖拽的更多相关文章
- wpf mvvm模式下CommandParameter传递多参
原文:wpf mvvm模式下CommandParameter传递多参 CommandParameter一般只允许设置一次,所以如果要传递多参数,就要稍微处理一下.我暂时还没找到更好的方案,下面介绍的这 ...
- 告诉你吧,一套皮肤在winform与wpf开发模式下实现的界面效果同样精彩,winform界面和wpf界面。
一.同一资源: 二.先上软件界面: (1)wpf界面: 在wpf中实现这样类似web风格的软件界面就不用我多说了,在wpf实现这样的风格是很简单的,完全像网页设计一样的. (2)winform界面 在 ...
- WPF MVVM模式下的无阻塞刷新探讨
很多时候我们需要做一个工作,在一个方法体里面,读取大数据绑定到UI界面,由于长时间的读取,读取独占了线程域,导致界面一直处于假死状态.例如,当应用程序开始读取Web资源时,读取的时效是由网络链路的速度 ...
- WPF MVVM模式下ComboBox级联效果 选择第一项
MVVM模式下做的省市区的级联效果.通过改变ComboBox执行命令改变市,区. 解决主要问题就是默认选中第一项 1.首先要定义一个属性,继承自INotifyPropertyChanged接口.我这里 ...
- wpf图片查看器,支持鼠标滚动缩放拖拽
最近项目需要,要用到一个图片查看器,类似于windows自带的图片查看器那样,鼠标滚动可以缩放,可以拖拽图片,于是就写了这个简单的图片查看器. 前台代码: <Window x:Class=&qu ...
- WPF MVVM模式下路由事件
一,路由事件下三种路由策略: 1 冒泡:由事件源向上传递一直到根元素.2直接:只有事件源才有机会响应事件.3隧道:从元素树的根部调用事件处理程序并依次向下深入直到事件源.一般情况下,WPF提供的输入事 ...
- WPF MVVM模式下实现ListView下拉显示更多内容
在手机App中,如果有一个展示信息的列表,通常会展示很少一部分,当用户滑动到列表底部时,再加载更多内容.这样有两个好处,提高程序性能,减少网络流量.这篇博客中,将介绍如何在WPF ListView中实 ...
- wpf mvvm模式下的image绑定
view文件 <Image Grid.Column="2" Width="48" Height="64" Stretch=" ...
- wpf mvvm模式下 在ViewModel关闭view
本文只是博主用来记录笔记,误喷 使用到到了MVVM中消息通知功能 第一步:在需要关闭窗体中注册消息 public UserView() { this.DataContext = new UserVie ...
随机推荐
- 【转】JMeter代理录制脚本
JMeter代理录制脚本 使用JMeter代理录制脚本的过程如下: 1.启动JMeter,在测试计划中添加“线程组”. 2.在“线程组”中添加“HTTP请求默认值”,参数设定如下: 3.在“”中添加“ ...
- 五 搭建kafka集群
1 下载 wget http://mirrors.tuna.tsinghua.edu.cn/apache/kafka/2.0.0/kafka_2.12-2.0.0.tgz 2 tar -zxv ...
- Venom的简单使用
工具地址:https://github.com/r00t-3xp10it/venom 打开到venom目录,输入./venom.sh 打开程序 按回车键继续 这里有很多的模块,要用哪个模块就输入它的编 ...
- 使用ReentrantReadWriteLock类
读读共享 类ReentrantReadWriteLock的使用:写写互斥 读写互斥
- Collision Detection
[Collision Detection] Collision Detection是Rigidbody中的一个属性.所以显然Collision Detection指定的类型只在Rigidbody之间才 ...
- sql server2008 跨服务器之间复制表数据
首先2个数据库要能互相访问,在本地数据库用 select * into 新表 from opendatasource('SQLOLEDB','Data Source=远程数据库IP;User ID=用 ...
- 认识WebRoot和WebContent目录
1.webRoot是不需要加的,因为它是默认的JSP目录,完整的路径应该是:项目名/xxx.jsp,如果在webroot下边建立了文件夹abc,又在abc中建立了xxx.jsp那么此时的路径应为htt ...
- codeforce469DIV2——D. A Leapfrog in the Array
题意: 给出1<=n<=10^18和1<=q<=200000,有一个长度为2*n-1的数组,初始时单数位置存(i+1)/2,双数位置是空的.每次找出最右边的一个数将它跳到离它最 ...
- 252. Meeting Rooms 区间会议室
[抄题]: Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2],.. ...
- python 输入参数解包,模块导入,接收IO输入参数
#coding=utf-8 from sys import argv script,first,second,third = argv print "the script is=" ...