WPF 矩形框8个控制点伸缩及拖拽
最近在研发图片控件矩形框8个控制点进行控制边框的大小、位置等信息,之前查阅了相关的信息,比如别人整合的类:ControlResizer 这个类虽然是好,但是很大程度上是有限制,换句话说,它需要你二次更改代码和调整成适应你的代码结构,否则很多边框拖拉的时候无法使用,这也是当时使用的时候很头疼的事情,废话不多说,先上效果图:
如上图所示,分析下:有四个层,第一层是主窗体,第二层是传入的图片控件,第三层是遮罩、第四层也就是控制层(图中显示的可操作的蓝色区域,以下称为裁剪区),注意实现的效果是:
1、直接拖拽移动裁剪区,不能跑到图片外面,并且在裁剪区之外的所有区域需要实时重新蒙上遮罩。
2、拖拉图中的8个裁剪区蓝色控制条,要实现控制条相关方向上的任意拖拉实现伸缩,并且有最小裁剪区域。
3、能根据传入的图片控件信息(图中的包括宽-高-角度(30°),位置信息)进行定位和裁剪。
下面提供个人的方案进行参考,不一定是最佳方案,不过比对了很多人的控制点方案,我觉得这个可移植性比较高,适用于轻量级的操作,网上的都是各种类与类引用,然后一堆的与Windows自带的东西结合比如Thumb类。相对复杂。不利于学习研究之用,总之我们只要掌握了原理,接下来解决问题就是比较容易。
解决方案步骤:
一:创建构造函数(模拟图片控件信息,实际项目中可自行传入信息仅供参考)。
示例:
/// <summary> /// 构造一个图片控件函数包含位置、角度等信息 /// </summary> public class StructureSource { public Point Images_Point { get; set; } public double ImageAngle; public double ImageHeight; public double ImageWidth; public BitmapImage ImageSource; public StructureSource() { Images_Point = new Point(200, 100); ImageAngle = 30.0; ImageSource = new BitmapImage(new Uri("C:\\Users\\Administrator\\Desktop\\CompanyLogo\\XXX.PNG")); ImageHeight = ImageSource.Height; ImageWidth = ImageSource.Width; } }
二:准备主窗体,主窗体需要准备好结构,前面提到了这种伸缩性的功能,它需要放在Canvas容器中进行操作,所以一定要注意它的结构性。
三:准备好一个用户控件(在图中是蓝色控制条部分)。
四:初始化主窗体,包含根据图片信息定位,角度等,遮罩,进行裁剪。
示例:
this.Loaded += (sender, ex) => { ImageItem = new StructureSource(); this._Images.Source = ImageItem.ImageSource; this._Images.Height = ImageItem.ImageHeight; this._Images.Width = ImageItem.ImageWidth; //定位+旋转角度 Matrix m = this._Images.RenderTransform.Value; m.OffsetX = ImageItem.Images_Point.X; m.OffsetY = ImageItem.Images_Point.Y; m.RotateAt(ImageItem.ImageAngle, ImageItem.Images_Point.X, ImageItem.Images_Point.Y); this._Images.RenderTransform = new MatrixTransform(m); //设置背景为黑色 _GridBackGround.Background = Brushes.Black; //添加内容 _ContentObject = new CuttingControl(); _Content.Children.Add(_ContentObject); _ContentObject.SizeChanged += _ContentObject_SizeChanged; _ContentObject.BackGroundDrag += _ContentObject_BackGroundDrag; _Content.Height = ImageItem.ImageHeight; _Content.Width = ImageItem.ImageWidth; //定位+翻转角度 Matrix m2 = this._Content.RenderTransform.Value; m2.OffsetX = ImageItem.Images_Point.X; m2.OffsetY = ImageItem.Images_Point.Y; m2.RotateAt(ImageItem.ImageAngle, m2.OffsetX, m2.OffsetY); this._Content.RenderTransform = new MatrixTransform(m2); //对选定的区域进行裁剪 CroppedRegionMethod(this._Content.RenderTransform); };
五:核心功能代码。拖拽部分(采用矩阵拖拽)
/// <summary> /// 移动具体方法 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void TryToMoveForward(object sender, MouseEventArgs e) { CroppedRegionEndPoint = e.GetPosition((UIElement)this.Parent); var MatrixCroppedRegion = this.RenderTransform.Value; var offsetX = CroppedRegionEndPoint.X - CroppedRegionStartPoint.X; var OffsetY = CroppedRegionEndPoint.Y - CroppedRegionStartPoint.Y; if (offsetX <= 0) { offsetX = 0; } else { if (offsetX + this.ActualWidth >= ((UIElement)this.Parent as FrameworkElement).Width) { offsetX = ((UIElement)this.Parent as FrameworkElement).Width - this.ActualWidth; } } if (OffsetY <= 0) { OffsetY = 0; } else { if (OffsetY + this.ActualHeight >= ((UIElement)this.Parent as FrameworkElement).Height) { OffsetY = ((UIElement)this.Parent as FrameworkElement).Height - this.ActualHeight; } } MatrixCroppedRegion.OffsetX = offsetX; MatrixCroppedRegion.OffsetY = OffsetY; this.RenderTransform = new MatrixTransform(MatrixCroppedRegion); ClippedRegionChanged(this.RenderTransform, e); }
六:核心代码(控制条拖拉、伸缩)
示例:
/// <summary> /// 尝试拖拽边框 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void TryToMoveBoarder(object sender, MouseEventArgs e) { CroppedBoarderEndPoint = e.GetPosition((UIElement)this.Parent); //控制裁剪区域大小 var OffsetX = CroppedBoarderEndPoint.X - CroppedBoarderStartPoint.X; var OffsetY = CroppedBoarderEndPoint.Y - CroppedBoarderStartPoint.Y; //控制裁剪区域位置 var MoveX = CroppedBoarderEndPoint.X - CroppedBoarderPanningStartPoint.X; var MoveY = CroppedBoarderEndPoint.Y - CroppedBoarderPanningStartPoint.Y; //放大拖拉边框 switch (sender) { case "_LeftTop"://OK(研发) OK(修正) OK(测试) #region --左上角控制点-- if (CroppedRegionWidth - OffsetX >= this.MinWidth) { //防止宽度先到达临界点而高度不能变化 this.Width = CroppedRegionWidth - OffsetX; TryToMoveChangingBoarder("_LeftTop", MoveX, MoveY, "X"); //修正超出部分 if (this.RenderTransform.Value.OffsetX <= 0) { this.Width = this.Width + MoveX; } } if (CroppedRegionHeight - OffsetY >= this.MinHeight) { this.Height = CroppedRegionHeight - OffsetY; TryToMoveChangingBoarder("_LeftTop", MoveX, MoveY, "Y"); //修正超出部分 if (this.RenderTransform.Value.OffsetY <= 0) { this.Height = this.Height + MoveY; } } #endregion break; case "_CenterTop"://OK(研发) OK(修正) OK(测试) #region --顶中部控制点-- if (CroppedRegionHeight - OffsetY < this.MinHeight) return; this.Height = CroppedRegionHeight - OffsetY; TryToMoveChangingBoarder("_CenterTop", MoveX, MoveY, ""); //修正超出部分 if (this.RenderTransform.Value.OffsetY <= 0) { this.Height = this.Height + MoveY; } #endregion break; case "_RightTop"://OK(研发) OK(修正) OK(测试) #region --右上角控制点-- if (CroppedRegionWidth + OffsetX < this.MinWidth) { //防止宽度先到达临界点而高度不能变化 if (MoveY >= 0) { if (CroppedRegionHeight - OffsetY < this.MinHeight) return; this.Height = CroppedRegionHeight - OffsetY; TryToMoveChangingBoarder("_RightTop", MoveX, MoveY, ""); } } else { this.Width = CroppedRegionWidth + OffsetX; if (CroppedRegionHeight - OffsetY >= this.MinHeight) { this.Height = CroppedRegionHeight - OffsetY; TryToMoveChangingBoarder("_RightTop", MoveX, MoveY, ""); //修正超出部分 if (this.RenderTransform.Value.OffsetY <= 0) { this.Height = this.Height + MoveY; } } } #endregion break; case "_LeftCenter"://OK(研发) OK(修正) OK(测试) #region --左中部控制点-- if (CroppedRegionWidth - OffsetX < this.MinWidth) return; this.Width = CroppedRegionWidth - OffsetX; TryToMoveChangingBoarder("_LeftCenter", MoveX, MoveY, ""); //修正超出部分 if (this.RenderTransform.Value.OffsetX <= 0) { this.Width = this.Width + MoveX; } #endregion break; case "_RightCenter"://OK(研发) OK(修正) OK(测试) #region --右中部控制点-- if (CroppedRegionWidth + OffsetX < this.MinWidth) return; this.Width = CroppedRegionWidth + OffsetX; #endregion break; case "_LeftBottom": //OK(研发) OK(修正) OK(测试) #region --左下角控制点-- if (CroppedRegionWidth - OffsetX >= this.MinWidth) { //防止宽度先到达临界点而高度不能变化 this.Width = CroppedRegionWidth - OffsetX; TryToMoveChangingBoarder("_LeftBottom", MoveX, MoveY, "X"); //修正超出部分 if (this.RenderTransform.Value.OffsetX <= 0) { this.Width = this.Width + MoveX; } } if (CroppedRegionHeight + OffsetY >= this.MinHeight) { this.Height = CroppedRegionHeight + OffsetY; } #endregion break; case "_CenterBottom"://OK(研发) OK(修正) OK(测试) #region --底中部控制点-- if (CroppedRegionHeight + OffsetY < this.MinHeight) return; this.Height = CroppedRegionHeight + OffsetY; #endregion break; case "_RightBottom"://OK(研发) OK(修正) OK(测试) #region --右下角控制点-- if (CroppedRegionWidth + OffsetX < this.MinWidth) { //防止宽度先到达临界点而高度不能变化 if (CroppedRegionHeight + OffsetY < this.MinHeight) return; this.Height = CroppedRegionHeight + OffsetY; } else { this.Width = CroppedRegionWidth + OffsetX; if (CroppedRegionHeight + OffsetY >= this.MinHeight) { this.Height = CroppedRegionHeight + OffsetY; } } #endregion break; } #region --限定边界范围针对无动画,修正长宽-- if (this.RenderTransform.Value.OffsetX + this.Width >= ((UIElement)this.Parent as FrameworkElement).Width) this.Width = ((UIElement)this.Parent as FrameworkElement).Width - this.RenderTransform.Value.OffsetX; if (this.RenderTransform.Value.OffsetY + this.Height >= ((UIElement)this.Parent as FrameworkElement).Height) this.Height = ((UIElement)this.Parent as FrameworkElement).Height - this.RenderTransform.Value.OffsetY; #endregion if (this.Height >= this.MinHeight) ClippedRegionChanged(this.RenderTransform, e); } /// <summary> /// 矩阵平移 /// </summary> /// <param name="sender"></param> /// <param name="MoveX">平移的X轴距离</param> /// <param name="MoveY">平移的Y轴距离</param> private void TryToMoveChangingBoarder(string sender, double MoveX, double MoveY, string DirectionXY) { var m = this.RenderTransform.Value; switch (sender) { case "_LeftTop": if (DirectionXY == "X") m.OffsetX = MoveX; else { if (MoveY <= 0) m.OffsetY = 0; else m.OffsetY = MoveY; } break; case "_CenterTop": if (MoveY <= 0) m.OffsetY = 0; else m.OffsetY = MoveY; break; case "_RightTop": if (MoveY <= 0) m.OffsetY = 0; else m.OffsetY = MoveY; break; case "_LeftCenter": m.OffsetX = MoveX; break; case "_LeftBottom": if (DirectionXY == "X") m.OffsetX = MoveX; else m.OffsetY = MoveY; break; } //限制边框不出裁剪区域 if (m.OffsetX < 0) m.OffsetX = 0; this.RenderTransform = new MatrixTransform(m); }
目前已经整合成用户控件,方便之后的项目进行引用。这边提供了核心的拖拽,伸缩代码,作为笔记方便以后进行查阅。