How to reference a DataTemplate inside a DataTemplate? - c#

I have this ResourceDictionary:
<DataTemplate DataType="{x:Type vm:MainViewModel}">
<ListView>
<ListView.ItemTemplate>
<DataTemplate /> <----- Here I'd like to load an external DataTemplate from the same folder
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
In the same folder I have another ResourceDictionary:
<DataTemplate DataType="{x:Type vm:EditRecordViewModel}">
<StackPanel>
<TextBlock Text="Test" />
</StackPanel>
</DataTemplate>
Question
In my first ResourceDictionary how do I get the 2nd ResourceDictionary to display in the first, where I have the <DataTemplate /> displayed?
I have added resources in the App.xaml like so, but how do actually use them?:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Views/MainViewModel.xaml" />
<ResourceDictionary Source="Views/EditRecordViewModel.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>

You can provide a key to a data template
<DataTemplate x:Key="test" DataType="{x:Type local:EditRecordViewModel}">...
and then reference it
<ListView ItemTemplate="{StaticResource test}">...

You have declared Data Templates without a key, which means that they will be applied to the corresponding types by default.
You don't need any links for this.
The only thing that matters is that the ListView gets a collection of items of type EditRecordViewModel.
Example.
TwoDataTemplte/ViewModels.cs:
namespace TwoDataTemplte.ViewModel
{
public class EditRecordViewModel
{
public string Text { set; get; }
}
public class MainViewModel
{
public EditRecordViewModel[] EditRecords { get; } =
{
new EditRecordViewModel() {Text = "First"},
new EditRecordViewModel() {Text = "Second"}
};
}
}
TwoDataTemplte\MainDictionary.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:TwoDataTemplte.ViewModel">
<DataTemplate DataType="{x:Type vm:MainViewModel}">
<ListView ItemsSource="{Binding EditRecords}"/>
</DataTemplate>
</ResourceDictionary>
TwoDataTemplte\ItemDictionary.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:TwoDataTemplte.ViewModel">
<DataTemplate DataType="{x:Type vm:EditRecordViewModel}">
<StackPanel>
<TextBlock Text="{Binding Text}" />
</StackPanel>
</DataTemplate>
</ResourceDictionary>
App.xaml:
<Application x:Class="Febr20y.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Febr20y"
StartupUri="TwoDataTemplte/ExampleWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="TwoDataTemplte/MainDictionary.xaml" />
<ResourceDictionary Source="TwoDataTemplte/ItemDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
TwoDataTemplte\ExampleWindow.xaml:
<Window x:Class="TwoDataTemplte.ExampleWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TwoDataTemplte"
xmlns:vm="clr-namespace:TwoDataTemplte.ViewModel"
mc:Ignorable="d"
Title="ExampleWindow" Height="450" Width="800">
<Grid>
<ContentControl>
<vm:MainViewModel/>
</ContentControl>
</Grid>
</Window>

Related

Set App.xaml to a UserControl WPF (to connect Locator)

Situation :
I'm currently trying to connect my DataContext to my ViewModel. I'm using GalaSoftMvvmLight.
But the fact is that I don't have a Window because I'll integrate this code in another program, which has a Window. So I just have UserControl.
Problem :
I don't know why, but I can't connect my DataContext in UserControl.
I get this error {"Cannot find resource named 'Locator'. Resource names are case sensitive."}
Question :
How can I connect properly my App.xaml resources to my View ? And if it's not possible without Window, how can I call DataContext with something like this
<UserControl.DataContext>
SOMETHING TO SET DATACONTEXT WITH BINDING !
</UserControl.DataContext>
Here is my code :
App.xaml
<Application x:Class="SOMETHING.App" xmlns:vm="clr-namespace:SOMETHING.ViewModel" StartupUri="ApplicationView.xaml">
<Application.Resources>
<ResourceDictionary>
<vm:ViewModelLocator x:Key="Locator" />
</ResourceDictionary>
</Application.Resources>
</Application>
ApplicationView.xaml
<UserControl x:Class="SOMETHING.View.ApplicationView"
<!-- THIS ONE DOESN'T WORK -->
DataContext="{Binding ApplicationVM, Source={StaticResource Locator}}">
<!-- THIS ONE DOESN'T WORK IF I SET <vm:ViewModelLocator x:Key="Locator" /> IN STYLE.XAML, BUT I CAN'T USE IT IN USERCONTROL PARAMETERS (LIKE ABOVE) -->
<UserControl.Resources>
<ResourceDictionary >
<ResourceDictionary.MergedDictionaries >
<ResourceDictionary Source="Style.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Label Content="{Binding Title}" />
</Grid>
</UserControl>
ViewModelLocator
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
}
else
{
SimpleIoc.Default.Register<IDataService, DataService>();
}
SimpleIoc.Default.Register<ApplicationViewModel>();
}
public ApplicationViewModel ApplicationVM
{
get { return SimpleIoc.Default.GetInstance<ApplicationViewModel>();
}
}
Provided that the ViewModelLocator class and the UserControl reside in the same project/assembly, you could define the ViewModelLocator resource in a ResourceDictionary that you merge into the UserControl like this:
<UserControl ...>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Global.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Path="ApplicationVM" Source="{StaticResource Locator}" />
</UserControl.DataContext>
<Grid>
</Grid>
</UserControl>
Global.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfControlLibrary1">
<local:ViewModelLocator x:Key="Locator" />
</ResourceDictionary>

