Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Event Binding to ViewModel (for non-Command classes)

Tags:

I'm working an the second version of an application, and as part of the rewrite I have to move to an MVVM architecture. I'm getting pressure to put absolutely every bit of code in the view model class--having c# in the code behind file is frowned upon. (I know, I know...I understand that code behind isn't a bad thing, but it isn't my call this time).

For objects which implement the command interface, it's easy. I've been able to find a ton of information on how to bind the Command of these objects to an ICommand in the view model. The problem is for objects which don't have this interface, e.g.

<ListBox    x:Name="myListBox"    MouseDoubleClick="myCallbackFunction">  <!-- ... -->  </ListBox> 

I want to know how to bind the MouseDoubleClick event for the Listbox to myCallbackFunction, which is implemented in the view model. Is this even possible?

Thanks!

like image 320
RobotNerd Avatar asked Jun 21 '11 18:06

RobotNerd


2 Answers

This isn't directly possible. It could be done via an Attached Property or Behavior, though it would still be a little tricky to find and invoke the appropriate method (this could be done via Reflection fairly easily).

That being said, this is typically handled via ICommand - For example, MVVM Light has a great EventToCommand behavior to map any event to an ICommand on the ViewModel. The advantage of using ICommand is that you can still use DataBinding, since the ICommand is exposed as a property.

like image 52
Reed Copsey Avatar answered Sep 30 '22 08:09

Reed Copsey


WPF supports markup extensions on events as of .NET 4.5. Using that capability, I implemented a versatile method binding extension and wrote about it here:

http://www.singulink.com/CodeIndex/post/building-the-ultimate-wpf-event-method-binding-extension

Can be used to bind to a method using full property path syntax, supports bindings and other markup extensions as arguments, and automatically routes to the method that matches the signature of the arguments provided. Here are some usage examples:

<!--  Basic usage  --> <Button Click="{data:MethodBinding OpenFromFile}" Content="Open" />  <!--  Pass in a binding as a method argument  --> <Button Click="{data:MethodBinding Save, {Binding CurrentItem}}" Content="Save" />  <!--  Another example of a binding, but this time to a property on another element  --> <ComboBox x:Name="ExistingItems" ItemsSource="{Binding ExistingItems}" /> <Button Click="{data:MethodBinding Edit, {Binding SelectedItem, ElementName=ExistingItems}}" />  <!--  Pass in a hard-coded method argument, XAML string automatically converted to the proper type  --> <ToggleButton Checked="{data:MethodBinding SetWebServiceState, True}"                 Content="Web Service"                 Unchecked="{data:MethodBinding SetWebServiceState, False}" />                  <!--  Pass in sender, and match method signature automatically --> <Canvas PreviewMouseDown="{data:MethodBinding SetCurrentElement, {data:EventSender}, ThrowOnMethodMissing=False}">     <controls:DesignerElementTypeA />     <controls:DesignerElementTypeB />     <controls:DesignerElementTypeC /> </Canvas>      <!--  Pass in EventArgs  --> <Canvas MouseDown="{data:MethodBinding StartDrawing, {data:EventArgs}}"         MouseMove="{data:MethodBinding AddDrawingPoint, {data:EventArgs}}"         MouseUp="{data:MethodBinding EndDrawing, {data:EventArgs}}" />  <!-- Support binding to methods further in a property path --> <Button Content="SaveDocument" Click="{data:MethodBinding CurrentDocument.DocumentService.Save, {Binding CurrentDocument}}" /> 

View model method signatures:

public void OpenFromFile(); public void Save(DocumentModel model); public void Edit(DocumentModel model);  public void SetWebServiceState(bool state);  public void SetCurrentElement(DesignerElementTypeA element); public void SetCurrentElement(DesignerElementTypeB element); public void SetCurrentElement(DesignerElementTypeC element);  public void StartDrawing(MouseEventArgs e); public void AddDrawingPoint(MouseEventArgs e); public void EndDrawing(MouseEventArgs e);  public class Document {     // Fetches the document service for handling this document     public DocumentService DocumentService { get; } }  public class DocumentService {     public void Save(Document document); } 
like image 28
Mike Marynowski Avatar answered Sep 30 '22 07:09

Mike Marynowski