title: Serializing WPF DataTemplates and {Binding Expressions} (from PowerShell?)
body: <p>Ok, here's the deal: I have code that works in C#, but when I call it from PowerShell, it fails.  I can't quite figure it out, but it's something specific to PowerShell. Here's the relevant code calling the library (assuming you've added a reference ahead of time) from C#:</p>
  
  <pre><code>public class Test {
     [STAThread]
     public static void Main()
     {
        Console.WriteLine(  PoshWpf.XamlHelper.RoundTripXaml(
             "&lt;TextBlock Text=\"{Binding FullName}\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"/&gt;"
        ) );
     }
  }
  </code></pre>
  
  <p>Compiled into an executable, that works fine ... but if you call that method from PowerShell, it returns with no <code>{Binding FullName}</code> for the Text!</p>
  
  <pre><code>add-type -path .\PoshWpf.dll
  [PoshWpf.Test]::Main()
  </code></pre>
  
  <p>I've pasted below the entire code for the library, all wrapped up in a PowerShell Add-Type call so you can just compile it by pasting it into PowerShell (you can leave off the first and last lines if you want to paste it into a new console app in Visual Studio.</p>
  
  <p>To output (from PowerShell 2) as an executable, just change the -OutputType parameter to ConsoleApplication and the -OutputAssembly to PoshWpf.exe (or something).  Thus, you can see that running the <strong>SAME CODE</strong> from the executable gives you the correct output.</p>
  
  <p><strong>But running the two lines as above or manually calling <code>[PoshWpf.XamlHelper]::RoundTripXaml</code> or <code>[PoshWpf.XamlHelper]::ConvertToXaml</code> from PowerShell just doesn't seem to work at all ... <em>HELP?!</em></strong></p>
  
  <pre><code>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(
                 "&lt;TextBlock Text=\"{Binding FullName}\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"/&gt;"
            ) );
         }
      }
  
     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&lt;PropertyDescriptor&gt;().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
  </code></pre>
  
  <p>Again, you can get an executable by just altering the last line like so:</p>
  
  <pre><code>"@ -language CSharpVersion3 -reference PresentationCore, PresentationFramework, WindowsBase -OutputType ConsoleApplication -OutputAssembly PoshWpf.exe
  </code></pre>