在WPF中点击并拖动选择框

26

在WPF中是否能够实现鼠标点击和拖动选框?应该通过简单绘制一个矩形、计算其点的坐标并评估此框内其他对象的位置来完成吗?还是有其他方法?

您能提供一些示例代码或链接吗?


对于绘图来说,情况并不那么简单,因为您可能希望选择框绘制在所有对象的顶部,并且您的对象很可能是UIElement本身。您需要使用adorner。 - Pavel Minaev
Pavel,谢谢你的提示。我会深入研究装饰器主题。如果你能再给我一些信息(只是学习方向)关于如何使用装饰器来实现这个目的,我将不胜感激。无论如何,谢谢你。 - rem
4个回答

48

以下是我过去使用的简单技巧的示例代码,用于绘制拖动选择框。

XAML:

<Window x:Class="DragSelectionBox.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300"
    >
    <Grid
        x:Name="theGrid"
        MouseDown="Grid_MouseDown"
        MouseUp="Grid_MouseUp"
        MouseMove="Grid_MouseMove"
        Background="Transparent"
        >
        <Canvas>
            <!-- This canvas contains elements that are to be selected -->
        </Canvas>
        
        <Canvas>
            <!-- This canvas is overlaid over the previous canvas and is used to 
                place the rectangle that implements the drag selection box. -->
            <Rectangle
                x:Name="selectionBox"
                Visibility="Collapsed"
                Stroke="Black"
                StrokeThickness="1"
                />
        </Canvas>
    </Grid>
</Window>

C#:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
    }

    bool mouseDown = false; // Set to 'true' when mouse is held down.
    Point mouseDownPos; // The point where the mouse button was clicked down.

    private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
    {
        // Capture and track the mouse.
        mouseDown = true;
        mouseDownPos = e.GetPosition(theGrid);
        theGrid.CaptureMouse();

        // Initial placement of the drag selection box.         
        Canvas.SetLeft(selectionBox, mouseDownPos.X);
        Canvas.SetTop(selectionBox, mouseDownPos.Y);
        selectionBox.Width = 0;
        selectionBox.Height = 0;
        
        // Make the drag selection box visible.
        selectionBox.Visibility = Visibility.Visible;
    }

    private void Grid_MouseUp(object sender, MouseButtonEventArgs e)
    {
        // Release the mouse capture and stop tracking it.
        mouseDown = false;
        theGrid.ReleaseMouseCapture();
        
        // Hide the drag selection box.
        selectionBox.Visibility = Visibility.Collapsed;
        
        Point mouseUpPos = e.GetPosition(theGrid);
        
        // TODO: 
        //
        // The mouse has been released, check to see if any of the items 
        // in the other canvas are contained within mouseDownPos and 
        // mouseUpPos, for any that are, select them!
        //
    }

    private void Grid_MouseMove(object sender, MouseEventArgs e)
    {
        if (mouseDown)
        {
            // When the mouse is held down, reposition the drag selection box.
            
            Point mousePos = e.GetPosition(theGrid);

            if (mouseDownPos.X < mousePos.X)
            {
                Canvas.SetLeft(selectionBox, mouseDownPos.X);
                selectionBox.Width = mousePos.X - mouseDownPos.X;
            }
            else
            {
                Canvas.SetLeft(selectionBox, mousePos.X);
                selectionBox.Width = mouseDownPos.X - mousePos.X;
            }

            if (mouseDownPos.Y < mousePos.Y)
            {
                Canvas.SetTop(selectionBox, mouseDownPos.Y);
                selectionBox.Height = mousePos.Y - mouseDownPos.Y;
            }
            else
            {
                Canvas.SetTop(selectionBox, mousePos.Y);
                selectionBox.Height = mouseDownPos.Y - mousePos.Y;
            }
        }
    }
}

我写了一篇关于这个的文章:

https://www.codeproject.com/Articles/148503/Simple-Drag-Selection-in-WPF


5
太聪明了!在你的矩形中添加 StrokeDashArray="2,1" 就可以得到虚线选择器。 - Carter Medlin
很好的解决方案。我所做的一个改进是在Grid_MouseMove()中的Point mousePos = e.GetPosition(theGrid);之后添加以下代码,以将选择矩形限制为父网格: if (mousePos.X < 0) mousePos.X = 0; if (mousePos.X > theGrid.ActualWidth) mousePos.X = theGrid.ActualWidth; if (mousePos.Y < 0) mousePos.Y = 0; if (mousePos.Y > theGrid.ActualHeight) mousePos.Y = theGrid.ActualHeight; - matori82
在这种情况下,使用VisualTreeHelper.HitTest(theGrid,mousePos)更容易。 - VitaliyK
非常好用!但是在选择和“裁剪”图像时遇到了一些问题,这是因为图像的DPI不一定与显示器的DPI匹配,导致出现“偏移”或“位移”。我只需在Image元素中添加:Width =“{Binding Source.PixelWidth,RelativeSource = {RelativeSource Self}}”Height =“{Binding Source.PixelHeight,RelativeSource = {RelativeSource Self}}”,就可以自动规范化DPI。 - Rafael Ventura
人们还在使用 WPF?哇,这个问题我已经回答了10年。您应该查看我写的文章:https://www.codeproject.com/Articles/148503/Simple-Drag-Selection-in-WPF - Ashley Davis

7

通过添加InkCanvas并将其EditingMode设置为Select,您可以轻松获得此功能。尽管它主要用于Tablet PC墨迹收集和呈现,但很容易将其用作基本的设计师表面。

<Window Width="640" Height="480" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <InkCanvas EditingMode="Select">
        <Button Content="Button" Width="75" Height="25"/>
        <Button Content="Button" Width="75" Height="25"/>
    </InkCanvas>
</Window>

嗨,Josh, 谢谢。我一定会学习InkCanvas的功能。请告诉我,您是指在InkCanvas上绘制矩形将自动匹配其下方的所有对象,并允许将它们选择吗? - rem
很不幸,我似乎找不到一个简单的方法让InkCanvas使用矩形选择。它使用套索选择。但是,你可以把元素放进去,然后用套索选择它们并拖动、调整大小等。你可以通过设置InkCanvas上的属性来禁用拖动/调整大小功能。 - Josh
1
我将您的代码放入了一个测试WPF项目中,并进行了一段时间的试验。是的,它似乎有很多有趣的功能,包括您所说的套索、拖动和调整大小。我之前并不知道这些。谢谢。但是,您知道吗,我没有想到查找选择框信息会这么困难。老实说,我以为它会像在画布上放置按钮那样成为标准预定义功能之一.. :) - rem
InkCanvas在M. MacDonald的书《Pro WPF in C# 2010》第96页有详细介绍。供参考。 - Sabuncu

0

这个项目创建了一个自定义的MultiSelector,支持多种选择方法,包括矩形“套索”样式:

由Teofil Cobzaru开发的MultiSelector

这里不可能全部复制。设计的关键要素是创建一个自定义的ItemContainer,它知道如何与其MultiSelector父级交互。这类似于ListBoxItem/ListBox

这可能不是最简单的方法,但如果您已经在使用某种类型的ItemsControl来托管可能需要被选择的项,那么它可以很容易地适应该设计。


-2

MouseDown逻辑:

MouseRect.X = mousePos.X >= MouseStart.X ? MouseStart.X : mousePos.X;
MouseRect.Y = mousePos.Y >= MouseStart.Y ? MouseStart.Y : mousePos.Y;
MouseRect.Width = Math.Abs(mousePos.X - MouseStart.X);
MouseRect.Height = Math.Abs(mousePos.Y - MouseStart.Y);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接