Resource Dictionary not working - Exception raised for Name/Key not found

I have just started working with Resource Dictionaries and I am stuck on this because my resource dictionary is not working at all. I have tried code-behind and XAML but every time I get exceptions and the app crashes.
If I reference the Dictionary through XAML I get the exception at runtime that Name/Key is not found. The code I used in App.xaml is:
<Application
x:Class="WatchfreeWebsite.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WatchfreeWebsite.Helpers">
<Application.Resources>
<TransitionCollection x:Key="TransCollection">
<EdgeUIThemeTransition Edge="Right"/>
</TransitionCollection>
<ResourceDictionary x:Key="resourcesDictionary">
<ResourceDictionary.MergedDictionaries>
<local:GlobalTemplates Source="Helpers/GlobalTemplates.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
The resource dictionary holds aDataTemplate and a MediaTransportControlsStyle but I cant seem to reference it through XAML because it gives syntax errors and during the runtime the page produces exception while loading XAML at InitializeComponent(); stage.
Resource Dictionary:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WatchfreeWebsite.Helpers"
x:Class="WatchfreeWebsite.Helpers.GlobalTemplatesClass"
xmlns:data="using:WatchfreeWebsite.Helpers">
<DataTemplate x:Key="StreamBoxItemTemplate"
x:DataType="data:StreamingHelper">
<TextBlock Text="{x:Bind StreamName, Mode=OneWay}"
Style="{StaticResource BodyTextBlockStyle}"
TextWrapping="NoWrap"
MaxLines="1"
TextTrimming="WordEllipsis"/>
</DataTemplate>
<Style TargetType="MediaTransportControls"
x:Key="myCustomTransportControls">
<Setter Property="IsTabStop" Value="False" />
.......
</Style>
</ResourceDictionary>
The class behind the resource dictionary is:
public partial class GlobalTemplatesClass
{
public GlobalTemplatesClass()
{
this.InitializeComponent();
}
}
I reference the DataTemplate inside the above style and this style is referenced in another page as:
<MediaPlayerElement x:Name="MediaView"
Grid.Row="2"
Source="{Binding MediaSourceObject, Mode=OneWay}"
DoubleTapped="MediaView_DoubleTapped"
AreTransportControlsEnabled="True">
<MediaPlayerElement.TransportControls>
<data:CustomTransportControlsHelper Style="{StaticResource ResourceKey=myCustomTransportControls}"/>
</MediaPlayerElement.TransportControls>
</MediaPlayerElement>
But this is not working and there is a red line below the resource name saying that the resource is not found.
Is there something that I am missing? If someone can help me here please provide your suggestions. Thanks
When you add multiple items under your resources, each of them should fall within the <ResourceDictionary> tag and not directly under <Application.Resources>.
That's because Resources itself is a dictionary, so you're in effect trying to replace that collection rather than add elements to it. Docs here: https://msdn.microsoft.com/en-us/library/system.windows.application.resources%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
I created a sample project with just an App.xaml at the project base level, a folder called Helpers and a ResourceDictionary under Helpers named GlobalTemplates.xaml to match yours.
Created a simple brush as an example in GlobalTemplates.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1.Helpers">
<SolidColorBrush x:Key="DefaultForeground" Color="DarkGreen" />
</ResourceDictionary>
In App.xaml
<Application
x:Class="App1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
RequestedTheme="Light">
<Application.Resources>
<ResourceDictionary>
<TransitionCollection x:Key="TransCollection">
<EdgeUIThemeTransition Edge="Right"/>
</TransitionCollection>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Helpers/GlobalTemplates.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
And then in MainPage.xaml successfully referenced the style from the dictionary:
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Foreground="{StaticResource DefaultForeground}">Hello world</TextBlock>
</Grid>
</Page>

