C# + WPF 技巧 轻松实现 Canvas 中控件的拖动与大小调整

1,078 阅读3分钟

前言

我们做图片编辑工具、视频编辑工具、或者画板有时需要实现控件缩放功能,比如图片或图形可以拉伸放大或缩小,实现这种功能通常需要8个点,对应4条边和4个角,在wpf中通常可以使用装饰器实现。

一、功能说明

8个点方放置在控件的8个方位上,通过拖动这些点对控件进行拉伸或缩小,示意图如下:

二、如何实现?

1、继承Adorner

通过装饰器的方式添加8个点在控件上,这样既可以不影响控件布局,又可以自由摆放8点控件。

通过重写方法,给装饰添加控件。

必要的重写的方法如下面示例所示:

public class CanvasAdorner : Adorner
{
  //获取装饰器的元素个数
  protected override Visual GetVisualChild(int index);
  //指定装饰器子元素个数
  protected override int VisualChildrenCount{get;}
  //布局,添加的子元素需要手动布局。
  protected override Size ArrangeOverride(Size finalSize);      
}

2、使用Thumb

因为Thumb实现拖动比较容易,有相关事件获取拖动距离。

在装饰器中定义8个Thumb,对应8个方位点。

示例代码如下:

//4条边
Thumb _leftThumb, _topThumb, _rightThumb, _bottomThumb;
//4个角
Thumb _lefTopThumb, _rightTopThumb, _rightBottomThumb, _leftbottomThumb;

初始化

public CanvasAdorner(UIElement adornedElement) : base(adornedElement)
{
     //初始化thumb
     _leftThumb = new Thumb();
     _leftThumb.HorizontalAlignment = HorizontalAlignment.Left;
     _leftThumb.VerticalAlignment = VerticalAlignment.Center;
     _leftThumb.Cursor = Cursors.SizeWE;
     //其他略...
}

3、实现拖动逻辑

在Thumb的DragDelta事件可以获取拖动距离,根据八个方位的不同计算并修改控件的大小。

private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
{
//1.右侧点HorizontalChange加宽
//2.左侧点HorizontalChange减宽,加左移
//3.下侧点VerticalChange加高
//4.上侧点VerticalChange减高,加上移
}

三、完整代码

代码如下:

public class CanvasAdorner : Adorner
{
    //4条边
    Thumb _leftThumb, _topThumb, _rightThumb, _bottomThumb;
    //4个角
    Thumb _lefTopThumb, _rightTopThumb, _rightBottomThumb, _leftbottomThumb;
    //布局容器,如果不使用布局容器,则需要给上述8个控件布局,实现和Grid布局定位是一样的,会比较繁琐且意义不大。
    Grid _grid;
    UIElement _adornedElement;
    public CanvasAdorner(UIElement adornedElement) : base(adornedElement)
    {
        _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)
        {
            thumb.Width = 16;
            thumb.Height = 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;
        double left, top, width, height;
        if (thumb.HorizontalAlignment == HorizontalAlignment.Left)
        {

            left =double.IsNaN( Canvas.GetLeft(c))?0: Canvas.GetLeft(c) + e.HorizontalChange;
            width = c.Width - e.HorizontalChange;
        }
        else
        {
            left = Canvas.GetLeft(c);
            width = c.Width + e.HorizontalChange;
        }
        if (thumb.VerticalAlignment == VerticalAlignment.Top)
        {
            top = double.IsNaN(Canvas.GetTop(c)) ? 0 : Canvas.GetTop(c) + e.VerticalChange;
            height = c.Height - e.VerticalChange;
        }
        else
        {
            top = Canvas.GetTop(c);
            height = c.Height + e.VerticalChange;
        }
        if (thumb.HorizontalAlignment != HorizontalAlignment.Center)
        {
            if (width >= 0)
            {
                Canvas.SetLeft(c, left);
                c.Width = width;
            }
        }
        if (thumb.VerticalAlignment != VerticalAlignment.Center)
        {
            if (height >= 0)
            {
                Canvas.SetTop(c, top);
                c.Height = height;
            }
        }
    }
    //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)2);
        return fef;
    }
}

四、使用示例

示例代码如下:

<Canvas Margin="20">
    <Border  x:Name="border"  Width="200" Height="200"  Background="Gray"  ></Border>
</Canvas>

在窗口或控件的Loaded事件中添加装饰器:
cs

private void window_Loaded(object sender, RoutedEventArgs e)
{
    var layer = AdornerLayer.GetAdornerLayer(border);
    layer.Add(new CanvasAdorner(border));
}

效果预览:

总结

本文讲述了控件缩放的方法与实现,都相对较简单,尤其是实现也是特意精简了代码,比如布局直接使用Grid控件,拖动的逻辑也是较为简化,最终呈现的效果还是比较不错的。

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

优秀是一种习惯,欢迎大家留言学习!

作者:CodeOfCC

出处:blog.csdn.net/u013113678/article/details/121466724

声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!