我正在使用具有几个用户定义的依赖项属性的自定义控件.我遇到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内部使用的,那么我总是在构造函数中获得默认设置(即使数据模板显式设置了另一个值).
在模板中使用控件时,还有其他方法可以指定默认值吗?我能想到的唯一选择是将控件分成几个单独的但类似的控件,每个控件都使用一个默认值,该默认值已注册到dependency属性中(这消除了基于Mode属性设置默认设置的需要).
解决方法:
在添加少量检查的同时在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();
}