开发者

How do I reset a DependencyProperty back to it's default value in XAML

I'm implementing a user-adjus开发者_StackOverflow中文版table Effect using sliders and I have a reset button beside the slider. The idea is to allow the user to reset back to the default value of the Effect's property as specified in metadata.

I think it might be trivial to do that in XAML.


Dependency properties don't really have default values. If a dependency property doesn't have a local value, it will obtain its value either through value inheritance or through coercion, depending on how the property has been implemented.

You can't really get rid of the property's local value in XAML - that would require you to call ClearValue on the property, and there's no way to find the object and call a method on it declaratively. But as long as the property's getting its value through value inheritance (instead of value coercion) - you can accomplish fundamentally the same thing by binding the property to the property it's inheriting from on the appropriate ancestor

For instance, here's a style you'd use to create a ListBox that sets the foreground color of all items that aren't selected:

<ListBox.ItemContainerStyle>
  <Style TargetType="ListBoxItem">
    <Setter Property="Foreground" Value="Red"/>
    <Style.Triggers>
        <Trigger Property="IsSelected" Value="True">
            <Setter 
               Property="Foreground" 
               Value="{Binding 
                  RelativeSource={RelativeSource FindAncestor, AncestorType=ListBox},
                  Path=Foreground}" />
        </Trigger>
    </Style.Triggers>
  </Style>
</ListBox.ItemContainerStyle>

This basically explicitly implements value inheritance through binding. It does mean that the ListBoxItem.Foreground property now has a local value, and that whenever you change the Foreground property on the ListBox it's binding that updates the ListBoxItem.Foreground and not the dependency-property system. This might actually matter if you have hundreds of thousands of items in your ListBox. But in most real-world cases, you'll never notice the difference.


In order to avoid logic cycles, WPF strongly resists attempts to set a DependencyProperty value to its current value. This is a problem during initialization, where you'd like to have dependent logic triggered to set everything up for the first time based on the DefaultValue recorded in the metadata, for each of the various dependent properties. WPF won't do it because, as a special case, all those properties already obtain their default values without ever having executed any such logic.

As far as I can tell, no combination of InvalidateProperty, CoerceValue, or ClearValue will convince WPF to do the work. A trivial fix would be to somehow conjure a hopefully harmless non-default value to change it to, and then after that, reset the value via ClearValue. This seems hacky, though, since it may not be practical to determine a "harmless" value. Perhaps worse, this approach will unnecessarily ripple the dependency graph twice, instead of just once.

As a perhaps more elegant solution, you can call the following extension method in the constructor of each relevant object to manually invoke the PropertyChanged logic for each DependencyProperty that needs to have its changes propagated. This helper will trigger follow-on changes according the default value stored in the respective metadata.

public static void ResetValue(this DependencyObject _this, DependencyProperty dp)
{
    var md = dp.GetMetadata(_this.GetType());
    if (_this.GetValue(dp).Equals(md.DefaultValue))
    {
        var args = new DependencyPropertyChangedEventArgs(dp, DependencyProperty.UnsetValue, md.DefaultValue);
        md.PropertyChangedCallback(_this, args);
    }
    else
        _this.ClearValue(dp);
}

As shown, the function includes a check to see if ClearValue would in fact be effective, so beyond initialization scenarios, you can also use this function as a replacement for ClearValue in general. For clarity of demonstration, this method does not verify that the host object in fact exposes any property-change logic, but this would be a simple change. In C# 7, for example:

        // ...
        md.PropertyChangedCallback?.Invoke(_this, args);

Example usage in your class:

class MyClass : DependencyObject
{
    public static readonly DependencyProperty MyDp = /* ... */

    public MyClass()
    {
        this.ResetValue(MyDp);
        /* ... */
    }
};
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