# WPF databinding with ruby objects. Workaround :)

**URL:** <https://www.ruby-forum.com/t/wpf-databinding-with-ruby-objects-workaround/155482>\
**Category:** IronRuby\
**Created:** [December 12, 2008, 7:56am UTC](https://www.ruby-forum.com/t/wpf-databinding-with-ruby-objects-workaround/155482 "2008-12-12T07:56:27Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![Joshua\_Nussbaum](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/ccd318/32.png) [@Joshua\_Nussbaum](https://www.ruby-forum.com/u/Joshua_Nussbaum)\
**Post date:** [December 12, 2008, 7:56am UTC](https://www.ruby-forum.com/t/wpf-databinding-with-ruby-objects-workaround/155482/1 "2008-12-12T07:56:27Z")

</div>

Hey yall

Seems IronRuby objects do not yet work with WPF databinding. One  
approach to  
a workaround is to emit a wrapper type that defines CLR properties. When  
WPF  
calls the getter/setter the call is forwarded ot the contained contained  
ruby object. This will allow defining business objects in ruby and  
databing  
them to the UI.

This might be useful to thers, so here is my approach. The first part is  
C#  
code that defines a type given a list of property names. The second part  
is  
ruby code that creates a wrapper instance for a given ruby object and  
caches  
the type so that you dont generate a type for each element in an array…

The C# code to generate a type:

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Reflection;  
using System.Reflection.Emit;

namespace GenerateType  
{

```
public class TypeGenerator
{
    public delegate object GetPropertyDelegate(string propertyName);
    public delegate object SetPropertyDelegate(string propertyName,

```

object value);

```
    public static Type Generate(string className, List<string>

```

properties)  
{  
AssemblyName asmName = new AssemblyName(“BindingTypes”);  
AssemblyBuilder asmBuilder =  
AppDomain.CurrentDomain.DefineDynamicAssembly(asmName,  
AssemblyBuilderAccess.Run);  
ModuleBuilder modBuilder =  
asmBuilder.DefineDynamicModule(“Types”);  
TypeBuilder typeBuilder = modBuilder.DefineType(className,  
TypeAttributes.Public |  
TypeAttributes.Class |  
TypeAttributes.AutoClass |  
TypeAttributes.AnsiClass |  
TypeAttributes.BeforeFieldInit |  
TypeAttributes.AutoLayout);

```
        FieldBuilder getFieldBuilder = 

```

typeBuilder.DefineField(“OnGet”,  
typeof(GetPropertyDelegate), FieldAttributes.Public);  
FieldBuilder setFieldBuilder =  
typeBuilder.DefineField(“OnSet”,  
typeof(SetPropertyDelegate), FieldAttributes.Public);

```
        MethodAttributes getSetAttr =
                    MethodAttributes.Public |

```

MethodAttributes.SpecialName |  
MethodAttributes.HideBySig;

```
        foreach (string propertyName in properties)
        {
            PropertyBuilder propBuilder =

```

typeBuilder.DefineProperty(propertyName, PropertyAttributes.None,  
typeof(object), new Type[] {});

```
            MethodBuilder getter =
                               typeBuilder.DefineMethod("get_" +

```

propertyName,  
getSetAttr,  
typeof(string),  
Type.EmptyTypes);

```
            ILGenerator ilGen = getter.GetILGenerator();

            ilGen.Emit(OpCodes.Ldarg_0);
            ilGen.Emit(OpCodes.Ldfld, getFieldBuilder);
            ilGen.Emit(OpCodes.Ldstr, propertyName);
            ilGen.Emit(OpCodes.Callvirt,

```

typeof(GetPropertyDelegate).GetMethod(“Invoke”));  
ilGen.Emit(OpCodes.Ret);

```
            // Define the "set" accessor method for CustomerName.
            MethodBuilder setter =
                typeBuilder.DefineMethod("set_" + propertyName,
                                           getSetAttr,
                                           null,
                                           new Type[] { 

```

typeof(string)  
});

```
            ilGen = setter.GetILGenerator();

            ilGen.Emit(OpCodes.Ldarg_0);
            ilGen.Emit(OpCodes.Ldfld, setFieldBuilder);
            ilGen.Emit(OpCodes.Ldstr, propertyName);
            ilGen.Emit(OpCodes.Ldarg_1);
            ilGen.Emit(OpCodes.Callvirt,

```

typeof(SetPropertyDelegate).GetMethod(“Invoke”));  
ilGen.Emit(OpCodes.Pop);  
ilGen.Emit(OpCodes.Ret);

```
            // Last, we must map the two methods created above to 

```

our  
PropertyBuilder to  
// their corresponding behaviors, “get” and “set”  
respectively.  
propBuilder.SetGetMethod(getter);  
propBuilder.SetSetMethod(setter);

```
        }

        return typeBuilder.CreateType();
    }
}

```

}

And here is the ruby code to generate the wrapper type

require ‘mscorlib’  
require ‘C:\Documents and Settings\Josh\My Documents\Visual Studio  
2008\Projects\GenerateType\GenerateType\bin\Release\GenerateType.dll’  
require ‘PresentationFramework, Version=3.0.0.0,Culture=neutral,  
PublicKeyToken=31bf3856ad364e35’  
require ‘PresentationCore, Version=3.0.0.0, Culture=neutral,  
PublicKeyToken=31bf3856ad364e35’  
require ‘WindowsBase, Version=3.0.0.0, Culture=neutral,  
PublicKeyToken=31bf3856ad364e35’

include System  
include System::Collections::Generic  
include GenerateType  
include System::Collections::ObjectModel  
include System::Windows  
include System::Windows::Controls  
include System::Windows::Data  
include System::Windows::Input

class WrapperGenerator  
def initialize  
@wrapper\_cache = {}  
end

def wrap(ruby\_object)  
if ruby\_object.is\_a? Array  
ruby\_object.map {|o| wrap(o) }  
else  
cache(ruby\_object) unless cached(ruby\_object)  
wrapper\_class = cached(ruby\_object)  
wrapper\_class.new(ruby\_object)  
end  
end

def invalidate  
@wrapper\_cache.clear  
end  
private  
def cached(object)  
@wrapper\_cache[object.class.name]  
end

def cache(object)  
@wrapper\_cache[object.class.name] = generate\_wrapper(object)  
end

def generate\_wrapper(object)  
wrapper\_name = “#{object.class.name}Wrapper”  
properties = List.of(System::String).new

```
(object.methods - Object.instance_methods).each {|m| properties.add

```

m.to\_s}

```
wrapper_base_type = TypeGenerator.generate("#{wrapper_name}Base",

```

properties)  
base\_instance = Activator.create\_instance wrapper\_base\_type

```
eval <<EOS
  class #{wrapper_name} < base_instance.class
    def initialize(original)
      self.on_get = lambda do |prop|
        original.send prop
      end

      self.on_set = lambda do |prop, val|
        original.send "\#{prop}=", val
      end
    end
  end
  return #{wrapper_name} # return the class

```

EOS

end  
end

class Person  
attr\_accessor :first\_name, :last\_name

def initialize(first\_name, last\_name)  
@first\_name, @last\_name = first\_name, last\_name  
end

def full\_name  
“#{last\_name}, #{first\_name}”  
end  
end

# A sample UI

people = []

