Сериализация шаблонов данных WPF и {Binding Expressions} (из PowerShell?) - PullRequest
3 голосов
/ 21 марта 2010

Хорошо, вот в чем дело: у меня есть код, который работает на 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()

Я вставил ниже весь код библиотеки, завернутый в вызов Add-Type PowerShell, так что вы можете просто скомпилировать его, вставив в PowerShell (вы можете пропустить первую и последнюю строки, если хотите вставьте его в новое консольное приложение в Visual Studio.

Чтобы вывести (из PowerShell 2) в виде исполняемого файла, просто измените параметр -OutputType на ConsoleApplication и -OutputAssembly на PoshWpf.exe (или что-то еще). Таким образом, вы можете видеть, что запуск SAME CODE из исполняемого файла дает правильный вывод.

Но запуск двух строк, как указано выше, или ручной вызов [PoshWpf.XamlHelper]::RoundTripXaml или [PoshWpf.XamlHelper]::ConvertToXaml из PowerShell просто не работают вообще ... HELP?!

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 ]

1 голос
/ 14 ноября 2013

После всего этого времени (и учитывая количество просмотров этого вопроса) стоит вернуться сюда, чтобы отметить, что это исправлено в PowerShell 3 - я не уверен, что это потому, что они исправили ошибку или PS3 работает на .Net CLR 4 или что.

Независимо от того, если вы добавите System.Xaml в список -reference сборок, код в исходном вопросе работает как есть в PowerShell 3 и 4. Я думаю, я отмечу это как ответ, в основном, чтобы могу перестать приходить сюда, чтобы попытаться ответить на него;)

0 голосов
/ 23 марта 2010

Я немного смущен настройкой TypeConverter, которую вы выполняете в инициализаторе типов XamlHelper.Что должен делать BindingConverter?Вы намереваетесь обрабатывать расширение разметки {Binding} так, как это обычно делается в WPF?

В любом случае расширения разметки не могут отправлять туда и обратно через XAML, как это было задумано.Следующая выдержка из страницы MSDN, касающаяся ограничений сериализации XAML :

Общие ссылки на объекты, сделанные различными форматами расширений разметки, такими как StaticResource или Binding, будут разыменовываться при сериализациипроцесс.Они уже были разыменованы в то время, когда объекты в памяти создавались средой выполнения приложения, и логика Save не восстанавливает исходный XAML для восстановления таких ссылок в сериализованный вывод.Это потенциально замораживает любое значение базы данных или полученное из ресурсов значение, которое будет последним использованным представлением во время выполнения, с ограниченной или косвенной способностью отличать такое значение от любого другого значения, установленного локально.Изображения также сериализуются как ссылки на объекты к изображениям в том виде, в каком они существуют в проекте, а не как ссылки на оригинальные источники, теряя любое имя файла или URI, на которые изначально ссылались.Даже ресурсы, объявленные на одной и той же странице, считаются сериализованными в точку, на которую они ссылались, а не сохраняются в качестве ключа коллекции ресурсов

Учитывая это, я не уверен, зачем этодолжен даже работать вообще в скомпилированном приложении.Но, как я уже сказал, я должен признать, что не уверен, что вы делаете с TypeConverter, так что, возможно, вы уже рассмотрели вышеуказанное ограничение.

0 голосов
/ 23 марта 2010

Я не разработчик PowerShell, но вы пытались экранировать {} с помощью `? Возможно, он пытается быть умным и оценивать привязку как выражение powershell?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...