好的,这是交易:我的代码在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
答案 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做什么,所以也许你已经解决了上述限制。