Site Logo

Pixel-in-Gene

Exploring Frontend Development with Design / Graphics / Technology

The PyBinding MarkupExtension: use Python statements for Data Binding

Many of us have felt the need to have custom expressions inside of Bindings. The need has ranged from applying mathematical operators to calling methods on bound objects. For simple needs you can probably create a custom IValueConverter or a IMultiValueConverter that can do the job, but when you want to use arbitrary expressions, something more elaborate is needed.

The ScriptConverter class that I blogged about earlier provides the underpinning for the PyBinding MarkupExtension. The MarkupExtension provides a simplified way of using the ScriptConverter by automatically setting up the Bindings used in the Python statements. Thus the PyBinding extension provides you the following:

  • Allows use of IronPython for scripting arbitrary expressions
  • Each expression will be converted to a Binding or a MultiBinding depending on the number of subexpressions
  • A syntax for specifying the sub-expression. Essentially a DSL for creating Bindings.

image

The Inspiration

The original work on PyBinding was done by my colleague at work, Andrew Kutruff. He was the one who took my ScriptConverter and wrapped it into a MarkupExtension. He chose to adopt a different syntax and added a few more features like working inside of Blend, performance improvements, etc. I think you will hear more about his work when he starts blogging. I should mention that Andrew was also the guy who did a bunch of work with CLinq, especially around continuous values, eg. automatically getting a continuously updating Min, Max or any aggregation. I decided to branch off of his work and tailored it according to the needs of my projects. The sections below talk about my implementation.

Syntax of PyBinding

The PyBinding MarkupExtension allows you to specify arbitrary python statements consisting of binding expressions. Each binding expression can be treated as the variable over which the python statements operate. The syntax to specify a single binding expression looks as below. If you are jQuery fan, you will see that I have some of its influence :).

$( *<source>.<property-path>#<converter-name>(<converter-parameter>*) )

<source> can be either a

  • MarkupExtension like StaticResource, FindAncestor, Static, eg. StaticResource(RedBrush), FindAncestor(ItemsControl)
  • Keywords like Self (meaning the element on which the binding has been set up), TemplatedParent
  • Element names (x:Name)
  • Empty source, which points to the DataContext

<property-path>can be any path supported by the standard Binding.Path. This includes the dotted object notation, array indexers, attached properties. For XPath, the property-path starts with the XPath:prefix.

<converter-name> is the type-name of the converter that will automatically be created. This allows you to apply a IValueConverter on this binding expression before it is used in the script. Although not useful on a frequent basis there are times when a converter is required inside of the Binding expression.

<converter-parameter>is the parameter that you want to pass to the <converter>

Only the <source> value is required, everything else is optional.

Few examples of PyBinding expressions

The following set of examples should make it easier to understand the PyBinding syntax.

  • $() refers to the DataContext. This is equivalent to {Binding}.
  • $(.Address.Street)– Address.Street property on the DataContext.
  • $(StaticResource(RedBrush).Color)– points to the Color property of SolidColorBrush resource with the x:Key=RedBrush
  • $(Self.Width)– binds to Width property of the currently bound object
  • $(TemplatedParent.BorderBrush)
  • $(FindAncestor(ItemsControl).Items.Count)
  • $(Rect.(Canvas.Left))– refers to the Canvas.Left attached property on the Rectangle named (x:Name) Rect
  • $(Static(ModelRoot.Instance).XPath:/SolarSystem/Planet[1]/Orbit)– specifying xpath
  • $(TheSlider.IsEnabled#BooleanToVisibilityConverter)– specifying a converter

Usage in Xaml

The PyBinding extension is useful by itself for a simplified way of specifying Bindings. However you can also combine them inside of Python statements for more power and flexibility. The example below shows off the Python list comprehension syntax for creating collections:

<Polyline Points="{ln:PyBinding PointCollection([Point(p.Time,
        p.Value * $(Container.ActualHeight))
          for p in $(.MarketData.DataPoints)])}"

      Stroke="IndianRed"
      StrokeThickness="1"/>

[Note: The Points value is spanning multiple lines for readability. In Xaml it would be a single line.]

The above PyBinding creates a PointCollection out of the MarketData.DataPoints collection. We are using python’s list comprehension syntax where we loop over the MarketData.DataPoints and create a Point for every DataPoint p. You may have to squint a bit to read the line ;)

Current Status

I am currently using PyBinding in two live projects and putting it through its paces. So far it is turning out extremely well and I am happy with the results. Hopefully I will be able to release this soon, as part of FluidKit. Although we are calling it PyBinding, the core architecture can use any DLR based scripting language, which means you should be able to use languages like Ruby, JScript, etc.

The PyBinding MarkupExtension: use Python statements for Data Binding
Pavan Podila
Pavan Podila
December 24th, 2008