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

41,844

Solution 1

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.

Solution 2

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);
}

Solution 3

To directly answer your question, please refer to Why to avoid the codebehind in WPF MVVM pattern? It suggests two possible things you want.

However, why do you want to bind the MouseDoubleClick of the ListBox to your ICommand in the viewmodel?

The alternative way is that you write a method in a codebehind to register the MouseDoubleClick. It is not bad due to the facts of the following.

  1. The meaningful databinding is the interaction between a view and a viewmodel. For example, when a user input some text to a TextBox, a viewmodel is also updated. On the contrary, if a viewmodel gets data from a database, it will be shown at a view. However, it is not this case that the ICommand in your viewmodel binds to the view.

  2. Of course, the CanExcute of the ICommand would important to your viewmodel, but in many cases, it is not related with the viewmodel or not concerned. In this case, the difference between the ICommand binding and the writing the codebehind is where the MouseDoubleClick event is binded with a ICommand or registered with a event handler.

Solution 4

One way could be to handle the event in the code behind and call appropriate method of view model from code behind

You can also go for some ready made commanding library like this tutorial which is using ACB

Share:
41,844
RobotNerd
Author by

RobotNerd

Updated on September 25, 2021

Comments

  • RobotNerd
    RobotNerd almost 3 years

    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!