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

Viewed 43135

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!

5 Answers

Try EventBinder which will allow you to bind your method directly to any event, including your own events, without a need to wrap the method in ICommand container.

https://github.com/Serg046/EventBinder
https://www.nuget.org/packages/EventBinder

.NET Framework 3.0 + and .NET Core 3.0 + are supported

Features:

  • Binding to methods without ICommand
  • Binding to methods with return types
  • Binding to async methods
  • Binding to nested objects using . delimiter, properties and fields are supported
  • Passing user parameters of int, double, decimal or string type
  • Passing event parameters using $ sign and position number ($0, $1, etc)
  • Passing default {Binding} as a parameter

Usage:

public class ViewModel
{
    public MetadataViewModel Metadata { get; } = new MetadataViewModel();

    public async Task ShowMessage(string msg, decimal centenary, double year)
    {
        await Task.Delay(0);
        MessageBox.Show(msg + centenary + year);
    }

    public class MetadataViewModel
    {
        public void ShowInfo(Window window, double windowWidth, ViewModel viewModel, object sender, MouseButtonEventArgs eventArgs)
        {
            var sb = new StringBuilder("Window width: ")
                .AppendLine(windowWidth.ToString())
                .Append("View model type: ").AppendLine(viewModel.GetType().Name)
                .Append("Sender type: ").AppendLine(sender.GetType().Name)
                .Append("Clicked button: ").AppendLine(eventArgs.ChangedButton.ToString())
                .Append("Mouse X: ").AppendLine(eventArgs.GetPosition(window).X.ToString())
                .Append("Mouse Y: ").AppendLine(eventArgs.GetPosition(window).Y.ToString());
            MessageBox.Show(sb.ToString());
        }
    }
}

Binding:

<Window xmlns:e="clr-namespace:EventBinder;assembly=EventBinder" Name="Wnd">
    <Rectangle Fill="LightGray" Name="Rct"
        MouseLeftButtonDown="{e:EventBinding ShowMessage, `Happy `, 20m, 20.0 }"
        MouseRightButtonDown="{e:EventBinding Metadata.ShowInfo, {Binding ElementName=Wnd},
            {Binding ElementName=Wnd, Path=ActualWidth}, {Binding}, $0, $1 }" />
</Window>

or

EventBinding.Bind(Rct, nameof(Rct.MouseLeftButtonDown),
    nameof(ViewModel.ShowMessage),
    "`Happy `", 20m, 20.0);
EventBinding.Bind(Rct, nameof(Rct.MouseRightButtonDown),
    nameof(ViewModel.Metadata) + "." + nameof(ViewModel.Metadata.ShowInfo),
    new Binding { ElementName = nameof(Wnd)},
    new Binding("ActualWidth") { ElementName = nameof(Wnd) },
    new Binding(),
    "$0", "$1");
Related