可框选的ListBox
最近项目当中遇到一个需要有数据条目框选功能的ListBox,写了一个简单的Demo。效果如下:
要想实现这样的效果主要要实现以下两点:
1、选择框的绘制
2、绘制过程中计算与选择框相交的Item。
矩形选择框的绘制,实现原理比较简单,按照下面的方式定义ListBox的模板,这样可以在Thumb的DragDelta事件中方便的计算出拖动时矩形选择框的位置和大小信息进行绘制。
ListBox模板内容:
1 <Grid> 2 <Thumb Name="PART_DragThumb" Template="{StaticResource DragThumbTemplate}" /> 3 <WrapPanel IsItemsHost="True" Orientation="Horizontal" /> 4 <Canvas Name="PanelParent" ClipToBounds="True"> 5 <Rectangle Name="PART_SelectArea" 6 Width="0" Height="0" 7 Fill="#6ca3a3a3" Stroke="LightBlue" 8 StrokeThickness="1" /> 9 </Canvas> 10 </Grid>
DragDelta事件:
1 /// <summary> 2 /// 拖拽 3 /// </summary> 4 private void ThumbDragDelta(object sender, DragDeltaEventArgs e) 5 { 6 // 绘制选择框 7 if (e.HorizontalChange < 0) 8 { 9 var right = Canvas.GetLeft(_selectArea) + _selectArea.Width; 10 Canvas.SetLeft(_selectArea, right + e.HorizontalChange); 11 } 12 13 if (e.VerticalChange < 0) 14 { 15 var bottom = Canvas.GetTop(_selectArea) + _selectArea.Height; 16 Canvas.SetTop(_selectArea, bottom + e.VerticalChange); 17 } 18 19 _selectArea.Width = Math.Abs(e.HorizontalChange); 20 _selectArea.Height = Math.Abs(e.VerticalChange); 21 }
每当绘制矩形框后,需要计算出哪些数据项和所绘制的矩形框相交,并将与选择框区域相交的数据项容器附加属性IsDragSelected为true,之后再利用该属性在ListBox的ItemContainerStyle中使用触发器实现选中效果即可,代码如下:
1 // 选择框区域信息 2 var selectAreaLocation = new Point(Canvas.GetLeft(_selectArea), Canvas.GetTop(_selectArea)); 3 var selectAreaSize = new Size(_selectArea.Width, _selectArea.Height); 4 var selectRect = new Rect(selectAreaLocation, selectAreaSize); 5 Debug.WriteLine("selectRect:{0}", selectRect); 6 7 foreach (var item in this.Items) 8 { 9 var container = this.ItemContainerGenerator.ContainerFromItem(item) as ContentControl; 10 if (container != null) 11 { 12 var transform = container.TransformToAncestor(this); 13 var location = transform.Transform(new Point()); 14 15 // 数据项容器区域信息 16 var containerRect = new Rect(location, new Size(container.ActualWidth, container.ActualHeight)); 17 Debug.WriteLine("containerRect:{0}", containerRect); 18 SetIsDragSelected(container, selectRect.IntersectsWith(containerRect)); 19 }
上面之所以没有直接设置数据项容器的IsSelected属性,是因为不想将框选和ListBox默认的选择混在一起,Demo中在Thumb的DragCompleted事件里找出IsDragSelected附加属性为true的数据项,并将这些数据用事件参数向外抛出,具体的操作放在事件中。
PS:最后,由于DragSelectListBox中各个数据项容器间的间距较小,导致框选触发不易实现,所以需要在ItemTemplate中做下处理,方法如下:
1 <DataTemplate> 2 <Grid> 3 <Border IsHitTestVisible="False" 4 BorderBrush="LightBlue" BorderThickness="1" 5 Width="50" Height="50" Background="AliceBlue"> 6 <TextBlock Text="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center"/> 7 </Border> 8 <Rectangle Margin="5" Fill="Transparent" /> 9 </Grid> 10 </DataTemplate>
第3行设置Border的IsHitTestVisible属性为False, 然后再放一个Margin为5的Rectangle,这样每个数据项容器边缘都会多出5像素的可触发框选区域,使框选更容易触发。
附上源代码
版权说明:本文章版权归本人及博客园共同所有,未经允许请勿用于任何商业用途。转载请标明原文出处:
http://www.cnblogs.com/talywy/archive/2012/10/09/DragSelectListBox.html 。