Изменяемый размер WPF Adorner для Control User - PullRequest
2 голосов
/ 18 января 2020

Я создал простой элемент оформления для элемента TextBlock, который позволяет пользователю изменять его размер. Вы можете изменить размер в четырех углах блока, то есть два размера изменяются одновременно (верхний левый большой палец, верхний правый большой палец, нижний левый большой палец, нижний правый большой палец). Все работает хорошо, вы можете увидеть это на первом GIF:

enter image description here

Также я хочу добавить возможность пропорционального изменения размера с нажатой клавишей Shift вниз. Результат этого вы можете увидеть на втором gif:

enter image description here

Как видите, верхний левый и нижний правый большой палец позволяют вам правильно изменить размеры. Тем не менее, два других элемента большого пальца не работают, я не понимаю, как это было сделано.

XAML:

<Window x:Class="BagControlResize.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"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Canvas x:Name="canvas">
        <TextBlock x:Name="testBlock" Canvas.Left="250" Canvas.Top="120" Width="300" Height="200" Background="Green"/>
    </Canvas>
</Window>

CREATE ADORNER:

using System.Windows;
using System.Windows.Documents;

namespace BagControlResize
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            Loaded += (sender, e) =>
            {
                var adorner = AdornerLayer.GetAdornerLayer(canvas);
                adorner.Add(new TextBlockAdorner(testBlock));
            };
        }

    }
}