how to access other resources inside a ResourceDictionary

A DataTemplate inside a Resource Dictionary needs to refer to a Styles.xaml, so I have the following
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:WPFApp">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="resources/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
<DataTemplate DataType="{x:Type src:MyFileInfo}">
<Grid>
grid stuff
</Grid>
<TextBlock> stuff </TextBlock>
</DataTemplate>
</ResourceDictionary>
but there is an error at DataTemplate saying that The proprety "Visual Tree" can only be set once. What does this mean? Is it good practice to put a DataTemplate inside a ResourceDictionary? How to access other resources inside a ResourceDictionary?
A DataTemplate should only have one child. Use this:
<DataTemplate DataType="{x:Type src:MyFileInfo}">
<Grid>
grid stuff
<TextBlock> stuff </TextBlock>
</Grid>
</DataTemplate>

Reference one resource from another ResourceDictionary

For example, we have two ResourceDictionary:
Global.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<FontFamily x:Key="GlobalFontFamily">Segoe UI</FontFamily>
</ResourceDictionary>
Part.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ReousrceDictionary Source="Global.xaml" />
</ResourceDictionary.MergedDictionaries>
<FontFamily x:Key="PartFontFamily">**Reference GlobalFontFamily here**</FontFamily>
</ResourceDictionary>
MainWindow.xaml
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<ResourceDictionary.MergedDictionaries>
<ReousrceDictionary Source="Part.xaml" />
</ResourceDictionary.MergedDictionaries>
</Window.Resources>
<Grid>
<TextBlock FontFamily="{StaticResource PartFontFamily}" />
</Grid>
</Window>
In some view, I would use PartFontFamily to set the element font family. Which I would like to achieve is, use the specified font family when PartFontFamily is set, otherwise use GlobalFontFamily instead. So I want to keep the PartFontFamily key, and reference it to GlobalFontFamily since customers have no specified font family for PartFontFamily.
Any good suggestions?
Merge your Part.xaml to the Global.xaml... so that you can refer every thing from the Global.xaml...
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ReousrceDictionary Source="Part.xaml" />
</ResourceDictionary.MergedDictionaries>
<FontFamily x:Key="GlobalFontFamily">Segoe UI</FontFamily>
Merge the Global.xaml to your MainWindow...
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<ResourceDictionary.MergedDictionaries>
<ReousrceDictionary Source="Global.xaml" />
</ResourceDictionary.MergedDictionaries>
</Window.Resources>
<Grid>
<TextBlock FontFamily="{DynamicResource PartFontFamily}" />
</Grid>
</Window>
You can directly access the PartFontFamily here...
I guess you want to make an alias for your resource.
<DynamicResource x:Key="PartFontFamily" ResourceKey="GlobalFontFamily"/>

Contentcontrol doesn't find my datatemplate it's ViewModel

