如何创建具有旋转、调整大小和移动功能的选择矩形?

问题描述 投票:0回答:1

我正在尝试制作一个选择矩形,可以更改其位置和大小,还可以旋转它 - 一切都将通过拖动鼠标来完成。

我移动成功,但旋转和调整大小时遇到问题。

在旋转之前调整大小工作正常,但在旋转之后,矩形在调整大小时会改变位置。

第一次旋转时,一切似乎都工作正常,但第二次矩形会跳跃并改变位置。

附上一张图片,以及我写的代码。

如何让

RotateThumb
CenterBottomThumb
正常工作?

我将完整的项目上传到 GitHub,这里

<Window x:Class="SelectionRect.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:local="clr-namespace:SelectionRect"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="MainWindow"
        Width="400"
        Height="400"
        d:DataContext="{d:DesignInstance Type=local:RectModel}"
        mc:Ignorable="d">
    <Window.Resources>
        <Style x:Key="ThumbStyle" TargetType="Thumb">
            <Setter Property="Focusable" Value="True" />
            <Setter Property="Height" Value="7" />
            <Setter Property="Width" Value="7" />
            <Setter Property="BorderBrush" Value="Blue" />
            <Setter Property="Background" Value="LightBlue" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Thumb}">
                        <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1" />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <Canvas x:Name="Canvas1">
            <Grid Canvas.Left="{Binding Left}" Name="RectGrid"
                  Canvas.Top="{Binding Top}"
                  Width="{Binding Width}"
                  Height="{Binding Height}">
                <Grid.LayoutTransform>
                    <RotateTransform Angle="{Binding Angle}" />
                </Grid.LayoutTransform>
                <Border Background="#4C0000FF" BorderBrush="Blue" BorderThickness="1" />

                <Thumb Name="LeftTopThumb"
                       Margin="-5,-5,0,0"
                       HorizontalAlignment="Left"
                       VerticalAlignment="Top"
                       Cursor="SizeNWSE"
                       DragDelta="LeftTopThumb_DragDelta"
                       Style="{StaticResource ThumbStyle}" />
                <Thumb Name="RightTopThumb"
                       Margin="0,-5,-5,0"
                       HorizontalAlignment="Right"
                       VerticalAlignment="Top"
                       Cursor="SizeNESW"
                       DragDelta="RightTopThumb_DragDelta"
                       Style="{StaticResource ThumbStyle}" />
                <Thumb Name="LeftBottomThumb"
                       Margin="-5,0,0,-5"
                       HorizontalAlignment="Left"
                       VerticalAlignment="Bottom"
                       Cursor="SizeNESW"
                       DragDelta="LeftBottomThumb_DragDelta"
                       Style="{StaticResource ThumbStyle}" />
                <Thumb Name="RightBottomThumb"
                       Margin="0,0,-5,-5"
                       HorizontalAlignment="Right"
                       VerticalAlignment="Bottom"
                       Cursor="SizeNWSE"
                       DragDelta="RightBottomThumb_DragDelta"
                       Style="{StaticResource ThumbStyle}" />

                <Thumb Name="CenterBottomThumb"
                       Margin="0,0,0,-5"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Bottom"
                       Cursor="SizeNS"
                       DragDelta="CenterBottomThumb_DragDelta"
                       Style="{StaticResource ThumbStyle}" />

                <Thumb Name="CenterTopThumb"
                       Margin="0,-5,0,0"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Top"
                       Cursor="SizeNS"
                       DragDelta="CenterTopThumb_DragDelta"
                       Style="{StaticResource ThumbStyle}" />

                <Thumb Name="RotateThumb"
                       Margin="0,-25,0,0"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Top"
                       Cursor="Cross"
                       DragDelta="RotateThumb_DragDelta"
                       DragStarted="RotateThumb_DragStarted"
                       Style="{StaticResource ThumbStyle}" />

                <Thumb Name="CenterLeftThumb"
                       Margin="-5,0,0,0"
                       HorizontalAlignment="Left"
                       VerticalAlignment="Center"
                       Cursor="SizeWE"
                       DragDelta="CenterLeftThumb_DragDelta"
                       Style="{StaticResource ThumbStyle}" />

                <Thumb Name="CenterRightThumb"
                       Margin="0,0,-5,0"
                       HorizontalAlignment="Right"
                       VerticalAlignment="Center"
                       Cursor="SizeWE"
                       DragDelta="CenterRightThumb_DragDelta"
                       Style="{StaticResource ThumbStyle}" />

                <Thumb x:Name="CenterThumb"
                       Width="Auto"
                       Height="Auto"
                       HorizontalAlignment="Stretch"
                       VerticalAlignment="Stretch"
                       Cursor="SizeAll"
                       DragDelta="CenterThumb_DragDelta"
                       Focusable="True">
                    <Thumb.Template>
                        <ControlTemplate>
                            <Border Name="CenterThumbBorder"
                                    Margin="10"
                                    Background="Transparent"
                                    MouseDown="CenterThumbBorder_MouseDown" />
                        </ControlTemplate>
                    </Thumb.Template>
                </Thumb>
            </Grid>
        </Canvas>
    </Grid>
</Window>

cs:

using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using Linq;

namespace SelectionRect
{
    public partial class MainWindow : Window
    {
        private double _initialLeft;
        private double _initialTop;

        public MainWindow()
        {
            InitializeComponent();
            DataContext = new RectModel() { Height = 200, Width = 100 , Left = 20, Top = 50};
        }

        private void CenterBottomThumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {
            var thumb = sender as Thumb;
            var model = thumb.DataContext as RectModel;

            if (model.Height + e.VerticalChange >= 1)
            {
                model.Height += e.VerticalChange;
            }
        }

