The Control Local Values Bug Solution and new WPF 4.0 related APIs


Intro

Previously I did a post on the “Control Local Values bug” and how a subtle bug can be introduced when setting dependency properties of controls to local values.  In WPF 4.0 (dev10), a mechanism was added to the property engine to solve this problem.  Here are the new APIs in dev10:



public class DependencyObject



   // Sets the value of a dependency property without changing its value source.


   public void SetCurrentValue(DependencyProperty dp, object value);
}

 



public struct ValueSource


   public bool IsCurrent { get; }


}


 


Basically, when using DependencyObject.SetCurrentValue instead of DependencyObject.SetValue you set its effective value but its ValueSource remains the same.  This is very similar to coercion. 


Sample Test App


To make it more clear, I wrote a sample test app that shows the BaseValueSource and ValueSource properties when changing a DependencyProperty (DP) via SetCurrentValue.  I created a custom control with a DP, TitleProperty, where I track its values.  In addition, I setup several scenarios where the TitleProperty is initially set by a particular BaseValueSource.  I’ll go through one scenario here. 


I setup the custom control where the TitleProperty is set from a Style with a binding.



<Style x:Key=”StyleBindingCustomControlStyle” TargetType=”{x:Type local:CustomControl}”>


   <Setter Property=”Title”


           Value=”{Binding RelativeSource={RelativeSource Self},


                           Path=WorkingTag,


                           StringFormat=’Set via Style w/ Binding: {0}’}” />


   <Setter Property=”Template”>


       <Setter.Value>


         <ControlTemplate TargetType=”{x:Type local:CustomControl}”>


            <Button Content=”{TemplateBinding Title}”


                    Background=”{TemplateBinding Background}” />


         </ControlTemplate>


       </Setter.Value>


   </Setter>


</Style>

 



<GroupBox Header=”Style Binding”>


   <local:CustomControl x:Name=”CustomControl_Style_Binding”


                        Style=”{StaticResource StyleBindingCustomControlStyle}”/>


</GroupBox>


 


Initially it will look like this:


 CLV_1


Notice the BaseValueSource is ‘Style’, IsExpression is ‘True’, and IsCurrent is ‘False’ as expected.  After pressing the “Set Current Value” button the values will update accordingly:


 CLV_2


Now the local value has updated to a new string that I set and IsCurrent has updated to ‘True’.  Also notice that BaseValueSource and IsExpression have not changed which is also expected.  If you press “Clear Local Value” the values will update back to their initial values.  Now to bring back the original issue with the control local values bug, if you were to set or clear the local value any previous BaseValueSource would be overwritten and any expression lost.  I have created other scenarios in the sample with different BaseValueSource behavior so you can understand how setting the current value behaves in those scenarios.  Please be sure to check it out below.


Usage Recommendation


For a control developer, the general recommendation is to always use DependencyObject.SetCurrentValue over DependencyObject.SetValue in Control code.  You’ll notice that our stock controls in the 4.0 framework have all been updated to use this API instead of setting the properties with local values.


Here is the sample app which works with VS 2010 beta 1.

ControlLocalValuesSample.zip

Comments (8)

  1. PaulF says:

    Please don’t confuse lack of comments with lack of interest in what you are posting.  

    That is a pretty easy thing to do.

    This is one of my favorite msdn blogs, but…., I don’t really have anything to add to what you said in this post or others aside from please keep it up.

    Great work keeping all of us developers informed on what is going on at Microsoft.

    Also, I am very happy to see Microsoft fixing issues like this.

  2. Rune says:

    I just discovered this blog. I’ve bookmarked it for future refernce and I agree with what Paul said.

    (I came here because I’m trying to discover how to enable deferred scrolling in the datagrid)

  3. Fahad says:

    This only solves if we use .NET 4.0 beta (or future versions), but how do we solve it in .NET 3.5 FW, this seems to be rather trivial that I do not wish to upgrade to VS2010 yet, then how do i do it? Do we simply reset the BindingExpression?

    var be = tb.GetBindingExpression(TextBox.TextProperty);

    // make my changes in internally

    // reset the BindingExpression

    tb.SetBinding(TextBox.TextProperty, be)

    I just thought of this idea, What do you think about it?

  4. Fahad,

    The solution you propose make work for a simple problem but it does not scale for many of the common scenarios.  Take the DatePicker example that I talk about in the previous blog post, http://blogs.msdn.com/vinsibal/archive/2009/03/24/the-control-local-values-bug.aspx.  In OnIsDropDownChanged a member, _popUp.IsOpen is set to a local value.  How would you undo that?  From the outside you need to get access to _popUp which may or may not be possible and you need to know the kind of expression and ValueSource on IsOpen prior to it being set to a local value.  If you knew exactly where this would happen in code and had access to everything then it may work.  Since you don’t have access to a lot of the Control specific implementation, it makes it impossible to work around this issue.  So unfortunately, there is not work around this issue in pre 4.0 bits.

  5. Fahad says:

    Thanks for your views Vincent, I agree with your point!!!