C# PowerShell WPF应用程序中的动态数据模板
我使用XAML和PowerShell创建了一个简单的WPF应用程序,它由一个TabControl组成,我希望在其子TabItems中显示多种数据。根据提供的数据类型,我希望TabControl的子TabItems使用不同的DataTemplate 我知道在我的情况下,最好(唯一?)的方法是在C#中创建一个自定义DataTemplateSelector类来处理模板选择 我尝试过这样做,但在使用自定义类时遇到了困难。以下是我得到的错误:C# PowerShell WPF应用程序中的动态数据模板,c#,wpf,powershell,C#,Wpf,Powershell,我使用XAML和PowerShell创建了一个简单的WPF应用程序,它由一个TabControl组成,我希望在其子TabItems中显示多种数据。根据提供的数据类型,我希望TabControl的子TabItems使用不同的DataTemplate 我知道在我的情况下,最好(唯一?)的方法是在C#中创建一个自定义DataTemplateSelector类来处理模板选择 我尝试过这样做,但在使用自定义类时遇到了困难。以下是我得到的错误: Exception calling "Load" with "
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
我怀疑我未正确加载所需的程序集或命名空间,因此无法访问自定义命名空间和自定义类。我以前从未使用过C#,因此我非常感谢您提供的任何帮助
一旦我解决了这个问题,我知道我的C#自定义类的内部逻辑将无法按预期工作,但这是一个单独的问题。C代码似乎是有效的,因为我可以独立运行它并实例化我的自定义类
如果删除所有与DataTemplateSelector相关的位并将以下内容添加到TabControl中,XAML和代码也可以正常工作:
ContentTemplate="{StaticResource UserDataTemplate}"
ContentTemplate=“{StaticResource UserDataTemplate}”
下面是代码(包括C#、XAML、PowerShell):
$Assemblies=@(“系统”、“PresentationFramework”、“WindowsBase”、“System.Xaml,版本=4.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089”)
$cSharpSource=@”
使用制度;
使用System.Windows;
使用System.Windows.Controls;
名称空间myNamespace
{
公共类myDataTemplateSelector:DataTemplateSelector
{
公共数据模板UserDataTemplate
{get;set;}
公共数据模板GroupDataTemplate
{get;set;}
公共覆盖数据模板SelectTemplate(对象项,DependencyObject容器)
{
如果(项目作为字符串==“用户”)
{
返回UserDataTemplate;
}
else if(项目作为字符串==“组”)
{
返回GroupDataTemplate;
}
其他的
{
返回null;
}
}
}
}
"@
添加类型-TypeDefinition$cSharpSource-ReferencedAssemblys$Assemblys
添加类型-AssemblyName PresentationFramework
[xml]$XAML=@”
"@
#解析XAML
$Reader=(新对象System.Xml.XmlNodeReader$XAML)
$Window=[Windows.Markup.XamlReader]::加载($Reader)
#迭代每个XAML节点,并为每个节点创建一个变量
$XAML.SelectNodes(“/*[@*[包含(翻译(名称(.),'n','n'),'name')]]”)ForEach对象{
新变量-Name$\ux.Name-Value$Window.FindName($\ux.Name)-Force
}
#示例数据
$UserTabItem=[PSCustomObject]@{
“ObjectClass”=“用户”
}
$GroupTabItem=[PSCustomObject]@{
“对象类”=“组”
}
#单击可将子TabItems添加到TabControl
$UserTabItem\按钮。添加\单击({
$TabControl.AddChild($UserTabItem)
})
$GroupTabItem\按钮。添加\单击({
$TabControl.AddChild($GroupTabItem)
})
$Window.ShowDialog()
我还探讨了将XAML DataTemplates存储为PowerShell变量,并在添加子项之前将TabControl的ContentTemplate属性设置为适当的DataTemplate。这是不成功的,在阅读了WPF的模板文档之后,这可能是不可能的
我对其他方法持开放态度。谢谢您的时间。您需要指定程序集和命名空间。local=“clr namespace:myNamespace”只需指定名称空间。我不确定它在PS环境中是如何工作的
正如Slime recipe所建议的那样,首先在Visual Studio中创建控件库要轻松得多 我在Visual Studio中创建了一个新的“类库(.NET Framework)”项目,将现有语法有效的C#代码粘贴到解决方案中,添加对适当程序集的引用,并构建了该项目 我将生成的myDataTemplateSelector.dll文件复制到与PowerShell脚本文件相同的目录中 我加载了一个新的PowerShell控制台(重新使用控制台未正确加载程序集),并运行以下命令来测试DLL:
Add-Type -Path .\myDataTemplateSelector.dll
[myNamespace.myDataTemplateSelector]::New()
这成功地实例化了我的自定义类
最后,我更新了我的XAML:
xmlns:local="clr-namespace:myNamespace;assembly=myDataTemplateSelectorLibrary"
WPF应用程序现在运行
我希望其他答案能够解释如何在不必在Visual Studio中编译C#代码的情况下完成同样的任务,因为我不希望在这个项目中依赖非人类可读的文件(即DLL文件)
编辑-完全回答:
Slice recipe建议以编程方式查找程序集名称,而不是依赖于我假设的程序集名称(基于我的C#代码),这使我走上了正确的道路。再次感谢
在PowerShell中运行C#代码时,通常使用Add Type,它只将代码加载到内存中
如果指定源代码,“添加类型”将编译指定的源代码并生成包含新的.NET Framework类型的内存中程序集
为了访问添加代码的元数据,必须使用Add Type的-Passthru参数:
-穿越
返回表示已添加类型的System.Runtime对象。默认情况下,[添加类型]不生成任何输出
然后,我存储了修改后的Add Type命令的输出:
$Type = Add-Type -TypeDefinition $cSharpSource -ReferencedAssemblies $Assemblies -PassThru
然后将访问程序集的全名:
> $Type.Assembly.Fullname
m0m5m4la, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
第一个字符串“m0m5m4la”是所需的程序集名称,在添加类型时随机生成,并用作内存中程序集的引用
最后,可以在脚本运行并插入XAML时访问它:
...
$Type = Add-Type -TypeDefinition $cSharpSource -ReferencedAssemblies $Assemblies -PassThru
$AssemblyName = $Type.Assembly.Fullname.Split(",",2)[0]
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;assembly=$($AssemblyName)">
...
。。。
$Type=添加类型-类型定义
...
$Type = Add-Type -TypeDefinition $cSharpSource -ReferencedAssemblies $Assemblies -PassThru
$AssemblyName = $Type.Assembly.Fullname.Split(",",2)[0]
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;assembly=$($AssemblyName)">
...
xmlns:local="clr-namespace:$([YourClass].Namespace);assembly=$([YourClass].Assembly.FullName)"
<Window.Resources>
<local:YourClass x:Key="_yclass" />
</Window.Resources>