【发布时间】:2016-12-26 15:31:26
【问题描述】:
所以我开始关注 http://www.codeproject.com/Articles/22952/WPF-Diagram-Designer-Part 的 MoveResizeRotate 项目并以此为基础。
我添加了一些东西:
- 如果拖动角拇指,则可以使用纵横比调整大小。
- 使用 ScaleTransform 在 x 轴上翻转 ContentControl 的功能 - 即 ScaleX = -1。
但是,现在我在调整控件大小时遇到了麻烦。它在旋转之前工作正常,但是当它旋转时,它似乎从中间而不是那个角落/侧面调整大小。当控件的 ScaleX 设置为 -1 时,这一点尤其明显。
这是我的 ResizeThumb 代码:
namespace ResizeRotateFlip
{
public class ResizeThumb : Thumb
{
private double angle;
private Point transformOrigin;
private ContentControl designerItem;
public ResizeThumb()
{
DragStarted += new DragStartedEventHandler( this.ResizeThumb_DragStarted );
DragDelta += new DragDeltaEventHandler( this.ResizeThumb_DragDelta );
}
private void ResizeThumb_DragStarted( object sender, DragStartedEventArgs e )
{
this.designerItem = DataContext as ContentControl;
if( this.designerItem != null )
{
this.transformOrigin = this.designerItem.RenderTransformOrigin;
RotateTransform rotateTransform = this.designerItem.RenderTransform as RotateTransform;
if( rotateTransform != null )
{
this.angle = rotateTransform.Angle * Math.PI / 180.0;
}
else
{
this.angle = 0;
}
}
}
private void ResizeThumb_DragDelta( object sender, DragDeltaEventArgs e )
{
if( this.designerItem != null )
{
//variables
double deltaVertical = 0, deltaHorizontal = 0;
double newHeight = 0, newWidth = 0;
double startHeight = this.designerItem.Height;
double startWidth = this.designerItem.Width;
//calculate deltas
switch( VerticalAlignment )
{
case System.Windows.VerticalAlignment.Bottom:
deltaVertical = Math.Min( -e.VerticalChange, this.designerItem.ActualHeight - this.designerItem.MinHeight );
break;
case System.Windows.VerticalAlignment.Top:
deltaVertical = Math.Min( e.VerticalChange, this.designerItem.ActualHeight - this.designerItem.MinHeight );
break;
default:
break;
}
switch( HorizontalAlignment )
{
case System.Windows.HorizontalAlignment.Left:
deltaHorizontal = Math.Min( e.HorizontalChange, this.designerItem.ActualWidth - this.designerItem.MinWidth );
break;
case System.Windows.HorizontalAlignment.Right:
deltaHorizontal = Math.Min( -e.HorizontalChange, this.designerItem.ActualWidth - this.designerItem.MinWidth );
break;
default:
break;
}
// resize
if( dragging_from_corner() )
{
newHeight = this.designerItem.Height - deltaVertical;
newWidth = this.designerItem.Width - deltaHorizontal;
if( newHeight >= 0 && newWidth >= 0 )
{
aspect_ratio_resizing( this.designerItem.Height, this.designerItem.Width, newHeight, newWidth );
}
}
else
{
this.designerItem.Height -= deltaVertical;
this.designerItem.Width -= deltaHorizontal;
}
// translate
double verticalChange = startHeight - this.designerItem.Height;
double horizontalChange = startWidth - this.designerItem.Width;
TransformGroup transformGroup = designerItem.RenderTransform as TransformGroup;
ScaleTransform scaleTransform = transformGroup.Children[(int)TransformType.ScaleTransform] as ScaleTransform;
switch( VerticalAlignment )
{
case System.Windows.VerticalAlignment.Bottom:
if( scaleTransform.ScaleY != -1 )
{
Canvas.SetTop( this.designerItem, Canvas.GetTop( this.designerItem ) + ( this.transformOrigin.Y * verticalChange * ( 1 - Math.Cos( -this.angle ) ) ) );
Canvas.SetLeft( this.designerItem, Canvas.GetLeft( this.designerItem ) - verticalChange * this.transformOrigin.Y * Math.Sin( -this.angle ) );
}
else
{
Canvas.SetTop( this.designerItem, Canvas.GetTop( this.designerItem ) + verticalChange * Math.Cos( -this.angle ) + ( this.transformOrigin.Y * verticalChange * ( 1 - Math.Cos( -this.angle ) ) ) );
Canvas.SetLeft( this.designerItem, Canvas.GetLeft( this.designerItem ) + verticalChange * Math.Sin( -this.angle ) - ( this.transformOrigin.Y * verticalChange * Math.Sin( -this.angle ) ) );
}
break;
case System.Windows.VerticalAlignment.Top:
if( scaleTransform.ScaleY != -1 )
{
Canvas.SetTop( this.designerItem, Canvas.GetTop( this.designerItem ) + verticalChange * Math.Cos( -this.angle ) + ( this.transformOrigin.Y * verticalChange * ( 1 - Math.Cos( -this.angle ) ) ) );
Canvas.SetLeft( this.designerItem, Canvas.GetLeft( this.designerItem ) + verticalChange * Math.Sin( -this.angle ) - ( this.transformOrigin.Y * verticalChange * Math.Sin( -this.angle ) ) );
}
else
{
Canvas.SetTop( this.designerItem, Canvas.GetTop( this.designerItem ) + ( this.transformOrigin.Y * verticalChange * ( 1 - Math.Cos( -this.angle ) ) ) );
Canvas.SetLeft( this.designerItem, Canvas.GetLeft( this.designerItem ) - verticalChange * this.transformOrigin.Y * Math.Sin( -this.angle ) );
}
break;
default:
break;
}
switch( HorizontalAlignment )
{
case System.Windows.HorizontalAlignment.Left:
if( scaleTransform.ScaleX != -1 )
{
Canvas.SetTop( this.designerItem, Canvas.GetTop( this.designerItem ) + horizontalChange * Math.Sin( this.angle ) - this.transformOrigin.X * horizontalChange * Math.Sin( this.angle ) );
Canvas.SetLeft( this.designerItem, Canvas.GetLeft( this.designerItem ) + horizontalChange * Math.Cos( this.angle ) + ( this.transformOrigin.X * horizontalChange * ( 1 - Math.Cos( this.angle ) ) ) );
}
else
{
Canvas.SetTop( this.designerItem, Canvas.GetTop( this.designerItem ) - this.transformOrigin.X * horizontalChange * Math.Sin( this.angle ) );
Canvas.SetLeft( this.designerItem, Canvas.GetLeft( this.designerItem ) + ( horizontalChange * this.transformOrigin.X * ( 1 - Math.Cos( this.angle ) ) ) );
}
break;
case System.Windows.HorizontalAlignment.Right:
if( scaleTransform.ScaleX != -1 )
{
Canvas.SetTop( this.designerItem, Canvas.GetTop( this.designerItem ) - this.transformOrigin.X * horizontalChange * Math.Sin( this.angle ) );
Canvas.SetLeft( this.designerItem, Canvas.GetLeft( this.designerItem ) + ( horizontalChange * this.transformOrigin.X * ( 1 - Math.Cos( this.angle ) ) ) );
}
else
{
Canvas.SetTop( this.designerItem, Canvas.GetTop( this.designerItem ) + horizontalChange * Math.Sin( this.angle ) - this.transformOrigin.X * horizontalChange * Math.Sin( this.angle ) );
Canvas.SetLeft( this.designerItem, Canvas.GetLeft( this.designerItem ) + horizontalChange * Math.Cos( this.angle ) + ( this.transformOrigin.X * horizontalChange * ( 1 - Math.Cos( this.angle ) ) ) );
}
break;
default:
break;
}
}
e.Handled = true;
}
private bool dragging_from_corner()
{
if( ( VerticalAlignment == VerticalAlignment.Top || VerticalAlignment == VerticalAlignment.Bottom ) &&
( HorizontalAlignment == HorizontalAlignment.Left || HorizontalAlignment == HorizontalAlignment.Right ) )
{
return true;
}
return false;
}
private void aspect_ratio_resizing( double originalHeight, double originalWidth, double newHeight, double newWidth )
{
double ratioWidth = newWidth / originalWidth;
double ratioHeight = newHeight / originalHeight;
double ratio = Math.Min( ratioWidth, ratioHeight );
if( originalHeight * ratio > this.designerItem.MinHeight && originalWidth * ratio > this.designerItem.MinWidth )
{
this.designerItem.Height = originalHeight * ratio;
this.designerItem.Width = originalWidth * ratio;
}
}
}
}
这是我的 RotateThumb 代码:
namespace ResizeRotateFlip
{
public class RotateThumb : Thumb
{
private Point centerPoint;
private Vector startVector;
private double initialAngle;
private Canvas designerCanvas;
private ContentControl designerItem;
public RotateThumb()
{
DragDelta += new DragDeltaEventHandler( this.RotateThumb_DragDelta );
DragStarted += new DragStartedEventHandler( this.RotateThumb_DragStarted );
}
private void RotateThumb_DragStarted( object sender, DragStartedEventArgs e )
{
this.designerItem = DataContext as ContentControl;
if( this.designerItem != null )
{
this.designerCanvas = VisualTreeHelper.GetParent( this.designerItem ) as Canvas;
if( this.designerCanvas != null )
{
this.centerPoint = this.designerItem.TranslatePoint(
new Point( this.designerItem.Width * this.designerItem.RenderTransformOrigin.X,
this.designerItem.Height * this.designerItem.RenderTransformOrigin.Y ),
this.designerCanvas );
Point startPoint = Mouse.GetPosition( this.designerCanvas );
this.startVector = Point.Subtract( startPoint, this.centerPoint );
TransformGroup transformGroup = this.designerItem.RenderTransform as TransformGroup;
RotateTransform rotateTransform = transformGroup.Children[(int)TransformType.RotateTransform] as RotateTransform;
if( rotateTransform == null )
{
initialAngle = 0;
}
else
{
this.initialAngle = rotateTransform.Angle;
}
}
}
}
private void RotateThumb_DragDelta( object sender, DragDeltaEventArgs e )
{
if( this.designerItem != null && this.designerCanvas != null )
{
TransformGroup transformGroup = this.designerItem.RenderTransform as TransformGroup;
ScaleTransform scaleTransform = transformGroup.Children[(int)TransformType.ScaleTransform] as ScaleTransform;
double multiplier = scaleTransform.ScaleX * scaleTransform.ScaleY;
Point currentPoint = Mouse.GetPosition( this.designerCanvas );
Vector deltaVector = Point.Subtract( currentPoint, this.centerPoint );
double angle = Vector.AngleBetween( this.startVector, deltaVector );
RotateTransform rotateTransform = transformGroup.Children[(int)TransformType.RotateTransform] as RotateTransform;
rotateTransform.Angle = this.initialAngle + ( Math.Round( angle, 0 ) ) * multiplier;
this.designerItem.InvalidateMeasure();
}
}
}
}
我猜我对 X 和 Y 变化的计算(在 ResizeThumb 中)不正确。因此,调整大小后无法正确应用翻译。虽然我注意到我对 RotateThumb 的更改对调整大小有影响,但在将 TransformGroup 放入 RotateThumb 之前,控件在旋转时会正确调整大小。
我引用的项目使用:
case System.Windows.VerticalAlignment.Bottom:
Canvas.SetTop( this.designerItem, Canvas.GetTop( this.designerItem ) + ( this.transformOrigin.Y * verticalChange * ( 1 - Math.Cos( -this.angle ) ) ) );
Canvas.SetLeft( this.designerItem, Canvas.GetLeft( this.designerItem ) - verticalChange * this.transformOrigin.Y * Math.Sin( -this.angle ) );
break;
例如用于计算 X 和 Y 的新位置:但如果控件被翻转,我不确定如何计算。 - 我应该注意到我在这里使用verticalChange而不是deltaVertical的原因是,当使用deltaHorizontal和deltaVertical时,控件往往会在画布上移动。
我想我的问题更多的是数学方面,当控件旋转和翻转时,我如何计算 X 和 Y 的新位置?
编辑 我刚刚注意到的一件事是在 ResizeThumb 的 DragStarted 方法中,我没有访问过我的 TransformGroup 的 RotateTransform。
我变了:
RotateTransform rotateTransform = this.designerItem.RenderTransform as RotateTransform;
到:
TransformGroup transformGroup = designerItem.RenderTransform as TransformGroup;
RotateTransform rotateTransform = transformGroup.Children[(int)TransformType.RotateTransform] as RotateTransform;
这解决了在 ScaleX 和 ScaleY 未更改时调整大小错误的问题,但是当我翻转控件时我仍然遇到调整大小问题。
【问题讨论】: