问题
I want to create a wizard UI for inventories management. The relevant line in the xaml is:
<ContentPresenter Content="{Binding Current}" ContentTemplateSelector="{StaticResource inventorySelector}"/>
"Current" is the currently active view model, one of AvailableInventoriesViewModel, GroupsViewModel, NewArticlesViewModel, ResultViewModel. The DataTemplateSelector I have defined as such:
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using Centron.WPF.WarehousingExtension.InventoryModule.ViewModels.WizardViewModels;
namespace Centron.WPF.WarehousingExtension.InventoryModule.UI.DataTemplateSelectors
{
public class InventoryDatatemplateSelector : DataTemplateSelector
{
public DataTemplate AvailableDatatype { get; set; }
public DataTemplate GroupsDatatype { get; set; }
public DataTemplate NewDatatype { get; set; }
public DataTemplate ResultDatatype { get; set; }
public InventoryDatatemplateSelector()
{
Debug.WriteLine("");
}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is AvailableInventoriesViewModel)
return AvailableDatatype;
else if (item is GroupsViewModel)
return GroupsDatatype;
else if (item is NewArticlesViewModel)
return NewDatatype;
else return ResultDatatype;
}
}
}
Then I create instances of the DataTemplates and a Selector like this:
<base:InventoryViewBase.Resources>
<DataTemplate DataType="viewModels:AvailableInventoriesViewModel" x:Key="availableInventoriesDatatype">
<controls:AvailableInventoriesView />
</DataTemplate>
<DataTemplate DataType="viewModels:GroupsViewModel" x:Key="groupsDatatype">
<controls:GroupsView />
</DataTemplate>
<DataTemplate DataType="viewModels:NewArticlesViewModel" x:Key="newArticlesDatatype">
<controls:NewArticlesView />
</DataTemplate>
<DataTemplate DataType="viewModels:ResultViewModel" x:Key="resultDatatype">
<controls:ResultView/>
</DataTemplate>
<selector:InventoryDatatemplateSelector
x:Key="inventorySelector"
AvailableDatatype="{StaticResource availableInventoriesDatatype}"
GroupsDatatype="{StaticResource groupsDatatype}"
NewDatatype="{StaticResource newArticlesDatatype}"
ResultDatatype="{StaticResource resultDatatype}"/>
</base:InventoryViewBase.Resources>
I set a breakpoint in the constructor of my InventoryDatatemplateSelector, and can step through it, but in the next Debug step, apparently when it tries to set the first property of that selector instance, I immediately get an exception with inner exception:
Cannot find resource named \"availableInventoriesDatatype\". Resource names are case sensitive.
What's the deal, why is the resource not found when it's clearly defined?
回答1:
Ok, I found the solution. The only error was that the "Key" property of a resource has to be set first. So instead of:
<DataTemplate DataType="viewModels:AvailableInventoriesViewModel" x:Key="availableInventoriesDatatype" >
<controls:AvailableInventoriesView />
</DataTemplate>
I need:
<DataTemplate x:Key="availableInventoriesDatatype" DataType="viewModels:AvailableInventoriesViewModel" >
<controls:AvailableInventoriesView />
</DataTemplate>
回答2:
I know you found your problem, but there is a simpler way to solve this that I thought you should know about. Since each of your DataTemplates are working on different classes, you don't really need to build your DataTemplateSelector to do this. If you simply declare your DataTemplates without keys like:
<DataTemplate DataType="{x:Type viewModels:AvailableInventoriesViewModel}">
<controls:AvailableInventoriesView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:GroupsViewModel}">
<controls:GroupsView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:NewArticlesViewModel}">
<controls:NewArticlesView />
</DataTemplate>
<DataTemplate DataType="viewModels:ResultViewModel">
<controls:ResultView/>
</DataTemplate>
And declare your ContentPresenter
without specifying the ContentTemplateSelector
such as:
<ContentPresenter Content="{Binding Current}" />
Then the appropriate DataTemplate will be selected for whatever Type Current
is set to.
This is a much cleaner solution and eliminates the need for the custom selector.
WPF is powerful and very flexible but can be it can be challenging to get your head wrapped around it. But once you understand what it can do, I think that you will change your opinion on the matter.
Hope this helps.
回答3:
Here was my case - I declared ItemTemplate before DataTemplate:
<ListBox Name="peopleListBox" Grid.Column="1" Grid.Row="2"
ItemsSource="{Binding Source={StaticResource ExpenseDataSource}, XPath=Person}"
ItemTemplate="{StaticResource nameItemTemplate}">
</ListBox>
<Grid.Resources>
<!-- Name item template -->
<DataTemplate x:Key="nameItemTemplate">
<Label Content="{Binding XPath=@Name}"/>
</DataTemplate>
</Grid.Resources>
Then I just change the order and put Grid.Resources before the Listbox like bellow, everything fine now!!!
<Grid.Resources>
<!-- Name item template -->
<DataTemplate x:Key="nameItemTemplate">
<Label Content="{Binding XPath=@Name}"/>
</DataTemplate>
</Grid.Resources>
<ListBox Name="peopleListBox" Grid.Column="1" Grid.Row="2"
ItemsSource="{Binding Source={StaticResource ExpenseDataSource}, XPath=Person}"
ItemTemplate="{StaticResource nameItemTemplate}">
</ListBox>
Is not XAML so stupid? :) :)
回答4:
I give more detail in case of styling nested components. In this example, MainWindowGrid contains UserControlGrid and PanelGrid.
The following code will not work.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:XeerSoft.UX.Themes">
<Thickness x:Key="PanelRowMargin">0,0,0,10</Thickness>
<Thickness x:Key="PanelPadding">10</Thickness>
<Color x:Key="Primary">#005C9D</Color>
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource Primary}"></SolidColorBrush>
<Color x:Key="Accent">#E3672B</Color>
<SolidColorBrush x:Key="AccentBrush" Color="{StaticResource Accent}"></SolidColorBrush>
<Color x:Key="DarkGray">#777777</Color>
<SolidColorBrush x:Key="DarkGrayBrush" Color="{StaticResource DarkGray}"></SolidColorBrush>
<Color x:Key="LightGray">#E4E4E4</Color>
<SolidColorBrush x:Key="LightGrayBrush" Color="{StaticResource LightGray}"></SolidColorBrush>
<Style x:Key="MainWindowGrid"
BasedOn="{StaticResource {x:Type Grid}}"
TargetType="Grid">
<Setter Property="Background" Value="{StaticResource LightGrayBrush}"/>
</Style>
<Style x:Key="GridPanel"
BasedOn="{StaticResource {x:Type Grid}}"
TargetType="Grid">
<Setter Property="Background" Value="White"/>
</Style>
<Style x:Key="UserControlPanel"
BasedOn="{StaticResource {x:Type UserControl}}"
TargetType="UserControl">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="{StaticResource DarkGrayBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
</Style>
</ResourceDictionary>
Now, if you swap the declaration sequence so the MainWindowGrid goes last, it will work. (Outer scope, goes last)
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:XeerSoft.UX.Themes">
<Thickness x:Key="PanelRowMargin">0,0,0,10</Thickness>
<Thickness x:Key="PanelPadding">10</Thickness>
<Color x:Key="Primary">#005C9D</Color>
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource Primary}"></SolidColorBrush>
<Color x:Key="Accent">#E3672B</Color>
<SolidColorBrush x:Key="AccentBrush" Color="{StaticResource Accent}"></SolidColorBrush>
<Color x:Key="DarkGray">#777777</Color>
<SolidColorBrush x:Key="DarkGrayBrush" Color="{StaticResource DarkGray}"></SolidColorBrush>
<Color x:Key="LightGray">#E4E4E4</Color>
<SolidColorBrush x:Key="LightGrayBrush" Color="{StaticResource LightGray}"></SolidColorBrush>
<Style x:Key="GridPanel"
BasedOn="{StaticResource {x:Type Grid}}"
TargetType="Grid">
<Setter Property="Background" Value="White"/>
</Style>
<Style x:Key="UserControlPanel"
BasedOn="{StaticResource {x:Type UserControl}}"
TargetType="UserControl">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="{StaticResource DarkGrayBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
</Style>
<Style x:Key="MainWindowGrid"
BasedOn="{StaticResource {x:Type Grid}}"
TargetType="Grid">
<Setter Property="Background" Value="{StaticResource LightGrayBrush}"/>
</Style>
</ResourceDictionary>
来源:https://stackoverflow.com/questions/15553446/cannot-find-resource-named-x-why-not