尝试在PowerShell中实现IValueConverter时出错-XAML GUI脚本

时间:2019-06-29 09:55:38

标签: wpf powershell xaml ivalueconverter

我想念什么?我试图将转换器实现为基于XAML的PowerShell脚本,但是没有运气。 我已经从StackOverflow之类的网站上获取了一些信息。但在基于Powershell XAML的GUI脚本中找不到转换器的成功实现。

在我正在测试转换器的代码中,它可以工作(您可以看到2个转换示例),这意味着Powershell本身接受了新的转换器类型,因此无法在我的xaml代码中实现该转换器。

$src = @'
    using System;
    using System.Windows;
    using System.Windows.Data;
    using System.Globalization;

    namespace MyProject
    {

        public class DemoConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                if (value == null)
                {
                    return "kuku";
                }
                else
                {            
                    return "bobo";
                }
            }
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {   
                return value;
            }
        }
    }
'@

Add-Type -AssemblyName PresentationFramework    
Add-Type -TypeDefinition $src -ReferencedAssemblies PresentationFramework

#Checking that the new type works and convert is done...
$c = new-object MyProject.DemoConverter
$c.Convert("gg", $null, $null, $null)
$c.Convert(55, $null, $null, $null)

#Now declaring and loading the xaml
[xml]$XAML = @'

<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cnv="clr-namespace:MyProject" >
    <Window.Resources>
        <cnv:DemoConverter x:Key="TestConverter" />
    </Window.Resources>
    <Grid>     
        <TextBox x:Name="txtTestValue" Text="I'm here to show that xaml loading works!" />
    </Grid>
</Window>
'@

$reader=(New-Object System.Xml.XmlNodeReader $xaml) 

$Window=[Windows.Markup.XamlReader]::Load( $reader )

$Window.ShowDialog() | out-null

我不断收到此错误:

使用“ 1”参数调用“ Load”的异常:“无法创建未知类型'{clr-namespace:MyProject} DemoConverter'。”

如果我删除以下行:<cnv:DemoConverter x:Key="TestConverter" /> 它不会给出上述错误,并且会显示窗口(但是,当然,xaml中的转换将不可用),所以我想我在XAML不喜欢的名称空间和/或程序集减速方面做错了事。 / p>

请注意,在我的xaml上,我尚未使用转换器。我只想在尝试使用转换器之前克服上述错误。

非常感谢您!

1 个答案:

答案 0 :(得分:0)

我注册了Stackoverflow只是为了参加这个问题。

在我在这里遇到这个问题之前,我搜索了HOURS来解决此问题,然后我的希望破灭了,因为它已经在这里呆了将近一年了,没有答案。

我早些时候找到了这个相关的问题,它提供了我们正在尝试解决的基本解决方案,但是没有给出使其真正起作用的任何细节。 How to use IValueConverter from powershell?

幸运的是,经过几个小时,然后将许多其他信息汇总在一起,我终于解决了!

有3件。

  1. 即使您在C#类代码中指定了表单的命名空间,通过C#代码添加的转换器类型也不是表单的同一命名空间的一部分。因此,需要额外的xmlns:行以在表单中包含转换器类型的名称空间。然后,当在表单资源中定义转换器时,您引用该命名空间而不是表单自己的命名空间(通常是“本地”,但是我看到您的示例中使用了“ cnv”)。而且,由于以下原因,您希望C#代码中的名称空间与表单自己的名称空间不同。 (我不确定,但是系统可能会感到困惑,因为它们实际上存在两个完全不同的命名空间。)
  2. 但是,即使添加了xmlns:行以包含转换器的名称空间,该窗体仍然无法找到它。我发现这是因为,添加的转换器类型也添加到了另一个Assembly中。显然,在使用“ Add-Type -TypeDefinition”添加自定义类型时,PowerShell将创建其自己的内存中程序集并将其添加到其中。因此,在添加的xmlns:行中包括转换器的名称空间,我们还必须为自定义转换器类型指定程序集。
  3. 但是,但是...更糟糕的是,此“组合”程序集具有随机生成的名称,并且每次运行脚本时,该程序集名称都是不同的。 (我在任何地方都找不到这个事实的记录。我完全是偶然发现的。)那么,如果您不知道程序集名称,该如何添加呢?基本上可以完成您的工作,在变量中创建新转换器对象的实例,然后检查该新对象变量的属性以确定程序集名称。然后使用字符串替换用程序集名称更新XAML字符串。

因此,请参阅我发布的更新代码,其中包含以下更改。如原始海报所示,此示例未显示转换器的实际运行情况,但脚本至少将运行并显示该表单而不会出现错误,表明该表单本身已接受TextBox中的转换器定义和用法。

  • 已将C#代码中的名称空间更改为MyConverter,因此有所不同 从表单的MyProject命名空间中获取。
  • 在创建转换器对象的$ c实例的行中,我将名称空间更改为MyConverter而不是MyProject,以匹配上述新名称空间。
  • 添加了$ AssemblyName变量以提取随机程序集名称。 (我还注释了对Convert和ConvertBack的调用。)
  • 将XAML代码的Here-String更改为字符串变量$ inputXML,以便可以在其上使用Replace。稍后将转换为xml文档。
  • 添加了
  • xlmns:Converter行以包括MyConverter命名空间。注意“; assembly = myassembly”。 “ myassembly”名称只是字符串Replace的占位符,以后可以轻松找到。
  • 在转换器Window.Resources的声明中,将“ cnv:”更改为“ Converter:”以匹配Converter类型的名称空间声明。
  • 添加了另一个TextBox以显示包括Converter的内容,并且表单版本接受了此操作。
  • 添加了[xml] $ XAML = $ inputXML ....行以转换为xml并替换为实际的程序集名称。

代码

$src = @'
    using System;
    using System.Windows;
    using System.Windows.Data;
    using System.Globalization;

    namespace MyConverter
    {

        public class DemoConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                if (value == null)
                {
                    return "kuku";
                }
                else
                {            
                    return "bobo";
                }
            }
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {   
                return value;
            }
        }
    }
'@

Add-Type -AssemblyName PresentationFramework    
Add-Type -TypeDefinition $src -ReferencedAssemblies PresentationFramework

#Checking that the new type works and convert is done...
$c = new-object MyConverter.DemoConverter
$AssemblyName = $c.gettype().Assembly.FullName.Split(',')[0]
#$c.Convert("gg", $null, $null, $null)
#$c.Convert(55, $null, $null, $null)

#Now declaring and loading the xaml
$inputXML = @'

<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Converter="clr-namespace:MyConverter;assembly=myassembly"
xmlns:cnv="clr-namespace:MyProject" >
    <Window.Resources>
        <Converter:DemoConverter x:Key="TestConverter" />
    </Window.Resources>
    <Grid>     
        <TextBox x:Name="txtTestValue" Text="I'm here to show that xaml loading works!" />
        <TextBox x:Name="txtTestValue2" Text="{Binding Path=Whatever, Converter={StaticResource TestConverter}}" />
   </Grid>
</Window>
'@

[xml]$XAML = $inputXML -replace 'myassembly', $AssemblyName

$reader=(New-Object System.Xml.XmlNodeReader $xaml) 

$Window=[Windows.Markup.XamlReader]::Load( $reader )

$Window.ShowDialog() | out-null