Похоже, это ошибка в генерации кода XAML.В дополнение к пользовательскому коду для файлов XAML существует версия, сгенерированная «компилятором», которая определяет InitializeComponent и поля класса для именованных элементов (т. Е. X: Name).
Приведенный простой пример:
<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 />
Если вы запустите это, вы получите следующее исключение:
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:\Users\TJoe\Documents\Visual Studio 10\Projects\Windows7Theme\Windows7Theme\MainWindow.xaml:line 13
at MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetConnectionId(Object root, Int32 connectionId, Object instance)
InnerException:
Сгенерированные файлы с выделенным кодом можно найти в папке obj
, поэтому, если мы проверим, что мы можемсм. следующий код:
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;
}
Проблема заключается в том, что сгенерированный код пытается преобразовать MenuItem
в Button
.Если мы изменим наш пример следующим образом:
<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 />
Тогда сгенерированный код будет:
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;
}
На основании моих тестов кажется, что генератор кода назначает идентификатор каждому элементу управления, который оннеобходимо "подключить" или добавить обработчики / вспомогательные поля для.В случае, когда ContextMenu включен inline (то есть в первом примере), его обработчики событий назначаются корневому элементу внутри окна и не получают его собственный идентификатор.
Если мы изменили Button насодержаться в сетке, то исключение выше будет означать, что ему не удалось привести MenuItem к сетке.Потому что теперь Сетка является корневым элементом.Это указывает на то, что он не имеет никакого отношения к типу целей Style.
Когда ContextMenu включено в качестве отдельного ресурса, генератор кода, похоже, правильно назначает ему идентификатор, поэтому его обработчики могут быть правильно подключены.
В конечном счете, это ошибка в генераторе кода XAML.