ADORNER:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace BagControlResize
{
    public class TextBlockAdorner : Adorner
    {
        private double angle = 0.0;
        private Point transformOrigin = new Point(0, 0);
        private TextBlock childElement;
        private VisualCollection visualChilderns;
        private Thumb leftTop, rightTop, leftBottom, rightBottom;

        public TextBlockAdorner(UIElement element) : base(element)
        {
            visualChilderns = new VisualCollection(this);
            childElement = element as TextBlock;
            CreateThumbPart(ref leftTop);
            leftTop.DragDelta += (sender, e) =>
            {
                double hor = e.HorizontalChange;
                double vert = e.VerticalChange;
                if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
                {
                    double _max = hor > vert ? hor : vert;
                    hor = _max;
                    vert = _max;
                }
                ResizeX(hor);
                ResizeY(vert);
                e.Handled = true;
            };
            CreateThumbPart(ref rightTop);
            rightTop.DragDelta += (sender, e) =>
            {
                double hor = e.HorizontalChange;
                double vert = e.VerticalChange;
                if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
                {
                    // THIS: NO WORKED
                    double _max = Math.Abs(hor) > Math.Abs(vert) ? Math.Abs(hor) : Math.Abs(vert);
                    if (hor >= 0 && vert <= 0)
                    {
                        hor = _max;
                        vert = -_max;
                    }
                    else
                    {
                        hor = -_max;
                        vert = _max;
                    }
                }
                ResizeWidth(hor);
                ResizeY(vert);
                e.Handled = true;
            };
            CreateThumbPart(ref leftBottom);
            leftBottom.DragDelta += (sender, e) =>
            {
                double hor = e.HorizontalChange;
                double vert = e.VerticalChange;
                if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
                {
                    // THIS: NO WORKED
                    double _max = Math.Abs(hor) > Math.Abs(vert) ? Math.Abs(hor) : Math.Abs(vert);
                    if (hor <= 0 && vert >= 0)
                    {
                        hor = -_max;
                        vert = _max;
                    }
                    else
                    {
                        hor = _max;
                        vert = -_max;
                    }
                }
                ResizeX(hor);
                ResizeHeight(vert);
                e.Handled = true;
            };
            CreateThumbPart(ref rightBottom);
            rightBottom.DragDelta += (sender, e) =>
            {
                double hor = e.HorizontalChange;
                double vert = e.VerticalChange;
                if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
                {
                    double _max = hor > vert ? hor : vert;
                    hor = _max;
                    vert = _max;
                }
                ResizeWidth(hor);
                ResizeHeight(vert);
                e.Handled = true;
            };
        }
        private void ResizeWidth(double e)
        {
            double deltaHorizontal = Math.Min(-e, childElement.ActualWidth - childElement.MinWidth);
            Canvas.SetTop(childElement, Canvas.GetTop(childElement) - transformOrigin.X * deltaHorizontal * Math.Sin(angle));
            Canvas.SetLeft(childElement, Canvas.GetLeft(childElement) + (deltaHorizontal * transformOrigin.X * (1 - Math.Cos(angle))));
            childElement.Width -= deltaHorizontal;
        }
        private void ResizeX(double e)
        {
            double deltaHorizontal = Math.Min(e, childElement.ActualWidth - childElement.MinWidth);
            Canvas.SetTop(childElement, Canvas.GetTop(childElement) + deltaHorizontal * Math.Sin(angle) - transformOrigin.X * deltaHorizontal * Math.Sin(angle));
            Canvas.SetLeft(childElement, Canvas.GetLeft(childElement) + deltaHorizontal * Math.Cos(angle) + (transformOrigin.X * deltaHorizontal * (1 - Math.Cos(angle))));
            childElement.Width -= deltaHorizontal;
        }
        private void ResizeHeight(double e)
        {
            double deltaVertical = Math.Min(-e, childElement.ActualHeight - childElement.MinHeight);
            Canvas.SetTop(childElement, Canvas.GetTop(childElement) + (transformOrigin.Y * deltaVertical * (1 - Math.Cos(-angle))));
            Canvas.SetLeft(childElement, Canvas.GetLeft(childElement) - deltaVertical * transformOrigin.Y * Math.Sin(-angle));
            childElement.Height -= deltaVertical;
        }
        private void ResizeY(double e)
        {
            double deltaVertical = Math.Min(e, childElement.ActualHeight - childElement.MinHeight);
            Canvas.SetTop(childElement, Canvas.GetTop(childElement) + deltaVertical * Math.Cos(-angle) + (transformOrigin.Y * deltaVertical * (1 - Math.Cos(-angle))));
            Canvas.SetLeft(childElement, Canvas.GetLeft(childElement) + deltaVertical * Math.Sin(-angle) - (transformOrigin.Y * deltaVertical * Math.Sin(-angle)));
            childElement.Height -= deltaVertical;
        }
        public void CreateThumbPart(ref Thumb cornerThumb)
        {
            cornerThumb = new Thumb { Width = 25, Height = 25, Background= Brushes.Black };
            visualChilderns.Add(cornerThumb);
        }
        public void EnforceSize(FrameworkElement element)
        {
            if (element.Width.Equals(Double.NaN))
                element.Width = element.DesiredSize.Width;
            if (element.Height.Equals(Double.NaN))
                element.Height = element.DesiredSize.Height;
            FrameworkElement parent = element.Parent as FrameworkElement;
            if (parent != null)
            {
                element.MaxHeight = parent.ActualHeight;
                element.MaxWidth = parent.ActualWidth;
            }
        }
        protected override Size ArrangeOverride(Size finalSize)
        {
            base.ArrangeOverride(finalSize);
            double desireWidth = AdornedElement.DesiredSize.Width;
            double desireHeight = AdornedElement.DesiredSize.Height;
            double adornerWidth = this.DesiredSize.Width;
            double adornerHeight = this.DesiredSize.Height;
            leftTop.Arrange(new Rect(-adornerWidth / 2 - 15, -adornerHeight / 2 - 15, adornerWidth, adornerHeight));
            rightTop.Arrange(new Rect(desireWidth - adornerWidth / 2 + 15, -adornerHeight / 2 - 15, adornerWidth, adornerHeight));
            leftBottom.Arrange(new Rect(-adornerWidth / 2 - 15, desireHeight - adornerHeight / 2 + 15, adornerWidth, adornerHeight));
            rightBottom.Arrange(new Rect(desireWidth - adornerWidth / 2 + 15, desireHeight - adornerHeight / 2 + 15, adornerWidth, adornerHeight));
            return finalSize;
        }
        protected override int VisualChildrenCount => visualChilderns.Count;
        protected override Visual GetVisualChild(int index) => visualChilderns[index];
        protected override void OnRender(DrawingContext drawingContext) => base.OnRender(drawingContext);
    }
}

Я показываю минимум самодостаточного примера, чтобы вы могли точно видеть, что я делаю. Код легко компилируется. Я отметил в коде два места, где я застрял.

Спасибо

UPD 1:

enter image description here

Ответы [ 2 ]

2 голосов
/ 21 января 2020

@ Решение Френчи помогает, но все еще есть проблемы с кодом. Попробуйте щелкнуть по левому верхнему или нижнему левому краю и сразу же перетащить прямо вверх или очень медленно перетащить верхнее правое с постоянной скоростью влево.

Проблема вызвана вычислением направления пропорционального перетаскивания при каждом вызове. DragDelta, в зависимости от направления самого большого движения. Код может легко изменить направление и запутаться, или даже не знать, каким образом он должен идти правильно.

