最近項目當中遇到一個需要有數據條目框選功能的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 。