Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>So, I think the ViewModel Undo/Redo article is a good one, but it's as much as about the ViewModel pattern as it is about how to write custom Undo/Redo functionality. Also, in response to confusedGeek, I think there could be examples where undoing changes in your model, not just in your individual controls is appropriate (say you had a textbox and a slider both bound to the sample property, you want to undo a change regardless of which control made it, so we're talking about app level undo instead of control level).</p> <p>So given that, here is a simple, if not somewhat kludgey example of doing precisely what you ask using a CommandBinding and a simplistic undo stack:</p> <pre><code>public partial class MainWindow : Window { public static readonly DependencyProperty MyStringProperty = DependencyProperty.Register("MyString", typeof(String), typeof(MainWindow), new UIPropertyMetadata("")); // The undo stack Stack&lt;String&gt; previousStrings = new Stack&lt;String&gt;(); String cur = ""; // The current textbox value Boolean ignore = false; // flag to ignore our own "undo" changes public String MyString { get { return (String)GetValue(MyStringProperty); } set { SetValue(MyStringProperty, value); } } public MainWindow() { InitializeComponent(); this.LayoutRoot.DataContext = this; // Using the TextChanged event to add things to our undo stack // This is a kludge, we should probably observe changes to the model, not the UI this.Txt.TextChanged += new TextChangedEventHandler(Txt_TextChanged); // Magic for listening to Ctrl+Z CommandBinding cb = new CommandBinding(); cb.Command = ApplicationCommands.Undo; cb.CanExecute += delegate(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = true; }; cb.Executed += delegate(object sender, ExecutedRoutedEventArgs e) { if (previousStrings.Count &gt; 0) { ignore = true; this.Txt.Text = previousStrings.Pop(); ignore = false; } e.Handled = true; }; this.CommandBindings.Add(cb); } void Txt_TextChanged(object sender, TextChangedEventArgs e) { if (!ignore) { previousStrings.Push(cur); } cur = this.Txt.Text; } private void SetStr_Click(object sender, RoutedEventArgs e) { this.MyString = "A Value"; } } </code></pre> <p>And here is the XAML:</p> <pre><code>&lt;Window x:Class="TestUndoBinding.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"&gt; &lt;StackPanel Name="LayoutRoot"&gt; &lt;TextBox Name="Txt" Text="{Binding Path=MyString, Mode=TwoWay}" /&gt; &lt;Button Name="SetStr" Click="SetStr_Click"&gt;Set to "A Value"&lt;/Button&gt; &lt;/StackPanel&gt; &lt;/Window&gt; </code></pre> <p>In this example the behavior is slightly different than typical TextBox undo behavior because 1) I'm ignoring selection, and 2) I'm not grouping multiple keystrokes into a single undo step, both of which are things you would want to consider in a real app, but should be relatively straightforward to implement yourself.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload