c# - 如何在绑定(bind)失败时避免设置本地值(因此继承的值将传播)

标签 c# wpf

考虑以下场景:

我想将 TextElement.FontWeight 属性绑定(bind)到 xml 属性。 xml 看起来有点像这样并且具有任意深度。

<text font-weight="bold">
    bold text here
    <inlinetext>more bold text</inlinetext>
    even more bold text
</text>

我使用分层模板来显示文本,没问题,但是在模板样式中有一个 Setter,例如:

<Setter Property="TextElement.FontWeight" Value="{Binding XPath=@font-weight}"/>

在第一级正确设置字体粗细,但用 null 覆盖第二级(因为绑定(bind)找不到 xpath),这将恢复为普通字体粗细。

我在这里尝试了各种方法,但似乎没有什么效果。

例如我使用转换器返回 UnsetValue,但没有用。

我目前正在尝试:

<Setter Property="custom:AttributeInserter.Wrapper" Value="{custom:AttributeInserter Property=TextElement.FontWeight, Binding={Binding XPath=@font-weight}}"/>

代码隐藏:

public static class AttributeInserter
{
    public static AttributeInserterExtension GetWrapper(DependencyObject obj)
    {
        return (AttributeInserterExtension)obj.GetValue(WrapperProperty);
    }
    public static void SetWrapper(DependencyObject obj, AttributeInserterExtension value)
    {
        obj.SetValue(WrapperProperty, value);
    }
    // Using a DependencyProperty as the backing store for Wrapper.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty WrapperProperty =
        DependencyProperty.RegisterAttached("Wrapper", typeof(AttributeInserterExtension), typeof(AttributeInserter), new UIPropertyMetadata(pcc));

    static void pcc(DependencyObject o,DependencyPropertyChangedEventArgs e)
    {
        var n=e.NewValue as AttributeInserterExtension;
        var c = o as FrameworkElement;
        if (n == null || c==null || n.Property==null || n.Binding==null)
            return;

        var bex = c.SetBinding(n.Property, n.Binding);
        bex.UpdateTarget();
        if (bex.Status == BindingStatus.UpdateTargetError)
            c.ClearValue(n.Property);
    }

}

public class AttributeInserterExtension : MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }

    public DependencyProperty Property { get; set; }
    public Binding Binding { get; set; }
}

哪个有点用,但不能跟踪属性的变化

有什么想法吗?有链接吗?

谢谢你的帮助

最佳答案

您走在正确的轨道上。使用附加属性是可行的方法。

最简单的解决方案

如果您愿意为每个继承的属性编写代码(它们不是很多),它会简单得多:

<Setter Property="my:OnlyIfSet.FontWeight" Value="{Binding XPath=@font-weight}"/>

代码

public class OnlyIfSet : DependencyObject
{
  public static FontWeight GetFontWeight(DependencyObject obj) { return (FontWeight)obj.GetValue(FontWeightProperty); }
  public static void SetFontWeight(DependencyObject obj, FontWeight value) { obj.SetValue(FontWeightProperty, value); }
  public static readonly DependencyProperty FontWeightProperty = DependencyProperty.RegisterAttached("FontWeight", typeof(FontWeight), typeof(Object), new PropertyMetadata
  {
    PropertyChangedCallback = (obj, e) =>
      {
        if(e.NewValue!=null)
          obj.SetValue(TextElement.FontWeightProperty, e.NewValue);
        else
          obj.ClearValue(TextElement.FontWeightProperty);
      }
  });
}

泛化多个属性

可以通过多种方式概括这一点。一种是创建一个通用的属性注册方法,但仍然使用多个附加属性:

public class OnlyIfSet
{
  static DependencyProperty CreateMap(DependencyProperty prop)
  {
    return DependencyProperty.RegisterAttached(
      prop.Name, prop.PropertyType, typeof(OnlyIfSet), new PropertyMetadata
      {
        PropertyChangedCallback = (obj, e) =>
        {
          if(e.NewValue!=null)
            obj.SetValue(prop, e.NewValue);
          else
          obj.ClearValue(prop);
        }
      });
  }

  public static FontWeight GetFontWeight(DependencyObject obj) { return (FontWeight)obj.GetValue(FontWeightProperty); }
  public static void SetFontWeight(DependencyObject obj, FontWeight value) { obj.SetValue(FontWeightProperty, value); }
  public static readonly DependencyProperty FontWeightProperty =
    CreateMap(TextElement.FontWeightProperty);

  public static double GetFontSize(DependencyObject obj) { return (double)obj.GetValue(FontSizeProperty); }
  public static void SetFontSize(DependencyObject obj, double value) { obj.SetValue(FontSizeProperty, value); }
  public static readonly DependencyProperty FontSizeProperty =
    CreateMap(TextElement.FontSizeProperty);

  ...    
}

允许任意属性

另一种方法是使用两个附加属性:

<Setter Property="my:ConditionalSetter.Property" Value="FontWeight" />
<Setter Property="my:ConditionalSetter.Value" Value="{Binding XPath=@font-weight}"/>

代码

public class ConditionalSetter : DependencyObject
{
  public string GetProperty( ... // Attached property
  public void SetProperty( ...
  public static readonly DependencyProperty PropertyProperty = ...
  {
    PropertyChangedCallback = Update,
  });

  public object GetValue( ... // Attached property
  public void SetValue( ...
  public static readonly DependencyProperty ValueProperty = ...
  {
    PropertyChangedCallback = Update,
  });

  void Update(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  {
    var property = GetProperty(obj);
    var value = GetValue(obj);
    if(property==null) return;

    var prop = DependencyPropertyDescriptor.FromName(property, obj.GetType(), typeof(object)).DependencyProperty;
    if(prop==null) return;

    if(value!=null)
      obj.SetValue(prop, value);
    else
      obj.ClearValue(prop);
  }
}

关于c# - 如何在绑定(bind)失败时避免设置本地值(因此继承的值将传播),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2462785/

相关文章:

c# - 为什么我无法更改实例化后用作属性的结构中的字段?

wpf - 有没有办法注释掉包含注释的 XAML?

c# - WPF白屏渲染问题

c# - WPF 图表控件

c# - 当传递到 ASP.NET Core 2.2 中 [FromForm] 使用的模型时,GUID 列表为空

c# - Visual Studio 2017 扩展,用于跨整个解决方案进行自定义 C# 重构

c# - AesManaged.KeySize 属性的默认值是多少?

c# - 如何将.exe从C++项目复制到C#项目的输出目录?

c# - CollectionViewSource 代码隐藏绑定(bind) MVVM 的奇怪行为

c# - 由组合框组成的非常简单的颜色选择器