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 ...
随机推荐
- codeforce 977 F. Consecutive Subsequence
F. Consecutive Subsequence time limit per test 2 seconds memory limit per test 256 megabytes input s ...
- Python中调用设置环境变量的bat
工作中用到一个python脚本,自动化运行某目录下的多个vc工程代码. 编译工程代码的命令如下,直接运行会失败,系统找不到devenv,我们需要添加devenv.exe所在的目录到环境变量中. os. ...
- CGI/MIME/servlet术语解释
CGI→一种协议, 一种标准, 一种规范 使用CGI协议, 能够让用户访问某些动态资源的时候, 触发web服务器, 让web服务器根据CGI协议能够调用外部(web服务器外部)的程序来执行处理这个动态 ...
- jQuery笔记——基础知识
jQuery是一个JavaScript库,它通过封装原生的JavaScript函数得到一整套定义好的方法.在jQuery程序中,不管是页面元素的选择.内置的功能函数,都是美元符号“$”来起 始的.而这 ...
- python's twelth day for me
闭包: 内层函数对外层函数的变量(非全局变量)的引用. python 遇到闭包,有一个机制,会开辟一个空间,将闭包中的全部变量放入其中.且不会随着函数的结束而关闭. 闭包的完美体现:装饰器. 打印函数 ...
- L1,L2范数和正则化 到lasso ridge regression
一.范数 L1.L2这种在机器学习方面叫做正则化,统计学领域的人喊她惩罚项,数学界会喊她范数. L0范数 表示向量xx中非零元素的个数. L1范数 表示向量中非零元素的绝对值之和. L2范数 表 ...
- WARNING: cell0 mapping not found - not syncing cell0
WARNING: cell0 mapping not found - not syncing cell0
- javascript中的垃圾回收
1引用计数垃圾回收 核心:跟踪记录对象被引用的次数.思路是如果一个对象A被赋值给了一个变量v,则该对象A的引用计数值加1,如果变量v又被赋予其他值了,比如a="str",则该对象A ...
- Spring Cloud Eureka 1(eureka简介)
Spring Cloud Eureka 是 Spring Cloud Netflix微服务套件中的一部分,基于netflix eureka做了二次封装,主要负责完成微服务架构中的服务治理功能. 服务治 ...
- Idea搭建Scala开发环境的注意事项
Idea搭建Scala开发环境的注意事项 1.下载ideaIC-2017.3.tar.gz 2.解压安装idea 3.File->setting->plugins 下载2个插件scala. ...