序列化WPF DataTemplates和{Binding Expressions}(来自PowerShell?)

时间:2010-03-21 05:34:37

标签: c# wpf xaml serialization powershell

好的,这是交易:我的代码在C#中有效,但是当我从PowerShell调用它时,它失败了。我无法弄明白,但这是PowerShell特有的。这是调用库的相关代码(假设您提前添加了引用)来自C#:

public class Test {
   [STAThread]
   public static void Main()
   {
      Console.WriteLine(  PoshWpf.XamlHelper.RoundTripXaml(
           "<TextBlock Text=\"{Binding FullName}\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"/>"
      ) );
   }
}

编译成可执行文件,工作正常......但如果从PowerShell调用该方法,则返回文本没有{Binding FullName}

add-type -path .\PoshWpf.dll
[PoshWpf.Test]::Main()

我已粘贴到库的整个代码下面,所有代码都包含在PowerShell Add-Type调用中,因此您可以通过将其粘贴到PowerShell中进行编译(如果您愿意,可以省略第一行和最后一行)将其粘贴到Visual Studio中的新控制台应用程序中。

要将(从PowerShell 2)输出为可执行文件,只需将-OutputType参数更改为ConsoleApplication,将-OutputAssembly更改为PoshWpf.exe(或其他内容)。因此,您可以看到从可执行文件中运行 SAME CODE 可以为您提供正确的输出。

但是如上所述运行两行或从PowerShell手动调用[PoshWpf.XamlHelper]::RoundTripXaml[PoshWpf.XamlHelper]::ConvertToXaml似乎根本不起作用... 帮助?!

Add-Type -TypeDefinition @"

using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;

namespace PoshWpf
{
    public class Test {
       [STAThread]
       public static void Main()
       {
          Console.WriteLine(  PoshWpf.XamlHelper.RoundTripXaml(
               "<TextBlock Text=\"{Binding FullName}\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"/>"
          ) );
       }
    }

   public class BindingTypeDescriptionProvider : TypeDescriptionProvider
   {
      private static readonly TypeDescriptionProvider _DEFAULT_TYPE_PROVIDER = TypeDescriptor.GetProvider(typeof(Binding));

      public BindingTypeDescriptionProvider() : base(_DEFAULT_TYPE_PROVIDER) { }

      public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
      {
         ICustomTypeDescriptor defaultDescriptor = base.GetTypeDescriptor(objectType, instance);
         return instance == null ? defaultDescriptor : new BindingCustomTypeDescriptor(defaultDescriptor);
      }
   }

   public class BindingCustomTypeDescriptor : CustomTypeDescriptor
   {
      public BindingCustomTypeDescriptor(ICustomTypeDescriptor parent) : base(parent) { }

      public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
      {
         PropertyDescriptor pd;
         var pdc = new PropertyDescriptorCollection(base.GetProperties(attributes).Cast<PropertyDescriptor>().ToArray());
         if ((pd = pdc.Find("Source", false)) != null)
         {
            pdc.Add(TypeDescriptor.CreateProperty(typeof(Binding), pd, new Attribute[] { new DefaultValueAttribute("null") }));
            pdc.Remove(pd);
         }
         return pdc;
      }
   }

   public class BindingConverter : ExpressionConverter
   {
      public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
      {
         return (destinationType == typeof(MarkupExtension)) ? true : false;
      }
      public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
      {
         if (destinationType == typeof(MarkupExtension))
         {
            var bindingExpression = value as BindingExpression;
            if (bindingExpression == null) throw new Exception();
            return bindingExpression.ParentBinding;
         }

         return base.ConvertTo(context, culture, value, destinationType);
      }
   }

   public static class XamlHelper
   {
      static XamlHelper()
      {
         // this is absolutely vital:
         TypeDescriptor.AddProvider(new BindingTypeDescriptionProvider(), typeof(Binding));
         TypeDescriptor.AddAttributes(typeof(BindingExpression), new Attribute[] { new TypeConverterAttribute(typeof(BindingConverter)) });
      }

      public static string RoundTripXaml(string xaml)
      {
         return XamlWriter.Save(XamlReader.Parse(xaml));
      }

      public static string ConvertToXaml(object wpf)
      {
         return XamlWriter.Save(wpf);
      }
   }
}



"@ -language CSharpVersion3 -reference PresentationCore, PresentationFramework, WindowsBase -OutputType Library -OutputAssembly PoshWpf.dll

同样,您可以通过改变最后一行来获得可执行文件:

"@ -language CSharpVersion3 -reference PresentationCore, PresentationFramework, WindowsBase -OutputType ConsoleApplication -OutputAssembly PoshWpf.exe

3 个答案:

答案 0 :(得分:1)

在这段时间之后(考虑到这个问题的观看次数),值得回到这里注意 PowerShell 3 已修复 - 我我不确定是不是因为他们修复了一个错误,或者是因为PS3在 .Net CLR 4上运行或者是什么。

无论如何,如果您将System.Xaml添加到-reference程序集列表中,原始问题中的代码将在PowerShell 3和4中按原样运行。我想我会将此标记为答案,大多数人都可以停止来这里尝试回答;)

答案 1 :(得分:0)

我不是PowerShell dev,但是你试过用`逃避{}吗?也许它试图变得聪明并将绑定评估为powershell表达式?

答案 2 :(得分:0)

我对你在XamlHelper的类型初始化程序中所做的TypeConverter设置有点困惑。什么是BindingConverter应该做的?您是否打算像在WPF中一样处理{Binding}标记扩展?

在任何情况下,标记扩展都不能通过设计的XAML往返。以下摘录自MSDN页面XAML serialization limitations

  

对象的公共引用   各种标记扩展格式,例如   作为StaticResource或Binding,将是   序列化取消引用   处理。这些已经是   当时被解除引用   内存中的对象是由   应用程序运行时和Save   逻辑不重新审视原文   XAML恢复这样的引用   序列化输出。这可能   冻结任何数据绑定或资源   获得的值是最后的值   由运行时表示使用,   只有有限或间接的能力   将这样的价值与任何价值区分开来   其他值在本地设置。图像是   也被序列化为对象引用   图像存在于图像中   项目,而不是原创   来源引用,失去了什么   文件名或URI最初是   引用。甚至宣布资源   在同一页面内可以看到   序列化到他们的地步   被引用,而不是被引用   保留为资源的关键   集合

鉴于此,我不确定为什么它应该在编译的应用程序中完全工作。但正如我所说,我必须承认我不确定你在使用TypeConverter做什么,所以也许你已经解决了上述限制。