【C#/WPF】图像变换的Undo撤销——用Stack命令栈
来源:互联网 发布:seo文章代写 编辑:程序博客网 时间:2024/05/21 06:24
需求:
图层中有一张图片,可以对该图层进行平移、缩放、旋转操作,现在要求做Undo撤销功能,使得图层回复上一步操作时的状态。
关于图像的平移、缩放、旋转,可以参考在下的另一篇博客的整理:
http://blog.csdn.net/qq_18995513/article/details/72765269
问题:
C#中系统自带的Undo是针对文字编辑的撤销,而项目需求中是对图层图片的Transform变换属性的修改进行撤销。
思路:
- 图层是自定义的类,图层对象除了包含该图片外,带有大量的属性(比如很多其他自定义的属性),如果做Undo撤销是用一个List集合记录每一步操作时图层的所有属性,那么该List数据会很庞大,且保存了很多做Undo撤销时不需要的属性数据。
- 改为记录各种操作命令Command,比如平移就只记录是平移操作的命令,并记下平移的X,Y值变化量。之后的Undo撤销就是执行反方向平移即可。
- 因为Undo撤销是记录多步骤后,可以一步一步地往回撤,所以考虑改用Stack堆栈数据结构来记录每一步命令(而不是用List线性表)。
- 命令栈的属性设计:
- 一个记录操作类型的枚举属性,如记录当前修改操作是图像放大,则Undo撤销时执行图像缩小。
- 一个记录附加数据的float[]数组,如记录当前修改操作的平移X轴往右移100,Y轴往上移200,则Undo撤销时执行反方向平移,即X轴往左移100,Y轴往下移200。当然,因为该图像可能还有很多其他类型的数据,为了命令栈的通用性,可以把这个数组类型改为Object[],即可存放任意附加数据。
下面定义这样一个命令栈:CommandStack
public class CommandStack{ // 记录操作的类型 public enum CommandType { Move, // 平移 ZoomIn, // 放大 ZoomOut, // 缩小 RotateLeft, // 左转 RotateRight, // 右转 } // 命令栈中存放的元素对象 public class CommandInfo { public Image img { get; set; } // 被操作的前台Image控件 public CommandType CommandType { get; set; } // 操作的类型 public object[] Object { get; set; } // 记录操作的数据 } public static Stack UndoStack; // Undo撤销栈 static CommandStack() { UndoStack = new Stack(); // 构造函数中实例化 } /// <summary> /// 往Undo撤销命令栈中添加一个元素 /// </summary> /// <param name="commandimgType">被操作的Image控件</param> /// <param name="commandType">命令的种类</param> /// <param name="obj">附带的数据</param> public static void Add(Image img, CommandType commandType, object[] obj = null) { CommandInfo commandInfo = new CommandInfo(); commandInfo.Image = img; commandInfo.CommandType = commandType; commandInfo.Object = obj; // 压入栈中,这里没有考虑栈的容量 UndoStack.Push(commandInfo); }}
前台XAML中对该Image控件Transform组:
<Image x:Name="targetImage"> <Image.RenderTransform> <TransformGroup> <TranslateTransform/> <ScaleTransform/> <RotateTransform/> </TransformGroup> </Image.RenderTransform></Image>
平移图像后,将本次平移操作记入命令栈:X轴正方向+100,Y轴正方向+200。
CommandStack.Add(targetImage, CommandType.Move, new object[]{ 100, 200 });
Undo撤销按钮的操作:
public void UndoCommand(){ if (CommandStack.UndoStack.Count == 0) { // 已经撤销到头了 MessageBox.Show("无法再往前撤销了!"); return; } // 栈顶元素出栈,并获得它的引用 CommandStack.CommandInfo commandInfo = CommandStack.UndoStack.Pop() as CommandStack.CommandInfo; // 获得被操作的Image控件 Image img = commandInfo.Image; // 根据操作的类型,分类处理 switch (commandInfo.CommandType) { case CommandStack.CommandType.Move: // 撤销平移,X、Y值取相反的值 double translationX = commandInfo.Parameters[0]; // 注意:是相对于上一次位置的平移,不是相对于原始位置的Offset! double translationY = commandInfo.Parameters[1]; UndoMove(img, translationX, translationY); break; case CommandStack.CommandType.ZoomIn: // 撤销放大,即要缩小 ZoomOut(img); break; case CommandStack.CommandType.ZoomOut: // 撤销缩小,即要放大 ZoomIn(img); break; case CommandStack.CommandType.RotateLeft: // 撤销左转,即要右转 RotateRight(img); break; case CommandStack.CommandType.RotateRight: // 撤销右转,即要左转 RotateLeft(img); break; }}/// <summary>/// 撤销平移/// </summary>/// <param name="img">被操作的前台Image控件</param>/// <param name="translationX">X轴相对于上一次的偏移,不是相对于原始位置!</param>/// <param name="translationY">Y轴相对于上一次的偏移,不是相对于原始位置!</param>private void UndoMove(Image img, double translationX, double translationY){ TransformGroup tg = img.RenderTransform as TransformGroup; var tgnew = tg.CloneCurrentValue(); if (tgnew != null) { TranslateTransform transform = tgnew.Children[0] as TranslateTransform; transform.X += translationX; transform.Y += translationY; // 重新给图像赋值Transform变换属性 img.RenderTransform = tgnew; }}/// <summary>/// 图像缩小/// </summary>/// <param name="img">被操作的前台Image控件</param>public void ZoomOut(Image img){ TransformGroup tg = img.RenderTransform as TransformGroup; var tgnew = tg.CloneCurrentValue(); if (tgnew != null) { ScaleTransform st = tgnew.Children[1] as ScaleTransform; img.RenderTransformOrigin = new Point(0.5, 0.5); if (st.ScaleX >= 0.2) { st.ScaleX -= 0.05; st.ScaleY -= 0.05; } else if (st.ScaleX <= -0.2) { st.ScaleX += 0.05; st.ScaleY -= 0.05; } } // 重新给图像赋值Transform变换属性 img.RenderTransform = tgnew;}/// <summary>/// 图片放大/// </summary>/// <param name="img">被操作的前台Image控件</param>public void ZoomIn(Image img){ TransformGroup tg = img.RenderTransform as TransformGroup; var tgnew = tg.CloneCurrentValue(); if (tgnew != null) { ScaleTransform st = tgnew.Children[1] as ScaleTransform; img.RenderTransformOrigin = new Point(0.5, 0.5); if (st.ScaleX > 0 && st.ScaleX <= 2.0) { st.ScaleX += 0.05; st.ScaleY += 0.05; } else if (st.ScaleX < 0 && st.ScaleX >= -2.0) { st.ScaleX -= 0.05; st.ScaleY += 0.05; } } // 重新给图像赋值Transform变换属性 img.RenderTransform = tgnew;}/// <summary>/// 图片左转/// </summary>/// <param name="img">被操作的前台Image控件</param>public void RotateLeft(Image img){ TransformGroup tg = img.RenderTransform as TransformGroup; var tgnew = tg.CloneCurrentValue(); if (tgnew != null) { RotateTransform rt = tgnew.Children[2] as RotateTransform; img.RenderTransformOrigin = new Point(0.5, 0.5); rt.Angle -= 5; } // 重新给图像赋值Transform变换属性 img.RenderTransform = tgnew;}/// <summary>/// 图片右转/// </summary>/// <param name="img">被操作的前台Image控件</param>public void RotateRight(Image img){ TransformGroup tg = img.RenderTransform as TransformGroup; var tgnew = tg.CloneCurrentValue(); if (tgnew != null) { RotateTransform rt = tgnew.Children[2] as RotateTransform; img.RenderTransformOrigin = new Point(0.5, 0.5); rt.Angle += 5; } // 重新给图像赋值Transform变换属性 img.RenderTransform = tgnew;}
题外话:
如果还想做个Redo重做功能,即跟Undo撤销反向的功能,可以考虑用两个Stack栈。
在CommandStack类中再加一个RedoStack栈,思路是把Undo撤销时UndoStack栈顶移出的元素存放到RedoStack栈中!
阅读全文
0 0
- 【C#/WPF】图像变换的Undo撤销——用Stack命令栈
- 撤销Undo
- Simple Undo/redo library for C#/.NET(简单的C#.Net撤销、重做库)
- 浅谈emacs的撤销undo机制
- 图像的正交变换---沃尔什——哈达马变换
- qt的redo和undo undo撤销(后退),redo取消撤销(前进)
- 设计模式 - 命令模式(command pattern) 撤销(undo) 详解
- 图像变换——对数变换
- 图像变换——仿射变换
- vi的撤销命令
- vi的撤销命令
- 撤销重做(Undo/Redo)
- 【Unity】Undo/撤销
- 【C#/WPF】修改图像的DPI、Resolution
- java记事本编写中 实现撤销的undo用法问题
- 重做(redo)和撤销(undo)的完整实现
- 字符的替换及撤销(Undo操作)
- 图像变换——图像反转
- 我和 flow.ci 的第一次亲密接触
- Linux下wineQQ国际版安装方法
- PHP服务器的网页显示空白
- Android内存泄漏的八种可能(上)
- 解析字符串,每八位的二进制转为十进制
- 【C#/WPF】图像变换的Undo撤销——用Stack命令栈
- android sqlite 判断表和表中字段是否存在方法
- mybaits中dao层的使用方式1:用自动生成工具生成
- NB-IoT频段
- java.sql.BatchUpdateException异常解决
- js浏览器调用qq聊天
- pandas学习(一)
- MII和MDIO接口详解
- 内存管理