мне интересно, как лучше всего реализовать пользовательские команды отмены/повтора?
В моем случае я реализую редактор диаграмм и хочу создать команды «Отменить/Повторить» для отмены манипуляций с элементами холста, например, для отмены вращения, перемещения или изменения размера элемента.
Идеи, в которых я не уверен: -Написать собственную реализацию ICommand или производный класс от RelayCommand и хранить команды в основной модели представления в стеках; -Напишите репозиторий, который будет хранить стеки команд в памяти сеанса, и службу, которая будет реализовывать методы Register, Execute и Undo.
Привет Клаус, только для команд
Команда — это не то, на чем стоит сосредоточиться. Основная проблема заключается в представлении различных версий. Связанный список различий или набор просмотров обычно являются опциями.
Если кому-то интересно, вот как я реализовал это для своего случая:
/// <summary>
/// An interface that defines base methods to manage undoable commands.
/// </summary>
public interface IUndoableCommandManager
{
/// <summary>
/// Executes command.
/// </summary>
/// <param name = "command">Target command with execution logic.</param>
public void Execute(IUndoableCommand command);
/// <summary>
/// Cancels the effects of an command execution.
/// </summary>
public void Undo();
/// <summary>
/// Performs command execution again.
/// </summary>
public void Redo();
/// <summary>
/// Clears history of all commands inside manager.
/// </summary>
public void Clear();
}
/// <summary>
/// A class that implements <see cref = "IUndoableCommandManager"/>. Manages undoable commands.
/// </summary>
public sealed class UndoableCommandManager : IUndoableCommandManager
{
private readonly Stack<IUndoableCommand> _undoStack = [];
private readonly Stack<IUndoableCommand> _redoStack = [];
/// <summary>
/// Gets a value indicating whether an undo operation can be performed.
/// </summary>
private bool CanUndo => _undoStack.Count > 0;
/// <summary>
/// Gets a value indicating whether a redo operation can be performed.
/// </summary>
private bool CanRedo => _redoStack.Count > 0;
/// <inheritdoc/>
public void Execute(IUndoableCommand command)
{
command.Execute(null);
_undoStack.Push(command);
_redoStack.Clear();
}
/// <inheritdoc/>
public void Undo()
{
if (CanUndo)
{
var command = _undoStack.Pop();
command.Undo();
_redoStack.Push(command);
}
}
/// <inheritdoc/>
public void Redo()
{
if (CanRedo)
{
var command = _redoStack.Pop();
command.Execute(null);
_undoStack.Push(command);
}
}
/// <inheritdoc/>
public void Clear()
{
_undoStack.Clear();
_redoStack.Clear();
}
}
/// <summary>
/// An interface expanding <see cref = "ICommand"/> with undo behavior.
/// </summary>
public interface IUndoableCommand : ICommand
{
/// <summary>
/// Undoes previous execution.
/// </summary>
void Undo();
}
/// <summary>
/// Initializes a new instance of the <see cref = "UndoableCommand"/> class that can undo previous execution.
/// </summary>
public sealed class UndoableCommand : IUndoableCommand
{
private readonly Action _execute;
private readonly Action _undo;
/// <inheritdoc/>
public event EventHandler? CanExecuteChanged;
/// <summary>
/// Initializes a new instance of the <see cref = "UndoableCommand"/> class.
/// </summary>
/// <param name = "execute">The execution logic.</param>
/// <param name = "undo">The undo behavior logic.</param>
public UndoableCommand(Action execute, Action undo)
{
ArgumentNullException.ThrowIfNull(execute);
ArgumentNullException.ThrowIfNull(undo);
_execute = execute;
_undo = undo;
}
/// <inheritdoc/>
public bool CanExecute(object? parameter = null)
{
return true;
}
/// <inheritdoc/>
public void Execute(object? parameter = null)
{
_execute();
}
/// <inheritdoc/>
public void Undo()
{
_undo();
}
}
Отменить/Повторить можно только команды или, например, изменения свойств?