WPF Grid新增框選功能


有時候會有框選的需求,類似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

}
View Code

 


免責聲明!

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



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