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(, );
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 <= )
{
offsetX = ;
}
else
{
if (offsetX + this.ActualWidth >= ((UIElement)this.Parent as FrameworkElement).Width)
{
offsetX = ((UIElement)this.Parent as FrameworkElement).Width - this.ActualWidth;
}
}
if (OffsetY <= )
{
OffsetY = ;
}
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 <= )
{
this.Width = this.Width + MoveX;
}
} if (CroppedRegionHeight - OffsetY >= this.MinHeight)
{
this.Height = CroppedRegionHeight - OffsetY;
TryToMoveChangingBoarder("_LeftTop", MoveX, MoveY, "Y");
//修正超出部分
if (this.RenderTransform.Value.OffsetY <= )
{
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 <= )
{
this.Height = this.Height + MoveY;
} #endregion
break;
case "_RightTop"://OK(研发) OK(修正) OK(测试)
#region --右上角控制点--
if (CroppedRegionWidth + OffsetX < this.MinWidth)
{
//防止宽度先到达临界点而高度不能变化
if (MoveY >= )
{
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 <= )
{
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 <= )
{
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 <= )
{
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 <= )
m.OffsetY = ;
else
m.OffsetY = MoveY;
}
break;
case "_CenterTop":
if (MoveY <= )
m.OffsetY = ;
else
m.OffsetY = MoveY;
break;
case "_RightTop":
if (MoveY <= )
m.OffsetY = ;
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 < )
m.OffsetX = ; this.RenderTransform = new MatrixTransform(m);
}
目前已经整合成用户控件,方便之后的项目进行引用。这边提供了核心的拖拽,伸缩代码,作为笔记方便以后进行查阅。
WPF 矩形框8个控制点伸缩及拖拽的更多相关文章
- Javascript实现鼠标框选元素后拖拽被框选的元素
之前需要做一个框选元素后拖拽被框选中的元素功能,在网上找资料做了一些修改,基本达到了需要的效果,希望对也需要实现框选后拖拽元素功能的人有用. 页面加载后效果 框选后的内容可以拖拽,如下图: 代码下载
- C# GDI绘制矩形框,鼠标左键拖动可移动矩形框,滚轮放大缩小矩形框
最近工作需要,要做一个矩形框,并且 用鼠标左键拖动矩形框移动其位置.网上查了一些感觉他们做的挺复杂的.我自己研究一天,做了一个比较简单的,发表出来供大家参考一下.如觉得简单,可路过,谢谢.哈哈. 先大 ...
- 【python】PIL 批量绘制图片矩形框工具
工具采用PIL:Python Imaging Library,图像处理标准库.PIL功能非常强大,但API却非常简单易用. 安装PIL 在Debian/Ubuntu Linux下直接通过apt安装 $ ...
- WPF 文本框添加水印效果
有的时候我们需要为我们的WPF文本框TextBox控件添加一个显示水印的效果来增强用户体验,比如登陆的时候提示输入用户名,输入密码等情形.如下图所示: 这个时候我们除了可以修改TextBox控件的控件 ...
- WPF文本框密码框添加水印效果
WPF文本框密码框添加水印效果 来源: 阅读:559 时间:2014-12-31 分享: 0 按照惯例,先看下效果 文本框水印 文本框水印相对简单,不需要重写模板,仅仅需要一个VisualBrush ...
- 如何用 matlab 在图片上绘制矩形框 和 添加文字 ?
如何给图像添加矩形框?以及添加想要输入的文字 ? 案例程序,如下所示: clc; close all; clear all;image = imread('/home/wangxiao/Picture ...
- WPF提示框效果
WPF提示框效果 1,新建WPF应用程序 2,添加用户控件Message 3,在Message中编写如下代码 <Border x:Name="border" BorderTh ...
- Android摄像头:只拍摄SurfaceView预览界面特定区域内容(矩形框)---完整(原理:底层SurfaceView+上层绘制ImageView)
Android摄像头:只拍摄SurfaceView预览界面特定区域内容(矩形框)---完整实现(原理:底层SurfaceView+上层绘制ImageView) 分类: Android开发 Androi ...
- c#在pictureBox控件上绘制多个矩形框及删除绘制的矩形框
在pictureBox上每次只绘制一个矩形框,绘制下一个矩形框时上次绘制的矩形框取消,代码如链接:https://www.cnblogs.com/luxiao/p/5625196.html 在绘制矩形 ...
随机推荐
- Unity3D开发之3D按钮的声音播放
这里我们首先就简易的制作一个非常简单的3D按钮![ 图中就一个cube 加个3DText,然后我们就编写代码 [RequireComponent(typeof(CompoundButton))]//特 ...
- VsCode 的使用
一.简介 VsCode(Visual Studio Code),官网地址:https://code.visualstudio.com/ Visual Studio Code is a lightwei ...
- JavaWeb后台购物车类的具体实现
相信大家肯定都在电商网站买过东西,当我们看中一件喜欢又想买的东西时,这时候你又不想这么快结账,这时候你就可以放入购物车: 就像我们平时去超市买东西一样,会推着购物车去买东西: 那么我们接下来看看jav ...
- DLL加载顺序
最近碰到了一个问题,要引入一个第三方的SDK,但是SDK中使用的一些dll和我原本程序里面有些dll是同名的,而且本程序的dll和sdk的dll名称都不能修改. 解决这个问题,首先想到的就是多进程,这 ...
- 4.3Python数据类型(3)之字符串类型
返回总目录 目录: 1.字符串的概念 2.字符串的形式 3.字符串的转义符 4.字符串一般操作 5.字符串函数操作 (一)字符串的概念 由单个字符组成的一个集合 (二)字符串的形式 双引号与单引号的效 ...
- if 嵌套if 的先后顺序的区别 (自测)
# 验证 verify very good! # username = input("请输入用户名").capitalize()# password = input("请 ...
- 软工实践作业2:个人项目实战之Sudoku
Github:Sudoku 项目相关要求 项目需求 利用程序随机构造出N个已解答的数独棋盘 . 输入 数独棋盘题目个数N(0<N<=1000000). 输出 随机生成N个不重复的已解答完毕 ...
- Sudoku 个人项目1
Github项目地址:Github 项目相关要求 随机构造出N个不重复的已解答的数独棋盘(0 < N <= 1000000) 在生成数独矩阵时,左上角的第一个数为:(学号后两位相加)% 9 ...
- call()和apply()
call()和apply()方法类似,区别是,call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组. 当一个函数在其主体中使用 this 关键字时,可以通过使用 ...
- 常用类(Date,Calendar,Math,枚举)
1.日期时间类 计算机时间戳是指距离历元(1970-01-01 00:00:00:000)的时间间隔(以毫秒ms为单位). 如:计算机时间2019-04-29 14:14:00是该时间距离历元经过的毫 ...