WPF: Embedding DLR Scripts in XAML (Python, Ruby) - Part 1, Hosting the DLR
A Visual Studio 2008 Solution with the complete code listing for this series is attached to the final part.
The XAML framework allows us to easily switch between the Declarative and Imperative programming models easily. Examples such as event handlers are obvious cases where this happens, while others, such as MarkupExtensions and ValueConverters for Bindings are not so clear - when you implement the ProvideValue method of a custom MarkupExtension, you are suddenly in the the imperative world of C# (or CLR language of your choice) where as the XAML document that declared the MarkupExtension is declarative in nature.
The ability to dynamically load XAML using the XamlParser class allows the programmer to switch in and out of the declarative and imperative worlds with ease, affording them great power and flexibility. Or so it would seem...
Dynamically loaded XAML does not integrate with the imperative world as well as it's compiled brethren. For example, you completely lose the ability to route events to handlers when loading XAML dynamically. This greatly hampers the usefulness of dynamically loaded XAML. The stance in the community is that you should simply compile your XAML if you want to handle events. In many instances this is just not an acceptable solution!
There are a number of interpreted (hence dynamic) imperative languages that bind to the CLR, such as Python and Ruby. The new Dynamic Language Runtime (DLR) will encourage the development off new dynamic languages as well as the binding of existing dynamic languages to .NET. In this series of 6 articles I will demonstrate how to embed any and all DLR languages in XAML.
First some background reading. The dynamic nature of XAML (or lack there of) has been discussed many times in public forums. There have been attempts to embed Python and other dynamic languages (such as the Lambda Converter Extension below) into XAML, all with mixed success. Below are links to interesting documents worth reading:
Hosting the DLR is not difficult. The DLR is still under development, but my experience with it has been positive. It seems quite stable and performs well with IronPython and IronRuby.
Note: The code in this series was written against the DLR redistributed with IronPython 2.0 Beta 3. There have been some breaking changes in the DLR API since then, so code listed here may need modification to work withh later versions of the DLR.
The DLR is contained in two assemblies, Microsoft.Scripting.dll and Microsoft.Scripting.Core.dll. The easiest way to get moving is to download a compiled version of either IronPython or IronRuby as both redistribute the DLR assemblies.
Your application should reference both Microsoft.Scripting.dll and Microsoft.Scripting.Core.dll, but does not need to reference the Python or Ruby assemblies as these will be loaded on demand by the DLR. To avoid issues with locating DLLs at runtime, I suggest just dumping all these DLLs in the bin directory of your app to get moving.
The following is a simple example of hosting the DLR using C# and evaluating a simple Python expression. After execution, result is set to 11 as expected.
In the interest of clarity and brevity, little attention will be paid to performance or error handling in this series. The following utility methods will be used throughout this series:
Using these utility methods, our original expressions of "a + b" could be written more concisely, like the following:
In all of the above examples, you can simply specify "ruby" in place of "python" to execute Ruby scripts instead. As more DLR languages pop up, you can execute them too.
Now that we can host the DLR and execute scripts written in any DLR language, let's do something useful in XAML with it. See part 2.