Ссылки на статические ресурсы 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='&lt;PIN&gt;'}"/>
        <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=&lt;Add Account&gt;}"/>
                </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, но было бы неплохо получить предупреждение о том, что это неправильно.

Другие вопросы по тегам