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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM