WPF:DependencyProperty 适用于 TextBlock 但不适用于自定义控件

WPF: DependencyProperty works on TextBlock but not on custom control

编辑: 可以找到示例项目 here

我在主 window 中使用 ListBox,稍后我将其绑定到 ObservableCollection。我同时使用 TextBlock 和绑定到集合的相同 属性 的自定义控件。我的问题是 TextBlock 得到正确更新,而自定义控件没有(它是默认构造的,但它的 Text 属性 永远不会被绑定更新)。

<ListBox Name="MyCustomItemList">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding ItemText}"/>
                <local:MyCustomBlock Text="{Binding ItemText}"/>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

我将 MyCustomBlock 实现为 System.Windows.Controls.Canvas 的子级,具有 Text 依赖性 属性:

public class MyCustomBlock : Canvas
{
    public MyCustomBlock() => Text = "<default>";
    public MyCustomBlock(string text) => Text = text;

    private static void TextChangedCallback(DependencyObject o,
                                            DependencyPropertyChangedEventArgs e)
    {
        ...
    }

    public string Text
    {
        get => (string)GetValue(TextProperty);
        set => SetValue(TextProperty, value);
    }

    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register(
              nameof(Text), typeof(string), typeof(MyCustomBlock),
              new FrameworkPropertyMetadata("", TextChangedCallback));
}

最后,这是我在 MainWindow 构造函数中绑定到 ListBox 的数据:

public class MyCustomItem
{
    public MyCustomItem(string text) => ItemText = text;
    public string ItemText { get; set; }
}

public MainWindow()
{
    InitializeComponent();

    var list = new ObservableCollection<MyCustomItem>();
    list.Add(new MyCustomItem("Hello"));
    list.Add(new MyCustomItem("World"));
    MyCustomItemList.ItemsSource = list;
}

我是不是忘记设置了什么?为什么 TextBlock.Text 看似正确更新但 MyCustomBlock.Text 却没有?

依赖属性可以从多个来源获取它们的值,因此 WPF 使用 precedence system to determine which value applies. "Local" values (provided using SetValue or SetBinding) 将覆盖创建模板提供的任何内容。

在您的情况下,您在构造函数中设置了一个 "local" 值(大概是打算将其用作默认值)。设置默认值的更好方法是在 PropertyMetadata 中提供它。

public static readonly DependencyProperty TextProperty =
    DependencyProperty.Register(
          nameof(Text), typeof(string), typeof(MyCustomBlock),
          new FrameworkPropertyMetadata("<default>", TextChangedCallback));