Erro baml do WPF: EventSetter no recurso estático sendo definido duas vezes, segunda vez como nulo

Se eu tentar armazenar uma coleção de objetos SetterBase no xaml, que inclui EventSetter, o carregador xaml gera um erro.

A causa principal é que o carregador xaml tenta definir o PresentationFramework.dll! System.Windows.EventSetters.Event duas vezes: na primeira vez com o valor correto (ButtonBase.Click RoutedEvent), mas na segunda vez como nulo, e isso gera uma exceção. Meu retorno de chamada de propriedade anexado não está envolvido.

Por que ele tenta adicionar o evento ao EventSetter duas vezes e por que é nulo na segunda vez? Eu verifiquei que o ctor que está sendo usado é o padrão, então EventSeetter não está interagindo com a coleção de nenhuma maneira incomum, então não é isso. O motivo real é um bug no wpf que desafia o desafio de analisar a estrutura de duas partes de um evento (Event e EventHandler).

Visão
<Window x:Class="Spec.Plain.MTCMinimal"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ContentToggleButton;assembly=ContentToggleButton"
        Title="MTCMinimal" Height="300" Width="300">

<Window.Resources>

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

</Window.Resources>

<Button Name="Button1"
        local:Behaviours.StyleSetters="{StaticResource ButtonStyleSetters}" />

O código por trás é apenas InitializeComponent e um esboço para o manipulador de eventos. O erro ocorre durante InitializeComponent.

Comportamento
public static readonly DependencyProperty StyleSettersProperty =
    DependencyProperty.RegisterAttached(
        "StyleSetters", typeof(MyStyleSetters),
        typeof(Behaviours),
        new PropertyMetadata(default(MyStyleSetters),
            ButtonSettersChanged));

private static void ButtonSettersChanged (DependencyObject d,
    DependencyPropertyChangedEventArgs args)
{
    var fe = d as FrameworkElement;
    if (fe == null) return;
    var ui = d as UIElement;

    var newValue = args.NewValue as MyStyleSetters;
    if (newValue != null)
    {
        foreach (var member in newValue)
        {
            var setter = member as Setter;
            if(setter != null)
            {
                fe.SetValue(setter.Property, setter.Value);
                continue;
            }
            var eventSetter = member as EventSetter;
            if (eventSetter == null) continue;
            if (ui == null || eventSetter.Event == null) continue;
            ui.AddHandler(eventSetter.Event, eventSetter.Handler);
        }
    }
}

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

public static MyStyleSetters GetStyleSetters (
    DependencyObject element)
{
    return (MyStyleSetters)element
        .GetValue(StyleSettersProperty);
}
Erro
System.Windows.Markup.XamlParseException occurred
  _HResult=-2146233087
  _message='Set property 'System.Windows.EventSetter.Event' threw an exception.' Line number '11' and line position '26'.
  HResult=-2146233087
  IsTransient=false
  Message='Set property 'System.Windows.EventSetter.Event' threw an exception.' Line number '11' and line position '26'.
  Source=PresentationFramework
  LineNumber=11
  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
Depuração

Defino um ponto de interrupção da função em System.Windows.EventSetter.Event com uma ação para registrar o valor passado ao setter ...

Então eu executo o aplicativo e verifico a janela de saída e posso ver que o setter foi atingido duas vezes, primeira vez com o valor correto, segunda vez com valor nulo ...

O exemplo de trabalho pode ser encontrado na solução nesteRepositório GITHub&nbsp;no projeto chamado EventSetterNull-SO-41604891-2670182

baml

Ao definir um BP no membro Index de XamlNodeList, eu poderia pegar os símbolos xaml associados ao objeto xaml SetterBaseCollection ...

