Project Description
A new binding that allows you to embed tiny, and efficient IronPython scripts in your XAML files. This helps reduce the number of one-off value converters that tend to plague XAML based applications.

PyBinding derives from MultiBinding, and will run a tiny IronPython script and update the value when property changes occur. When defining the script you to reference your DataContext and other elements in the visual tree using a shortcut syntax. (See below)
Authors and contributors
Andy Kutruff: Binding architecture, sub-binding syntax implementation, and script caching
Pavan Podila: Very slick interfacing with IronPython and original ScriptConverter
Rehan Azam: Script caching and performance improvements
David Nazarov: Being an all around bad ass

Examples

Conditonal tests

Display "Null" in a text field if the "People" property off your DataContext is null otherwise display "Not Null":

<TextBlock Text="{p:PyBinding \'Null\' if $[.People] \=\= None else \'Not Null\'}"/>

Math

Adds the value of a slider with x:Name="TheSlider" with the Age property off your data context and rounds the value:

<TextBlock Text="{p:PyBinding Math.Round($[TheSlider.Value] + $[.Age])}"/>

Drawing

Generate a line based on a slider value:

<Polyline Points="{p:PyBinding PointCollection([Point(1\,$[TheSlider.Value])\, Point(3\,4)])}" Stroke="Black" StrokeThickness="2" Stretch="Fill"/>

Sub-binding syntax

In order to use your DataContext, and your VisualTree inside the scripts, you embed sub-bindings with a compact syntax. These sub-bindings are all contained in a dollar sign and two brackets: $[] Under the covers, these are stripped out of the python script and replaced with real WPF bindings. This is similar to IdentityMine's EvalBinding. (An excellent system, however it does not work with Blend and hence the need for PyBinding.)

To DataContext

All references to DataContext begin with a period: $[.] This period means
  • $[.] - The object that is your DataContext
  • $[.Age] - The age property off your DataContext
  • $[.People[0]] - The first object in the People collection off the DataContext.

To UI Elements

All references to other objects in the visual tree use the x:Name of the object. Note: There is no period at the start of the sub-binding that references a visual element.
  • $[NameTextBlock.Text] - The text property of the element with x:Name equal to "NameTextBlock"
  • $[NameTextBlock] - An actual TextBlock instance, rather than one of its properties
  • $[{Self}] - Bind to your self. Equivalent to {Binding RelativeSource={RelativeSource Self}}
  • $[{Self}.Text] - The Text property off your self. Equivalent to {Binding Path=Text, RelativeSource={RelativeSource Self}}

StartupScript.py

In order to reference types beyond standard system types, you should define a file called StartupScript.py in the same directory as your executable. (Add a file called StartupScript.py to your executable project. Then, set the "Copy to Output Directory" to "Copy if newer" in the property pane for StartupScript.py.) Inside this file you should put import statement to types you use in your scripts,as well as any utility functions.

from System import *
from System import Environment
from System.Windows import *
from System.Windows.Media import *
from System.Windows.Media.Animation import *
from System.Windows.Controls import *
from System.Windows.Shapes import *

def BooleanToVisibility(bool):
    return Visibility.Visible if bool else Visibility.Collapsed
    
def BooleanToHidden(bool):
    return Visibility.Visible if bool else Visibility.Hidden

def IsNotNull(value):
	return value is not None

FindAncestor, StaticResource, and DynamicResource

We designed PyBinding to work inside Expression Blend. However, Blend is still not able to deal with custom markup extensions gracefully, and PyBinding can't use the compact sub-binding syntax for constructs that need x:Type or reference resources. To support this scenario, you must use the expanded binding syntax to specify your sub-bindings. Though a little bit annoying, it's not too bad. For example:

<TextBlock Text="Status">
   <TextBlock.Foreground>
      <pb:PyBinding Script="var_1 if var_0 >= 0 else var_2">
         <Binding Path="CurrentValue"/>
         <Binding Source="{StaticResource RedColorBrush}"/>
         <Binding Source="{StaticResource GreenColorBrush}"/>
      </pb:PyBinding>
   </TextBlock.Foreground>
</TextBlock>

In the above sample, you'll notice that the IronPython code is inside of a script attribute, and it also does not contain any $[] in that script. Instead, you see var_0. var_0 refers to the first child binding below. In this case, it would be the "CurrentValue" property on the DataContext. var_1 refers to a red brush that is defined in our resources. var_2 refers to a green brush also defined in our resources. In other words, to refer to a sub-binding, use the index of the sub-binding after var_

Last edited Jan 6, 2010 at 3:26 PM by akutruff, version 16