Одним из способов решения этой проблемы является определение направления перетаскивания, когда начинается пропорциональное перетаскивание, и продолжайте с этим пока это не кончится. Это, кажется, работает лучше, и устраняет проблемы выше. Код ниже.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace BagControlResize
{
    public class TextBlockAdorner : Adorner
    {
        private double angle = 0.0;
        private Point transformOrigin = new Point(0, 0);
        private TextBlock childElement;
        private VisualCollection visualChilderns;
        public Thumb leftTop, rightTop, leftBottom, rightBottom;
        private bool dragStarted = false;
        private bool isHorizontalDrag = false;

        public TextBlockAdorner(UIElement element) : base(element)
        {
            visualChilderns = new VisualCollection(this);
            childElement = element as TextBlock;
            CreateThumbPart(ref leftTop);
            leftTop.DragDelta += (sender, e) =>
            {
                double hor = e.HorizontalChange;
                double vert = e.VerticalChange;
                if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
                {
                    if (dragStarted) isHorizontalDrag = Math.Abs(hor) > Math.Abs(vert);
                    if (isHorizontalDrag) vert = hor; else hor = vert;
                }
                ResizeX(hor);
                ResizeY(vert);
                dragStarted = false;
                e.Handled = true;
            };
            CreateThumbPart(ref rightTop);
            rightTop.DragDelta += (sender, e) =>
            {
                double hor = e.HorizontalChange;
                double vert = e.VerticalChange;
                System.Diagnostics.Debug.WriteLine(hor + "," + vert + "," + (Math.Abs(hor) > Math.Abs(vert)) + "," + childElement.Height + "," + childElement.Width + "," + dragStarted + "," + isHorizontalDrag);
                if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
                {
                    if (dragStarted) isHorizontalDrag = Math.Abs(hor) > Math.Abs(vert);
                    if (isHorizontalDrag) vert = -hor; else hor = -vert;
                }
                ResizeWidth(hor);
                ResizeY(vert);
                dragStarted = false;
                e.Handled = true;
            };
            CreateThumbPart(ref leftBottom);
            leftBottom.DragDelta += (sender, e) =>
            {
                double hor = e.HorizontalChange;
                double vert = e.VerticalChange;
                System.Diagnostics.Debug.WriteLine(hor + "," + vert + "," + (Math.Abs(hor) > Math.Abs(vert)) + "," + childElement.Height + "," + childElement.Width + "," + dragStarted + "," + isHorizontalDrag);
                if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
                {
                    if (dragStarted) isHorizontalDrag = Math.Abs(hor) > Math.Abs(vert);
                    if (isHorizontalDrag) vert = -hor; else hor = -vert;
                }
                ResizeX(hor);
                ResizeHeight(vert);
                dragStarted = false;
                e.Handled = true;
            };
            CreateThumbPart(ref rightBottom);
            rightBottom.DragDelta += (sender, e) =>
            {
                double hor = e.HorizontalChange;
                double vert = e.VerticalChange;
                if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
                {
                    if (dragStarted) isHorizontalDrag = Math.Abs(hor) > Math.Abs(vert);
                    if (isHorizontalDrag) vert = hor; else hor = vert;
                }
                ResizeWidth(hor);
                ResizeHeight(vert);
                dragStarted = false;
                e.Handled = true;
            };
        }
        public void CreateThumbPart(ref Thumb cornerThumb)
        {
            cornerThumb = new Thumb { Width = 25, Height = 25, Background = Brushes.Black };
            cornerThumb.DragStarted += (object sender, DragStartedEventArgs e) => dragStarted = true;
            visualChilderns.Add(cornerThumb);
        }

        private void ResizeWidth(double e)
        {
            double deltaHorizontal = Math.Min(-e, childElement.ActualWidth - childElement.MinWidth);
            Canvas.SetTop(childElement, Canvas.GetTop(childElement) - transformOrigin.X * deltaHorizontal * Math.Sin(angle));
            Canvas.SetLeft(childElement, Canvas.GetLeft(childElement) + (deltaHorizontal * transformOrigin.X * (1 - Math.Cos(angle))));
            childElement.Width -= deltaHorizontal;
        }
        private void ResizeX(double e)
        {
            double deltaHorizontal = Math.Min(e, childElement.ActualWidth - childElement.MinWidth);
            Canvas.SetTop(childElement, Canvas.GetTop(childElement) + deltaHorizontal * Math.Sin(angle) - transformOrigin.X * deltaHorizontal * Math.Sin(angle));
            Canvas.SetLeft(childElement, Canvas.GetLeft(childElement) + deltaHorizontal * Math.Cos(angle) + (transformOrigin.X * deltaHorizontal * (1 - Math.Cos(angle))));
            childElement.Width -= deltaHorizontal;
        }
        private void ResizeHeight(double e)
        {
            double deltaVertical = Math.Min(-e, childElement.ActualHeight - childElement.MinHeight);
            Canvas.SetTop(childElement, Canvas.GetTop(childElement) + (transformOrigin.Y * deltaVertical * (1 - Math.Cos(-angle))));
            Canvas.SetLeft(childElement, Canvas.GetLeft(childElement) - deltaVertical * transformOrigin.Y * Math.Sin(-angle));
            childElement.Height -= deltaVertical;
        }
        private void ResizeY(double e)
        {
            double deltaVertical = Math.Min(e, childElement.ActualHeight - childElement.MinHeight);
            Canvas.SetTop(childElement, Canvas.GetTop(childElement) + deltaVertical * Math.Cos(-angle) + (transformOrigin.Y * deltaVertical * (1 - Math.Cos(-angle))));
            Canvas.SetLeft(childElement, Canvas.GetLeft(childElement) + deltaVertical * Math.Sin(-angle) - (transformOrigin.Y * deltaVertical * Math.Sin(-angle)));
            childElement.Height -= deltaVertical;
        }
        //public void EnforceSize(FrameworkElement element)
        //{
        //    if (element.Width.Equals(Double.NaN))
        //        element.Width = element.DesiredSize.Width;
        //    if (element.Height.Equals(Double.NaN))
        //        element.Height = element.DesiredSize.Height;
        //    FrameworkElement parent = element.Parent as FrameworkElement;
        //    if (parent != null)
        //    {
        //        element.MaxHeight = parent.ActualHeight;
        //        element.MaxWidth = parent.ActualWidth;
        //    }
        //}
        protected override Size ArrangeOverride(Size finalSize)
        {
            base.ArrangeOverride(finalSize);
            double desireWidth = AdornedElement.DesiredSize.Width;
            double desireHeight = AdornedElement.DesiredSize.Height;
            double adornerWidth = this.DesiredSize.Width;
            double adornerHeight = this.DesiredSize.Height;
            leftTop.Arrange(new Rect(-adornerWidth / 2 - 15, -adornerHeight / 2 - 15, adornerWidth, adornerHeight));
            rightTop.Arrange(new Rect(desireWidth - adornerWidth / 2 + 15, -adornerHeight / 2 - 15, adornerWidth, adornerHeight));
            leftBottom.Arrange(new Rect(-adornerWidth / 2 - 15, desireHeight - adornerHeight / 2 + 15, adornerWidth, adornerHeight));
            rightBottom.Arrange(new Rect(desireWidth - adornerWidth / 2 + 15, desireHeight - adornerHeight / 2 + 15, adornerWidth, adornerHeight));
            return finalSize;
        }
        protected override int VisualChildrenCount => visualChilderns.Count;
        protected override Visual GetVisualChild(int index) => visualChilderns[index];
        //protected override void OnRender(DrawingContext drawingContext) => base.OnRender(drawingContext);
    }
}
1 голос
/ 20 января 2020

