public interface ICommand { void Execute(); void Undo(); } public class CalculatorCommand : ICommand { private Calculator calculator; private char operation; private int operand; public CalculatorCommand(Calculator calculator, char operation, int operand) { this.calculator = calculator; this.operation = operation; this.operand = operand; } public void Execute() { calculator.Operation(operation, operand); } public void Undo() { calculator.Operation(UndoOperation(operation), operand); } private char UndoOperation(char operation) { char undoOperation; switch (operation) { case '+': undoOperation = '-'; break; case '-': undoOperation = '+'; break; case '*': undoOperation = '/'; break; case '/': undoOperation = '*'; break; default: undoOperation = ' '; break; } return undoOperation; } } public class Calculator { private int currentValue; public void Operation(char operation, int operand) { switch (operation) { case '+': currentValue += operand; break; case '-': currentValue -= operand; break; case '*': currentValue *= operand; break; case '/': currentValue /= operand; break; } Console.WriteLine("Current value = {0,3} (following {1} {2})", currentValue, operation, operand); } } public class Client { private Calculator calculator = new Calculator(); private Listcommands = new List (); private int current = 0; public void Redo(int levels) { Console.WriteLine("\n---- Redo {0} levels ", levels); for (int i = 0; i < levels; i++) { if (current < commands.Count - 1) { ICommand command = commands[current++]; command.Execute(); } } } public void Undo(int levels) { Console.WriteLine("\n---- Undo {0} levels ", levels); for (int i = 0; i < levels; i++) { if (current > 0) { ICommand command = commands[--current]; command.Undo(); } } } public void Compute(char @operator, int operand) { ICommand command = new CalculatorCommand(calculator, @operator, operand); command.Execute(); commands.Add(command); current++; } } public static void Main() { Client client = new Client(); client.Compute('+', 100); client.Compute('-', 50); client.Compute('*', 10); client.Compute('/', 2); // Undo 4 commands client.Undo(4); // Redo 3 commands client.Redo(3); // Wait for user Console.ReadKey(); }
using System; using System.Collections.Generic; using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace Commands { public partial class MainWindow : Window { private ListThis code shows how to use the Command pattern with WPF commands. The MainWindow class contains a list of documents and handles the AddDocument and DeleteDocument commands, which create or remove documents from the list. The Document class has a Title property that raises a PropertyChanged event when changed. The Document_PropertyChanged method handles the event and updates the ListBox item if necessary. The CanExecuteAlways, CanAddDocument and CanDeleteDocument methods check if the commands can be executed. Package libraries used: System, System.Collections.Generic, System.ComponentModel, System.Windows, System.Windows.Controls, System.Windows.Input, System.Windows.Media.documents = new List (); private int current = -1; public MainWindow() { InitializeComponent(); } private void AddDocument(object sender, ExecutedRoutedEventArgs e) { Document document = new Document(); DocumentWindow window = new DocumentWindow(); window.DataContext = document; window.Owner = this; document.PropertyChanged += new PropertyChangedEventHandler(Document_PropertyChanged); documents.Add(document); current++; window.Show(); } private void DeleteDocument(object sender, ExecutedRoutedEventArgs e) { if (current >= 0) { Document document = documents[current]; document.PropertyChanged -= Document_PropertyChanged; documents.RemoveAt(current); current--; } } private void Document_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "Title") { Document document = sender as Document; int index = documents.IndexOf(document); ListBoxItem item = listBox.ItemContainerGenerator.ContainerFromItem(document) as ListBoxItem; if (item != null) { TextBlock textBlock = VisualTreeHelper.GetChild(item, 0) as TextBlock; if (textBlock != null) { textBlock.Text = document.Title; } } } } private void CanExecuteAlways(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = true; e.Handled = true; } private void CanAddDocument(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = true; e.Handled = true; } private void CanDeleteDocument(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = (current >= 0); e.Handled = true; } } public class Document { private string title; public string Title { get { return title; } set { title = value; RaisePropertyChanged("Title"); } } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public partial class DocumentWindow : Window { public DocumentWindow() { InitializeComponent(); } private void Ok(object sender, RoutedEventArgs e) { DialogResult = true; Close(); } } public static class CustomCommands { public static RoutedCommand AddDocumentCommand = new RoutedCommand("Add Document", typeof(CustomCommands)); public static RoutedCommand DeleteDocumentCommand = new RoutedCommand("Delete Document", typeof(CustomCommands)); } }