我正在 WPF 中创建一个类似绘画的工具,并遇到了以下问题,当用户单击绘制的形状 (DrawingShape) 时,将出现一个带有 8 个拇指的装饰器(形状周围矩形的每一侧各一个)。调整大小是在下面的类中完成的。我面临的问题是我希望用户能够“进入负高度/宽度”,我的意思是当用户编辑绘制的矩形并将底部拇指拖动到矩形的顶部时(使高度为 0)它将继续朝该方向拖动,在另一侧创建 DrawingShape。有没有简单的方法可以实现这一点?
public class ResizeThumb : Thumb
{
private bool dragStarted;
private bool isHorizontalDrag;
static ResizeThumb()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ResizeThumb),
new FrameworkPropertyMetadata(typeof(ResizeThumb)));
}
private readonly DrawingShape childElement;
public ResizeThumb(DrawingShape adornedElement, CornerOrSide thumbCornerOrSide)
{
this.childElement = adornedElement;
switch (thumbCornerOrSide)
{
case CornerOrSide.TopLeft:
this.DragDelta += OnTopLeftDragDelta;
break;
case CornerOrSide.TopRight:
this.DragDelta += OnTopRightDragDelta;
break;
case CornerOrSide.BottomRight:
this.DragDelta += OnBottomRightDragDelta;
break;
case CornerOrSide.BottomLeft:
this.DragDelta += OnBottomLeftDragDelta;
break;
case CornerOrSide.Top:
this.DragDelta += OnTopDragDelta;
break;
case CornerOrSide.Bottom:
this.DragDelta += OnBottomDragDelta;
break;
case CornerOrSide.Left:
this.DragDelta += OnLeftDragDelta;
break;
case CornerOrSide.Right:
this.DragDelta += OnRightDragDelta;
break;
default:
throw new ArgumentOutOfRangeException(nameof(thumbCornerOrSide), thumbCornerOrSide, null);
}
this.DragStarted += OnDragStarted;
this.DragCompleted += OnDragCompleted;
}
#region Drag Delta Events per Thumb
private void OnDragStarted(object _, DragStartedEventArgs e) => this.dragStarted = false;
private void OnDragCompleted(object _, DragCompletedEventArgs e) => this.dragStarted = false;
private void OnTopDragDelta(object _, DragDeltaEventArgs e) => ResizeTopHeight(e.VerticalChange);
private void OnBottomDragDelta(object _, DragDeltaEventArgs e) => ResizeHeight(e.VerticalChange);
private void OnLeftDragDelta(object _, DragDeltaEventArgs e) => ResizeLeftWidth(e.HorizontalChange);
private void OnRightDragDelta(object _, DragDeltaEventArgs e) => ResizeWidth(e.HorizontalChange);
private void OnTopLeftDragDelta(object _, DragDeltaEventArgs e)
{
ResizeLeftWidth(e.HorizontalChange);
ResizeTopHeight(e.VerticalChange);
}
private void OnBottomRightDragDelta(object _, DragDeltaEventArgs e)
{
ResizeWidth(e.HorizontalChange);
ResizeHeight(e.VerticalChange);
}
private void OnBottomLeftDragDelta(object _, DragDeltaEventArgs e)
{
ResizeLeftWidth(e.HorizontalChange);
ResizeHeight(e.VerticalChange);
}
private void OnTopRightDragDelta(object _, DragDeltaEventArgs e)
{
ResizeTopHeight(e.VerticalChange);
ResizeWidth(e.HorizontalChange);
}
#endregion
#region Resize Width / Height / X / Y methods
private void ResizeWidth(double e)
{
childElement.Width += e;
}
private void ResizeHeight(double e)
{
childElement.Height += e;
}
/// <summary>
/// AKA as ResizeX
/// </summary>
private void ResizeTopHeight(double e)
{
childElement.Top += e; // < this is Canvas.SetTop
childElement.Height -= e; // Decrease Height if Top moves
}
/// <summary>
/// AKA as ResizeY
/// </summary>
private void ResizeLeftWidth(double e)
{
childElement.Left += e; // < this is Canvas.SetLeft
childElement.Width -= e; // Decrease Width of Left moves
}
#endregion
}
我尝试了多种方法,例如检查高度是否为 0,然后移动 childElement.Top 值,但这会导致非常不稳定的行为。
我经常遇到这个问题并使用一种非常简单的方法。首先定义一个这样的方法:
public static Rect NormalizeRect(double x1, double y1, double x2, double y2)
{
return new Rect(
Math.Min(x1, x2),
Math.Min(y1, y2),
Math.Abs(x1 - x2),
Math.Abs(y1 - y2));
}
这会创建一个始终一致的
Rect
,最小的 x 和 y 坐标始终位于左上角,并且宽度和高度为正值。我发现自己经常使用它,所以我喜欢将它作为自己的静态方法保留在整个项目可访问的地方。
然后是一个简单的辅助属性:
private Rect ChildElementBounds
{
get => new Rect(
Canvas.GetLeft(childElement),
Canvas.GetTop(childElement),
childElement.Width,
ChildElement.Height);
set
{
Canvas.SetLeft(childElement, value.Left);
Canvas.SetTop(childElement, value.Top);
childElement.Width = value.Width;
childElement.Height = value.Height;
}
}
现在,每当任何坐标发生变化时,获取当前的
ChildElementBounds
,将调整作为正在移动的边的简单平移(不用担心负值),获得归一化的 Rect
,并将其分配回ChildElementBounds
。例如:
private void OnLeftDragDelta(object _, DragDeltaEventArgs e)
{
var rc = this.ChildElementBounds;
this.ChildElementBounds = NormalizeRect(
rc.Left + e.HorizontalChange,
rc.Top,
rc.Right,
rc.Bottom);
}
private void OnTopLeftDragDelta(object _, DragDeltaEventArgs e)
{
var rc = this.ChildElementBounds;
this.ChildElementBounds = NormalizeRect(
rc.Left + e.HorizontalChange,
rc.Top + e.VerticalChange,
rc.Right,
rc.Bottom);
}
private void OnBottomRightDragDelta(object _, DragDeltaEventArgs e)
{
var rc = this.ChildElementBounds;
this.ChildElementBounds = NormalizeRect(
rc.Left,
rc.Top,
rc,Right + e.HorizontalChange,
rc.Top + e.VerticalChange);
}
等等