Data binding and IsUndoEnabled
-
Friday, November 16, 2007 3:45 AM
why does the XAML:
Code Block<
Window x:Class="WpfApplication4.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <StackPanel> <StackPanel.Resources> <Rect x:Key="foo" X="4"/> </StackPanel.Resources> <TextBox Name="bar" DataContext="{StaticResource foo}" Text="{Binding Path=X}" IsUndoEnabled="False"/> </StackPanel></
Window>result in the following exception?
System.Windows.Markup.XamlParseException occurred
Message="'System.Windows.Data.BindingExpression' value cannot be assigned to property 'Text' of object 'System.Windows.Controls.TextBox'. Cannot use UndoService while it is disabled. Error at object 'System.Windows.Data.Binding' in markup file 'WpfApplication4;component/window1.xaml' Line 10 Position 64."
Source="PresentationFramework"
LineNumber=10
LinePosition=64
NameContext="Text"
StackTrace:
at System.Windows.Markup.XamlParseException.ThrowException(String message, Exception innerException, Int32 lineNumber, Int32 linePosition, Uri baseUri, XamlObjectIds currentXamlObjectIds, XamlObjectIds contextXamlObjectIds, Type objectType)
at System.Windows.Markup.XamlParseException.ThrowException(ParserContext parserContext, Int32 lineNumber, Int32 linePosition, String message, Exception innerException)
at System.Windows.Markup.BamlRecordReader.ThrowException(String message, Exception innerException)
at System.Windows.Markup.BamlRecordReader.SetDependencyComplexProperty(Object currentTarget, BamlAttributeInfoRecord attribInfo, Object o)
at System.Windows.Markup.BamlRecordReader.SetPropertyValueToParent(Boolean fromStartTag, Boolean& isMarkupExtension)
at System.Windows.Markup.BamlRecordReader.SetPropertyValueToParent(Boolean fromStartTag)
at System.Windows.Markup.BamlRecordReader.ReadElementEndRecord(Boolean fromNestedBamlRecordReader)
at System.Windows.Markup.BamlRecordReader.ReadRecord(BamlRecord bamlRecord)
at System.Windows.Markup.BamlRecordReader.Read(Boolean singleRecord)
at System.Windows.Markup.TreeBuilderBamlTranslator.ParseFragment()
at System.Windows.Markup.TreeBuilder.Parse()
at System.Windows.Markup.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, Object parent, Boolean closeStream)
at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
at WpfApplication4.Window1.InitializeComponent() in c:\Users\Michael\Documents\Visual Studio 2008\Projects\WpfApplication4\WpfApplication4\Window1.xaml:line 1
at WpfApplication4.Window1..ctor() in C:\Users\Michael\Documents\Visual Studio 2008\Projects\WpfApplication4\WpfApplication4\Window1.xaml.cs:line 24
InnerException: System.InvalidOperationException
Message="Cannot use UndoService while it is disabled."
Source="PresentationFramework"
StackTrace:
at MS.Internal.Documents.UndoManager.Clear()
at System.Windows.Controls.TextBox.OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
at System.Windows.Controls.TextBox.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, OperationType operationType)
at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, OperationType operationType, Boolean isInternal)
at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
at System.Windows.Markup.BamlRecordReader.SetDependencyValueCore(DependencyObject dependencyObject, DependencyProperty dependencyProperty, Object value)
at System.Windows.Markup.BamlRecordReader.SetDependencyValue(DependencyObject dependencyObject, DependencyProperty dependencyProperty, Object value)
at System.Windows.Markup.BamlRecordReader.SetDependencyComplexProperty(Object currentTarget, BamlAttributeInfoRecord attribInfo, Object o)
InnerException:I is a simplified example, I want to implement Undo accross the application.
Answers
-
Thursday, November 22, 2007 2:14 AM
You can use CommandBinding to wire up your custom undo/redo logic, this will bypass the built-in undo/redo processing. Here is an example:
CommandBinding cb = new CommandBinding();
cb.Command = ApplicationCommands.Undo;
cb.CanExecute += delegate(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = textBox.IsFocused;
};
cb.Executed += delegate(object sender, ExecutedRoutedEventArgs e)
{
//Custom undo processing comes here.
e.Handled = true;
};
textBox.CommandBindings.Add(cb);
Hope this helps
All Replies
-
Tuesday, November 20, 2007 6:28 AM
When a BindingExpression is applied to TextBox.Text property, the undo and redo stack will be cleared. This will cause troubles if the IsUndoEnabled property is set to false. Specifically speaking why do you need to disable undo at this circumstance?
-
Tuesday, November 20, 2007 6:42 PM
I don't want the text box to handle undo/redo. I've implemented a command pattern in the data layer to handle undo. I have other things that need to be on the undo/redo stack, like collection manipulations, and method calls.
-
Wednesday, November 21, 2007 2:49 AM
Well, Then you'd better not to use binding (or other type of stuff such as DynamicResourceExtension etc which will result in an expression applied to the Text property), because applying expression and disabling undo will run into a conflicting situation.
-
Wednesday, November 21, 2007 3:54 AM
I'm sorry, but the bundling of undo and data binding seems really tragic (they seem unrelated to me). Is there a way of intercepting the undo/redo requests before the text boxes handle them? I.e. leaving the IsUndoEnabled value set to true, but bypassing the command?
-
Thursday, November 22, 2007 2:14 AM
You can use CommandBinding to wire up your custom undo/redo logic, this will bypass the built-in undo/redo processing. Here is an example:
CommandBinding cb = new CommandBinding();
cb.Command = ApplicationCommands.Undo;
cb.CanExecute += delegate(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = textBox.IsFocused;
};
cb.Executed += delegate(object sender, ExecutedRoutedEventArgs e)
{
//Custom undo processing comes here.
e.Handled = true;
};
textBox.CommandBindings.Add(cb);
Hope this helps

