代码之家  ›  专栏  ›  技术社区  ›  Robert Rossney

为什么在绑定上放置no-op转换器会改变其行为?

  •  7
  • Robert Rossney  · 技术社区  · 15 年前

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

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

    我在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 确实包含正确的值。无论转换器是否连接,都会发生这种情况。

    我一直试图用一个思想实验来逆向工程这个问题:如果你想创建一个绑定用户控件,如果一个no-op转换器附加到它的绑定上,它的行为会发生改变,你会怎么做?我对绑定的了解不够,无法给出答案。

    4 回复  |  直到 15 年前
        1
  •  2
  •   Robert Rossney    15 年前

    好消息是,我知道为什么 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
    

    这是预期的行为。这个 CODeloOkPosiTabe 属性已设置,因此设置 选定的代码 正确设置该集合中的某个项 选择项目 在底层 组合框 .

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

    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
    

    在这里, 选择码 正在设置属性 之前 这个 CODeloOkPosiTabe 属性是。所以当方法试图设置 选择项目 在底层 组合框 ,什么都没有发生,因为 项目源 是空的。

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

    我还假设,也许有点愚蠢,绑定更新其目标的顺序并不依赖于它们是否附加了值转换器。是的。我想知道它还依赖什么。

    谢天谢地,我有办法解决这个问题。自从我 CodeLookup 对象包含对 CODeloOkPosiTabe ,我可以做 选择码 setter设置 CODeloOkPosiTabe (因此) 项目源 )属性优先(如果尚未设置)。这将使这个问题消失,而不必在绑定上粘贴一个假值转换器,并希望绑定的行为方式永远不会改变。

    编辑

    属性声明如下所示:

    #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
    
        2
  •  0
  •   ceciliaSHARP    15 年前

    也尝试实现convertback函数,您使用的是双向绑定,因此问题可能是“xaml”将忽略该异常,但当您进入调试模式时,可能会停止“send value back”操作?

        3
  •  0
  •   JerKimball    15 年前

    嗯…很奇怪。我在使用多个转换器处理多绑定时看到过类似的行为,但在简单绑定上没有。出于好奇,你如何在控制中定义你的DPS?(包括回调、元数据选项等)

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

    • 默认模式(即删除twoway)
    • 删除validateData错误
    • 向选定的代码dp添加一个affectsrender
        4
  •  0
  •   Dan Puzey    15 年前

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

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

    推荐文章