有時候會有框選的需求,類似EXCEL一樣,畫一個框選擇里面的子控件。
選擇后比如可以將子控件的Border設置為紅色邊框
說下這個功能的大致原理。背景是一個Grid,比如里面放了很多的Button. 同時還有一個紅色邊框的RectAngele來顯示框框。
RectAngele默認不顯示。
鼠標左鍵按下時,記錄當時鼠標位置作為RectAngele的左上角起點,
鼠標按住移動時,記錄當時的鼠標位置作為RectAngele的右下角終點。
這樣兩點,就確定了RectAngele的位置,鼠標按住不停移動,這個RectAngele就會不停的變化大小。
用到了幾個事件
PreviewMouseMove,PreviewMouseLeftButtonDown,PreviewMouseLeftButtonUp。
這樣的功能,當然可以在業務層做 定義Grid Button RectRange 的界面XAML中做.。如果有多個界面要做框選功能,豈不是代碼要復制來復制去復制幾遍?
!這樣太LOW,不能忍,必須抽象到控件層次,與業務邏輯無關。
新增控件 GridRect 繼承 Grid,把框選功能集成到控件里。 這里有一個關鍵的地方,Grid有可能被分為很多Row 和Clomn。不同的Button放在不同的行列里。
但是這個RectRange 的位置其實是和行列無關的。它不能固定在某個行列里。所以RectRange要特殊處理。
廢話不多說,直接上源碼,拿去用吧!

public class GridRect : Grid { private Rectangle rect; private Grid rectgrid;//因為可能被分為很多列和行,而rect的父容器不能被分組 public GridRect() { Background = Brushes.Transparent; this.Loaded += GridRect_Loaded; } public delegate void delegateSelectChildChange(List<FrameworkElement> SelectedControls); public delegateSelectChildChange SelectChildChange { get; set; } private void GridRect_Loaded(object sender, RoutedEventArgs e) { rectgrid = new Grid(); rect = new Rectangle() { IsHitTestVisible = false, StrokeThickness = 1, Fill = Brushes.Transparent, Visibility = System.Windows.Visibility.Collapsed, Stroke = Brushes.Red, HorizontalAlignment = System.Windows.HorizontalAlignment.Left, VerticalAlignment = System.Windows.VerticalAlignment.Top }; //因為可能被分為很多列和行,而rect的父容器不能被分組 Grid.SetRowSpan(rectgrid, 100); Grid.SetColumnSpan(rectgrid, 100); rectgrid.Children.Add(rect); this.Children.Add(rectgrid); Panel.SetZIndex(rectgrid, 999); } #region 框選功能 protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnMouseLeftButtonDown(e); var StartPoint = e.GetPosition(this); RectStartPoint.X = Math.Truncate(StartPoint.X); RectStartPoint.Y = Math.Truncate(StartPoint.Y); } protected override void OnPreviewKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if (e.Key == Key.Escape && rect.Visibility == Visibility.Visible) { rect.Visibility = Visibility.Collapsed; } } private Point RectStartPoint = new Point(); private Point RectEndPoint = new Point(); protected override void OnPreviewMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (e.LeftButton == MouseButtonState.Pressed) { //該事件在界面加載完后會馬上出發,因為鼠標相對於grid的位置會更新,且Pressed,此時紅框不該顯示 if (rect.Visibility != Visibility.Visible && RectStartPoint.X + RectStartPoint.Y !=0) { rect.Visibility = Visibility.Visible; } Point p = e.GetPosition(this); double width = Math.Truncate(Math.Abs(p.X - RectStartPoint.X)); double height = Math.Truncate(Math.Abs(p.Y - RectStartPoint.Y)); rect.Margin = new Thickness(RectStartPoint.X, RectStartPoint.Y, 0, 0); rect.Height = height; rect.Width = width; RectEndPoint.X = RectStartPoint.X + width; RectEndPoint.Y = RectStartPoint.Y + height; } } protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e) { base.OnMouseLeftButtonUp(e); if (rect.Visibility == Visibility.Visible) { List<FrameworkElement> SelectedControlsTmp=new List<FrameworkElement>(); foreach (FrameworkElement item in this.Children) { if (item == rect || item ==rectgrid) { continue; } GeneralTransform generalTransform1 = item.TransformToVisual(this); Point lefttop = generalTransform1.Transform(new Point(0, 0)); Point rightbuttom = new Point(lefttop.X + item.ActualWidth, lefttop.Y + item.ActualHeight); Point btnrighttop = new Point(rightbuttom.X, lefttop.Y); Point btnleftbuttom = new Point(lefttop.X, rightbuttom.Y); Rect rectTmp = new Rect(lefttop, rightbuttom); Rect rectRed = new Rect(RectStartPoint, RectEndPoint); if (rectTmp.IntersectsWith(rectRed)) { SelectedControlsTmp.Add(item); } } SelectChildChange?.Invoke(SelectedControlsTmp); } } #endregion }