C# wpf 实现自定义撤销重做功能
新建wpf项目,新建Undoable.cs(操作记录),main.xaml(页面)
using System; using System.Collections.Generic; namespace WpfApp1 { /// <summary> /// 撤销重做对象 /// ceate by xin 2022.6.26 /// 2023.9.13去除Step的定义替换为KeyValuePair简化实现 /// </summary> public class Undoable { /// <summary> /// 撤销 /// </summary> public void Undo() { if (index > -1) _steps[index--].Key(); } /// <summary> /// 重做 /// </summary> public void Redo() { if (index < _steps.Count - 1) _steps[++index].Value(); } /// <summary> /// 清除 /// </summary> public void Clear() { _steps.Clear(); index = -1; } /// <summary> /// 添加操作 /// </summary> /// <param name="undo">撤销</param> /// <param name="redo">重做</param> public void Add(Action undo, Action redo) { if (++index < _steps.Count) _steps.RemoveRange(index, _steps.Count - index); _steps.Add(new KeyValuePair<Action, Action>(undo, redo)); } //记录的步骤 List<KeyValuePair<Action, Action>> _steps = new List<KeyValuePair<Action, Action>>(); //当前操作的下标 int index = -1; } }
<Window x:Class="WpfApp1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp1" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <StackPanel Orientation="Horizontal" VerticalAlignment="Top" > <Button Width="70" Height="20" Content="撤销" Click="Button_Click"></Button> <Button Width="70" Height="20" Content="重做" Click="Button_Click_1"></Button> </StackPanel> <Rectangle Width="240" Height=" 120" RadiusX="20" RadiusY="20" Fill="RoyalBlue" PreviewMouseDown="Rectangle_PreviewMouseDown" PreviewMouseMove="Rectangle_PreviewMouseMove" PreviewMouseUp="Rectangle_PreviewMouseUp"></Rectangle> <Ellipse HorizontalAlignment="Left" Width="120" Height=" 120" Fill="GreenYellow" PreviewMouseDown="Rectangle_PreviewMouseDown" PreviewMouseMove="Rectangle_PreviewMouseMove" PreviewMouseUp="Rectangle_PreviewMouseUp"></Ellipse> </Grid> </Window>
using System.Windows; using System.Windows.Input; namespace WpfApp1 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { Undoable _undoable = new Undoable(); //鼠标是否按下 bool _isMouseDown = false; //鼠标按下的位置 Point _mouseDownPosition; //鼠标按下控件的Margin Thickness _mouseDownMargin; public MainWindow() { InitializeComponent(); } private void Rectangle_PreviewMouseDown(object sender, MouseButtonEventArgs e) { var c = sender as FrameworkElement; _isMouseDown = true; _mouseDownPosition = e.GetPosition(this); _mouseDownMargin = c.Margin; c.CaptureMouse(); } private void Rectangle_PreviewMouseMove(object sender, MouseEventArgs e) { if (_isMouseDown) { var c = sender as FrameworkElement; var pos = e.GetPosition(this); var dp = pos - _mouseDownPosition; c.Margin = new Thickness(_mouseDownMargin.Left + dp.X, _mouseDownMargin.Top + dp.Y, _mouseDownMargin.Right - dp.X, _mouseDownMargin.Bottom - dp.Y); } } private void Rectangle_PreviewMouseUp(object sender, MouseButtonEventArgs e) { var c = sender as FrameworkElement; _isMouseDown = false; c.ReleaseMouseCapture(); var oldMargin = _mouseDownMargin; var newMargin = c.Margin; _undoable.Add(() => { //撤销 c.Margin = oldMargin; }, () => { //重做 c.Margin = newMargin; }); } private void Button_Click(object sender, RoutedEventArgs e) { _undoable.Undo(); } private void Button_Click_1(object sender, RoutedEventArgs e) { _undoable.Redo(); } } }