首页 > 技术文章 > WPF 移动控件、拖动边框修改控件大小

Lulus 2020-04-16 20:15 原文

效果:

下面是右上方文本框的源代码,其他效果请前往github示例代码查看

<Canvas x:Name="canvas" MouseMove="Canvas_MouseMove">
	<!--文本框-->
	<Border Name="border2" Canvas.Left="500" Canvas.Top="35"
			BorderBrush="DarkKhaki" BorderThickness="1"  CornerRadius="5" Padding="5"
			Height="30" Width="200" MinHeight="10" MinWidth="10"
			MouseLeftButtonDown="border_MouseLeftButtonDown"
			MouseLeave="border_MouseLeave"
			MouseEnter="border_MouseEnter">
		<Border.BitmapEffect>
			<DropShadowBitmapEffect Color="DarkGray"/>
		</Border.BitmapEffect>
		<Border.RenderTransform>
			<TransformGroup>
				<ScaleTransform/>
				<TranslateTransform/>
			</TransformGroup>
		</Border.RenderTransform>
		<!--需要Label/textblock的文字大小随resize改变,则使用Viewbox,否则,去掉Viewbox-->
		<!--https://stackoverflow.com/questions/1464185/how-to-set-textblock-or-label-with-resizable-font-size-in-wpf-->
		<Viewbox Stretch="Uniform">
			<TextBlock Name="myTextBlock" Text="move and rezise TextBlock"></TextBlock>
		</Viewbox>
	</Border>
</Canvas>
private Element current = new Element();

protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
	base.OnMouseLeftButtonDown(e);
	this.current.X = Mouse.GetPosition(this.canvas).X;
	this.current.Y = Mouse.GetPosition(this.canvas).Y;

	if (this.current.InputElement != null)
		this.current.InputElement.CaptureMouse();

	if (!this.current.IsStretching)
		this.current.IsDragging = true;
}

protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
	base.OnMouseLeftButtonUp(e);

	if (this.current.InputElement != null)
		this.current.InputElement.ReleaseMouseCapture();

	this.Cursor = Cursors.Arrow;
}

private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
	if (e.LeftButton == MouseButtonState.Pressed &&
		 current.InputElement != null)
	{
		//increment z-Order and pass it to the current element, 
		//so that it stays on top of all other elements
		//递增z-Order并将其传递给当前元素,以使其停留在所有其他元素的顶部
		((Border)this.current.InputElement).SetValue(Canvas.ZIndexProperty, this.current.ZIndex++);

		//判断操作类型为拖拽移动
		if (this.current.IsDragging)
			Drag(sender);

		//缩放大小
		if (this.current.IsStretching)
			Stretch(sender);
	}
}

private void border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
	//capture the last highest z index before pointing to new current element
	//在指向新的当前元素之前捕获最后的最高z索引
	int newZIndex = (int)((Border)sender).GetValue(Canvas.ZIndexProperty);
	this.current.ZIndex = newZIndex > this.current.ZIndex ? newZIndex : this.current.ZIndex;

	//capture the new current element
	//捕获新的当前元素
	this.current.InputElement = (IInputElement)sender;
}

private void border_MouseLeave(object sender, MouseEventArgs e)
{
	if (e.LeftButton == MouseButtonState.Pressed)
		return;

	// get coordinates
	//获得坐标
	//计算Border
	Border border = (Border)sender;
	var rightLimit = border.ActualWidth - border.Padding.Right;
	var bottomLimit = border.ActualHeight - border.Padding.Bottom;
	//获得鼠标点击(与特定元素的相对位置)
	var x = Mouse.GetPosition((IInputElement)sender).X;
	var y = Mouse.GetPosition((IInputElement)sender).Y;

	// figure out stretching directions - only to Right, Bottom 
	//找出缩放的方向-仅向右方、底部
	bool stretchRight = (x >= rightLimit && x < border.ActualWidth) ? true : false;
	bool stretchBottom = (y >= bottomLimit && y < border.ActualHeight) ? true : false;

	// update current element
	//更新当前元素
	this.current.InputElement = (IInputElement)sender;
	this.current.X = x;
	this.current.Y = y;
	//默认为“缩放”操作
	this.current.IsStretching = true;

	//set cursor to show stretch direction 
	//设置光标显示
	//右&底部,光标显示右下箭头
	if (stretchRight && stretchBottom)
	{
		this.Cursor = Cursors.SizeNWSE;
		return;
	}
	//右
	else if (stretchRight && !stretchBottom)
	{
		this.Cursor = Cursors.SizeWE;
		return;
	}
	//底部
	else if (stretchBottom && !stretchRight)
	{
		this.Cursor = Cursors.SizeNS;
		return;
	}
	//非缩放操作,显示箭头光标,设置IsStretching=false
	else //no stretch
	{
		this.Cursor = Cursors.Arrow;
		this.current.IsStretching = false;
	}

}

