65.9K
CodeProject 正在变化。 阅读更多。
Home

行进蚂蚁选择

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.74/5 (18投票s)

2008年7月15日

CPOL

3分钟阅读

viewsIcon

58787

downloadIcon

2089

一个带有行军蚁的矩形选框选择的WPF实现。

引言

这是一个著名的行军蚁选择模式的WPF实现,您几乎可以在每个启用选择的软件中看到它,例如 Adobe Photoshop 选框选择工具中的那个。 我在 Expression Blend 2.0 中创建了界面和动画,并使用 Visual Studio 2008 编写了行为代码。 通过这个简单的示例,您将了解一些关于 WPF 如何工作以及如何在代码隐藏中操作使用 Blend 创建的对象的基本概念。

背景

最近,在为软件项目使用 WPF 时,我不得不实现某种选择工具,毫不奇怪,我首先想到的就是使用著名的行军蚁,即带有动画虚线笔触的矩形。 我以前用 GDI+ 做过这件事,但这次,使用 Blend 和 VS,我创建了同样很酷的效果。 请注意,使用 WPF 比传统方法有很多优势,并且您可以获得更丰富的用户界面。 为了简单起见,我删除了许多选择样式,例如圆形矩形、闪烁的矩形、颜色渐变的矩形等等... 最初在我的应用程序中使用,因此更容易理解示例代码。 但是一旦你掌握了这个想法,你就可以用它做任何你想做的事情。

使用代码

该代码由一些 XAML 标记和几行 C# 代码组成,您可以根据需要使用。 请注意,实际选择对象的逻辑尚未实现,因为它必须在您正在开发的应用程序的上下文中定义。 可能是检测哪些对象被选择的最常见方法是 HitTest 概念,您可以在 WPF 中在 VisualTreeHelper.HitTest 中找到它。 我稍后会详细讨论它,但就目前而言,我在本文中的主要目的是向您展示如何在没有任何复杂性的情况下绘制一个完美的动画选择矩形,并且请注意,在实际项目中,您应该考虑将此代码转换为更可重用的东西,例如自定义组件或其他东西。

XAML

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="MarchingAntsSelection.Window1"
    x:Name="Window"
      Title="Marching Ants Selection"
      Width="563" Height="447" 
      Background="#FF353535" ResizeMode="NoResize">
    <Window.Resources>
        <Storyboard x:Key="MarchingAnts">
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
                               Storyboard.TargetName="rectangle" 
                               Storyboard.TargetProperty="(Shape.StrokeDashOffset)" 
                               RepeatBehavior="Forever">
                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                <SplineDoubleKeyFrame KeyTime="00:00:00.5000000"
                               Value="10"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </Window.Resources>
    <Window.Triggers>
        <EventTrigger RoutedEvent="FrameworkElement.Loaded">
            <BeginStoryboard Storyboard="{StaticResource MarchingAnts}"/>
        </EventTrigger>
    </Window.Triggers>

    <Grid x:Name="LayoutRoot">
        <Canvas x:Name="canvas" Background="#FF262626">
            <Rectangle Fill="#14FFFFFF" StrokeDashArray="5" 
                Stroke="#FFFFFFFF" Visibility="Hidden" 
                x:Name="rectangle" Width="50" Height="50" 
                StrokeDashOffset="0" StrokeThickness="1" 
                RadiusX="0" RadiusY="0"
                Canvas.Left="0" Canvas.Top="0"/>
            <TextBlock Width="Auto" Height="Auto" 
                FontFamily="Century Gothic" 
                FontSize="48" Foreground="#FF5B5B5B" 
                Text="MARCHING ANTS" TextWrapping="Wrap" 
                Canvas.Top="182" Canvas.Left="79"/>
        </Canvas>
    </Grid>
</Window>

