Как не задавать локальное значение при сбое привязки (чтобы наследственные значения распространялись) - PullRequest
3 голосов
/ 17 марта 2010

Рассмотрим следующий сценарий:

Я хочу связать свойство TextElement.FontWeight с атрибутом xml. XML выглядит примерно так и имеет произвольную глубину.

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

Я использую иерархические шаблоны для отображения текста, нет проблем, но у меня есть сеттер в стиле шаблона, например:

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

правильно устанавливает вес шрифта на первом уровне, но перезаписывает второй уровень нулем (поскольку привязка не может найти xpath), который возвращается к обычному весу шрифта.

Я пробовал все виды вещей здесь, но, похоже, ничего не работает.

например. я использовал конвертер для возврата UnsetValue, который не работал.

Я сейчас пытаюсь с:

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

Codebehind:

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; }
}

, который вроде работает, но не может отслеживать изменения свойства

Есть идеи? Любые ссылки?

спасибо за помощь

Ответы [ 2 ]

3 голосов
/ 17 марта 2010

Вы на правильном пути. Использование прикрепленного свойства - путь.

Самое простое решение

Если вы хотите написать код для каждого унаследованного свойства (их не так много), это может быть намного проще:

<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);
  }
}
0 голосов
/ 23 февраля 2011

, если кто-нибудь сталкивался с этим, мое окончательное решение было так:

<DataTrigger Binding="{Binding XPath=@font-weight,Converter={converters:IsNullConverter}}" Value="false">
    <Setter Property="TextElement.FontWeight" Value="{Binding XPath=@font-weight}" />
</DataTrigger>

с IsNullConverter, являющимся простым преобразователем значений с этим телом:

return value == null;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...