博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在WPF控件上添加Windows窗口式调整大小行为
阅读量:5078 次
发布时间:2019-06-12

本文共 19774 字,大约阅读时间需要 65 分钟。

原文:

起因

项目上需要对Canvas中的控件添加调整大小功能,即能在控件的四个角和四条边上可进行相应的拖动,类似Windows窗口那种。于是在参考以前同事写的代码基础上,完成了该功能。

代码实现

Adorner

我们是给现有的控件添加功能,属于装饰功能。当然首先想到的就是Adorner。在MSDN中Adorner的介绍如下:

装饰器是一个绑定到 UIElement 的自定义 FrameworkElement。 装饰器呈现在装饰器层中,它是一个呈现图面,始终位于装饰元素或装饰元素集合的顶部;呈现装饰器独立于呈现该装饰器绑定到的 UIElement。 装饰器通常相对于其绑定到的元素进行定位,且使用位于装饰元素的左上部的标准 2-D 坐标原点进行定位。

关于Adorner更详细的信息,可参考。Adorner是一个抽象类,我们可以继承自该类来实现自己的装饰功能。

Thumb

WPF中存在支持拖动的Thumb控件,而且Thumb控件继承自Control,可以定义控件模板。Thumb最重要的三个事件如下:

Thumb 提供 DragStarted, DragCompleted 和 DragDelta 事件来管理与鼠标指针相关的拖动操作。 当用户按下鼠标左键时,Thumb 控件接收逻辑焦点和鼠标捕获,并引发 DragStarted 事件。 在 Thumb 控件具有焦点和鼠标捕获的同时,可以无限制地多次引发 DragDelta 事件。 当用户释放鼠标左键时,Thumb 控件失去鼠标捕获,并引发 DragCompleted 事件。

实现原理

思路很明确,就是自定义一个Adorner,在四条边和四个角上添加相应的Thumb,处理相应的事件实现改变大小。值得注意的是,在左上角、右上角、左下角、上边、左边这些地方实际上不仅是改变大小,同时也会改变控件在宿主中的位置,所以我更愿意称之为调整布局。

主要类及其关系如下:

添加CanvasArrangementAdorner之后控件效果如下(浅蓝色为控件):

因为将Thumb设为透明了,看不出来是由8个Thumb组成的,如果改下颜色,会更容易理解些。

可以很明显的看出,在四个角和四条边上各有4个Thumb,我重新定义了Thumb的控件模板,控件模板内部是一个Rectangle。

主要类

各个主要类如下,因代码较简单,就不多解释了。

ArrangementDirection

using System;/// /// 布局方向/// [Flags]public enum ArrangementDirection{    None = 0,    LeftTop = 1,    Top = 2,    RightTop = 4,    Right = 8,    RightBottom = 16,    Bottom = 32,    LeftBottom = 64,    Left = 128,    All = LeftTop | Top | RightTop | Right | RightBottom | Bottom | LeftBottom | Left,}

ArrangementChangedEventArgs

using System;using System.Windows;/// /// 布局变化的事件/// public class ArrangementChangedEventArgs : EventArgs{    public ArrangementChangedEventArgs(Rect oldArrangement, Rect newArrangement)    {        this.OldArrangement = oldArrangement;        this.NewArrangement = newArrangement;    }    ///     /// 旧布局信息    ///     public Rect OldArrangement { get; private set; }    ///     /// 新布局信息    ///     public Rect NewArrangement { get; private set; }}

ArrangementDirection

using System;/// /// 布局方向/// [Flags]public enum ArrangementDirection{    None = 0,    LeftTop = 1,    Top = 2,    RightTop = 4,    Right = 8,    RightBottom = 16,    Bottom = 32,    LeftBottom = 64,    Left = 128,    All = LeftTop | Top | RightTop | Right | RightBottom | Bottom | LeftBottom | Left,}

ArrangementAdorner

