我正在测试我构建的用户控件,我遇到的东西对我来说是莫名其妙的。
控件是ComboBox的扩展,用于处理特定自定义类型的值。它具有该自定义类型的依赖项属性,该属性是Binding的目标属性。
我在setter中有一个trace语句,我可以看到该属性已经设置好了。但它并没有出现在我的用户控件中。
现在,通常我会说,好吧,我的用户控件中有一个错误。我可能会这样做,虽然我对此感到困惑。但这个问题不是要找到我控制中的错误。继续阅读;这是奇怪的地方。
我也在使用Bea Stollnitz的小值转换器来帮助调试绑定:
public class DebuggingConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value; // Add the breakpoint here!!
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException("This method should never be called");
}
}
这背后的想法是我将这个转换器添加到我的Binding中,并且可以设置一个断点来查看推送到目标的值。好的,这很好用。我可以看到价值被推出。
事实上,它有点太了。如果DebuggingConverter附加到Binding,则用户控件将显示该值。如果不是,那就没有。
怎么可能呢?如果一个值转换器不会影响绑定控件的行为?
修改
不是说它可能有所帮助,但这是用户控件的XAML:
<a:CodeLookupBox
Grid.Column="1"
Grid.IsSharedSizeScope="True"
MinWidth="100"
Style="{Binding Style}">
<a:CodeLookupBox.CodeLookupTable>
<Binding Path="Codes" Mode="OneWay"/>
</a:CodeLookupBox.CodeLookupTable>
<a:CodeLookupBox.SelectedCode>
<Binding Path="Value" Mode="TwoWay" ValidatesOnDataErrors="True"/>
</a:CodeLookupBox.SelectedCode>
</a:CodeLookupBox>
如果没有第二个绑定上的转换器,则控件的行为就像我没有设置SelectedCode
一样。即使OnSelectedCodePropertyChanged
处理程序中的trace语句显示e.Value
确实包含正确的值。无论转换器是否连接,都会发生这种情况。
我一直试图通过一个思想实验对这个问题进行逆向工程:如果你想创建一个绑定用户控件,如果一个no-op转换器连接到它的绑定,其行为会改变,你会怎么做?我不太了解绑定以得出答案。
答案 0 :(得分:2)
SelectedCode
没有被设置。坏消息是,我仍然有一些神秘感,但问题被推到了食物链上,我有一个解决方法。
这个控件本质上是一个强类型的组合框,其中包含一系列附加功能,这些功能可以通过它知道其中包含哪些项目来实现。 SelectedCode
和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
setter首先设置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
答案 1 :(得分:0)
尝试实现ConvertBack函数,你正在使用双向绑定,所以问题可能是“xaml”会忽略异常但是当你进入调试模式时你可能会停止“发送值” - 运转动作 - ?
答案 2 :(得分:0)
要尝试的一些东西(删除转换器挂钩后):
答案 3 :(得分:0)
设置SelectedCode有什么作用?你可以发布属性改变处理程序的代码吗?
您已建议调试显示属性设置为正确的值,因此最明显的建议是提供您的预期行为的代码不正确。