为什么在绑定上放置无操作转换器会改变其行为?
-
21-09-2019 - |
题
我正在测试我构建的用户控件,并且遇到了一些让我无法解释的事情。
该控件是 ComboBox 的扩展,用于处理特定自定义类型的值。它具有该自定义类型的依赖属性,该属性是绑定的目标属性。
我在设置器中有一个跟踪语句,我可以看到该属性正在设置。但它没有出现在我的用户控件中。
现在,通常我会说,好吧,我的用户控件中有一个错误。我可能会这样做,尽管我对此感到困惑。但这个问题并不是要找到我控制范围内的错误。继续阅读;这就是奇怪的地方。
我还使用 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");
}
}
这背后的想法是,我将此转换器添加到我的绑定中,并可以设置一个断点来查看哪些值被推送到目标。好的,效果很好。我可以看到价值正在被推出。
其实还是有点作用的 也 美好的。如果 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
处理程序表明 e.Value
确实包含正确的值。无论是否连接转换器,都会发生这种情况。
我一直在尝试通过思想实验来逆向工程这个问题:如果您想创建一个绑定用户控件,如果将无操作转换器附加到其绑定,该控件的行为就会发生变化,您会怎么做?我对绑定了解不够,无法给出答案。
解决方案
好了,好消息是,我知道为什么,当我不使用值转换器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
二传手设定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
其他提示
尝试实现ConvertBack功能也一样,使用的是双向绑定,所以这个问题可能是例外将由“XAML”,但是当你在调试模式步骤,你也许会停下来“发送值回”被忽略 - 运转动作 - ?
嗯……很奇怪。我在处理具有多个转换器的多重绑定时看到了类似的行为,但在简单绑定上却没有看到类似的行为。出于好奇,您如何在控件上定义 DP?(包括回调、元数据选项等)
一些可以尝试的东西(删除转换器挂钩后):
- 默认模式(即删除 TwoWay)
- 删除 ValidatesOnDataErrors
- 将 AffectsRender 添加到 SelectedCode DP
什么是设置SelectedCode吗?你可以张贴代码属性更改处理?
您已经提出,调试显示属性被设置为正确的值,因此,最明显的建议是,代码中提供了您预期的行为是不正确。