Wpf 为什么在绑定上放置无操作转换器会改变其行为?

Wpf 为什么在绑定上放置无操作转换器会改变其行为?,wpf,binding,user-controls,ivalueconverter,Wpf,Binding,User Controls,Ivalueconverter,我正在测试我构建的用户控件,我遇到了一些我无法解释的事情 该控件是ComboBox的扩展,用于处理特定自定义类型的值。它有一个自定义类型的依赖项属性,该属性是绑定的目标属性 我在setter中有一个trace语句,我可以看到属性正在被设置。但是它没有出现在我的用户控件中 现在,通常我会说,好吧,我的用户控制中有一个bug。我可能会,尽管我对此感到困惑。但是这个问题不是关于在我的控制下找到bug。继续读下去;这就是它变得奇怪的地方 我还使用Bea Stollnitz的小值转换器来帮助调试绑定: p

我正在测试我构建的用户控件,我遇到了一些我无法解释的事情

该控件是ComboBox的扩展,用于处理特定自定义类型的值。它有一个自定义类型的依赖项属性,该属性是绑定的目标属性

我在setter中有一个trace语句,我可以看到属性正在被设置。但是它没有出现在我的用户控件中

现在,通常我会说,好吧,我的用户控制中有一个bug。我可能会,尽管我对此感到困惑。但是这个问题不是关于在我的控制下找到bug。继续读下去;这就是它变得奇怪的地方

我还使用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已附加到绑定,则用户控件将显示该值。如果不是,也不是

这怎么可能呢?一个不影响绑定控件行为的值转换器怎么可能呢

编辑:

这可能没有帮助,但下面是用户控件的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
确实包含正确的值。无论转换器是否连接,都会发生这种情况


我一直在尝试用一个思想实验来逆向工程这个问题:如果你想创建一个绑定的用户控件,如果一个无操作转换器连接到它的绑定上,它的行为就会改变,你会怎么做?我对绑定的了解还不够,无法给出答案。

也尝试实现ConvertBack函数,您使用的是双向绑定,因此问题可能是异常将被“xaml”忽略,但当您进入调试模式时,您可能会停止“发送值返回”操作?

嗯……非常奇怪。在使用多个转换器处理多绑定时,我见过类似的行为,但在简单绑定上却没有。出于好奇,您如何定义控件上的DPs?(包括回调、元数据选项等)

(卸下转换器挂钩后)要尝试的一些东西:

  • 默认模式(即,删除双向)
  • 删除ValidateSondaErrors
  • 在所选代码DP中添加一个AffectsRender

设置所选代码的作用是什么?您可以发布属性更改处理程序的代码吗


您建议调试显示属性设置为正确的值,因此最明显的建议是提供您预期行为的代码不正确。

好消息是,我知道在我不使用值转换器的情况下,
SelectedCode
为什么没有设置。坏消息是,我仍然有一些神秘的东西,但这个问题已经被推到了食物链的顶端,我有一个解决办法

该控件本质上是一个强类型组合框,具有一系列附加功能,因为它知道其中包含哪些项。
SelectedCode
CodeLookupTable
属性是强类型的,它们隐藏了底层的
SelectedItem
ItemsSource
属性,而这些属性不是。(顺便说一句,这就是为什么这是一个用户控件,而不是
ComboBox
的子类;我不希望这些属性可见,因为如果设置不当,会发生很多事情,没有一个是好的。)

下面是正在发生的事情。这是我在连接值转换器时的调试输出(数字是控件的哈希代码,因为我有一堆在程序初始化时同时绘制的代码):

这是预期的行为。设置了
CodeLookupTable
属性,因此将
SelectedCode
设置为该集合中的一个项可以正确设置基础
组合框上的
SelectedItem

但如果没有值转换器,我们可以得到:

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
这里,在设置
CodeLookupTable
属性之前,先设置
SelectedCode
属性。因此,当该方法尝试在基础的
组合框
上设置
SelectedItem
时,不会发生任何事情,因为
ItemsSource
为空

这就是问题的根源。我愚蠢地假设绑定更新其目标的顺序与它们在XAML中声明的顺序相同。(我将绑定表示为元素而不是属性的原因之一是因为XML文档中元素的顺序是确定的,而属性的顺序不是。这并不是我没有想到的。)显然不是这样

我还假设,绑定更新其目标的顺序并不取决于它们是否有附加值转换器,这可能不那么愚蠢。嗯,是的。我不知道这还取决于什么

谢天谢地,我有办法解决这个问题。由于我的
CodeLookupTable
对象包含对
CodeLookupTable
的引用,我可以让
SelectedCode
设置器首先设置
CodeLookupTable
(因此,如果尚未设置
ItemsSource
)属性。这将消除这个问题,而不必在绑定上粘贴一个假值转换器,并希望绑定的行为方式永远不会改变

编辑

这里
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
#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