Как добавить установщики стилей в качестве прикрепленного поведения из статического ресурса

Я создал поведение, используя прикрепленное свойство зависимости, чтобы передать общий SetterBaseCollection для разных связанных стилей.

Поведение просто управляет коллекцией присоединенного свойства узла, чтобы выровнять ее со статическим ресурсом в представлении. Я только что подключил это к AP PropertyChangedCallback, и это отлично работает для другого поведения, которое у меня есть, для заполнения CommandBindings, но выбрасывает, когда я применяю его к установщикам стиля.

Ошибка

System.Windows.Markup.XamlParseException occurred
  _HResult=-2146233087
  _message='Set property 'System.Windows.EventSetter.Event' threw an exception.' Line number '12' and line position '26'.
  HResult=-2146233087
  IsTransient=false
  Message='Set property 'System.Windows.EventSetter.Event' threw an exception.' Line number '12' and line position '26'.
  Source=PresentationFramework
  LineNumber=12
  LinePosition=26
  StackTrace:
       at System.Windows.Markup.XamlReader.RewrapException(Exception e, IXamlLineInfo lineInfo, Uri baseUri)
  InnerException: System.ArgumentNullException
       _HResult=-2147467261
       _message=Value cannot be null.
       HResult=-2147467261
       IsTransient=false
       Message=Value cannot be null.
Parameter name: value
       Source=PresentationFramework
       ParamName=value
       StackTrace:
            at System.Windows.EventSetter.set_Event(RoutedEvent value)
       InnerException: 

Код поведения не вызывается до возникновения ошибки, поэтому это что-то в xaml.

Вид

<Window.Resources>

    <Thickness x:Key="ButtonMargin">6</Thickness>

    <SetterBaseCollection x:Key="ButtonStyleSetters">
        <EventSetter Event="ButtonBase.Click" Handler="StyleClick" />
        <Setter Property="FrameworkElement.Height" Value="30" />
        <Setter Property="FrameworkElement.Margin" Value="6" />
    </SetterBaseCollection>

    <Style TargetType="{x:Type Button}" 
           b:Behaviours.StyleSetters="{StaticResource ButtonStyleSetters}" />
    <Style TargetType="{x:Type ToggleButton}"
           b:Behaviours.StyleSetters="{StaticResource ButtonStyleSetters}" />
    <Style TargetType="{x:Type CheckBox}">
        <EventSetter Event="Click" Handler="StyleClick" />
    </Style>
    <Style TargetType="{x:Type b:MultiCommandButton}"
           b:Behaviours.StyleSetters="{StaticResource ButtonStyleSetters}" />

    <Style TargetType="UniformGrid">
        <Setter Property="Columns" Value="2" />
    </Style>

    <CommandBindingCollection x:Key="OutputToggleReceiveAll">
        <b:OutputToggleBind />
        <b:OutputToggleEnabledBind />
    </CommandBindingCollection>

</Window.Resources>

Поведение

public static class Behaviours
{
    #region AP CommandReceivers

    public static readonly DependencyProperty CommandReceiversProperty =
        DependencyProperty.RegisterAttached(
            "CommandReceivers", typeof(CommandBindingCollection),
            typeof(Behaviours),
            new PropertyMetadata(default(CommandBindingCollection),
                (t, a) => UpdateCollection<CommandBindingCollection, UIElement>
                    (t, a, "CommandBindings")));

    public static void SetCommandReceivers(DependencyObject element,
        CommandBindingCollection value)
    {
        element.SetValue(CommandReceiversProperty, value);
    }

    public static CommandBindingCollection GetCommandReceivers(
        DependencyObject element)
    {
        return (CommandBindingCollection) element
            .GetValue(CommandReceiversProperty);
    }

    #endregion

    #region AP StyleSetters

    public static readonly DependencyProperty StyleSettersProperty =
        DependencyProperty.RegisterAttached(
            "StyleSetters", typeof(SetterBaseCollection),
            typeof(Behaviours),
            new PropertyMetadata(default(SetterBaseCollection),
                (t, a) => UpdateCollection<SetterBaseCollection, UIElement>
                    (t, a, "Setters")));

    public static void SetStyleSetters (DependencyObject element,
        SetterBaseCollection value)
    {
        element.SetValue(StyleSettersProperty, value);
    }

    public static SetterBaseCollection GetStyleSetters (
        DependencyObject element)
    {
        return (SetterBaseCollection)element
            .GetValue(StyleSettersProperty);
    }

    #endregion

    private static void UpdateCollection<TCollection, THost> (
        DependencyObject target, DependencyPropertyChangedEventArgs args, 
        string targetCollection)
        where TCollection : IList
        where THost : class
    {
        var host = target as THost;
        if (host == null) return;

        var collection = typeof(THost).GetProperty(targetCollection)
            .GetValue(host) as IList;
        if (collection == null) return;

        if (args.OldValue != null)
        {
            foreach (var member in
                (TCollection) args.OldValue)
            {
                collection.Remove(member);
            }
        }
        if (args.NewValue != null)
        {
            foreach (var member in
                (TCollection) args.NewValue)
            {
                collection.Add(member);
            }
        }
    }
}

Вопрос

