I have created a simple WPF application using XAML and PowerShell, consisting of a TabControl in whose child TabItems I wish to display multiple kinds of data. Depending on the type of data provided, I want the TabControl's child TabItems to use a different DataTemplate.
I understand that the best (only?) way to do this in my situation is to create a custom DataTemplateSelector class in C# to handle the template selection.
I have attempted to do this, but am having difficulty with using my custom class. Here is the error I am getting:
Exception calling "Load" with "1" argument(s): "Cannot create unknown type
'{clr-namespace:myNamespace}myDataTemplateSelector'."
At line:101 char:1
+ $Window = [Windows.Markup.XamlReader]::Load($Reader)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : XamlParseException
I suspect I am improperly loading a required assembly or the namespace and thus unable to access my custom namespace and custom class. I have never used C# before, so I really appreciate any hand-holding offered.
Once I have resolved that problem, I know my C# custom class's internal logic will not work as desired, but that is a separate issue. The C# code appears to be valid, as I can run it independently and instantiate my custom class.
The XAML and code also works fine if I remove all of the DataTemplateSelector-related bits and add the following to the TabControl:
ContentTemplate="{StaticResource UserDataTemplate}"
Here is the code (including C#, XAML, PowerShell):
$Assemblies = @("System", "PresentationFramework", "WindowsBase", "System.Xaml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
$cSharpSource = @"
using System;
using System.Windows;
using System.Windows.Controls;
namespace myNamespace
{
public class myDataTemplateSelector : DataTemplateSelector
{
public DataTemplate UserDataTemplate
{ get; set; }
public DataTemplate GroupDataTemplate
{ get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item as string == "User")
{
return UserDataTemplate;
}
else if (item as string == "Group")
{
return GroupDataTemplate;
}
else
{
return null;
}
}
}
}
"@
Add-Type -TypeDefinition $cSharpSource -ReferencedAssemblies $Assemblies
Add-Type -AssemblyName PresentationFramework
[xml]$XAML = @"
<Window x:Name="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="650" Width="300" FontSize="11"
xmlns:local="clr-namespace:myNamespace">
<Window.Resources>
<DataTemplate x:Key="HeaderTemplate">
<Label Content="Header text" />
</DataTemplate>
<DataTemplate x:Key="UserDataTemplate">
<Grid>
<TextBlock Text="UserDataTemplate in use" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="GroupDataTemplate">
<Grid>
<TextBlock Text="GroupDataTemplate in use" />
</Grid>
</DataTemplate>
</Window.Resources>
<StackPanel>
<Button x:Name="UserTabItem_Button" Content="Load UserTabItem" />
<Button x:Name="GroupTabItem_Button" Content="Load GroupTabItem" />
<TabControl x:Name="TabControl" ItemTemplate="{StaticResource HeaderTemplate}">
<TabControl.ContentTemplateSelector>
<local:myDataTemplateSelector
UserDataTemplate="{StaticResource UserDataTemplate}"
GroupDataTemplate="{StaticResource GroupDataTemplate}"/>
</TabControl.ContentTemplateSelector>
</TabControl>
</StackPanel>
</Window>
"@
# Parse the XAML
$Reader = (New-Object System.Xml.XmlNodeReader $XAML)
$Window = [Windows.Markup.XamlReader]::Load($Reader)
# Iterate through each XAML node and create a variable for each node
$XAML.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach-Object {
New-Variable -Name $_.Name -Value $Window.FindName($_.Name) -Force
}
# Example data
$UserTabItem = [PSCustomObject]@{
'ObjectClass' = 'User'
}
$GroupTabItem = [PSCustomObject]@{
'ObjectClass' = 'Group'
}
# Clicks to add Child TabItems to TabControl
$UserTabItem_Button.Add_Click({
$TabControl.AddChild($UserTabItem)
})
$GroupTabItem_Button.Add_Click({
$TabControl.AddChild($GroupTabItem)
})
$Window.ShowDialog()
I have also explored storing XAML DataTemplates as PowerShell variables and setting the TabControl's ContentTemplate property to the appropriate DataTemplate before adding the Child. This was unsuccessful and perhaps not possible after reading about WPF's Templating documentation.
I am open to other approaches. Thanks for your time.