[Code Index] by Mike Marynowski

programming for fun and work

Updated Ultimate WPF Event Method Binding

After we started using our MethodBinding, we quickly ran into a few use cases that it could not accommodate very well so we ended up changing it a bit. 

If you haven't read my original article about Building the Ultimate WPF Event Method Binding Extension then that's probably a good place to start. It covers all the basics for how the binding works and the underlying methods used are the same. This isn't going to be a long post, it is just going to cover the changes we made since the original article was posted.

One problem with the original was that the only way to specify which object the method should be invoked on was through a path string passed in like this:

<Button Content="SaveDocument" Click="{data:MethodBinding CurrentDocument.DocumentService.Save, {Binding CurrentDocument}}" />

This was fine as long as there was a path of properties from the control's DataContext directly to the object with the method, but we found this wasn't always the case. Sometimes the method we wanted to invoke was on another control, or on the DataContext of a templated parent, or available on an object through a static property. 

So we split the path into two separate parameters: the method target and the method name. The example above is now written like this:

<Button Content="SaveDocument" Click="{data:MethodBinding CurrentDocument.DocumentService, Save, {Binding CurrentDocument}}" />

If you use this shorthand syntax and pass in a string as the first parameter then the method binding will automatically convert it into a binding on the control's data context. But we can also get fancy and use extension methods like so:

<!-- Call a method on a static property -->
<Button Content="SaveDocument" Click="{data:MethodBinding {x:Static services:DocumentService.Instance}, Save, {Binding CurrentDocument}}" />

<!-- Call a method on another element -->
<Button Content="SaveDocument" Click="{data:MethodBinding {Binding ElementName=DocumentEditor}, Save, {Binding CurrentDocument}}" />

<!-- Call a method on the data context of templated parent -->
<Button Content="SaveDocument" Click="{data:MethodBinding {Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext}, Save, {Binding CurrentDocument}}" />

<!-- Call a method on a static resource -->
<Button Content="SaveDocument" Click="{data:MethodBinding {StaticResource Document}, Save}" />

And well, why stop there...might as well make it completely flexible and allow extension methods on the method name as well :) This came in particularly handy in one situation where we had user actions exposed as methods on an object so we setup our method bindings dynamically to the list of names of the methods.

The method binding code is mostly the same as the previous post so I won't go through it in detail again. It has a couple bug fixes, the attached property code was refactored a bit and the method target and method name now get processed exactly the same as arguments by being assigned to attached properties.

Splitting the path into two components also mitigated an issue where ReSharper was showing an error because it was treating the first parameter as a property path but the method name at the end was not a valid property, which was kind of annoying:

Here is the updated source code: Updated Ultimate WPF Event Method Binding