XamlNode [0] "None: LineInfo: System.Xaml.LineInfo"
XamlNode [1] "StartObject: SetterBaseCollection"
XamlNode [2] "StartMember: _Items"
XamlNode [3] "None: LineInfo: System.Xaml.LineInfo"
XamlNode [4] "StartObject: Setter"
XamlNode [5] "StartMember: Property"
XamlNode [6] "Value: Height"
XamlNode [7] "EndMember: "
XamlNode [8] "StartMember: Value"
XamlNode [9] "Value: 30"
XamlNode [10] "EndMember: "
XamlNode [11] "None: LineInfo: System.Xaml.LineInfo"
XamlNode [12] "EndObject: "
XamlNode [13] "None: LineInfo: System.Xaml.LineInfo"
XamlNode [14] "None: LineInfo: System.Xaml.LineInfo"
XamlNode [15] "StartObject: EventSetter"
XamlNode [16] "StartMember: Event"
XamlNode [17] "Value: System.Windows.Baml2006.TypeConverterMarkupExtension"
XamlNode [18] "EndMember: "
               -->EventSetter value: {System.Windows.RoutedEvent}
XamlNode [19] "None: LineInfo: System.Xaml.LineInfo"
XamlNode [20] "StartMember: Event"
XamlNode [21] {System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Xaml.XamlNode.ToString() in ...\AppData\Local\JetBrains\Shared\v06\DecompilerCache\...\XamlNode.cs:line 159
   at <>x.<>m0(XamlNode& <>4__this)}
XamlNode [22] "EndMember: "
               -->EventSetter value: null
               !!!Then the null reference error throws
XamlNode [23] = "StartMember: Handler"
XamlNode [24] = "Value: StyleClick"
XamlNode [25] = "EndMember: "
XamlNode [26] = "None: LineInfo: System.Xaml.LineInfo"
XamlNode [27] = "EndObject: "
XamlNode [28] = "EndMember: "
XamlNode [29] = "EndObject: "
XamlNode [30] = "None: "
                 The remaining of the 41 nodes are all "None: "
O inseto?

O nodeList do baml parece estranho, antes de tudo, há um membro extra do Event começando em idx [20] e esse membro é realmente um System.NullReferenceException.
Isso está sendo passado para o XamlObjectWriter, que por sua vez é passado para a propriedade EventSetter e essa é a causa do erro.
O baml continua como esperado, mostrando o membro manipulador e finalizando adequadamente os membros e objetos.

Conclusão

O problema está na conversão de xaml para baml, então eu diria que é um bug. Embora seja um caso de borda evitável.

Gambiarra

Em vez de tentar definir o evento no estilo, use uma propriedade anexada em um objeto pai. Por exemplo, ButtonBase.Click = "StyleClick" em um StackPanel entregará o comportamento a tudo o que é clicável, o que eu estava originalmente tentando fazer. As coleções de Setters de propriedades ainda podem ser definidas em um recurso estático e consumidas por comportamentos baseados em propriedades anexados.

Mais pesquisas sobre a causa raiz

O problema é que uma propriedade de evento possui dois elementos: o evento e o manipulador. Quando oBaml2006Reader&nbsp;analisa um objeto no baml, ele precisa permitir sua estrutura para garantir que esteja no estado correto para interpretar fielmente os membros do objeto. Para fazer isso, ele possui uma máquina de estado, acionada a partir de um loop whileReadObject, chamadoProcess_OneBamlRecord. Esse método decodifica o próximo xamlNodeType e chama o método apropriado para analisá-lo e gravá-lo como um objeto. Um desses métodos é chamadoProcess_Property&nbsp;e possui uma lógica especial embutida para lidar com o complexo de eventos no baml.

O problema é que, se o evento for registrado no baml como umProcess_PropertyWithConverter, esse método não está ciente dos requisitos especiais de um evento e enche tudo. O manipulador de eventos é prefixado com uma tag de propriedade (provavelmente o analisador de eventos deveria recursar e usar a mesma sintaxe para essa subestrutura) e, como não houve alteração de estado EndMember, StartMember, a subpropriedade do manipulador é interpretada por ReadObject como uma propriedade Event. E o objeto do configurador de eventos que está sendo criado gera um erro porque sua propriedade Event já está configurada.