Хорошо, вот сделка: у меня есть код, который работает на 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
Кстати, теперь, когда я опубликовал это, я заметил, что я действительно не объяснял, как это имеет какое-либо отношение к DataTemplates: в основном вы должны создать источник XAML для создания FrameworkTemplate любого типа - и я пытаюсь создать их из уже созданных элементов управления, поэтому для этого мне нужно сериализовать обратно в XAML. – Jaykul
Не уверен в вашей немедленной проблеме, но вам не нужно использовать XAML для создания DataTemplate в WPF (хотя вы делаете с Silverlight.) В WPF вы можете использовать дерево объектов FrameworkElementFactory, которые в основном являются дескрипторами, которые включают тип элемента, домовладельцы, дети и т. д. Но это ни здесь, ни там. Должен подумать об этом. – Josh
Да, я посмотрел на FrameworkElementFactory, но он немного тупой и устарел. То, о чем я беспокоюсь, - довольно простые шаблоны, привязка данных которых ограничена такими вещами, как {Binding FullName} или {Binding Path = FullName}, как показано в примере - если бы нашел код, который «работает», я действительно как использовать его и не генерировать собственные строки Xaml;) – Jaykul