10.times { |n| people \<\< Person.new(“John #{n}”, “Smith”) }

wrapper = WrapperGenerator.new

w = Window.new  
combo = ComboBox.new  
combo.items\_source = wrapper.wrap(people) # wrap people objects  
combo.display\_member\_path = “full\_name”  
combo.margin = 20  
w.content = combo

Application.new.run w

---

<div class="post-metadata">

**Author:** ![Joshua\_Nussbaum](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/ccd318/32.png) [@Joshua\_Nussbaum](https://www.ruby-forum.com/u/Joshua_Nussbaum)\
**Post date:** [December 12, 2008, 1:46pm UTC](https://www.ruby-forum.com/t/wpf-databinding-with-ruby-objects-workaround/155482/2 "2008-12-12T13:46:11Z")

</div>

Great work Joshua!

IronRuby-objects don’t work in many parts of dotnet-framework because  
they are instances of Ruby-classes that are not dotnet-classes. Another  
example that has causes headache to me is XAML where names of  
user-defined classes can be used as elements but they must be  
user-defined dotnet-classes (not Ruby).

I suppose and hope that with the support for dynamic languages  
increasing in dotnet 4 / C# 4, there will be more built-in support for  
this (it would be really nice to use dynamic types as XAML-elements…)  
Robert B.  
Software architect  
Napa Ltd  
Tammasaarenkatu 3, Helsinki FI-00180  
P.O.Box 470, Helsinki FI-00181

Tel. +358 9 22 813 1  
Direct. +358 9 22 813 611  
GSM +358 45 11 456 02  
Fax. +358 9 22 813 800

Email: removed\_email\_address@domain.invalid [mailto:removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
[www.napa.fi](http://www.napa.fi) [http://www.napa.fi/](http://www.napa.fi/)

* * *

From: removed\_email\_address@domain.invalid  
[[mailto:removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)] On Behalf Of Joshua  
Nussbaum  
Sent: Friday, December 12, 2008 8:56 AM  
To: removed\_email\_address@domain.invalid  
Subject: [Ironruby-core] WPF databinding with ruby objects. Workaround  
🙂

Hey yall

Seems IronRuby objects do not yet work with WPF databinding. One  
approach to a workaround is to emit a wrapper type that defines CLR  
properties. When WPF calls the getter/setter the call is forwarded ot  
the contained contained ruby object. This will allow defining business  
objects in ruby and databing them to the UI.

This might be useful to thers, so here is my approach. The first part is  
C# code that defines a type given a list of property names. The second  
part is ruby code that creates a wrapper instance for a given ruby  
object and caches the type so that you dont generate a type for each  
element in an array…

The C# code to generate a type:

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Reflection;  
using System.Reflection.Emit;

namespace GenerateType  
{

```
public class TypeGenerator
{
    public delegate object GetPropertyDelegate(string propertyName);
    public delegate object SetPropertyDelegate(string propertyName,

```

object value);

```
    public static Type Generate(string className, List<string>

```

properties)  
{  
AssemblyName asmName = new AssemblyName(“BindingTypes”);  
AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.  
DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);  
ModuleBuilder modBuilder =  
asmBuilder.DefineDynamicModule(“Types”);  
TypeBuilder typeBuilder = modBuilder.DefineType(className,  
TypeAttributes.Public |  
TypeAttributes.Class |  
TypeAttributes.AutoClass |  
TypeAttributes.AnsiClass |  
TypeAttributes.BeforeFieldInit |  
TypeAttributes.AutoLayout);

```
        FieldBuilder getFieldBuilder =

```

typeBuilder.DefineField(“OnGet”, typeof(GetPropertyDelegate),  
FieldAttributes.Public);  
FieldBuilder setFieldBuilder =  
typeBuilder.DefineField(“OnSet”, typeof(SetPropertyDelegate),  
FieldAttributes.Public);

```
        MethodAttributes getSetAttr =
                    MethodAttributes.Public |

```

MethodAttributes.SpecialName |  
MethodAttributes.HideBySig;

```
        foreach (string propertyName in properties)
        {
            PropertyBuilder propBuilder =

```

typeBuilder.DefineProperty(propertyName, PropertyAttributes.None,  
typeof(object), new Type[] {});

```
            MethodBuilder getter =
                               typeBuilder.DefineMethod("get_" +

```

propertyName,  
getSetAttr,  
typeof(string),  
Type.EmptyTypes);

```
            ILGenerator ilGen = getter.GetILGenerator();

            ilGen.Emit(OpCodes.Ldarg_0);
            ilGen.Emit(OpCodes.Ldfld, getFieldBuilder);
            ilGen.Emit(OpCodes.Ldstr, propertyName);
            ilGen.Emit(OpCodes.Callvirt,

```

typeof(GetPropertyDelegate).GetMethod(“Invoke”));  
ilGen.Emit(OpCodes.Ret);

```
            // Define the "set" accessor method for CustomerName.
            MethodBuilder setter =
                typeBuilder.DefineMethod("set_" + propertyName,
                                           getSetAttr,
                                           null,
                                           new Type[] {

```

typeof(string) });

```
            ilGen = setter.GetILGenerator();

            ilGen.Emit(OpCodes.Ldarg_0);
            ilGen.Emit(OpCodes.Ldfld, setFieldBuilder);
            ilGen.Emit(OpCodes.Ldstr, propertyName);
            ilGen.Emit(OpCodes.Ldarg_1);
            ilGen.Emit(OpCodes.Callvirt,

```

typeof(SetPropertyDelegate).GetMethod(“Invoke”));  
ilGen.Emit(OpCodes.Pop);  
ilGen.Emit(OpCodes.Ret);

