Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
657 views
in Technique[技术] by (71.8m points)

wpf - Why "Setting event handlers inside a Setter.Value structure" gives compilation error?

I faced the problem exactly as described in Setting event handlers inside a Setter.Value structure. But I want to understand why the solution provided by the question's author doesn't work. It looks like I missed some concept.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

This appears to be a bug in the generation of the XAML code-behind. In addition to the user code-behind for XAML files, there is a "compiler" generated version that defines InitializeComponent and class fields for named elements (i.e. x:Name).

Given a simple example:

<Window.Resources>
    <Style TargetType="Button">
        <Setter Property="ContextMenu">
            <Setter.Value>
                <ContextMenu>
                    <MenuItem Header="Header" Click="MenuItem_Click"/>
                </ContextMenu>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

<Button />

If you run this, you will get the following exception:

System.Windows.Markup.XamlParseException occurred
  Message='Set connectionId threw an exception.' Line number '13' and line position '8'.
  Source=PresentationFramework
  LineNumber=13
  LinePosition=8
  StackTrace:
       at System.Windows.Markup.XamlReader.RewrapException(Exception e, IXamlLineInfo lineInfo, Uri baseUri)
  InnerException: System.InvalidCastException
       Message=Unable to cast object of type 'System.Windows.Controls.MenuItem' to type 'System.Windows.Controls.Button'.
       Source=Windows7Theme
       StackTrace:
            at Windows7Theme.MainWindow.System.Windows.Markup.IComponentConnector.Connect(Int32 connectionId, Object target) in c:UsersTJoeDocumentsVisual Studio 10ProjectsWindows7ThemeWindows7ThemeMainWindow.xaml:line 13
            at MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetConnectionId(Object root, Int32 connectionId, Object instance)
       InnerException: 

The generated code-behind files can be found in the obj folder, so if we examine that we can see the following code:

void System.Windows.Markup.IComponentConnector.Connect(int connectionId, object target) {
    switch (connectionId)
    {
    case 1:

    #line 13 "......MainWindow.xaml"
    ((System.Windows.Controls.Button)(target)).AddHandler(System.Windows.Controls.MenuItem.ClickEvent, new System.Windows.RoutedEventHandler(this.MenuItem_Click));

    #line default
    #line hidden
    return;
    }
    this._contentLoaded = true;
}

The issue here is the code generated is trying to cast the MenuItem to a Button. If we alter our sample like so:

<Window.Resources>
    <ContextMenu x:Key="ContextMenuKey">
        <MenuItem Header="Header" Click="MenuItem_Click"/>
    </ContextMenu>

    <Style TargetType="Button">
        <Setter Property="ContextMenu"
                Value="{StaticResource ContextMenuKey}" />
    </Style>
</Window.Resources>

<Button />

Then the code generated is:

void System.Windows.Markup.IComponentConnector.Connect(int connectionId, object target) {
    switch (connectionId)
    {
    case 1:

    #line 10 "......MainWindow.xaml"
    ((System.Windows.Controls.MenuItem)(target)).Click += new System.Windows.RoutedEventHandler(this.MenuItem_Click);

    #line default
    #line hidden
    return;
    }
    this._contentLoaded = true;
}

Based on my tests, it appears the code generator assigns an ID to each control that it needs to "connect" or add handlers/backing fields for. In the case where the ContextMenu is included inline (i.e. the first example), it's event handlers are getting assigned to the root element inside the window and is not getting an ID of it's own.

If we changed Button to be contained in a Grid, then the exception above would indicate it failed to cast MenuItem to a Grid. Because now the Grid is the root element. This indicates it has nothing to do with the type the Style targets.

When the ContextMenu is included as a separate resource, the code generator seems to properly assign it an ID so it's handlers can be properly attached.

Ultimately, this is a bug in the XAML code generator.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...