【问题标题】:Custom DependencyProperty set from DataTemplate从 DataTemplate 设置的自定义 DependencyProperty
【发布时间】:2014-09-29 18:38:51
【问题描述】:

我正在使用具有多个用户定义的依赖属性的自定义控件。我遇到了this question 中描述的相同问题。

我的控件正在其构造函数中设置自定义依赖属性的默认值。当我在DataTemplate 中使用控件时,始终使用在构造函数中设置的值,即使我尝试在 XAML 中设置它。

链接问题的答案解释说,在 C# 代码中设置的值具有更高的优先级,更好的方法是在依赖项属性的元数据中指定默认值。

就我而言,我无法指定默认值,因为依赖属性没有适用于所有情况的单个默认值。默认值依赖于另一个属性,所以我必须在创建控件时而不是在注册属性时查找它们。

这里有一些代码可以帮助说明我的问题:

public partial class MyControl : UserControl
{
    public static readonly DependencyProperty MyProperty = 
            DependencyProperty.Register(
                "MyProperty",
                typeof(int),
                typeof(MyControl),
                new FrameworkPropertyMetadata(
                    int.MinValue,
                    FrameworkPropertyMetadataOptions.None,
                    new PropertyChangedCallback("OnMyPropertyChanged")));

    public MyControl() : base()
    {
        InitializeComponent();
        this.MyProperty = GetDefaultPropertyValue();
    }

    public int MyProperty
    {
        get { return (int)GetValue(MyProperty); }
        set { SetValue(MyProperty, value); }
    }

    private int GetDefaultPropertyValue()
    {
        // look up the appropriate default based on some other criteria
        return 42;

        // (in reality, the default value for "MyProperty"
        // depends on the value of a "Mode" custom DependencyProperty.
        // this is just hard coded for testing)
    }   
}

XAML 用法如下所示:

<!-- View displays 4 (desired) -->
<local:MyControl MyProperty="4" />

<!-- View displays default of 42 (desired) -->
<local:MyControl />

<!-- View displays default of 42 (wanted 4) -->
<DataTemplate x:Key="MyTemplate">
    <local:MyControl MyProperty="4"/>
</DataTemplate>

总结一下:

所需的行为是首先使用来自 XAML 的值。如果 XAML 中未指定该值,那么我想回退到控件构造函数中设置的默认值。

如果我只是将控件直接包含在视图中,我会得到预期的行为。如果控件在DataTemplate 中使用,那么我总是在构造函数中获得默认设置(即使数据模板显式设置了另一个值)。

在模板中使用控件时,还有其他方法可以指定默认值吗?我能想到的唯一选择是将控件分解为几个单独但相似的控件,每个控件都使用使用依赖属性注册的默认值(这消除了基于@987654326 设置默认值的需要@ 属性)。

【问题讨论】:

    标签: c# wpf xaml datatemplate


    【解决方案1】:

    OnApplyTemplate 中设置默认值,同时添加一个小检查应该可以解决这个问题:

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
    
        // Only set the default value if no value is set.
        if (MyProperty == (int)MyPropertyProperty.DefaultMetadata.DefaultValue)
        {
            this.MyProperty = GetDefaultPropertyValue();
        }
    }
    

    请注意,虽然这会起作用,但它并不理想,因为通过代码设置属性的值基本上会清除该属性的所有数据绑定。例如,一旦您在代码中调用MyProperty = 42,以下绑定将不再起作用:

    <local:MyControl MyProperty="{Binding SomeProperty}" />
    

    应该可以通过使用SetCurrentValue(MyPropertyProperty, GetDefaultPropertyValue());而不是MyProperty = GetDefaultPropertyValue()修改属性来设置值,同时保持任何绑定,但我也不确定我是否喜欢这样。

    更好的解决方案

    我要做的是在现有属性之外引入一个新的只读属性,它将充当计算属性。例如:

    private static readonly DependencyPropertyKey MyCalculatedPropertyPropertyKey =
        DependencyProperty.RegisterReadOnly("MyCalculatedProperty", typeof(int), typeof(MyControl),
        new PropertyMetadata(int.MinValue));
    
    public static readonly DependencyProperty MyCalculatedPropertyProperty = MyCalculatedPropertyPropertyKey.DependencyProperty;
    
    public int MyCalculatedProperty
    {
        get { return (int)GetValue(MyCalculatedPropertyProperty); }
        private set { SetValue(MyCalculatedPropertyPropertyKey, value); }
    }
    
    private static void OnMyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((MyControl)d).MyCalculatedProperty = (int)e.NewValue;
    }
    
    public MyControl()
        : base()
    {
        InitializeComponent();
        MyCalculatedProperty = GetDefaultPropertyValue();
    }
    

    【讨论】:

    • 这确实有效,谢谢!由于我只是通过代码设置属性,如果它没有在 XAML 中设置,那么假设没有数据绑定要中断是否安全? (我们从不在代码中创建绑定)
    • 在您的示例中,您展示了如何在 xaml 中设置属性:&lt;local:MyControl MyProperty="4" /&gt; 如果这是设置属性的唯一方法,并且从不使用绑定(请参阅我的编辑),那么您不必担心about,尽管重写绑定不是一种好的做法。
    • 我知道MyProperty = 42; 会破坏任何绑定。如果我使用的是您在编辑中添加的绑定,那么只要绑定没有评估为MyProperty.DefaultMetadata.DefaultValue,就不会调用该绑定并且绑定不会被破坏,对吗?您能否详细说明您推荐的具有只读属性的解决方案?我不确定我是否跟随。
    • 我得到了更好的解决方案,并接受了您的回答。我不知道这是如何工作的。添加只读依赖属性对原始属性有何影响?
    • this answer中查看SetValueSetCurrentValue之间的区别的更多细节
    猜你喜欢
    • 1970-01-01
    • 2020-02-01
    • 2012-07-25
    • 2011-01-18
    • 1970-01-01
    • 2020-01-18
    • 2023-03-13
    • 2016-03-22
    • 1970-01-01
    相关资源
    最近更新 更多