2010-03-21 2 views
3

Хорошо, вот сделка: у меня есть код, который работает на C#, но когда я его вызываю из PowerShell, он терпит неудачу. Я не могу это понять, но это что-то особенное для PowerShell. Вот соответствующий код вызова библиотеки (предполагается, что вы добавили ссылку впереди времени) из C#:Сериализация WPF DataTemplates и {Binding Expressions} (из PowerShell?)

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 из исполняемого файла дает правильный результат.

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

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 
+0

Кстати, теперь, когда я опубликовал это, я заметил, что я действительно не объяснял, как это имеет какое-либо отношение к DataTemplates: в основном вы должны создать источник XAML для создания FrameworkTemplate любого типа - и я пытаюсь создать их из уже созданных элементов управления, поэтому для этого мне нужно сериализовать обратно в XAML. – Jaykul

+0

Не уверен в вашей немедленной проблеме, но вам не нужно использовать XAML для создания DataTemplate в WPF (хотя вы делаете с Silverlight.) В WPF вы можете использовать дерево объектов FrameworkElementFactory, которые в основном являются дескрипторами, которые включают тип элемента, домовладельцы, дети и т. д. Но это ни здесь, ни там. Должен подумать об этом. – Josh

+0

Да, я посмотрел на FrameworkElementFactory, но он немного тупой и устарел. То, о чем я беспокоюсь, - довольно простые шаблоны, привязка данных которых ограничена такими вещами, как {Binding FullName} или {Binding Path = FullName}, как показано в примере - если бы нашел код, который «работает», я действительно как использовать его и не генерировать собственные строки Xaml;) – Jaykul

ответ

1

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

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

0

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

+0

Это хорошая мысль. В моем тестовом примере {} вставляются в метод C# Main() только для того, чтобы избежать таких различий ... и я сознательно использовал Console.WriteLine, чтобы они не были «выводимыми», которые будут анализироваться PowerShell вообще. – Jaykul

0

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

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

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

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

+0

BindingConverter обеспечивает логику сохранения для выражения привязки (не заботясь о «исходном» per se), который на данный момент идеально подходит для того, что я делаю ..., так как мы хотим в любом случае установить источник позже. – Jaykul

+0

Кстати, BindingCustomTypeDescriptor и BindingTypeDescriptionProvider на самом деле пытаются сериализовать исходный код в случае, если это статическая ссылка, но у меня нет действительно полезных примеров того, где я буду использовать это (то есть: где было бы целесообразно совершить кругосветное путешествие это), поэтому я на самом деле не использую его в данный момент. – Jaykul

+0

Проверьте их для получения дополнительной информации о BindingConverter - http://www.codeproject.com/KB/WPF/xamlwriterandbinding.aspx и http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/a1984451 -4840-4e0f-abc5-8a8e34a4f8ca – akjoshi

Смежные вопросы