gpt4 book ai didi

c# - PowerShell WPF 应用程序中的动态数据模板

转载 作者:太空宇宙 更新时间:2023-11-03 22:41:31 29 4
gpt4 key购买 nike

我使用 XAML 和 PowerShell 创建了一个简单的 WPF 应用程序,它由一个 TabControl 组成,我希望在其子 TabItems 中显示多种数据。根据提供的数据类型,我希望 TabControl 的子 TabItem 使用不同的 DataTemplate。

我知道在我的情况下最好(唯一?)的方法是在 C# 中创建自定义 DataTemplateSelector 类来处理模板选择。

我曾尝试这样做,但在使用我的自定义类时遇到了困难。这是我收到的错误:

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}"

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()

我还探索了将 XAML DataTemplates 存储为 PowerShell 变量,并在添加 Child 之前将 TabControl 的 ContentTemplate 属性设置为适当的 DataTemplate。这是不成功的,并且在阅读了 WPF 的模板文档后可能是不可能的。

我对其他方法持开放态度。感谢您的宝贵时间。

最佳答案

正如 Slime 的建议,首先在 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,它仅将代码加载到内存中。

If you specify source code, Add-Type compiles the specified source code and generates an in-memory assembly that contains the new .NET Framework types.

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/add-type?view=powershell-5.1

为了访问添加代码的元数据,必须使用 Add-Type 的 -Passthru 参数:

-PassThru

Returns a System.Runtime object that represents the types that were added. By default, [Add-Type] does not generate any output.

然后我存储了修改后的 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)">
...

我不确定这有多 hack-y,但它可以工作并允许所有代码以明文形式保留,并且不需要软件工具(除了文本编辑器)来继续开发。

我可能搜索得不够好,但我在网上找不到这种事情的一个例子。希望这对外面的人有帮助!

关于c# - PowerShell WPF 应用程序中的动态数据模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52023418/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com