I'm using WPF, MVVM & PRISM.
I got a datatemplate in my View linked to a ViewModel UC2002_RFPBeheren_ViewModel cause the page were this code is included is linked to another ViewModel and I want the Button to have UC2002_RFPBeheren_ViewModel as ViewModel.
The datacontext of this page is UC2002_RFPBeheren_ProjectInfo_ViewModel but I want the SaveButton to use the ViewModel UC2002_RFPBeheren_ViewModel
Here is my code:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Resources/RFPModuleResources.xaml" />
<ResourceDictionary>
<DataTemplate x:Key="SaveButton" DataType="{x:Type vm:UC2002_RFPBeheren_ViewModel}">
<Button Command="{Binding SaveRFPCommand}">Save</Button>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<ContentControl ContentTemplate="{StaticResource SaveButton}"/>
<Button Command="{Binding CloseTabCommand}">Close</Button>
</StackPanel>
Although the SaveButton displays but don't reacts on my command.
Do I forget something or is there another way to solve this?
Thanks in advance ;) !
=================================================================================
EDIT:
So I made some changes but it still doesn't work.
Code example:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Resources/RFPModuleResources.xaml" />
<ResourceDictionary>
<DataTemplate x:Key="SaveButton" DataType="{x:Type vm:UC2002_RFPBeheren_ViewModel}">
<Button Command="{Binding SaveRFPCommand}">Save</Button>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
I set this property in the ViewModel of the page
public UC2002_RFPBeheren_ViewModel MySaveVM { get; set; }
My stackpanel looks now like this:
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<ContentControl Content="{Binding MySaveVM}" ContentTemplate="{StaticResource SaveButton}"/>
<Button Command="{Binding CloseTabCommand}">Close</Button>
</StackPanel>
what happens if you set your UV2002_RFPBeheren_ViewModel instance as the content for the ContentPresenter?
<ContentControl Content="{Binding MyUV2002_RFPBeheren_ViewModel}"/>
the DataTemplate just say how your Viewmodel should be displayed, but you have to set the DataContext or Binding to the instance of your viewmodel.
EDIT:
example
public class VMFoo
{
public UV2002_RFPBeheren_ViewModel MySaveVM {get; set;}
}
xaml
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Resources/RFPModuleResources.xaml" />
<ResourceDictionary>
<DataTemplate x:Key="SaveButton" DataType="{x:Type vm:UC2002_RFPBeheren_ViewModel}">
<Button Command="{Binding SaveRFPCommand}">Save</Button>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.DataContext>
<x:local VMFoo/>
</UserControl.DataContext>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<ContentControl Content="{Binding MySaveVM}"/>
<Button Command="{Binding CloseTabCommand}">Close</Button>
</StackPanel>
EDIT: Small working sample
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfApplication1="clr-namespace:WpfApplication1" Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type WpfApplication1:UV2002_RFPBeheren_ViewModel}">
<Button Command="{Binding SaveRFPCommand}">Save</Button>
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.DataContext>
<WpfApplication1:VMFoo/>
</Grid.DataContext>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<ContentControl Content="{Binding MySaveVM}"/>
<Button Command="{Binding CloseTabCommand}">Close</Button>
</StackPanel>
</Grid>
</Window>
Viewmodels
public class VMFoo
{
public VMFoo()
{
this.MySaveVM = new UV2002_RFPBeheren_ViewModel();
}
public UV2002_RFPBeheren_ViewModel MySaveVM { get; set; }
}
public class UV2002_RFPBeheren_ViewModel
{
private DelegateCommand _save;
public ICommand SaveRFPCommand
{
get{if(this._save==null)
{
this._save = new DelegateCommand(()=>MessageBox.Show("success"),()=>true);
}
return this._save;
}
}
}
This is because of the way that ContentControl's work. It is assumed that the things in the ContentTemplate are related to the Content and so the DataContext is set to the Content and therefore that is the DataContext that the button within the template has access to. You haven't specified a Content so the value is null and so the DataContext is explicitly set to null. You can see this in a basic example. One thing you can do is to bind the Content of the ContentControl to the DataContext - see the last contentcontrol in the example.
<StackPanel DataContext="Foo">
<StackPanel.Resources>
<DataTemplate x:Key="withBtn">
<Button Content="{Binding}" />
</DataTemplate>
</StackPanel.Resources>
<Button Content="{Binding}" />
<ContentControl ContentTemplate="{StaticResource withBtn}" />
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource withBtn}" />
</StackPanel>
If you are using MVVM you must expose some instance of UC2002_RFPBeheren_ViewModel within your UC2002_RFPBeheren_ProjectInfo_ViewModel to bind against. Either as a property, or an item of a collection that is a property.
Everything in the view must be ultimately accessible from the ViewModel that is your data context (UC2002_RFPBeheren_ProjectInfo_ViewModel)

Categories

Resources