I don’t have to pay the angle-bracket tax anymore![1]
In all of my RubyCLR WPF/Avalon samples, I was doing an HTML-esque style of programming where I was injecting XAML into an Avalon program as text/XML. I even created the equivalent of an innerHTML property to support my HTML habits.
However, since Ruby’s syntax is so flexible, I decided to see if I could implement something akin to Jim Weirich’s most excellent Builder that is used to create XML documents.
During a break at the Lang .NET symposium this week, I hacked out a proof-of-concept internal DSL for generating Avalon element trees. I grabbed Wilco Bauwer who was also in the audience (it’s hard to miss Wilco since he’s 6’7” tall!) and had him be a human Ruby compiler to see if my idea would work. It looked good to both of us, so I decided to implement it. After some hacking in my hotel room tonight, this is what I came up with:
x = Builder::AvalonBuilder.new
x.Window {
x.FlowDocument {
x.Section {
output = x.Paragraph('your output goes here', :font_size => 30)
x.Paragraph(:font_family => FontFamily.new('Consolas')) {
x.text! "This is text in consolas"
x.Button { |b|
x.text! "click me"
b.click do |sender, args|
output.add_child(Run.new('clicked me!'))
end
}
}
}
}
}
Application.new.run(x.result)The really cool thing about this is how I can have both the definition of the object model and executable Ruby code all packaged in an easy-to-read format. Notice how my event handler can reference the output Paragraph element.
Oh, and AvalonBuilder is implemented in around 70 lines of code :)
Is this sweet or what???!
[1] I coined this term when I crashed Scott Hanselman’s birds-of-a-feather session on code generation at some Tech Ed a few years ago. I was in an XML hating frame of mind that evening, lots of people were there, and apparently that term has stuck.