|
In my previous blog post, I took a look at a simple "Hello World" application that illustrated how you can create a cross-platform Silverlight application that uses the DLR, IronRuby, and can be done without involving the use of a Windows machine. It seemed like a pretty good ideal - Windows/Mac/Linux cross-platform RIA all built using Ruby without locking yourself into Windows for at least part of the development.
But does the actual experience live up to the ideal?
In the first post, it did. But, as you'll see in this post, I ran into some snags. As you probably already know, Silverlight data binding works very much like WPF data binding. For example, assuming that my parent control already has a valid data context, I can bind the Text property of this TextBlock to the FirstName property of the databound object:
<TextBlock x:Name="custNameText" Text="{Binding Path=FirstName}"/>Seems pretty easy, and it's an awesome syntax that has served me well in all my WPF applications and my Silverlight 2.0 apps. Now, I already know that Xaml doesn't know how to instantiate or refer to Ruby objects, so as we saw in my previous post there's no way to rig up a Ruby event handler within Xaml, it has to be done programmatically. Thankfully, the Ruby syntax for rigging up event handlers is dead simple so it's not that bad a trade-off.
This also means that I'm not going to be able to point to a Ruby object using an ObjectDataSource element. Instead what I need to do is set the DataContext of the parent control from Ruby. Or, if I'm doing something like binding a ListBox, I can set the ItemsSource of the ListBox to the Ruby object array. This actually works, and if I create a DataTemplate for my ListBox that looks something like this:
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
I will actually see the DLR marshalling type displayed (the default implementation of ToString() on CLR objects displays the type name). At this point I was pretty stoked because I knew I'd gotten some actual data binding to take place. The real test of course is whether you can bind to members of Ruby object.
Turns out, you can't. I tried every way you can think of accessing the Ruby attribute accessors and failed miserably. I have a theory about why. When you data bind to a member in WPF/Silverlight, you specify the Path to that member. This path is a string in WPF, so it has to be converted. WPF internally will take the string you passed it and use Reflection to crack open the object graph, bind the exact member that you want bound, and will use some magic to make it all work under the hood.
I thought that IronRuby would've supported Reflection so that if a consuming C# object asked for a particular member via Reflection, it would find it. Silverlight is currently behaving as though the Ruby object has no members that are visible to Reflection. I've searched on the Forums and through Google and haven't found anyone who has been able to get this kind of data binding to work (I have seen people set up binding programmatically, but that's another story).
So at this point here's my thinking: If I have to use a C# class for my model objects so that I can properly support 2-way GUI binding, then why would I use Ruby at all unless I have a very specific need (like consuming a Ruby library that hasn't been ported to any other language).
If anybody has a solution to this problem I'd love to hear it. Until then, however, my experiments with Ruby and Silverlight on the Mac are on hold.
This is just thinking out loud, but couldn't you implement a custom binding
object (that implements System.Windows.Data.BindingBase) and takes an
Expression parameter. This expression can then be eval'd in your favourite
dynamic language such as Javascript or Ruby. Forgive my ignorance I've not
worked with Ruby myself but I assume you have some way of using string
based access to an object's members. An alternative is to implement
IValueConverter to convert the Ruby object into one of it's properties (as
specified in a ConverterParameter), which might be simpler.
It's possible... But my thinking is that if I have to implement the custom
binding object in C# so that I can do dynamic binding syntax like with
IronPython (one of my colleagues actually just did something like this),
then it defeats the purpose of building the entire application in
Silverlight without touching Windows... if I have to pull out a little bit
of C# to make it work, I might as well just do it all in C# :) Again, keep
in mind that my goal was to do a 100% pure dynamic, mac-built Silverlight
app. If I had different requirements, then such binding syntax helpers
would sound pretty awesome.