好消息是,SelectedCode
当我不使用值转换器时,我知道为什么没有设置。坏消息是,我仍然有一些谜团,但问题已经被推到了食物链上一点,我有一个解决方法。
这个控件本质上是一个强类型的组合框,它具有许多附加功能,因为它知道其中有哪些类型的项目。SelectedCode
and属性是强类型的CodeLookupTable
,它们隐藏了底层的SelectedItem
和ItemsSource
属性,而这些属性不是。(顺便说一句,这就是为什么这是一个用户控件而不是 的子类ComboBox
;我不希望这些属性可见,因为如果设置不当会发生很多事情,但都不好。)
这是正在发生的事情。这是附加值转换器时的调试输出(数字是控件的哈希码,因为我有一堆在程序初始化时同时绘制的):
14626603: OnCodeLookupTablePropertyChanged
CodeLookupTable property set to Proceedings.Model.CodeLookupTable
box.MainComboBox.ItemsSource = MS.Internal.Data.EnumerableCollectionView
14626603: OnSelectedCodePropertyChanged:
SelectedCode property set to Unlicensed Driver [VC12500(A)]
box.MainComboBox.ItemsSource = MS.Internal.Data.EnumerableCollectionView
这是预期的行为。该CodeLookupTable
属性已设置,因此设置SelectedCode
为该集合中的项目之一正确设置SelectedItem
在基础ComboBox
.
但是如果没有值转换器,我们会得到:
16143157: OnSelectedCodePropertyChanged:
SelectedCode property set to Unlicensed Driver [VC12500(A)]
box.MainComboBox.ItemsSource =
16143157: OnCodeLookupTablePropertyChanged
CodeLookupTable property set to Proceedings.Model.CodeLookupTable
box.MainComboBox.ItemsSource = MS.Internal.Data.EnumerableCollectionView
在这里,SelectedCode
属性是在属性之前设置的CodeLookupTable
。因此,当该方法尝试SelectedItem
在底层设置时ComboBox
,什么也没有发生,因为它ItemsSource
是空的。
这就是问题的根源。我愚蠢地假设绑定更新其目标的顺序与它们在 XAML 中声明的顺序相同。(我将绑定表示为元素而不是属性的原因之一是因为 XML 文档中元素的顺序是确定性的,而属性的顺序不是。这并不是我没有考虑过这一点。)显然不是这样。
我还假设,也许不那么愚蠢,绑定更新其目标的顺序不取决于它们是否具有附加值转换器。嗯,是的。我想知道它还取决于什么。
幸运的是,我有办法解决这个问题。由于我的CodeLookup
对象包含对 的引用,因此如果尚未设置CodeLookupTable
,我可以让SelectedCode
设置器首先设置CodeLookupTable
(以及因此)属性。ItemsSource
这将使这个问题消失,而不必在绑定上粘贴一个假值转换器,并希望绑定的行为方式永远不会改变。
编辑
下面是属性声明的样子:
#region SelectedCode
public static readonly DependencyProperty SelectedCodeProperty = DependencyProperty.Register(
"SelectedCode", typeof(CodeLookup), typeof(CodeLookupBox),
new FrameworkPropertyMetadata(OnSelectedCodePropertyChanged));
private static void OnSelectedCodePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
CodeLookupBox box = (CodeLookupBox)source;
CodeLookup code = e.NewValue as CodeLookup;
// this right here is the fix to the original problem:
if (box.CodeLookupTable == null && code != null)
{
box.CodeLookupTable = code.Table;
}
box.MainComboBox.SelectedItem = e.NewValue;
}
public CodeLookup SelectedCode
{
get { return GetValue(SelectedCodeProperty) as CodeLookup; }
set { SetValue(SelectedCodeProperty, value); }
}
#endregion
#region CodeLookupTable
public static readonly DependencyProperty CodeLookupTableProperty = DependencyProperty.Register(
"CodeLookupTable", typeof(CodeLookupTable), typeof(CodeLookupBox),
new FrameworkPropertyMetadata(OnCodeLookupTablePropertyChanged));
private static void OnCodeLookupTablePropertyChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
CodeLookupBox box = (CodeLookupBox)source;
CodeLookupTable table = (CodeLookupTable)e.NewValue;
box.ViewSource = new CollectionViewSource { Source = table.Codes };
box.View = box.ViewSource.View;
box.MainComboBox.ItemsSource = box.View;
}
public CodeLookupTable CodeLookupTable
{
get { return GetValue(CodeLookupTableProperty) as CodeLookupTable; }
set { SetValue(CodeLookupTableProperty, value); }
}
#endregion