正如您在上面的标记中看到的,我有一个 Canvas 对象,其中包括一个 Rectangle。 这是实际的选择矩形。 请注意,我已经将 StrokeDashArray 属性设置为 5,这会将实线笔触转换为虚线。 您可以使用任何粗细、颜色、圆角半径或背景颜色来获得不同的选择矩形外观。 然后,我创建了一个 Storyborad,它可以动画矩形的 StrokeDashOffset 属性。 第一个关键帧位于 00:00:00,第二个关键帧位于 00:00:00.5000000。 如果你想要更快的蚂蚁,那么减少第二个 KeyTime。 平滑动画而不会跳动的蚂蚁的关键技巧是将第二个 SplineDoubleKeyFrame 值设置为等于 2*StrokeDashArray,在我们的例子中将是 2*5=10。 任何 2 的倍数而不是 2 本身都可以正常工作,但它们会使动画更快。 最初,我通过 Canvas.LeftCanvas.Top 属性将 Rectangle 位置设置为 (0,0),这也很重要,因为我在 C# 代码中使用 RenderTransform 属性将矩形转换为正确的位置。 您可以使用 Canvase.SetLeftCanvas.SetTop 代替。 最初,我还将选择矩形的 Visibility 设置为 Hidden。 稍后,通过我们的代码,当用户实际在表面上拖动鼠标时,我们使其 Visible

C#

using System;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Input;

namespace MarchingAntsSelection
{
    public partial class Window1
    {
        private Point startDrag;

        public Window1()
        {
            this.InitializeComponent();

            canvas.MouseDown += new MouseButtonEventHandler(canvas_MouseDown);
            canvas.MouseUp += new MouseButtonEventHandler(canvas_MouseUp);
            canvas.MouseMove += new MouseEventHandler(canvas_MouseMove);
        }

        private void canvas_MouseDown(object sender, MouseButtonEventArgs e)
        {
            //Set the start point
            startDrag = e.GetPosition(canvas);
            //Move the selection marquee on top of all other objects in canvas
            Canvas.SetZIndex(rectangle, canvas.Children.Count);
            //Capture the mouse
            if (!canvas.IsMouseCaptured)
                canvas.CaptureMouse();
            canvas.Cursor = Cursors.Cross;
        }

        private void canvas_MouseUp(object sender, MouseButtonEventArgs e)
        {
            //Release the mouse
            if (canvas.IsMouseCaptured)
                canvas.ReleaseMouseCapture();
            canvas.Cursor = Cursors.Arrow;
        }

        private void canvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (canvas.IsMouseCaptured)
            {
                Point currentPoint = e.GetPosition(canvas);

                //Calculate the top left corner of the rectangle 
                //regardless of drag direction
                double x = startDrag.X < currentPoint.X ? startDrag.X : currentPoint.X;
                double y = startDrag.Y < currentPoint.Y ? startDrag.Y : currentPoint.Y;

                if (rectangle.Visibility == Visibility.Hidden)
                    rectangle.Visibility = Visibility.Visible;

                //Move the rectangle to proper place
                rectangle.RenderTransform = new TranslateTransform(x, y);
                //Set its size
                rectangle.Width = Math.Abs(e.GetPosition(canvas).X - startDrag.X);
                rectangle.Height = Math.Abs(e.GetPosition(canvas).Y - startDrag.Y);
            }
        }
    }
}

这是整个 C# 代码,它绝对简单且不言自明。 每当用户在画布上单击鼠标时,我们设置起始点,捕获画布中的鼠标,并确保选择矩形是画布中使用 Canvas.SetZIndex 方法的最顶层对象。 正如我的朋友 Josh 建议的那样,您也可以使用装饰器层。 剩下的唯一一件事是,在 OnMouseMove 中,您再次计算矩形并相应地设置其位置和大小。

脚注

如果您按原样使用此代码,或将其用作应用程序中更高级选择样式的基础,如果您提及此文章并提供链接,我将不胜感激; 否则,它可以完全免费用于任何类型的商业或个人用途。

© . All rights reserved.