using System;    using System.Diagnostics.Contracts;    using System.Windows;    using System.Windows.Controls;    using System.Windows.Controls.Primitives;    using System.Windows.Documents;    using System.Windows.Input;    using System.Windows.Media;    using System.Windows.Shapes;    ///     /// 布局装饰器    ///     public abstract class ArrangementAdorner : Adorner    {        #region Fields        ///         /// 拖动方块的边长        ///         private const double ThumbSideLength = 6;        ///         /// 可视化对象集合        ///         private readonly VisualCollection visualCollection;        ///         /// 对齐方向        ///         private readonly ArrangementDirection direction;        ///         /// 各个方向的拖动方块        ///         private readonly Thumb topThumb,                               leftTopthumb,                               rightTopThumb,                               righThumb,                               rightBottomThumb,                               bottomThumb,                               leftBottomThumb,                               leftThumb;        ///         /// 当前位置        ///         private Point currentLocation;        ///         /// 拖动前的大小        ///         private Size oldSize;        ///         /// 拖动前左边缘的值        ///         private double oldLeft;        ///         /// 拖动前上边缘的值        ///         private double oldTop;        #endregion Fields        #region Constructors        ///         /// 构造函数        ///         /// 装饰器所要绑定到的元素。        /// 布局方向        protected ArrangementAdorner(FrameworkElement adornedElement, ArrangementDirection arrangementDirection = ArrangementDirection.All)            : base(adornedElement)        {            this.direction = arrangementDirection;            this.visualCollection = new VisualCollection(this);            this.AddThumbIfNeeded(                ref this.leftTopthumb,                ArrangementDirection.LeftTop,                HorizontalAlignment.Left,                VerticalAlignment.Top,                Cursors.SizeNWSE);            this.AddThumbIfNeeded(                ref this.topThumb,                ArrangementDirection.Top,                HorizontalAlignment.Stretch,                VerticalAlignment.Top,                Cursors.SizeNS);            this.AddThumbIfNeeded(                ref this.rightTopThumb,                ArrangementDirection.RightTop,                HorizontalAlignment.Right,                VerticalAlignment.Top,                Cursors.SizeNESW);            this.AddThumbIfNeeded(                ref this.righThumb,                ArrangementDirection.Right,                HorizontalAlignment.Right,                VerticalAlignment.Stretch,                Cursors.SizeWE);            this.AddThumbIfNeeded(                ref this.rightBottomThumb,                ArrangementDirection.RightBottom,                HorizontalAlignment.Right,                VerticalAlignment.Bottom,                Cursors.SizeNWSE);            this.AddThumbIfNeeded(                ref this.bottomThumb,                ArrangementDirection.Bottom,                HorizontalAlignment.Stretch,                VerticalAlignment.Bottom,                Cursors.SizeNS);            this.AddThumbIfNeeded(                ref this.leftBottomThumb,                ArrangementDirection.LeftBottom,                HorizontalAlignment.Left,                VerticalAlignment.Bottom,                Cursors.SizeNESW);            this.AddThumbIfNeeded(                ref this.leftThumb,                ArrangementDirection.Left,                HorizontalAlignment.Left,                VerticalAlignment.Stretch,                Cursors.SizeWE);        }        #endregion Constructors        public event EventHandler
ArrangementChanged; #region Protected Methods #region Overrides ///
/// 获取此元素内的可视化子元素的数目。 /// ///
/// 此元素内的可视化子元素的数目。 ///
protected override int VisualChildrenCount { get { return this.visualCollection.Count; } } ///
/// 定位子元素并确定大小。 /// ///
/// 所用的实际大小。 ///
///
排列自身及其子元素的最终区域。 protected override Size ArrangeOverride(Size finalSize) { this.ArrangeThumbIfNeeded( this.leftTopthumb, new Point(-ThumbSideLength, -ThumbSideLength), new Size(ThumbSideLength, ThumbSideLength)); this.ArrangeThumbIfNeeded( this.topThumb, new Point(0, -ThumbSideLength), new Size(finalSize.Width, ThumbSideLength)); this.ArrangeThumbIfNeeded( this.rightTopThumb, new Point(finalSize.Width, -ThumbSideLength), new Size(ThumbSideLength, ThumbSideLength)); this.ArrangeThumbIfNeeded( this.righThumb, new Point(finalSize.Width, 0), new Size(ThumbSideLength, finalSize.Height)); this.ArrangeThumbIfNeeded( this.rightBottomThumb, new Point(finalSize.Width, finalSize.Height), new Size(ThumbSideLength, ThumbSideLength)); this.ArrangeThumbIfNeeded( this.bottomThumb, new Point(0, finalSize.Height), new Size(finalSize.Width, ThumbSideLength)); this.ArrangeThumbIfNeeded( this.leftBottomThumb, new Point(-ThumbSideLength, finalSize.Height), new Size(ThumbSideLength, ThumbSideLength)); this.ArrangeThumbIfNeeded( this.leftThumb, new Point(-ThumbSideLength, 0), new Size(ThumbSideLength, finalSize.Height)); return base.ArrangeOverride(finalSize); } ///
/// 从子元素集合返回指定索引处的子级。 /// ///
/// 所请求的子元素。它不应返回 null;如果提供的索引超出范围,将引发异常。 ///
///
集合中所请求子元素从零开始的索引。 protected override Visual GetVisualChild(int index) { return this.visualCollection[index]; } #endregion Overrides #region Virtuals ///
/// 创建布局方块 /// ///
方块的水平对齐方向 ///
方块的垂直对齐方向 ///
方块的光标 ///
创建好的方块
protected virtual Thumb CreateResizeThumb( HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, Cursor cursor) { var thumb = new Thumb { HorizontalAlignment = horizontalAlignment, VerticalAlignment = verticalAlignment, Cursor = cursor, Template = this.GetResizeThumbControlTemplate() }; return thumb; } ///
/// 获取框架元素的位置 /// ///
框架元素 ///
框架元素所在的位置
protected abstract Point GetLocation(FrameworkElement element); ///
/// 判断框架元素的位置偏移是否合法 /// ///
框架元素 ///
偏移向量 ///
合法返回true,否则返回false
protected virtual bool IsLocationOffsetLegal(FrameworkElement element, Vector offset) { var targetLocation = this.currentLocation + offset; if (targetLocation.X < 0) { return false; } return true; } ///
/// 设置框架元素的位置 /// ///
框架元素 ///
新位置 protected abstract void SetLocation(FrameworkElement element, Point location); ///
/// 获取框架元素的宽度 /// ///
框架元素 ///
宽度
protected virtual double GetWidth(FrameworkElement element) { return element.Width; } ///
/// 获取框架元素的高度 /// ///
框架元素 ///
高度
protected virtual double GetHeight(FrameworkElement element) { return element.Height; } ///
/// 判断框架元素的宽度变化是否合法 /// ///
框架元素 ///
宽度变化 ///
变化是否合法
protected virtual bool IsWidthDeltaLegal(FrameworkElement element, double widthDelta) { double newWidth = this.GetWidth(element) + widthDelta; return this.IsInRange(element.MaxWidth, element.MinWidth, newWidth); } ///
/// 判断框架元素的高度变化是否合法 /// ///
框架元素 ///
高度变化 ///
变化是否合法
protected virtual bool IsHeightDeltaLegal(FrameworkElement element, double heightDelta) { double newHeight = this.GetHeight(element) + heightDelta; return this.IsInRange(element.MaxHeight, element.MinHeight, newHeight); } ///
/// 设置框架元素的宽度变化 /// ///
框架元素 ///
宽度变化 protected virtual void SetWidthDelta(FrameworkElement element, double widthDelta) { element.Width += widthDelta; } ///
/// 设置框架元素的高度变化 /// ///
框架元素 ///
高度变化 protected virtual void SetHeightDelta(FrameworkElement element, double heightDelta) { element.Height += heightDelta; } #endregion Virtuals #endregion Protected Methods #region Private Methods ///
/// 在需要时添加拖动方块 /// ///
类中对应的方块 ///
方块对应的布局方向 ///
方块的水平对齐方向 ///
方块的垂直对齐方向 ///
方块的光标 private void AddThumbIfNeeded( ref Thumb thumb, ArrangementDirection arrangementDirection, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, Cursor cursor) { if (this.HasDirectionFlagSet(arrangementDirection)) { thumb = this.CreateResizeThumb(horizontalAlignment, verticalAlignment, cursor); thumb.DragStarted += this.ThumbDragStarted; thumb.DragDelta += this.ThumbDragDelta; thumb.DragCompleted += this.ThumbDragCompleted; this.visualCollection.Add(thumb); } } ///
/// 判断布局方向是否被设置 /// ///
布局方向 ///
被设置返回true,否则返回false
private bool HasDirectionFlagSet(ArrangementDirection arrangementDirection) { return (this.direction & arrangementDirection) == arrangementDirection; } ///
/// 获取布局方块的控件模板 /// ///
控件模板
private ControlTemplate GetResizeThumbControlTemplate() { var factory = new FrameworkElementFactory(typeof(Rectangle)); factory.SetValue(Shape.FillProperty, Brushes.Transparent); factory.SetValue(Shape.StrokeProperty, Brushes.Transparent); var controlTemplate = new ControlTemplate { TargetType = typeof(Thumb), VisualTree = factory }; return controlTemplate; } ///
/// 在需要时定位并确定方块大小 /// ///
方块 ///
方块位置 ///
方块大小 private void ArrangeThumbIfNeeded(Thumb thumb, Point location, Size size) { if (thumb != null) { if (thumb.HorizontalAlignment != HorizontalAlignment.Stretch) { thumb.Width = size.Width; } if (thumb.VerticalAlignment != VerticalAlignment.Stretch) { thumb.Height = size.Height; } thumb.Arrange(new Rect(location, size)); } } ///
/// 判断一个值是否在范围中 /// ///
最大值,可取 ///
最小值,可取 ///
值 ///
值在范围中返回true,否则返回false
private bool IsInRange(double maximum, double minimum, double value) { return (value >= minimum) && (value <= maximum); } #endregion Private Methods #region Events Handler ///
/// 拖动开始的响应 /// ///
///
private void ThumbDragStarted(object sender, DragStartedEventArgs e) { var frameworkElement = this.AdornedElement as FrameworkElement; this.currentLocation = this.GetLocation(frameworkElement); this.oldLeft = this.currentLocation.X; this.oldTop = this.currentLocation.Y; var width = this.GetWidth(frameworkElement); var height = this.GetHeight(frameworkElement); this.oldSize = new Size(width, height); } ///
/// 拖动变化的响应 /// ///
///
private void ThumbDragDelta(object sender, DragDeltaEventArgs e) { var frameworkElement = this.AdornedElement as FrameworkElement; var thumb = sender as Thumb; Contract.Assert(thumb != null); switch (thumb.HorizontalAlignment) { case HorizontalAlignment.Left: { var offset = new Vector(e.HorizontalChange, 0); if (this.IsLocationOffsetLegal(frameworkElement, offset)) { this.currentLocation.Offset(e.HorizontalChange, 0); if (this.IsWidthDeltaLegal(frameworkElement, -e.HorizontalChange)) { this.SetWidthDelta(frameworkElement, -e.HorizontalChange); } } break; } case HorizontalAlignment.Right: { if (this.IsWidthDeltaLegal(frameworkElement, e.HorizontalChange)) { this.SetWidthDelta(frameworkElement, e.HorizontalChange); } break; } } switch (thumb.VerticalAlignment) { case VerticalAlignment.Top: { var offset = new Vector(0, e.VerticalChange); if (this.IsLocationOffsetLegal(frameworkElement, offset)) { this.currentLocation.Offset(0, e.VerticalChange); if (this.IsHeightDeltaLegal(frameworkElement, -e.VerticalChange)) { this.SetHeightDelta(frameworkElement, -e.VerticalChange); } } break; } case VerticalAlignment.Bottom: { if (this.IsHeightDeltaLegal(frameworkElement, e.VerticalChange)) { this.SetHeightDelta(frameworkElement, e.VerticalChange); } break; } } this.SetLocation(frameworkElement, this.currentLocation); } ///
/// 拖动结束的响应 /// ///
///
private void ThumbDragCompleted(object sender, DragCompletedEventArgs e) { if (this.ArrangementChanged != null) { var frameworkElement = this.AdornedElement as FrameworkElement; var oldArrangement = new Rect(new Point(this.oldLeft, this.oldTop), this.oldSize); var newArrangement = new Rect( this.GetLocation(frameworkElement), new Size(this.GetWidth(frameworkElement), this.GetHeight(frameworkElement))); this.ArrangementChanged(this, new ArrangementChangedEventArgs(oldArrangement, newArrangement)); } } #endregion Events Handler }

