IronPython Integration Sample and the WPF Designer

[Update Jan 04, 2008: Made <system.codedom> instructions more clear. Thanks to Cory Bloyd for the suggestion.]

If you've had the opportunity to work with WPF/.NET 3.0 yet, you've likely run into the x:Class attribute in XAML. If you specify a x:Class on a XAML page, during compilation the XAML compiler creates a class in code using the registered CodeDomProvider for the given language that you're compiling. This presented some challenges for getting the IronPython sample to work with the new designer. There were two major problems:

  1. The IronPython CodeDomProvider is not registered globally on the machine. This is required for the XAML compiler to be able to instantiate a new CodeDomProvider for the langauge. We didn't need this in WinForms, since all the CodeDom manipulation happens in the WinForms designer. In WinForms at compile time, you're compiling already-generated code.
  2. IronPython 1.1 does not support compiling to .NET-consumable types. The types that are produced are consumable from within an IronPython context, but you can't effectively use the assemblies that are produced from another .NET language (like VB/C#, etc...). The problem with WPF and IronPython 1.1 comes in at runtime when an attempt is made to instantiate the type you declared in the x:Class attribute. Since we wanted to remain on the IronPython 1.1 codebase, and the IronPython folks didn't want to add a full static type compiler (for obvious and very good reasons), we needed to work around this limitation.

For the first issue, the most simple and reliable way to do this is simply have the user of the sample manually register the IronPython CodeDomProvider in your machine.config file. Here are the instructions from the ReadMe to get this working:

When building an IronPython WPF Application, you may receive the following error message: Unknown build error, 'Object reference not set to an instance of an object'. In order for XAML compilation to succeed, a valid CodeDomProvider is required to be registered on the machine for the language being compiled. Because this is a global machine configuration setting, the IronPython sample does not attempt to register the IronPython CodeDomProvider automatically. In order to register the IronPython CodeDomProvider manually and build IronPython/WPF projects with Visual Studio or MSBuild, you can do the following:

  • Open %SystemRoot%\Microsoft.NET\Framework\v2.0.50727\CONFIG\machine.config in a text editor (such as Notepad)
  • Add the following to register the IronPython CodeDomProvider as a direct child of the root <configuration> node:
    <system.codedom>
      <compilers>
        <compiler language="py;IronPython" extension=".py" type="IronPython.CodeDom.PythonProvider, IronPython, Version=1.1.0.0, Culture=neutral, PublicKeyToken=4afbdc4d950a9602" />
      </compilers>
    </system.codedom>
  • Close & Save machine.config

For the second issue, we had to be a little more creative. By default, the WPF Designer (code-named Cider) will attempt to use a CodeDomProvider to do event handler generation if you have one for your language. In fact, if you have a CodeDomProvider that is good enough to work with Windows Forms and you support static .NET type compilation, you should be able to get the WPF designer working with almost no problems.

Fortunately, Cider also supports an extensibility mechanism in case you don't provide a CodeDom and wish to override the event handler generation. To do this, you need to implement a custom EventBindingProvider. The one that is included in the IronPython sample is called PythonEventBindingProvider. It should be noted that for most languages, you don't need to write your own EventBindingProvider like we did for IronPython. We only did this for Python to work around the static compilation issue.