更新时间:2022-09-17 19:47:54
需求:
图层中有一张图片,可以对该图层进行平移、缩放、旋转操作,现在要求做Undo撤销功能,使得图层回复上一步操作时的状态。
关于图像的平移、缩放、旋转,可以参考在下的另一篇博客的整理:
问题:
C#中系统自带的Undo是针对文字编辑的撤销,而项目需求中是对图层图片的Transform变换属性的修改进行撤销。
思路:
下面定义这样一个命令栈: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栈中!