CanvasArrangementAdorner

using System.Windows;    using System.Windows.Controls;    ///     /// 画布布局装饰器    ///     public class CanvasArrangementAdorner : ArrangementAdorner    {        ///         /// 构造函数        ///         /// 装饰器所要绑定到的元素。        /// 布局方向        public CanvasArrangementAdorner(FrameworkElement adornedElement, ArrangementDirection arrangementDirection = ArrangementDirection.All)            : base(adornedElement, arrangementDirection)        {        }        #region Overrides of ArrangementAdorner        ///         /// 获取框架元素的位置        ///         /// 框架元素        /// 
框架元素所在的位置
protected override Point GetLocation(FrameworkElement element) { return new Point(Canvas.GetLeft(element), Canvas.GetTop(element)); } /// /// 设置框架元素的位置 /// /// 框架元素 /// 新位置 protected override void SetLocation(FrameworkElement element, Point location) { Canvas.SetLeft(element, location.X); Canvas.SetTop(element, location.Y); } #endregion }

代码下载

博客园:

posted on
2019-01-04 00:58 阅读(
...) 评论(
...)

转载于:https://www.cnblogs.com/lonelyxmas/p/10217752.html

你可能感兴趣的文章
[Node.js] ECMAScript 6中的生成器及koa小析
查看>>
自定义图表技术解析
查看>>
c语言打开一个程序
查看>>
图像显示特效
查看>>
apollo配置中心初探
查看>>
elasticsearch配置
查看>>
jquery attr与prop的区别与联系
查看>>
swift基本运算符
查看>>
VMware Ubuntu安装
查看>>
斐波那契数列
查看>>
Web容量规划的艺术-要点
查看>>
c#高级编程第六版读书笔记二(委托)
查看>>
Linux打包压缩与安装卸载
查看>>
C++回调函数和静态成员函数。。。。
查看>>
study1
查看>>
python socket 模拟tcp通讯
查看>>
python初学笔记(三)
查看>>
201671010130 2016-2017-2 《Java程序设计》第十四周学习小结
查看>>
HashSet和HashMap的区别
查看>>
composer下载tp5第三方扩展
查看>>