PowerShell WPF应用程序中的动态DataTemplate

时间:2018-08-26 05:20:38

标签: c# wpf powershell

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

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

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

print (df)
  col1  col2  col3  col4  col5   average
0  abc   123     1     4     9  4.666667
1  def  None     2     7  None  4.500000
2  ghi   456     5  None     3  4.000000
3  jkl   789  None     8     8  8.000000

我怀疑我没有正确加载所需的程序集或名称空间,因此无法访问我的自定义名称空间和自定义类。我以前从未使用过C#,因此,我非常感谢提供的任何手持工具。

一旦我解决了这个问题,我就知道我的C#自定义类的内部逻辑将无法正常工作,但这是一个单独的问题。 C#代码似乎有效,因为我可以独立运行它并实例化自定义类。

如果我删除所有与DataTemplateSelector相关的位并将以下内容添加到TabControl,则XAML和代码也可以正常工作:

ContentTemplate="{StaticResource UserDataTemplate}"

以下是代码(包括C#,XAML,PowerShell):

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

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

我愿意接受其他方法。感谢您的宝贵时间。

3 个答案:

答案 0 :(得分:2)

正如史莱姆食谱所建议的那样,首先在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文件)。这个项目。

编辑-完整回答:

切片配方建议以编程方式找到程序集名称,而不是依赖于我假定的名称(基于C#代码)使我走了正确的道路。再次感谢。

在PowerShell中运行C#代码时,通常使用Add-Type,它将代码仅加载到内存中。

  

如果您指定源代码,则Add-Type会编译指定的源代码并生成一个包含新.NET Framework类型的内存中程序集。

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

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

  

-PassThru

     

返回一个System.Runtime对象,该对象表示添加的类型。默认情况下,[Add-Type]不生成任何输出。

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

我不确定这有多糟糕,但是它可以工作并且允许所有代码保留为纯文本格式,并且不需要软件工具(除了文本编辑器)即可继续开发。

我可能搜索的不够好,但是在网上找不到这样的例子。希望这对外面的人有帮助!

答案 1 :(得分:1)

您需要指定程序集和名称空间。 local =“ clr-namespace:myNamespace”仅指定名称空间。我不确定您在PS环境中如何工作。

https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/xaml-namespaces-and-namespace-mapping-for-wpf-xaml

答案 2 :(得分:0)

要在当前脚本中引用类,可以做到这一点。

xmlns:local="clr-namespace:$([YourClass].Namespace);assembly=$([YourClass].Assembly.FullName)"

然后您可以创建一个静态资源并瞧瞧:)

<Window.Resources>
        <local:YourClass x:Key="_yclass" />
</Window.Resources>