Ссылки на статические ресурсы WPF на логические ресурсы в шаблонах данных не разрешаются во время выполнения
Я что-то пропустил в шаге вверх от.net 3.5 к.net 4, потому что я вижу, казалось бы, глючное поведение, которое кажется противоречащим цели системы.
Я пытаюсь собрать простую библиотеку MVVM для работы, используя несколько примеров. Я использую его в клиентском приложении Twitter для дополнительного изучения и столкнулся с большим камнем преткновения.
Сценарий такой. Моему корневому объекту ViewModel (TwitterClientViewModel) предоставляется экземпляр объекта DialogViewModel для отображения. DialogViewModel добавляется в коллекцию, а для bool HasDialogs устанавливается значение true. События PropertyChanged вызываются для коллекции и флага, если это необходимо. Эта часть работает сказочно.
Представление для TwitterClientViewModel называется TwitterClientTemplate и делает Visible наложением для хостинга DialogViewTemplate (представление DialogViewModel). Шаблон хостинга ContentControl ссылается на DialogViewTemplate с расширением DynamicResource. Это прекрасно отображается в дизайнере и во время выполнения.
Здесь вещи становятся странными. "Тело" DialogViewTemplate содержит содержимое диалога с дополнительным элементом управления контентом, связанным с DialogViewModel.Content (тип объекта). Была надежда, что с использованием TemplateSelector (о котором я написал хороший декларативный, но закомментировал для целей тестирования), я мог отображать как текстовые, так и интерактивные элементы. Например, запрос информации от пользователя при аутентификации учетной записи Twitter. В этом случае ПИН-код.
На данный момент у меня есть два вложенных элемента управления контентом для реализации диалога. В целях тестирования contentcontrol в теле DialogViewTemplate использует расширение staticresource для извлечения EnterPINDialogTemplate (представление для EnterPINDialogViewModel). И EnterPINDialogTemplate, и DialogViewTemplate находятся в одном файле (первый, конечно, определяется первым), хотя изначально они были отдельными.
Во время выполнения расширение staticresource генерирует исключение XamlParseException с сообщением; "Предоставить значение для" System.Windows.Markup.StaticResourceHolder ", вызвало исключение".
и внутреннее сообщение об исключении;
"Не удается найти ресурс с именем" EnterPINDialogTemplate ". Имена ресурсов чувствительны к регистру
Использование динамического ресурса возвращает значение NULL и отображает полное имя типа EnterPINDialogViewModel в contentcontrol - как и ожидалось, когда ресурс не разрешен. Разбивая мой собственный TemplateSelector, вызывая FrameWorkElement.FindResource(), выдает похожее исключение (TryFindResource возвращает ноль).
Моей первой мыслью было, что логическое дерево разбивается, когда создаются шаблоны данных, и я вспомнил проблему в этой области из более раннего проекта. Я попытался использовать свойство MergeDictionaries ResourceDictionary, чтобы сделать словари ресурсов доступными из DataTemplate, но дизайнеру не понравился этот бит, и ошибка описана здесь: http://connect.microsoft.com/VisualStudio/feedback/details/498844/wpf-designer-throws-invalidcastexception
Поцарапайте эту идею. Я попытался объединить словари на уровнях Application, Window и TwitterClientTemplate, но мне не повезло.
Ниже приведены файлы xaml.
DialogTemplates.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:VM="clr-namespace:EpicTweet.ViewModel"
xmlns:ET="clr-namespace:EpicTweet"
xmlns:T="clr-namespace:EpicTweet.Tools"
xmlns:MV="clr-namespace:MVVM;assembly=MVVM"
xmlns:Loc="clr-namespace:EpicTweet.Localization"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
<DataTemplate DataType="VM:EnterPINDialogViewModel" x:Key="EnterPINDialogTemplate">
<Grid d:DesignWidth="453.89" d:DesignHeight="78.92" Loc:ResXManagerProperty.ResourceManager="{x:Static ET:Language.ResourceManager}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="{Loc:ResxExtension ResourceName=String_PIN, FallbackValue='<PIN>'}"/>
<TextBox Grid.Column="1"/>
<TextBlock Grid.Row="1" Grid.RowSpan="2"></TextBlock>
</Grid>
</DataTemplate>
<DataTemplate x:Key="DialogViewTemplate" DataType="MV:DialogViewModel">
<Border BorderBrush="Black" BorderThickness="1">
<Grid d:DesignWidth="277.419" d:DesignHeight="74.96" Background="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" Height="Auto" Width="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border d:LayoutOverrides="Width, Height" BorderThickness="0,0,0,1" BorderBrush="Black">
<Label Content="{Binding DisplayName, FallbackValue=Header}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
</Border>
<ContentControl Content="{Binding Content, FallbackValue=Body}" ContentTemplate="{StaticResource EnterPINDialogTemplate}" HorizontalAlignment="Stretch" d:LayoutOverrides="Height" Grid.Row="1" Margin="5">
<!--<ContentControl.ContentTemplateSelector>
<T:TypeTemplateSelector>
<T:TemplateTypeRelationship Type="{x:Type VM:EnterPINDialogViewModel}" ResourceKey="EnterPINDialogTemplate"/>
</T:TypeTemplateSelector>
</ContentControl.ContentTemplateSelector>-->
</ContentControl>
<ItemsControl Grid.Row="2" Margin="10"
ItemsSource="{Binding Commands, Mode=OneTime, FallbackValue={x:Static VM:TwitterClientViewModel.DEFAULT_DIALOG_COMMANDS}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Content="{Binding DisplayName, FallbackValue=CommandName, Mode=OneWay}"
Command="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</Border>
</DataTemplate>
TwitterClientDataTemplate.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:VM="clr-namespace:EpicTweet.ViewModel"
xmlns:ET="clr-namespace:EpicTweet"
xmlns:MV="clr-namespace:MVVM;assembly=MVVM"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="DialogTemplates.xaml"/>
</ResourceDictionary.MergedDictionaries>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<DataTemplate x:Key="TwitterClientTemplate" DataType="MV:TwitterClientViewModel">
<ScrollViewer d:DesignWidth="285.083" d:DesignHeight="119.96">
<Grid>
<StackPanel d:LayoutOverrides="Width, Height">
<StackPanel Orientation="Horizontal">
<Button Command="{Binding AddAccountCommand.Command}" Content="{Binding AddAccountCommand.DisplayName, FallbackValue=<Add Account>}"/>
</StackPanel>
<ContentControl/>
</StackPanel>
<Border BorderThickness="1" Background="#80000000" Visibility="{Binding HasDialogs, Converter={StaticResource BooleanToVisibilityConverter}, FallbackValue=Collapsed, Mode=OneWay}">
<Grid VerticalAlignment="Stretch" MinWidth="50" MaxWidth="200">
<ContentControl Content="{Binding Dialogs[0], Mode=OneWay}" ContentTemplate="{DynamicResource DialogViewTemplate}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Border>
</Grid>
</ScrollViewer>
</DataTemplate>
Помоги мне stackru, ты моя единственная надежда!
РЕДАКТИРОВАТЬ: сделал некоторые дальнейшие работы по этому вопросу. Если оба шаблона находятся в одном и том же файле, то динамические и статические расширения ресурсов разрешают ресурс без проблем. Если они находятся в отдельных файлах, ресурс не будет разрешен независимо от того, как я объединяю словари; каждое расширение возвращает ноль.
Очевидно, что решение состоит в том, чтобы выбросить оба ресурса в один и тот же словарь, но, насколько я понимаю, это взлом, а не намеренное поведение системы поиска логических ресурсов. Я не счастливый кролик прямо сейчас. Это кажется довольно недокументированным...
1 ответ
Если когда-либо был дурак, это я. После 4 часов в пятницу вечером, пытаясь решить эту проблему, я взломал ее, не благодаря тому, что я могу назвать только ошибочными сообщениями об ошибках.
Вот кайф.
<DataTemplate x:Key="TwitterClientTemplate" DataType="MV:TwitterClientViewModel">
должно быть
<DataTemplate x:Key="TwitterClientTemplate" DataType="{x:Type MV:TwitterClientViewModel}">
И БАМ, это работает.
Мой большой захват остается однако. Почему неправильный синтаксис работает в конструкторе, а не во время выполнения? Я предполагаю, что оптимизация во время выполнения не беспокоит заполнение словаря плохо написанным xaml, но было бы неплохо получить предупреждение о том, что это неправильно.