none
TextBox shows "old" value after being Coerced when bound to a Dependency Property

    Question

  • Hello,

     

    I am writing a WPF Custom Control.  I want to be able to limit the input for the DependancyProperty "Text".  I have the OnCoerce event handlers connected and this property is bound to the "Text" property on a TextBox in my content template as shown below:

     

    Code Snippet

    <TextBox Name="PART_txtEdit"

    Background="{TemplateBinding Background}"

    BorderBrush="{TemplateBinding BorderBrush}"

    BorderThickness="{TemplateBinding BorderThickness}"

    Text="{Binding RelativeSource={RelativeSource FindAncestor,

    AncestorType={x:Type local:MyCustomControl}},

    Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

     

     

    The binding works as expected.  I am able to set the Text property of the TextBox from my dependency property.  I get notification of property changed when I press a key in the textbox.  My static coerce calls a virtual one (shown below):

     

     

    Code Snippet

    public static object TextCoerce(DependencyObject d, object baseValue) {

    MyCustomControl c = d as MyCustomControl;

    if (edit != null) {

    return c.TextCoerce(baseValue as string);

    } else

    return baseValue;

    }

     

    public virtual object TextCoerce(string value) {

    return (Logic.IsValid(value, true)) ? value : DependencyProperty.UnsetValue;

    }

     

     

    As you can see I use "DependencyProperty.UnsetValue" to indicate that I want to reject the change.  When the databinding is complete my text has the correct value but my textbox has the original input.  For example say this text box only allowed upper-case chars.  If I enter "ABC" everything is fine.  If I enter "ABc" the coerce forces the value back to "AB" but the textbox still shows "ABc".  I can test this by doing this in the main window:

     

    Code Snippet

    <local:MyCustomControl x:Name="myControl" Text="AB" Height="25" Width="150" />

     

    <TextBox Name="TextBox1" Height="25" Width="85" Text="{Binding Path=Text, ElementName=myControl, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

     

     

    The correct behavior exists with TextBox1 where if you enter "ABc" in the "myControl" box only "AB" shows in "TextBox1".  Respectivly, if you enter "ABc" in TextBox1 you will see "ABc" in that textbox but only "AB" in myControl. 

     

    I don't really want to inherit directly from TextBox as the textbox is only a portion of a bigger control.

     

    Any help would be appreciated!

     

    Thanks!

    Nathan Zaugg

     

    Saturday, March 15, 2008 7:55 PM