ваши тесты, когда нажатие клавиши не правильно, я изменил это: вы должны оставить только два теста, когда hor и vert имеют противоположный знак, остальные тесты должны быть отброшены.

        rightTop.DragDelta += (sender, e) =>
        {
            double hor = e.HorizontalChange;
            double vert = e.VerticalChange;

            if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
            {

                double _max = Math.Abs(hor) > Math.Abs(vert) ? Math.Abs(hor) : Math.Abs(vert);
                if( _max >50)return;//if distance of mouse from thumb is > 50pixel, no action
                if (hor >= 0 && vert <= 0)    // review the tests
                {
                    hor = _max;
                    vert = -_max;
                }
                else if(hor <= 0 && vert >=0)
                {
                    hor = -_max;
                    vert = _max;
                }
                else
                {
                    return;
                }
            }
            ResizeWidth(hor);
            ResizeY(vert);
            e.Handled = true;
        };
        CreateThumbPart(ref leftBottom);
        leftBottom.DragDelta += (sender, e) =>
        {
            double hor = e.HorizontalChange;
            double vert = e.VerticalChange;

            if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
            {
                double _max = Math.Abs(hor) > Math.Abs(vert) ? Math.Abs(hor) : Math.Abs(vert);
                if( _max >50)return;
                if (hor <= 0 && vert >= 0)  //same things
                {
                    hor = -_max;
                    vert = _max;
                }
                else if (hor >= 0 && vert <= 0)
                {
                    hor = _max;
                    vert = -_max;
                }
                else
                {
                    return;
                }
            }
            ResizeX(hor);
            ResizeHeight(vert);
            e.Handled = true;
        };
...