Fork me on GitHub

可框选的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 

posted @ 2012-10-09 23:23  Taly.W.Y  阅读(2799)  评论(6编辑  收藏  举报