DevExpress MVVM Framework. FunctionBindingBehavior

13 April 2015

All the features described herein are available in both the free and commercial versions of the DevExpress MVVM framework (included in the DevExpress WPF Subscription)

In this post, we’ll examine FunctionBinding behavior, consider why it may be helpful and run through its features.

FunctionBindingBehavior

Let’s start with a common situation - a function in your View Model that filters a collection and the need to bind the function result to your View.

   1: public class MainViewModel {
   2:     protected MainViewModel() { … }
   3:     public virtual ObservableCollection<object > Points { get; set; }
   4:     public IList<object> GetFilteredItems(DateTime start, DateTime end) {
   5:
   6:        return list;
   7:     }
   8: }

The function should be re-invoked once its parameters change. A standard solution for this issue is to introduce additional properties to your View Model that will responsible for the results returned by the function and each of its parameters. Additionally, you’ll need to implement an updating mechanism so it re-invokes the function with newly updated parameters. As you can see in the code snippet below, all of this requires additional code and complication of the View Model in general.

   1: <UserControl x:Class="FunctionBindingExample.View.MainView" 
   2:     ...
   3:     DataContext="{dxmvvm:ViewModelSource Type=vm:CommonMainViewModel}">
   4:     <Grid>
   5:         ...
   6:         <dxc:ChartControl ... >
   7:             ...
   8:             <dxc:FunnelSeries2D 
   9:                 x:Name="Series" 
  10:                 DataSource="{Binding FilteredItems}" ... />
  11:             ...
  12:         </dxc:ChartControl>
  13:         <dxe:RangeControl 
  14:             SelectionRangeStart="{Binding StartRangeDate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
  15:             SelectionRangeEnd="{Binding EndRangeDate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ... />
  16:     </Grid>
  17: </UserControl>

 

   1: public class CommonMainViewModel {
   2:     ...
   3:     public virtual ObservableCollection<DataItem> Points { get; set; }
   4:  
   5:     public IList<DataItem> GetFilteredItems(DateTime start, DateTime end) {
   6:         return this.Points.Where(x => x.Date.Date >= start && x.Date.Date <= end).ToList();
   7:     }
   8:     
   9:     public virtual IList<DataItem> FilteredItems { get; set; }
  10:     [BindableProperty(OnPropertyChangedMethodName = "Update")]
  11:     public virtual DateTime StartRangeDate { get; set; }
  12:     [BindableProperty(OnPropertyChangedMethodName = "Update")]
  13:     public virtual DateTime EndRangeDate { get; set; }
  14:         
  15:     protected void Update() {
  16:         FilteredItems = GetFilteredItems(this.StartRangeDate, this.EndRangeDate);
  17:     }
  18: }

FunctionBindingBehavior allows you to accomplish this requirement in a more straightforward and elegant way as it allows you to directly bind target property to a function in the View Model or View object without adding extra properties and synchronization code (function will be automatically re-invoked when even one of its parameters changes).

Consider the following code:

   1: public class MainViewModel {
   2:     ...
   3:     public virtual ObservableCollection<DataItem> Points { get; set; }
   4:     public IList<DataItem> GetFilteredItems(DateTime start, DateTime end) {
   5:         return this.Points.Where(x => x.Date.Date >= start && x.Date.Date <= end).ToList();
   6:     }
   7: }
   1: <UserControl x:Class="FunctionBindingExample.View.MainView" 
   2:     ...
   3:     xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors" 
   4:     xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm" 
   5:     xmlns:dxcr="http://schemas.devexpress.com/winfx/2008/xaml/charts/rangecontrolclient" 
   6:     xmlns:dxc="http://schemas.devexpress.com/winfx/2008/xaml/charts" 
   7:     xmlns:vm="clr-namespace:FunctionBindingExample.ViewModel"
   8:     DataContext="{dxmvvm:ViewModelSource Type=vm:MainViewModel}">
   9:     <Grid>
  10:        ...
  11:        <dxc:ChartControl ... >
  12:           ...
  13:           <dxc:FunnelSeries2D x:Name="Series" ... />
  14:           ...
  15:           <dxmvvm:Interaction.Behaviors>
  16:               <dxmvvm:FunctionBindingBehavior
  17:                   Target="{Binding ElementName=Series}"
  18:                   Property="DataSource"
  19:                   Source="{Binding}"
  20:                   Function="GetFilteredItems"
  21:                   Arg1="{Binding SelectionRangeStart, ElementName=rangeControl}"
  22:                   Arg2="{Binding SelectionRangeEnd, ElementName=rangeControl}"/>
  23:           </dxmvvm:Interaction.Behaviors>
  24:        </dxc:ChartControl>
  25:        <dxe:RangeControl Name="rangeControl" ... />
  26:     </Grid>
  27: </UserControl>

As you can see, the View Model’s GetFilteredItems function is bound to the DataSource property of Series FunnelSeries2D. The parameters of the GetFilteredItems function are based on the RangeControl’s SelectionRangeStart and SelectionRangeEnd property values.

The Target property contains an object whose property will be populated (by default, the target-object is behavior’s associated object). In our example, the target-object is FunnelSeries2D. Target-object property should be specified via Property.

   1: <dxmvvm:FunctionBindingBehavior
   2:     Target="{Binding ElementName=Series}"
   3:     Property="DataSource"
   4:     ...
   5: />

The Source property contains an object whose function will be bound to the target-property specified in Property (by default, the source-object is an object located in the data context of the associated object.). The source-function is specified via the Function property.

   1: <dxmvvm:FunctionBindingBehavior
   2:     ...
   3:     Source="{Binding}"
   4:     Function="GetFilteredItems"
   5:     ...
   6: />

FunctionBindingBehavior also allows you to specify the source-function’s parameters (if necessary). The maximum number of parameters is 15. Each specified parameter will be automatically converted to the parameterized type where possible.

   1: <dxmvvm:FunctionBindingBehavior
   2:     ...
   3:     Arg1="{Binding SelectionRangeStart, ElementName=rangeControl}"
   4:     Arg2="{Binding SelectionRangeEnd, ElementName=rangeControl}"
   5: />

In certain scenarios, you may need to manually re-invoke the source-function. You can do so by using the UpdateFunctionBinding extension method in your View Model:

   1: public static class POCOViewModelExtensions {
   2:     public static void UpdateFunctionBinding<T>(this T viewModel, Expression<Action<T>> methodExpression) { … }
   3:
   4: }

For example:

   1: this.UpdateFunctionBinding(x => x.GetFilteredItems(default(DateTime), default(DateTime)));

FunctionBindingBehavior provides full SmartTag support. You can easily define this behavior and configure it in a few clicks. Let’s review how you can define FunctionBindingBehavior at design time.

To begin, assign the FunctionBindingBehavior to the required control. Click the Smart Tag glyph at the top right corner of the control:

1

Next, open the MVVM tab and add the FunctionBindingBehavior item via the «Add Behavior» menu:

1(1)

Specify the target-object using the Target property.

2

And choose the target’s property from the drop-down list.

3

Select the source object and a function of the source object and specify its arguments (By default, Smart Tag uses the DataContext of the target object). Smart Tag automatically generates property lines for the function’s arguments based upon the maximum number of parameters for the selected function parameters. The maximum number of argument property lines is 15.

4

The result will be as follows:

5

An example that illustrates use of the FunctionBindingBehavior class can be found here.

Should you have any questions or need additional assistance feel free to share your comment with us. We’d love to hear what you think.

Tags
no comments
No Comments

Please login or register to post comments.