private void border_MouseEnter(object sender, MouseEventArgs e)
{
	Border border = (Border)sender;

	var rightLimit = border.ActualWidth - border.Padding.Right;
	var bottomLimit = border.ActualHeight - border.Padding.Bottom;

	var x = Mouse.GetPosition((IInputElement)sender).X;
	var y = Mouse.GetPosition((IInputElement)sender).Y;

	if (x < rightLimit && y < bottomLimit)
		this.Cursor = Cursors.Arrow;
}

/// <summary>
/// 拖拽移动
/// </summary>
/// <param name="sender"></param>
private void Drag(object sender)
{
	this.Cursor = Cursors.Hand;

	// Retrieve the current position of the mouse.
	var newX = Mouse.GetPosition((IInputElement)sender).X;
	var newY = Mouse.GetPosition((IInputElement)sender).Y;

	// Reset the location of the object (add to sender's renderTransform newPosition minus currentElement's position
	var transformGroup = ((UIElement)this.current.InputElement).RenderTransform as TransformGroup;
	if (transformGroup == null)
		return;

	var translateTransforms = from transform in transformGroup.Children
							  where transform.GetType().Name == "TranslateTransform"
							  select transform;

	foreach (TranslateTransform tt in translateTransforms)
	{
		tt.X += newX - current.X;
		tt.Y += newY - current.Y;
	}

	// Update the beginning position of the mouse
	current.X = newX;
	current.Y = newY;
}

/// <summary>
/// 缩放大小
/// </summary>
/// <param name="sender"></param>
private void Stretch(object sender)
{

	// Retrieve the current position of the mouse.
	var mousePosX = Mouse.GetPosition((IInputElement)sender).X;
	var mousePosY = Mouse.GetPosition((IInputElement)sender).Y;


	//get coordinates
	Border border = (Border)this.current.InputElement;
	var xDiff = mousePosX - this.current.X;
	var yDiff = mousePosY - this.current.Y;
	var width = ((Border)this.current.InputElement).Width;
	var heigth = ((Border)this.current.InputElement).Height;


	//make sure not to resize to negative width or heigth
	xDiff = (border.Width + xDiff) > border.MinWidth ? xDiff : border.MinWidth;
	yDiff = (border.Height + yDiff) > border.MinHeight ? yDiff : border.MinHeight;


	// stretchRight && stretchBottom ?
	if (this.Cursor == Cursors.SizeNWSE)
	{
		((Border)this.current.InputElement).Width += xDiff;
		((Border)this.current.InputElement).Height += yDiff;
	}
	// stretchRight ? 
	else if (this.Cursor == Cursors.SizeWE)
		((Border)this.current.InputElement).Width += xDiff;

	// stretchBottom ?
	else if (this.Cursor == Cursors.SizeNS)
		((Border)this.current.InputElement).Height += yDiff;

	//no stretch
	else
	{
		this.Cursor = Cursors.Arrow;
		this.current.IsStretching = false;
	}

	// update current coordinates with the latest postion of the mouse
	this.current.X = mousePosX;
	this.current.Y = mousePosY;
}

Element.cs

public class Element
{
	#region Fields
	private bool isDragging = false;
	private bool isStretching = false;
	private bool stretchLeft = false;
	private bool stretchRight = false;
	private IInputElement inputElement = null;
	private double x, y = 0;
	private int zIndex = 0;
	#endregion

	#region Constructor
	public Element() { }
	#endregion

	#region Properties
	public IInputElement InputElement
	{
		get { return this.inputElement; }
		set
		{
			this.inputElement = value;
			this.isDragging = false;
			this.isStretching = false;
		}
	}
	public double X
	{
		get { return this.x; }
		set { this.x = value; }
	}
	public double Y
	{
		get { return this.y; }
		set { this.y = value; }
	}
	public int ZIndex
	{
		get { return this.zIndex; }
		set { this.zIndex = value; }
	}
	public bool IsDragging
	{
		get { return this.isDragging; }
		set
		{
			this.isDragging = value;
			this.isStretching = !this.isDragging;
		}
	}
	public bool IsStretching
	{
		get { return this.isStretching; }
		set
		{
			this.isStretching = value;
			this.IsDragging = !this.isStretching;
		}
	}
	public bool StretchLeft
	{
		get { return this.stretchLeft; }
		set { this.stretchLeft = value; this.stretchRight = !this.stretchLeft; }
	}
	public bool StretchRight
	{
		get { return this.stretchRight; }
		set { this.stretchRight = value; this.stretchLeft = !this.stretchRight; }
	}
	#endregion
}

示例代码

MoveAndResizeControl

参考资料

How to change size of WPF controls at runtime

推荐阅读