【问题标题】:WPF – Converter as DependencyObjectWPF - 转换器作为 DependencyObject
【发布时间】:2019-10-27 09:20:27
【问题描述】:

无法将值绑定到BindingConverterParametrBinding 只能设置在 DependencyPropertyDependencyObject 上。

我很好奇将IValueConverter 转换器实现为DependencyObject

public class AddConverter : DependencyObject, IValueConverter
{
    public static readonly DependencyProperty AddIntegerProperty =
        DependencyProperty.Register(nameof(AddInteger),
            typeof(int),
            typeof(AddConverter),
            new PropertyMetadata(0));

    public int AddInteger
    {
        get => (int)GetValue(AddIntegerProperty);
        set => SetValue(AddIntegerProperty, value);
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!(value is int intValue)) return 0;
        return intValue + AddInteger;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        int intValue;
        try
        {
            intValue = System.Convert.ToInt32(value);
        }
        catch (Exception)
        {
            return 0;
        }

        return intValue - AddInteger;
    }
}

让我们在示例视图中使用它。

<TextBox>
    <TextBox.Text>
        <Binding Path="MyIntegerProperty">
            <Binding.Converter>
                <local:AddConverter AddInteger="{Binding MyAddIntegerProperty, Mode=OneWay}" />
            </Binding.Converter>
        </Binding>
    </TextBox.Text>
</TextBox>

结果是AddInteger 仍然返回默认值。通过提供的Binding 没有改变AddInteger 的依赖属性是什么原因?


脚注:MultiBinding 对我没有帮助,因为ConvertBack 方法仅包含控件提供的值。这东西也应该在 ViewModel 中解决,但我很好奇有转换器的解决方案。

【问题讨论】:

    标签: wpf binding wpf-controls converters


    【解决方案1】:

    问题是,首先,转换器无法继承它所在的 DataContext,因此绑定无法工作:如果您在 VS 输出中添加跟踪,您将看到“未找到框架导师”您拥有的绑定(参见附录 A)。这也是您不能仅从 FrameworkElement 派生并使用 RelativeSource={RelativeSource AncestorType=Whatever} 的原因:您已经脱离了可视化树。没有祖先。此外,即使有框架指导者,DependencyObject 也无法为绑定提供源。来源必须是明确的。只有从 FrameworkElement 继承的类才能继承 DataContext。

    所以我偷了一个 BindingProxy 类 (from this answer) 并用它来提供绑定源。这有点笨拙,但我想到的另一种选择是从 Freezable 继承转换器,本质上赋予它 BindingProxy 的属性,并在资源中创建转换器。它有效,但我更喜欢这种组织方式。

    public class BindingProxy : Freezable
    {
        #region Overrides of Freezable
    
        protected override Freezable CreateInstanceCore()
        {
            return new BindingProxy();
        }
    
        #endregion
    
        #region Data Property
        public Object Data
        {
            get { return (Object)GetValue(DataProperty); }
            set { SetValue(DataProperty, value); }
        }
    
        public static readonly DependencyProperty DataProperty =
            DependencyProperty.Register(nameof(Data), typeof(Object), typeof(BindingProxy),
                new PropertyMetadata(null));
        #endregion Data Property
    }
    

    XAML

    <StackPanel.Resources>
        <local:BindingProxy
            x:Key="VMProxy"
            Data="{Binding}"
            />
    </StackPanel.Resources>
    <TextBlock>
        <TextBlock.Text>
            <Binding Path="MyIntegerProperty">
                <Binding.Converter>
                    <local:AddConverter
                        AddInteger="{Binding Data.MyAddIntegerProperty, Source={StaticResource VMProxy}}" 
                        />
                </Binding.Converter>
            </Binding>
        </TextBlock.Text>
    </TextBlock>
    

    附录 A

    AddInteger="{Binding MyAddIntegerProperty, Mode=OneWay, 
        PresentationTraceSources.TraceLevel=High}"
    
    System.Windows.Data Warning: 56 : Created BindingExpression (hash=14964341) for Binding (hash=21653700)
    System.Windows.Data Warning: 58 :   Path: 'MyAddIntegerProperty'
    System.Windows.Data Warning: 60 : BindingExpression (hash=14964341): Default mode resolved to OneWay
    System.Windows.Data Warning: 61 : BindingExpression (hash=14964341): Default update trigger resolved to PropertyChanged
    System.Windows.Data Warning: 62 : BindingExpression (hash=14964341): Attach to WpfApp2.AddConverter2.AddInteger (hash=57434139)
    System.Windows.Data Warning: 64 : BindingExpression (hash=14964341): Use Framework mentor <null>
    System.Windows.Data Warning: 67 : BindingExpression (hash=14964341): Resolving source 
    System.Windows.Data Warning: 69 : BindingExpression (hash=14964341): Framework mentor not found
    System.Windows.Data Warning: 65 : BindingExpression (hash=14964341): Resolve source deferred
    System.Windows.Data Warning: 67 : BindingExpression (hash=14964341): Resolving source 
    System.Windows.Data Warning: 69 : BindingExpression (hash=14964341): Framework mentor not found
    

    【讨论】:

    • 因为转换器不是FrameworkElement,同样通过RelativeSource FindAncestor, …找到DataContext也无济于事。我知道了。谢谢你的例子。
    • @David 正确。此外,即使您确实从 FrameworkElement 继承,FindAncestor 也不会工作,因为您已经脱离了可视化树 - 再也没有框架导师了。
    猜你喜欢
    • 1970-01-01
    • 2018-09-30
    • 1970-01-01
    • 1970-01-01
    • 2016-08-02
    • 2010-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多