WPF,拖动并选择元素(鼠标)



如何在C#/wpf中使用鼠标绘制的矩形来选择元素?

我在搜索如何通过在元素周围绘制矩形选择/遍历元素来选择元素(在我的例子中是listview(,也许是因为当时是深夜,但我发现的例子有点难以理解。然后,经过几个小时的尝试,我发现了很多东西,我做了一个小项目来帮助人们做一个非常基本的例子。

这真的很完美,可以超越限制进行选择,但我想做一些非常基本的事情,这是第一步。我让很多评论来帮助理解。我尽量将设计部分和代码部分分开,我让形状的名称来帮助区分调试,还有一个选择框的名称。我希望它会有用。

  • Wpf部件:它有一个显示限制的边框,您可以选择元素的部件有一个要显示的边距,这对于指定对鼠标做出反应的容器很重要。如果你只想在页面/窗口中选择一个零件,重要的是要记住有相对的位置。如果没有,你会画一个有间隙的矩形

形状将修改其笔划厚度,以帮助了解其工作原理。可以更改几何体以查看其反应。

<Window x:Class="FirstSelect.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:FirstSelect"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Border BorderBrush="Black" BorderThickness="1" Margin="50" >
<Grid  
MouseLeftButtonDown="Grid_MouseLeftButtonDown"
MouseLeftButtonUp="Grid_MouseLeftButtonUp"
MouseMove="Grid_MouseMove" 
>
<!-- Used for the overlay  -->
<Canvas >
<Rectangle Fill="AliceBlue"  x:Name="SelectBox" Stroke="Blue" StrokeThickness="2" RadiusX="5"/>
</Canvas>
<!-- Container, set background to transparent is important to see the overlay -->
<Grid x:Name="Container" Background="Transparent" >
<Rectangle x:Name="vert" HorizontalAlignment="Left" Height="129" Margin="154,136,0,0" Stroke="Green" VerticalAlignment="Top" Width="110"/>
<Rectangle x:Name="rose" HorizontalAlignment="Left" Height="161" Margin="519,71,0,0" Stroke="pink" VerticalAlignment="Top" Width="100"/>
</Grid>
</Grid>
</Border>
</Grid>
</Window>
  • 代码部分:
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
namespace FirstSelect
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
/// <summary>
/// Keep the initial position when you clicked
/// </summary>
Point _InitPos;
/// <summary>
/// A Flag to filter and active selection only when mouse button is held
/// </summary>
bool _LeftMouseHeld = false;
/// <summary>
/// Collection of elements selected
/// </summary>
private List<object> _ResultsList = new List<object>();
private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var Limit = (FrameworkElement)sender;
foreach (Rectangle rectangle in _ResultsList)
{
rectangle.StrokeThickness = 1;
}
_ResultsList.Clear();
// Get position relative to the grid content
_InitPos = e.GetPosition(Limit);
// With this, selection is more fluid but you can draw beyond limit of your container
Limit.CaptureMouse();
// Initialization of the SelectBox
Canvas.SetLeft(SelectBox, _InitPos.X);
Canvas.SetTop(SelectBox, _InitPos.X);
SelectBox.Visibility = Visibility.Visible;
// Set left mouse button state because mosemove will continue to draw if not filtered
_LeftMouseHeld = true;
Debug.WriteLine($"{_InitPos.X}, {_InitPos.Y}");
}

private void Grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
var Limit = (FrameworkElement)sender;
// Set left mouse button state to released
_LeftMouseHeld = false;
Limit.ReleaseMouseCapture();
// Hide all the listbox (if you forget to specify width and height you will have remanent coordinates
SelectBox.Visibility = Visibility.Collapsed;
SelectBox.Width = 0;
SelectBox.Height = 0;
if (_ResultsList.Count > 0)
{
foreach (FrameworkElement r in _ResultsList)
Debug.WriteLine(r.Name);
}
}

private void Grid_MouseMove(object sender, MouseEventArgs e)
{
Grid Limit = (Grid)sender;
// to calculate only if left mouse button is held;
if (_LeftMouseHeld)
{
// Get current position relative to the grid content
Point currentPos = e.GetPosition(Limit);
/*
Parameters can't be negative then we will invert base according to mouse value
*/
// X coordinates
if (currentPos.X > _InitPos.X)
{
Canvas.SetLeft(SelectBox, _InitPos.X);
SelectBox.Width = currentPos.X - _InitPos.X;
}
else
{
Canvas.SetLeft(SelectBox, currentPos.X);
SelectBox.Width = _InitPos.X - currentPos.X;
}
// Y coordinates
if (currentPos.Y > _InitPos.Y)
{
Canvas.SetTop(SelectBox, _InitPos.Y);
SelectBox.Height = currentPos.Y - _InitPos.Y;
}
else
{
Canvas.SetTop(SelectBox, currentPos.Y);
SelectBox.Height = _InitPos.Y - currentPos.Y;
}
/*
* With a rectangle geometry you could add every shapes INSIDE the rectangle
* With a point geometry you must go over the shape to select it.
*/
VisualTreeHelper.HitTest(Container,
new HitTestFilterCallback(Filter),
new HitTestResultCallback(MyHitTestResult),
/*new PointHitTestParameters(currentPos)*/
new GeometryHitTestParameters(new RectangleGeometry(new Rect(_InitPos, currentPos)))
);
}
}

private HitTestFilterBehavior Filter(DependencyObject potentialHitTestTarget)
{
// Type of return is very important
if (potentialHitTestTarget is Rectangle)
{
if (!_ResultsList.Contains(potentialHitTestTarget))
{
_ResultsList.Add(potentialHitTestTarget);
((Rectangle)potentialHitTestTarget).StrokeThickness = 5;
}
return HitTestFilterBehavior.ContinueSkipChildren;
}

return HitTestFilterBehavior.Continue;
}

// Return the result of the hit test to the callback.
public HitTestResultBehavior MyHitTestResult(HitTestResult result)
{
// Set the behavior to return visuals at all z-order levels.
return HitTestResultBehavior.Continue;
}
}
}

致问候,Vonkel/Daerlnaxe

相关内容

最新更新