WPF:设计时支持具有默认值的依赖属性

WPF: Design time support for dependency properties with default values

我写了一个基于ListBox的自定义控件。它包含一个默认值 ItemTemplate,显示由自定义依赖项 属性 提供给 ListBox 的图像。该控件还包含一个默认图像,当用户未将图像提供给依赖项时使用该图像 属性.

到目前为止一切正常,但现在我发现了一个小问题,我不知道如何解决。

当我在 XAML 设计器中使用我的自定义控件时,它首先显示默认图像。当我将图像的依赖项 属性 设置为另一个图像时,新图像立即显示在 XAML 设计器中。

但是当我再次删除新图像的 XAML 属性时,XAML 设计器只显示一个白色矩形而不是默认图像。

我认为这是因为将图像的依赖项 属性 设置为某个值然后将其删除我使该值无效。但即使我在 CoerceCallback 中检查 null 并在强制值为 null 时返回默认图像,也不起作用。

支持依赖属性回退值的最佳方式是什么?


TestControl.vb

Public Class TestControl
    Inherits ListBox

    Private Shared _defaultResources As ResourceDictionary

    Shared Sub New()
        _defaultResources = New ResourceDictionary
        _defaultResources.Source = New Uri("...")
    End Sub

    Public Shared ReadOnly TestProperty As DependencyProperty = DependencyProperty.Register(NameOf(TestControl.Test),
                                                                                            GetType(ImageSource),
                                                                                            GetType(TestControl),
                                                                                            New FrameworkPropertyMetadata(Nothing,
                                                                                                                          AddressOf TestControl.OnTestChanged,
                                                                                                                          AddressOf TestControl.OnTestCoerce))

    Public Property Test As ImageSource
        Get
            Return DirectCast(MyBase.GetValue(TestControl.TestProperty), ImageSource)
        End Get
        Set(value As ImageSource)
            MyBase.SetValue(TestControl.TestProperty, value)
        End Set
    End Property

    Private Shared Sub OnTestChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)

    End Sub

    Private Shared Function OnTestCoerce(d As DependencyObject, value As Object) As Object
        If (value Is Nothing) Then
            Return TryCast(_defaultResources.Item("TestImage"), ImageSource)
        End If

        Return value
    End Function

    Public Sub New()
        Me.Test = TryCast(_defaultResources.Item("TestImage"), ImageSource)
    End Sub
End Class

当我像这样使用那个控件时

<local:TestControl ItemsSource="{Binding Items}" />

每个项目在设计时显示默认图像。当我将 XAML 更改为

<local:TestControl ItemsSource="{Binding Items}"
                   Test="{StaticResource NewImage}" />

每个项目在设计时显示新项目。但是当我再次删除 Test="{StaticResource NewImage}" 时,它不会返回到默认图像。

好的,经过一些测试(使用 this technique)我发现了您问题的根源。

首先,您没有使用 PropertyMetadata 来设置默认值,而是使用构造函数。我假设您有充分的理由这样做,但这实际上意味着您现在依赖强制回调来设置默认值。

但是,在您删除
之后,它不会被调用(框架假定您的“真实”默认值 - Nothing - 不需要验证) Test="{StaticResource TestString}" 行。只有 OnTestChanged 叫做。这意味着我们可以使用它来恢复默认值:

void OnTestChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
{
   if (e.NewValue is null)
   {
      ((TestControl)d).Test = yourDefaultImage;
      return;
   }

   //Actual "OnChanged" code
}

确实是一个笨拙的解决方案,但它确实有效。根据您的具体情况,您可能还想查看 BindingFallbackValueTargetNullValue 属性:
Test="{Binding Source={ }, FallbackValue={ }, TargetNullValue={ }}"