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