Habe ich etwas in der Stufe von .net 3.5 zu .net 4 verpasst, weil ich scheinbar fehlerhaftes Verhalten sehe, das dem Ziel des Systems widerspricht.WPF statische Ressourcenverweise auf logische Ressourcen in DataTemplates, die zur Laufzeit nicht auflösen
Ich versuche, eine einfache MVVM-Bibliothek für die Arbeit mit ein paar Beispielen zu trommeln. Ich benutze es in einer Twitter-Client-Anwendung für etwas zusätzliches Lernen und habe einen großen Stolperstein getroffen.
Das Szenario ist dies. Mein Root-Objekt ViewModel (TwitterClientViewModel) erhält eine Instanz eines DialogViewModel-Objekts zur Anzeige. Das DialogViewModel wird zu einer Sammlung hinzugefügt und ein bool HasDialogs wird auf true festgelegt. PropertyChanged-Ereignisse werden bei Bedarf für die Auflistung und das Flag aufgerufen. Dieser Teil funktioniert fabelhaft.
Die Ansicht für TwitterClientViewModel heißt TwitterClientTemplate und macht sichtbar ein Overlay für das Hosting von DialogViewTemplate (DialogViewModel's view). Die Vorlage des Hosting-ContentControl verweist auf DialogViewTemplate mit einer DynamicResource-Erweiterung. Dies zeigt sich hervorragend im Designer und zur Laufzeit.
Hier wird es merkwürdig. Der 'body' von DialogViewTemplate enthält den Dialoginhalt mit einem weiteren Inhaltssteuerelement, das an DialogViewModel.Content (type object) gebunden ist. Die Hoffnung war, dass ich mit einem TemplateSelector (von dem ich einen netten deklarativen geschrieben habe, aber zu Testzwecken auskommentiert habe), sowohl Text als auch interaktive Elemente anzeigen konnte. Zum Beispiel, um Details vom Benutzer anzufordern, wenn ein Twitter-Account authentifiziert wird. In diesem Fall eine PIN-Nummer.
An dieser Stelle habe ich zwei verschachtelte Contentcontrols für die Dialogimplementierung. Zu Testzwecken verwendet die Inhaltssteuerung im Hauptteil von DialogViewTemplate eine statische Ressourcenerweiterung, um EnterPINDialogTemplate (View für EnterPINDialogViewModel) abzurufen. Sowohl EnterPINDialogTemplate als auch DialogViewTemplate befinden sich in derselben Datei (die erstere wird natürlich zuerst definiert), obwohl sie ursprünglich getrennt waren.
Zur Laufzeit löst die statische Ressourcenerweiterung eine XamlParseException mit der Nachricht aus; 'Bereitstellen von Wert für' System.Windows.Markup.StaticResourceHolder 'hat eine Ausnahme ausgelöst.'
und eine innere Ausnahmebedingungsnachricht;
'Die Ressource' EnterPINDialogTemplate 'kann nicht gefunden werden. Bei Ressourcennamen wird die Groß-/Kleinschreibung berücksichtigt '
Die Verwendung einer dynamicresource gibt null zurück und zeigt den vollständigen Namen des EnterPINDialogViewModel-Typs im contentcontrol an - wie erwartet, wenn die Ressource nicht aufgelöst wird. Der Aufruf von FrameWorkElement.FindResource() löst in meinem benutzerdefinierten TemplateSelector eine ähnliche Ausnahme aus (TryFindResource gibt null zurück).
Mein erster Gedanke war, dass der logische Baum geteilt wird, wenn die Datenplatten erstellt werden, und ich erinnerte mich an ein Problem in diesem Bereich von einem früheren Projekt. Ich versuchte, die MergeDictionaries Eigenschaft Resource mit der Ressource Wörterbücher aus dem Datatemplate avaliable zu machen, aber der Designer nicht wie, dass ein Bit, und der Fehler wird hier beschrieben: http://connect.microsoft.com/VisualStudio/feedback/details/498844/wpf-designer-throws-invalidcastexception
Scratch diese Idee. Ich habe versucht, die Wörterbücher auf den Ebenen Application, Window und TwitterClientTemplate zusammenzuführen, hatte aber kein Glück.
Unten sind die XAML-Dateien.
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>
Hilf mir Stackoverflow, du bist meine einzige Hoffnung!
EDIT: Hat einige weitere Arbeiten zu diesem Thema. Wenn sich beide Vorlagen in derselben Datei befinden, wird sowohl die Ressource dynamicresource als auch die Erweiterung statresresource die Ressource ohne Probleme auflösen. Wenn sie sich in separaten Dateien befinden, wird die Ressource nicht aufgelöst, unabhängig davon, wie die Wörterbücher zusammengeführt werden. Jede Erweiterung gibt null zurück.
Offensichtlich ist die Lösung, beide Ressourcen in das gleiche Wörterbuch zu werfen, aber soweit es mich betrifft, ist dies ein Hack und ist nicht vorgesehen, Verhalten der logischen Ressourcen-Lookup-System. Ich bin gerade kein fröhlicher Hase. Das scheint ziemlich undokumentiert ...