```
            // Last, we must map the two methods created above to

```

our PropertyBuilder to  
// their corresponding behaviors, “get” and “set”  
respectively.  
propBuilder.SetGetMethod(getter);  
propBuilder.SetSetMethod(setter);

```
        }

        return typeBuilder.CreateType();
    }
}

```

}

And here is the ruby code to generate the wrapper type

require ‘mscorlib’  
require ‘C:\Documents and Settings\Josh\My Documents\Visual Studio  
2008\Projects\GenerateType\GenerateType\bin\Release\GenerateType.dll’  
require ‘PresentationFramework, Version=3.0.0.0 [http://3.0.0.0/](http://3.0.0.0/)  
,Culture=neutral, PublicKeyToken=31bf3856ad364e35’  
require ‘PresentationCore, Version=3.0.0.0 [http://3.0.0.0/](http://3.0.0.0/) ,  
Culture=neutral, PublicKeyToken=31bf3856ad364e35’  
require ‘WindowsBase, Version=3.0.0.0 [http://3.0.0.0/](http://3.0.0.0/) ,  
Culture=neutral, PublicKeyToken=31bf3856ad364e35’

include System  
include System::Collections::Generic  
include GenerateType  
include System::Collections::ObjectModel  
include System::Windows  
include System::Windows::Controls  
include System::Windows::Data  
include System::Windows::Input

class WrapperGenerator  
def initialize  
@wrapper\_cache = {}  
end

def wrap(ruby\_object)  
if ruby\_object.is\_a? Array  
ruby\_object.map {|o| wrap(o) }  
else  
cache(ruby\_object) unless cached(ruby\_object)  
wrapper\_class = cached(ruby\_object)  
wrapper\_class.new(ruby\_object)  
end  
end

def invalidate  
@wrapper\_cache.clear  
end  
private  
def cached(object)  
@wrapper\_cache[object.class.name [http://object.class.name/](http://object.class.name/) ]  
end

def cache(object)  
@wrapper\_cache[object.class.name [http://object.class.name/](http://object.class.name/) ] =  
generate\_wrapper(object)  
end

def generate\_wrapper(object)  
wrapper\_name = “#{object.class.name [http://object.class.name/](http://object.class.name/)  
}Wrapper”  
properties = List.of(System::String).new

```
(object.methods - Object.instance_methods).each {|m| properties.add

```

m.to\_s}

```
wrapper_base_type = TypeGenerator.generate("#{wrapper_name}Base",

```

properties)  
base\_instance = Activator.create\_instance wrapper\_base\_type

```
eval <<EOS
  class #{wrapper_name} < base_instance.class
    def initialize(original)
      self.on_get = lambda do |prop|
        original.send prop
      end

      self.on_set = lambda do |prop, val|
        original.send "\#{prop}=", val
      end
    end
  end
  return #{wrapper_name} # return the class

```

EOS

end  
end

class Person  
attr\_accessor :first\_name, :last\_name

def initialize(first\_name, last\_name)  
@first\_name, @last\_name = first\_name, last\_name  
end

def full\_name  
“#{last\_name}, #{first\_name}”  
end  
end

# A sample UI

people = []

10.times { |n| people \<\< Person.new(“John #{n}”, “Smith”) }

wrapper = WrapperGenerator.new

w = Window.new  
combo = ComboBox.new  
combo.items\_source = wrapper.wrap(people) # wrap people objects  
combo.display\_member\_path = “full\_name”  
combo.margin = 20  
w.content = combo

Application.new.run w
