【问题标题】:How can I get the binding object of TextProperty through an attached Property如何通过附加属性获取 TextProperty 的绑定对象
【发布时间】:2017-05-10 18:49:50
【问题描述】:

我有一个名为 TxtBox 的类,带有一个附加属性:

public class TxtBox
{
    public static readonly DependencyProperty TypeProperty = DependencyProperty.RegisterAttached(
        "Type", typeof (Enums.FieldType), typeof (TextBox), new PropertyMetadata(default(Enums.FieldType),OnTypeChanged));

    public static void SetType(DependencyObject element, Enums.FieldType value)
    {
        element.SetValue(TypeProperty, value);
    }

    public static Enums.FieldType GetType(DependencyObject element)
    {
        return (Enums.FieldType) element.GetValue(TypeProperty);
    }

    private static void OnTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var src = (TextBox) d; //(FrameworkElement)d;
        var binding = BindingOperations.GetBinding(src, TextBox.TextProperty);
        if (binding != null) //Binding here is always null ?????????
        {
            binding.Converter = new NumberConverter();
            binding.ConverterParameter = e.NewValue;
        }
    }
}

在 MainWindow.xaml 中:

<Grid Margin="10">
   <TextBox Text="{Binding RequestNo}"  att:TxtBox.Type="Number" />
<\Grid>

一旦我通过附加属性(类型)设置了文本框控件的类型,我需要为TextProperty 分配ConverterConverterParameter。当OnTypeChanged 方法触发时,我无法获取绑定,因为它始终为空!!!

提前致谢:)

【问题讨论】:

  • 依赖于设置属性的顺序不是一个好主意。现在您假设 Text 属性是在附加属性之前设置的,但情况似乎并非如此。首先设置附加属性,然后设置文本。
  • 更好的方法是强制同时设置绑定和类型。一种可能的方法是在附加属性语法中包含绑定本身,或者使用指定绑定目标的简单文本值,或者允许提供整个 Binding 对象。另一种可能更好的方法是编写一个标记扩展来代替{Binding} 标记。尽管如此,我不相信你的设计是一个好的开始;仅在 {Binding} 语法本身中指定转换器和参数有什么问题?真的有那么麻烦吗?
  • 不,这并不繁琐,但我怎样才能让转换器只接受已知类型为枚举?,而且我不希望在 xaml 文件中看到拥挤的代码,以提高可读性和简单性。
  • 另外我尝试设计一个简单的库来在我的整个项目中使用它。

标签: c# wpf xaml binding


【解决方案1】:

在将绑定应用于文本框的文本属性之前设置您的附加属性。您可以通过在 Text 的值更改时尝试更新 Binding 来解决此问题:

private static void OnTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var src = (TextBox)d;

    var dpd = DependencyPropertyDescriptor.FromProperty(TextBox.TextProperty, typeof(TextBox));

    dpd.AddValueChanged(src, UpdateBindingHandler);

    UpdateBinding(src);
}

protected static void UpdateBindingHandler(object sender, EventArgs e)
{
    UpdateBinding((TextBox)sender);
}

private static void UpdateBinding(TextBox tbox)
{
    var binding = BindingOperations.GetBinding(tbox, TextBox.TextProperty);

    if (binding != null)
    {
        binding.Converter = new NumberConverter();
        binding.ConverterParameter = GetType(tbox);

        var dpd = DependencyPropertyDescriptor.FromProperty(TextBox.TextProperty, typeof(TextBox));

        //  Don't do this every time the value changes, only the first time 
        //  it changes after TxtBox.Type has changed. 
        dpd.RemoveValueChanged(tbox, UpdateBindingHandler);
    }
}

当您这样做时,您会发现您的整个设计存在缺陷:Binding 一旦被使用就无法更改。它抛出一个异常。

您也许可以摆脱创建一个新绑定,克隆旧绑定的属性,并在其上放置一个转换器。但是,绑定有很多属性,如果已经有一个转换器,您需要用一个链式转换器替换它,该转换器在添加您的时会保留该属性。

我不确定这个功能是否会奏效。

【讨论】:

    【解决方案2】:

    最后,我找到了解决方案,我改变了设计,因为 Peter Duniho 先生建议我编写一个标记扩展来代替 {Binding}

    public class TextBoxTypeExtension:MarkupExtension
        {
            private readonly Binding _binding;
            public TextBoxTypeExtension(Binding binding,Enums.FieldType type)
            {
                _binding = binding;
                _binding.Converter = new NumberConverter();
                _binding.ConverterParameter = type;
            }
            public override object ProvideValue(IServiceProvider serviceProvider)
            {
                return _binding.ProvideValue(serviceProvider);
            }
        }
    

    在 MainWindow.xaml 中:

      <TextBox  MaxLength="10" Grid.Row="1" Grid.Column="1"
                            Text="{extension:TextBoxType {Binding Request.RequestNo},Number}"/>
    

    参考: MarkupExtension that uses a DataBinding value

    【讨论】:

      猜你喜欢
      • 2011-10-03
      • 1970-01-01
      • 2011-08-09
      • 1970-01-01
      • 1970-01-01
      • 2019-10-19
      • 2013-01-02
      • 2014-02-22
      • 2018-01-03
      相关资源
      最近更新 更多