Я думаю, что мне, вероятно, также придется добавить обработчик OnAttached, но я делаю что-то не так в xaml? Неприятно, что прикрепленное свойство не было найдено, но обычно это не проблема, и я ожидаю, что оно исчезнет после успешной компиляции сборки.

РЕДАКТИРОВАТЬ

Основываясь на отзывах @mm8 и этом ответе, я попробовал следующий подход, но также выдает ошибки.

Вид

<Window.Resources>

    <SetterBaseCollection x:Key="ButtonStyleSetters">
        <EventSetter Event="ButtonBase.Click" Handler="StyleClick" />
        <Setter Property="FrameworkElement.Height" Value="30" />
        <Setter Property="FrameworkElement.Margin" Value="6" />
    </SetterBaseCollection>

    <Style TargetType="{x:Type Button}" >
        <Setter Property="local:Behaviours.StyleSetters" 
                Value="{StaticResource ButtonStyleSetters}"/>
    </Style>

</Window.Resources>

В классе поведения

public static readonly DependencyProperty StyleSettersProperty =
    DependencyProperty.RegisterAttached(
        "StyleSetters", typeof(SetterBaseCollection),
        typeof(Behaviours),
        new PropertyMetadata(default(SetterBaseCollection),
            (t, a) => UpdateCollection<SetterBaseCollection, Style>
                (((FrameworkElement)t).Style, a, "Setters")));

public static void SetStyleSetters (DependencyObject element,
    SetterBaseCollection value)
{
    element.SetValue(StyleSettersProperty, value);
}

public static SetterBaseCollection GetStyleSetters (
    DependencyObject element)
{
    return (SetterBaseCollection)element
        .GetValue(StyleSettersProperty);
}

#endregion

private static void UpdateCollection<TCollection, THost> (
    object target, DependencyPropertyChangedEventArgs args, 
    string targetCollection)
    where TCollection : class, IList
    where THost : class
{
    var host = target as THost;
    if (host == null) return;

    var collection = typeof(THost).GetProperty(targetCollection)
        .GetValue(host) as IList;
    if (collection == null) return;

    var oldValue = args.OldValue as TCollection;
    if (oldValue != null)
    {
        foreach (var member in oldValue)
        {
            collection.Remove(member);
        }
    }
    try
    {
        var newValue = args.NewValue as TCollection;
        if (newValue != null)
        {
            foreach (var member in newValue)
            {
                collection.Add(member);
            }
        }

    }
    catch (Exception e)
    {
        Debug.WriteLine("{0} in UpdateCollection<TCollection, THost>");
    }
}

Значение StaticResource всегда равно null.
Обратный вызов OnPropertyChanged выполняется каждый раз, когда свойство устанавливается для кнопки, и кнопка передается в качестве первого аргумента.


person Cool Blue    schedule 10.01.2017    source источник
comment
Style не является DependencyObject, поэтому вы не сможете установить присоединенное свойство StyleSetters для Style.   -  person mm8    schedule 10.01.2017
comment
@ мм8 а, ладно, конечно. Спасибо.   -  person Cool Blue    schedule 10.01.2017


Ответы (1)


Style не является DependencyObject, поэтому вы не сможете установить присоединенное свойство StyleSetters для Style.

person mm8    schedule 10.01.2017
comment
Есть ли другой способ? Я попробовал другой подход, который позволяет избежать этой проблемы, но все еще не может заставить его работать. У вас есть мнение об этом? - person Cool Blue; 11.01.2017
comment
Я так не думаю. Вы не можете установить свойство Setters триггера непосредственно в объект SetterBaseCollection, который вы определяете как ресурс в XAML, поскольку свойство доступно только для чтения: social.msdn.microsoft.com/Forums/vstudio/en-US/ - person mm8; 11.01.2017
comment
Он доступен только для чтения, но не для членов. Я должен иметь возможность добавлять в коллекцию, как в своем коде, не так ли? - person Cool Blue; 11.01.2017
comment
Вы не можете добавить сеттеры в стиль кнопки, потому что он запечатан. - person mm8; 11.01.2017
comment
Но сеттер является только членом коллекции сеттеров, и это всего лишь присоединенное свойство (зависимость), он не пытается ничего установить в стиле. я не понимаю... - person Cool Blue; 11.01.2017
comment
Именно коллекция Setters запечатана, т.е. в нее нельзя ничего добавить. - person mm8; 11.01.2017
comment
ОК... Я новичок в этом, но я понимаю, что запечатанный влияет только на наследование: запечатанный класс не может быть базовым классом, а запечатанное переопределение виртуального члена базового класса преобразует член в конкретный с точки зрения любых других производных классов. Вы говорите, что это также влияет на изменчивость состояния экземпляра? - person Cool Blue; 11.01.2017
comment
Это еще один тип уплотнения... среда выполнения WPF запечатывает стиль из соображений производительности: streamingcon.blogspot.se/2015/07/ - person mm8; 11.01.2017
comment
Ах хорошо. Да, я видел это в исходном коде: IsSealed. Я думаю, что наткнулся на запечатывание WPF WTF (sic). Я перестану вас беспокоить и почитаю блог. Ваше здоровье! - person Cool Blue; 11.01.2017