Answers

  • Even with a binding, the text element is going to present what is typed into it.  You would have to force a target update on the binding when the coersion fails if you want the value to revert to the current DP value.

     

        txtEdit.GetBindingExpression(TextBox.TextProperty).UpdateTarget();

     

    Note that coersion only changes the effective value of the property.  The base value would still be "ABc".

     

     Nathan Zaugg wrote:

    I don't really want to inherit directly from TextBox as the textbox is only a portion of a bigger control.

     

    I would definitely derive from TextBox.  If you are composing several inter-related elements, a UserControl might be better suited for your task.

    Saturday, March 15, 2008 9:09 PM
  • David,

     

     David Cater wrote:

    "Anything that directly uses B gets the coerced value. However, A gets the base value rather than the coerced value.  This is a serious problem for me.  Of course, B shouldn't know anything about who has bound to it, so I don't have any acceptable way to force A to update itself again once it gets the new bound (base) value from B."

     

    I agree 100%, I think the chain of events for Dependency property should never leave the the base value in the control -- I am betting, however, that the reason it doesn't do that is because you could have infinite recursion on your hands.  If your coerce function were to be non-deterministic (i.e. it mutates the value even if it had done so before) then it would leave things in a real mess. 

     

    I think this is something to be looked at for the next version of WPF.  The only two solutions break the look-less aspect of these controls. 

     

    One thing I intend to try is setting Metadata Flags (http://msdn2.microsoft.com/en-us/library/ms753358.aspx scroll to the section Setting Appropriate Metadata Flags) and try setting the AffectsRender flag.  This or one of these other flags might work.

     

    If you come across another solution, let me know!

     

    Thanks!

    Nathan Zaugg

    Monday, March 31, 2008 3:52 PM

All replies

  • Even with a binding, the text element is going to present what is typed into it.  You would have to force a target update on the binding when the coersion fails if you want the value to revert to the current DP value.

     

        txtEdit.GetBindingExpression(TextBox.TextProperty).UpdateTarget();

     

    Note that coersion only changes the effective value of the property.  The base value would still be "ABc".

     

     Nathan Zaugg wrote:

    I don't really want to inherit directly from TextBox as the textbox is only a portion of a bigger control.

     

    I would definitely derive from TextBox.  If you are composing several inter-related elements, a UserControl might be better suited for your task.

    Saturday, March 15, 2008 9:09 PM
  • I'd like to be able to keep the specific visual implementation of the control "lookless".  Is there any way to cause the target to be updated in XAML, using the Command Pattern, or by adding a DataTrigger/EventTrigger?

     

    Sunday, March 16, 2008 4:44 AM
  •  Nathan Zaugg wrote:

    Is there any way to cause the target to be updated in XAML, using the Command Pattern, or by adding a DataTrigger/EventTrigger?

     

    Not without baking that behavior into a custom TextBox derivative. 

     

    The issue at hand is more about behavior (code behind) than it is about look and feel (templating/styling).  That is why I would tend toward creating a custom control.  Then I would try to prevent the invalid text from ever being entered (by filtering input via keystroke filters and overriding OnPreviewTextInput).  That way, you never have to concern yourself with bindings on your Text property.  You can truly be lookless.

     

    If you want to use the binding approach and force a target update for the binding, then I would choose the UserControl option.  Again, a binding target update would fall under behavior, so doing this in the code behind of a user control makes sense.

    Sunday, March 16, 2008 5:37 AM
  • There is still an issue here, though, even if you take the issue of defining a custom UserControl out of the picture.

     

    I have a DependencyProperty A that is bound to DependencyProperty B.  B has a coercion function, as described above.  In my case this has to do with a custom object that implements IScrollInfo, and the coercion ensures that B isn't outside the bounds of the scrollable area.

     

    Anything that directly uses B gets the coerced value. However, A gets the base value rather than the coerced value.  This is a serious problem for me.  Of course, B shouldn't know anything about who has bound to it, so I don't have any acceptable way to force A to update itself again once it gets the new bound (base) value from B. 

     

    Is this simply a bug in the DependencyProperty code?  Presumably the intent should be that if A is bound to B then A will equal B after the binding has been resolved, even if the value of B has to be coerced.

     

    Thanks,

     

    David Cater

    Monday, March 31, 2008 3:31 PM
  • David,

     

     David Cater wrote:

    "Anything that directly uses B gets the coerced value. However, A gets the base value rather than the coerced value.  This is a serious problem for me.  Of course, B shouldn't know anything about who has bound to it, so I don't have any acceptable way to force A to update itself again once it gets the new bound (base) value from B."

     

    I agree 100%, I think the chain of events for Dependency property should never leave the the base value in the control -- I am betting, however, that the reason it doesn't do that is because you could have infinite recursion on your hands.  If your coerce function were to be non-deterministic (i.e. it mutates the value even if it had done so before) then it would leave things in a real mess. 

     

    I think this is something to be looked at for the next version of WPF.  The only two solutions break the look-less aspect of these controls. 

     

    One thing I intend to try is setting Metadata Flags (http://msdn2.microsoft.com/en-us/library/ms753358.aspx scroll to the section Setting Appropriate Metadata Flags) and try setting the AffectsRender flag.  This or one of these other flags might work.

     

    If you come across another solution, let me know!

     

    Thanks!

    Nathan Zaugg

    Monday, March 31, 2008 3:52 PM
  • For my case I simply moved the "coercion" code into the change handler for property B.  It basically looks like this (pseudocode):

     

    private static void BChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) {

    double value = DoCoercion((double) args.NewValue);

    if (value != (double)args.NewValue)

    B = value;

    else

    DoSideEffectsOfValueBeingSet();

    }

     

    So the property changed handler will get called twice, meaning Property A (the bound property) will also get updated twice, once with the out-of-bounds value and once with the coerced value.  That's not a problem for me, but it could be a problem in other situations.  Well, it is a performance issue for me, because when Property A changes some time-consuming things run, and I shouldn't be running that twice.  But Property A does end up with the right value now by doing this and removing the coercion handler from the metadata altogether.

     

    D.

    Monday, March 31, 2008 3:59 PM
  • Dr. WPF + Dave et al,
    I am faced with a similar problem wherein my WPF app has several toggle buttons bound to a bool in live CLR object model.  The source object in the CLR object model implements INotifyPropertyChanged.  Problem is that when a toggle button is clicked it's visual state gets out of sync and does not match the actual bool value in the object model nor the state of all of the other toggle buttons that are bound to that same property.  Do you see any drawbacks with the following proposed target-side general work-around to this problem? 

    It appears that the click event handler for the ToggleButton occurs after all of the dependency property changes settle down.   I don't know if other standard controls come with a similar handler such that this soultion could be applied.

    The idea for the Depedency Property enumumerator comes from another thread: Enumerate Bindings.
    public static class DependencyPropertyHelper  
    {  
        public static void UpdateTargets(DependencyObject element)  
        {  
            if (element == null)  
            {  
                throw new ArgumentNullException("element");  
            }  
     
            LocalValueEnumerator lve = element.GetLocalValueEnumerator();  
     
            while (lve.MoveNext())  
            {  
                LocalValueEntry entry = lve.Current;  
     
                if (BindingOperations.IsDataBound(element, entry.Property))  
                {  
                    BindingExpression be = (entry.Value as BindingExpression);  
                    if(null!=be)  
                    {  
                        be.UpdateTarget();  
                    }  
                    MultiBindingExpression mbe = (entry.Value as MultiBindingExpression);  
                    if(null!=mbe)  
                    {  
                        mbe.UpdateTarget();  
                    }  
                }  
            }  
        }  
    }  
     
    // Target Window  
    public partial class Window1 : Window  
    {  
    //...  
       private void ToggleButton_Click(object sender, RoutedEventArgs e)  
       {  
          DependencyPropertyHelper.UpdateTargets(sender as DependencyObject);  
       }  
    //...  
    }  
     

    Regards,
    -Ron
    Tuesday, July 01, 2008 3:23 PM
  • Even with a binding, the text element is going to present what is typed into it.  You would have to force a target update on the binding when the coersion fails if you want the value to revert to the current DP value.

     

        txtEdit.GetBindingExpression(TextBox.TextProperty).UpdateTarget();

     

    Note that coersion only changes the effective value of the property.  The base value would still be "ABc".


    Unfortunately, this doesn't always work in WPF4 (see this post). One solution is to set SourceTrigger to Explicit and use UpdateSource() instead of UpdateTarget().

    I submitted this to Connect. According to Microsoft, if the coercion undoes the change, the TextBox displays the wrong text, even though its Text property has the correct coerced value.

    • Proposed as answer by DanielRose Monday, October 11, 2010 11:47 AM
    • Edited by DanielRose Wednesday, October 13, 2010 10:31 AM Added link to Connect.
    Monday, October 11, 2010 11:46 AM