        private void CenterTopThumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {

        }

        private void RotateThumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {
            var thumb = sender as Thumb;
            var model = thumb.DataContext as RectModel;

            var initialAngle = model.Angle;
            var center = new Point(model.CenterX, model.CenterY);
            center = RectGrid.TranslatePoint(center, Canvas1);
            var currentPos = Mouse.GetPosition(Canvas1);
            model.Angle = center.GetAngle(currentPos) + 90;

            var Rect = new Rect(_initialLeft, _initialTop, model.Width, model.Height);
            var cornerPoints = new[] { Rect.TopLeft, Rect.TopRight, Rect.BottomRight, Rect.BottomLeft };
            var m = new Matrix();

            m.RotateAt(model.Angle, center.X, center.Y);
            m.Transform(cornerPoints);

            model.Left = cornerPoints.Min(p => p.X);
            model.Top = cornerPoints.Min(p => p.Y);
        }

        private void RotateThumb_DragStarted(object sender, System.Windows.Controls.Primitives.DragStartedEventArgs e)
        {
            var thumb = sender as Thumb;
            var model = thumb.DataContext as RectModel;

            _initialLeft = model.Left;
            _initialTop = model.Top;
        }

        private void CenterLeftThumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {

        }

        private void CenterRightThumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {

        }

        private void CenterThumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {
            var thumb = sender as Thumb;
            var model = thumb.DataContext as RectModel;
            model.Top += e.VerticalChange;
            model.Left += e.HorizontalChange;
        }

        private void CenterThumbBorder_MouseDown(object sender, MouseButtonEventArgs e)
        {

        }

        private void LeftBottomThumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {

        }

        private void LeftTopThumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {

        }

        private void RightTopThumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {

        }

        private void RightBottomThumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {

        }
    }
    public static class Ex
    {
       public static double GetAngle(this System.Windows.Point p1, System.Windows.Point p2)
       {
           var xDiff = p2.X - p1.X;
           var yDiff = p2.Y - p1.Y;
           return Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI;
       }
    }
    class RectModel : INotifyPropertyChanged
    {
        private double _Angle;
        public double Angle
        {
            get { return _Angle; }
            set
            {
                if (value == _Angle) return;
                _Angle = value;
                OnPropertyChanged(nameof(Angle));
            }
        }

        private double _Height;
        public double Height
        {
            get { return _Height; }
            set
            {
                if (value == _Height) return;
                _Height = value;
                OnPropertyChanged(nameof(Height));
            }
        }

        private double _Width;
        public double Width
        {
            get { return _Width; }
            set
            {
                if (value == _Width) return;
                _Width = value;
                OnPropertyChanged(nameof(Width));
            }
        }

        private double _Top;
        public double Top
        {
            get { return _Top; }
            set
            {
                if (value == _Top) return;
                _Top = value;
                OnPropertyChanged(nameof(Top));
            }
        }

        private double _Left;
        public double Left
        {
            get { return _Left; }
            set
            {
                if (value == _Left) return;
                _Left = value;
                OnPropertyChanged(nameof(Left));
            }
        }

        public double CenterX => Width / 2;
        public double CenterY => Height / 2;

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
c# .net wpf math geometry
1个回答
0
投票

应用旋转时,变换原点和坐标空间会发生变化:您应该确保旋转的变换原点设置为矩形的中心。这将使旋转行为更加可预测。
并且在调整大小时,您需要考虑当前的旋转以正确计算新的大小和位置。

RotateThumb_DragDelta
中,您正确地重新计算了角度,但您还需要在旋转后调整矩形的位置以确保其保持在原位。这涉及考虑新角度重新计算
Left
Top
属性。
修改方法,根据旋转调整位置。您可能需要计算旋转后矩形的新边界并相应地设置
Left
Top

private void RotateThumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
    var thumb = sender as Thumb;
    var model = thumb.DataContext as RectModel;

    var initialAngle = model.Angle;
    var center = new Point(model.CenterX, model.CenterY);
    center = RectGrid.TranslatePoint(center, Canvas1);
    var currentPos = Mouse.GetPosition(Canvas1);
    model.Angle = center.GetAngle(currentPos) + 90;

    var rotatedRect = RotateRect(new Rect(_initialLeft, _initialTop, model.Width, model.Height), model.Angle, center);
    model.Left = rotatedRect.Left;
    model.Top = rotatedRect.Top;
}

private Rect RotateRect(Rect rect, double angle, Point center)
{
    var cornerPoints = new[] { rect.TopLeft, rect.TopRight, rect.BottomRight, rect.BottomLeft };
    var m = new Matrix();
    m.RotateAt(angle, center.X, center.Y);
    m.Transform(cornerPoints);

    double minX = cornerPoints.Min(p => p.X);
    double minY = cornerPoints.Min(p => p.Y);
    double maxX = cornerPoints.Max(p => p.X);
    double maxY = cornerPoints.Max(p => p.Y);

    return new Rect(minX, minY, maxX - minX, maxY - minY);
}

您还需要实现其他拇指(

DragDelta
CenterTopThumb
CenterLeftThumb
等)的
CenterRightThumb
事件的逻辑,类似于您对
CenterBottomThumb
所做的操作。根据拖动的拇指和当前的旋转来计算新的大小和位置。

private void CenterTopThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
    // Logic to resize from the top, considering current rotation
    // Similar implementations are needed for other thumbs
}

对于调整大小方法(

CenterTopThumb_DragDelta
等),您需要实现类似的逻辑,在考虑当前旋转和位置的同时调整大小。这将防止矩形在调整大小时意外改变位置。

© www.soinside.com 2019 - 2024. All rights reserved.