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
}
确实是一个笨拙的解决方案,但它确实有效。根据您的具体情况,您可能还想查看 Binding
的 FallbackValue
和 TargetNullValue
属性:
Test="{Binding Source={ }, FallbackValue={ }, TargetNullValue={ }}"
我写了一个基于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
}
确实是一个笨拙的解决方案,但它确实有效。根据您的具体情况,您可能还想查看 Binding
的 FallbackValue
和 TargetNullValue
属性:
Test="{Binding Source={ }, FallbackValue={ }, TargetNullValue={ }}"