写在前面
前奏:笔者在最近上班过程中,突遇同事遇到图片放控件中放大缩小的问题,本来自己也是处于忙碌状态之中,奈何架不住一包华子的诱惑,就去翻看了之前写过的关于控件动态添加和改变的项目,写了有一段时间了,把代码发给了他,顺带自己也做个总结
难点分析
首先,要想实现功能就得知道具体要完成的是什么,对于这个控件可移动,光靠控件本身肯定是不行的,必须对控件进行改造,或者给控件加点东西,在这里我选择了使用第二种方法,也就是给控件加点东西,给他外面套一个Grid或者其他的容器,控件的四周加上可以托拉拽的Thumb,如图所示
矩形为控件本身,我们要做的事在他的四周安插"眼线",以便随时都能控制控件的大小(画的有些丑勿介意)
关键就是画thumb以及定义thumb的dragdelta事件,这里时使用Grid作为容器,一般情况大家都会习惯使用Canvas控件,直接上这个类的代码,主要就是他的初始化方法和拖动逻辑
public class GridAdorner : Adorner
{
//4条边
Thumb _leftThumb, _topThumb, _rightThumb, _bottomThumb;
//4个角
Thumb _lefTopThumb, _rightTopThumb, _rightBottomThumb, _leftbottomThumb;
//布局容器,如果不使用布局容器,则需要给上述8个控件布局,实现和Grid布局定位是一样的,会比较繁琐且意义不大。
public Grid _grid;
UIElement _adornedElement;
public GridAdorner(UIElement adornedElement) : base(adornedElement)
{
哪些需要隐藏thumb
//bool disappear = false;
//if(adornedElement.GetType() == typeof(Border))
//{
// disappear = true;
//}
_adornedElement = adornedElement;
//初始化thumb
_leftThumb = new Thumb();
_leftThumb.HorizontalAlignment = HorizontalAlignment.Left;
_leftThumb.VerticalAlignment = VerticalAlignment.Center;
_leftThumb.Cursor = Cursors.SizeWE;
_topThumb = new Thumb();
_topThumb.HorizontalAlignment = HorizontalAlignment.Center;
_topThumb.VerticalAlignment = VerticalAlignment.Top;
_topThumb.Cursor = Cursors.SizeNS;
_rightThumb = new Thumb();
_rightThumb.HorizontalAlignment = HorizontalAlignment.Right;
_rightThumb.VerticalAlignment = VerticalAlignment.Center;
_rightThumb.Cursor = Cursors.SizeWE;
_bottomThumb = new Thumb();
_bottomThumb.HorizontalAlignment = HorizontalAlignment.Center;
_bottomThumb.VerticalAlignment = VerticalAlignment.Bottom;
_bottomThumb.Cursor = Cursors.SizeNS;
_lefTopThumb = new Thumb();
_lefTopThumb.HorizontalAlignment = HorizontalAlignment.Left;
_lefTopThumb.VerticalAlignment = VerticalAlignment.Top;
_lefTopThumb.Cursor = Cursors.SizeNWSE;
_rightTopThumb = new Thumb();
_rightTopThumb.HorizontalAlignment = HorizontalAlignment.Right;
_rightTopThumb.VerticalAlignment = VerticalAlignment.Top;
_rightTopThumb.Cursor = Cursors.SizeNESW;
_rightBottomThumb = new Thumb();
_rightBottomThumb.HorizontalAlignment = HorizontalAlignment.Right;
_rightBottomThumb.VerticalAlignment = VerticalAlignment.Bottom;
_rightBottomThumb.Cursor = Cursors.SizeNWSE;
_leftbottomThumb = new Thumb();
_leftbottomThumb.HorizontalAlignment = HorizontalAlignment.Left;
_leftbottomThumb.VerticalAlignment = VerticalAlignment.Bottom;
_leftbottomThumb.Cursor = Cursors.SizeNESW;
_grid = new Grid();
_grid.Children.Add(_leftThumb);
_grid.Children.Add(_topThumb);
_grid.Children.Add(_rightThumb);
_grid.Children.Add(_bottomThumb);
_grid.Children.Add(_lefTopThumb);
_grid.Children.Add(_rightTopThumb);
_grid.Children.Add(_rightBottomThumb);
_grid.Children.Add(_leftbottomThumb);
AddVisualChild(_grid);
foreach (Thumb thumb in _grid.Children)
{
//if (disappear == true)
// thumb.Visibility = Visibility.Collapsed;
thumb.Width = 8;//16
thumb.Height = 8;//16
thumb.Background = Brushes.Green;
thumb.Template = new ControlTemplate(typeof(Thumb))
{
VisualTree = GetFactory(new SolidColorBrush(Colors.White))
};
thumb.DragDelta += Thumb_DragDelta;
}
}
protected override Visual GetVisualChild(int index)
{
return _grid;
}
protected override int VisualChildrenCount
{
get
{
return 1;
}
}
protected override Size ArrangeOverride(Size finalSize)
{
//直接给grid布局,grid内部的thumb会自动布局。
_grid.Arrange(new Rect(new Point(-_leftThumb.Width / 2, -_leftThumb.Height / 2), new Size(finalSize.Width + _leftThumb.Width, finalSize.Height + _leftThumb.Height)));
return finalSize;
}
//拖动逻辑
private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
{
var c = _adornedElement as FrameworkElement;
var thumb = sender as FrameworkElement;
var HorizonLocation = c.HorizontalAlignment;
var VerticaLocation = c.VerticalAlignment;
double left, top, right, bottom, width, height;
if (thumb.HorizontalAlignment == HorizontalAlignment.Left)
{
right = c.Margin.Right;
left = c.Margin.Left + e.HorizontalChange;
width = (double.IsNaN(c.Width) ? c.ActualWidth : c.Width) - e.HorizontalChange;
}
else
{
left = c.Margin.Left;
right = c.Margin.Right - e.HorizontalChange;
width = (double.IsNaN(c.Width) ? c.ActualWidth : c.Width) + e.HorizontalChange;
}
if (thumb.VerticalAlignment == VerticalAlignment.Top)
{
bottom = c.Margin.Bottom;
top = c.Margin.Top + e.VerticalChange;
height = (double.IsNaN(c.Height) ? c.ActualHeight : c.Height) - e.VerticalChange;
}
else
{
top = c.Margin.Top;
bottom = c.Margin.Bottom - e.VerticalChange;
height = (double.IsNaN(c.Height) ? c.ActualHeight : c.Height) + e.VerticalChange;
}
if (thumb.HorizontalAlignment != HorizontalAlignment.Center)
{
if (width >= 0)
{
c.Margin = new Thickness(left, c.Margin.Top, right, c.Margin.Bottom);
c.Width = width;
}
}
if (thumb.VerticalAlignment != VerticalAlignment.Center)
{
if (height >= 0)
{
c.Margin = new Thickness(c.Margin.Left, top, c.Margin.Right, bottom);
c.Height = height;
}
}
c.HorizontalAlignment = HorizonLocation;
c.VerticalAlignment = VerticaLocation;
}
//thumb的样式
FrameworkElementFactory GetFactory(Brush back)
{
var fef = new FrameworkElementFactory(typeof(Ellipse));
fef.SetValue(Ellipse.FillProperty, back);
fef.SetValue(Ellipse.StrokeProperty, new SolidColorBrush((Color)ColorConverter.ConvertFromString("#999999")));
fef.SetValue(Ellipse.StrokeThicknessProperty, (double)1);
return fef;
}
}
到这里其实关键的步骤已经解决了,我这里事自定义了textbox控件,也就是自定义一个用户控件,在里面放一个Grid包着的TextBox,使用的时候用这个自定义好的自定义控件
<UserControl x:Class="IQCP.Controls.SelfTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:IQCP.Controls"
mc:Ignorable="d">
<Grid>
<TextBox Name="content"></TextBox>
</Grid>
</UserControl>
代码中添加这个控件并给控件的相关事件做绑定
/// <summary>
/// 添加textbox
/// </summary>
public ICommand AddTextBox=> new DelegateCommand(obj =>
{
try
{
StartMW start = obj as StartMW;
SelfTextBox tb = new SelfTextBox();
tb.content.Text = "模板文本框 内容可改";
tb.content.BorderThickness = new Thickness(0);
tb.Height = 40;
tb.Width = 120;
tb.Name = "textbox_" + start.content.Children.Count + 1;
//tb.MouseDown += TextBox_MouseDown;
//tb.MouseUp += TextBox_MouseUp;
tb.content.MouseDoubleClick += Tb_MouseDoubleClick;
tb.PreviewKeyDown += Tb_PreviewKeyDown;
tb.content.PreviewMouseDown += Tb_PreviewMouseDown;
tb.content.PreviewMouseUp += Tb_PreviewMouseUp;
tb.content.MouseMove += TextBox_MouseMove;
tb.content.GotMouseCapture += tbox_GotMouseCapture;
tb.content.HorizontalAlignment = HorizontalAlignment.Center;
tb.content.VerticalAlignment = VerticalAlignment.Center;
start.content.Children.Add(tb);
AdornerLayer layer = AdornerLayer.GetAdornerLayer(tb);
GridAdorner grid = new GridAdorner(tb);
layer.Add(grid);
}
catch (Exception err)
{
FileHandler.WriteErrLog(System.Reflection.MethodBase.GetCurrentMethod().Name + "|Errmsg:" + err.Message);
}
});
private void Tb_PreviewKeyDown(object sender, KeyEventArgs e)
{
try
{
SelfTextBox mdg = sender as SelfTextBox;
StartMW start = Window.GetWindow(mdg) as StartMW;
if ((Keyboard.Modifiers == ModifierKeys.Control) && (e.Key == Key.C))
{
SelfTextBox tb = new SelfTextBox();
tb.content.Text = "模板文本框 内容可改";
tb.content.BorderThickness = new Thickness(0);
tb.Height = 40;
tb.Width = 120;
tb.Name = "textbox_" + start.content.Children.Count + 1;
//tb.MouseDown += TextBox_MouseDown;
//tb.MouseUp += TextBox_MouseUp;
tb.content.MouseDoubleClick += Tb_MouseDoubleClick;
tb.PreviewKeyDown += Tb_PreviewKeyDown;
tb.content.PreviewMouseDown += Tb_PreviewMouseDown;
tb.content.PreviewMouseUp += Tb_PreviewMouseUp;
tb.content.MouseMove += TextBox_MouseMove;
tb.content.GotMouseCapture += tbox_GotMouseCapture;
tb.content.HorizontalAlignment = HorizontalAlignment.Center;
tb.content.VerticalAlignment = VerticalAlignment.Center;
start.content.Children.Add(tb);
AdornerLayer layer = AdornerLayer.GetAdornerLayer(tb);
GridAdorner grid = new GridAdorner(tb);
layer.Add(grid);
}
if (e.Key == Key.Delete)
{
start.content.Children.Remove(mdg);
}
}
catch (Exception err)
{
FileHandler.WriteErrLog(System.Reflection.MethodBase.GetCurrentMethod().Name + "|Errmsg:" + err.Message);
}
}
private void Tb_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var c = sender as TextBox;
c.Focusable = true;
c.SelectionStart = c.Text.Length;
c.Focus();
}
private void Tb_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
var c = sender as TextBox;
IsMouseDown = false;
c.ReleaseMouseCapture();
}
private void Tb_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
try
{
var c = sender as TextBox;
var cself = (c.Parent as Grid).Parent as SelfTextBox;
StartMW swin = Window.GetWindow(c) as StartMW;
swin.controloprations.Visibility = Visibility.Visible;
currentname = cself.Name;
IsMouseDown = true;
TbmoveCount = 0;
c.Focusable = false;
_mouseDownPosition = e.GetPosition(swin);
var transform = c.RenderTransform as TranslateTransform;
if (transform == null)
{
transform = new TranslateTransform();
c.RenderTransform = transform;
}
var TBtransform = cself.RenderTransform as TranslateTransform;
if (TBtransform == null)
{
TBtransform = new TranslateTransform();
cself.RenderTransform = TBtransform;
}
_mouseDownControlPosition = new Point(transform.X, transform.Y);
_mouseDownTBControlPosition = new Point(TBtransform.X, TBtransform.Y);
c.CaptureMouse();
}
catch (Exception err)
{
FileHandler.WriteErrLog(System.Reflection.MethodBase.GetCurrentMethod().Name + "|Errmsg:" + err.Message);
}
}
private int TbmoveCount = 0;
private void TextBox_MouseMove(object sender, MouseEventArgs e)
{
try
{
TextBox sb = sender as TextBox;
var c = (sb.Parent as Grid).Parent as SelfTextBox;
TbmoveCount++;
if (IsMouseDown && TbmoveCount >3)
{
TbmoveCount = 0;
StartMW swin = Window.GetWindow(c) as StartMW;
//移动过程中thumb隐藏
AdornerLayer mylayer = AdornerLayer.GetAdornerLayer(c);
Adorner[] adorners = mylayer.GetAdorners(c);
var gridadorner = adorners.Where(t => t.GetType() == typeof(GridAdorner)).FirstOrDefault() as GridAdorner;
if (gridadorner != null)
{
foreach (Thumb thumb in gridadorner._grid.Children)
{
thumb.Visibility = Visibility.Collapsed;
}
}
var pos = e.GetPosition(swin);
//var dp = pos - _mouseDownPosition;
//var transform = sb.RenderTransform as TranslateTransform;
//transform.X = _mouseDownControlPosition.X + dp.X;
//transform.Y = _mouseDownControlPosition.Y + dp.Y;
var tbdp = pos - _mouseDownPosition;
var TBtransform = c.RenderTransform as TranslateTransform;
TBtransform.X = _mouseDownTBControlPosition.X + tbdp.X;
TBtransform.Y = _mouseDownTBControlPosition.Y + tbdp.Y;
}
}
catch (Exception err)
{
FileHandler.WriteErrLog(System.Reflection.MethodBase.GetCurrentMethod().Name + "|Errmsg:" + err.Message);
}
}
private void tbox_GotMouseCapture(object sender, MouseEventArgs e)
{
TextBox sb = sender as TextBox;
var c = (sb.Parent as Grid).Parent as SelfTextBox;
//if(c.IsMouseOver)
//{
// StartMW swin = Window.GetWindow(c) as StartMW;
// IsMouseDown = true;
// _mouseDownPosition = e.GetPosition(swin);
// var transform = c.RenderTransform as TranslateTransform;
// if (transform == null)
// {
// transform = new TranslateTransform();
// c.RenderTransform = transform;
// }
// _mouseDownControlPosition = new Point(transform.X, transform.Y);
// c.CaptureMouse();
//}
//鼠标capture显示thumb
AdornerLayer mylayer = AdornerLayer.GetAdornerLayer(c);
Adorner[] adorners = mylayer.GetAdorners(c);
var gridadorner = adorners.Where(t => t.GetType() == typeof(GridAdorner)).FirstOrDefault() as GridAdorner;
if (gridadorner != null)
{
foreach (Thumb thumb in gridadorner._grid.Children)
{
thumb.Visibility = Visibility.Visible;
}
}
}
OK 到这基本上功能已经是完成了,代码需要自己提取一下,因为我写的是一个demo工程