Click here to Skip to main content
15,915,094 members
Articles / Desktop Programming / WPF
Tip/Trick

Mouse Event Commands for MVVM

Rate me:
Please Sign up or sign in to vote.
4.95/5 (19 votes)
22 Jan 2013CPOL 111.4K   3.2K   22   28
Use an Attached Property to execute an ICommand.

Introduction

If you ever want to pass MouseEventArgs to a ViewModel, here's a neat way to do it Smile | <img src=

The Attached Property 

C#
public class MouseBehaviour
{
    public static readonly DependencyProperty MouseUpCommandProperty =
        DependencyProperty.RegisterAttached("MouseUpCommand", typeof(ICommand), 
        typeof(MouseBehaviour), new FrameworkPropertyMetadata(
        new PropertyChangedCallback(MouseUpCommandChanged)));

    private static void MouseUpCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        FrameworkElement element = (FrameworkElement)d;

        element.MouseUp += new MouseButtonEventHandler(element_MouseUp);
    }

    static void element_MouseUp(object sender, MouseButtonEventArgs e)
    {
        FrameworkElement element = (FrameworkElement)sender;

        ICommand command = GetMouseUpCommand(element);

        command.Execute(e);
    }

    public static void SetMouseUpCommand(UIElement element, ICommand value)
    {
        element.SetValue(MouseUpCommandProperty, value);
    }

    public static ICommand GetMouseUpCommand(UIElement element)
    {
        return (ICommand) element.GetValue(MouseUpCommandProperty);
    }
}

We simply register the attached property, hook the MouseUp event for the FrameworkElement, and invoke the Command in the handler. Simple enough, right? 

Usage 

XML
<Image Source="c:/temp.png" [Your xmlns]:MouseBehaviour.MouseUpCommand="{Binding MouseUpCommand}"></Image>

You do not, of course, have to use an <Image>, any framework element will work just fine. That's the beauty of Attached Properties! 

Having trouble attaching source, which contains Attached Properties for handling any mouse event MVVM style. But when it's up, includes 

  • MouseUp
  • MouseDown
  • MouseEnter
  • MouseLeave
  • MouseLeftButtonDown
  • MouseLeftButtonUp
  • MouseMove
  • MouseRightButtonDown
  • MouseRightButtonUp
  • MouseWheel

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Lovatts Publications
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralRe: My vote of 5 Pin
Matt Searles11-Mar-14 15:00
Matt Searles11-Mar-14 15:00 
QuestionBubbled events prevent this from working Pin
RickB_AZ1-Jan-14 5:32
RickB_AZ1-Jan-14 5:32 
AnswerRe: Bubbled events prevent this from working Pin
selfwalker27-Mar-14 16:39
selfwalker27-Mar-14 16:39 
SuggestionGarbage Collection? Pin
haindl17-Oct-12 21:39
haindl17-Oct-12 21:39 
GeneralRe: Garbage Collection? Pin
Dakko18-Oct-12 0:49
Dakko18-Oct-12 0:49 
GeneralRe: Garbage Collection? Pin
haindl18-Oct-12 1:50
haindl18-Oct-12 1:50 
GeneralRe: Garbage Collection? Pin
MarkWardell11-May-15 14:38
MarkWardell11-May-15 14:38 
AnswerRe: Garbage Collection? Pin
haindl12-May-15 2:15
haindl12-May-15 2:15 
You may not believe it, but almost 3 years after my last comment I still struggle with this topic.
As a matter of fact I had a similar problem in my current development work about an hour ago and just came to CodeProject to revisit this article again.
And as funny as it may sound - I then saw that you commented shortly before and decided to give it another go.

Back then I ended up using a MemoryProfiler to verify the references.
Now I've made a simple ConsoleApplication to demonstrate the situation.
As you can see I have mocked the essential parts.

For the sake of simplicity I omitted the Attached MouseUpCommand-DependencyProperty.
This doesn't affect any memory references.
(Feel free to try it with a real WPF-FrameworkElement in XAML.)

As you can clearly see if you run the ConsoleApplication:
After the last reference to the FrameworkElement is gone, it will be finalized correctly.
As long as MouseBehaviour doesn't have a field to store the FrameworkElement, there is no permanent reference.

The reason behind this is the fact that an event is backed by a field (residing in FrameworkElement) which stores the (Multicast)Delegate to the method of the subscriber that does the "+=" (MouseBehaviour.MouseUpHappened in my case).
So FrameworkElement has a reference to MouseBehaviour and not the other way around.
When the last reference to the FrameworkElement is deallocated, then the GC will happily finalize it.

If MouseBehaviour would have stored the FrameworkElement in a field then FrameworkElement would not be finalized.
(You can uncomment the _fe = fe line to see the difference.)
So the essence to success is to never store the FrameworkElement inside of MouseBehaviour.

I hope that I haven't overlooked something important.
Please don't hesitate to leave a comment if there is something unclear. Smile | :)

C#
using System;

namespace ReferenceTestConsoleApplication
{
    public class Program
    {
        private static FrameworkElement _fe = null;

        public static void Main(string[] args)
        {
            _fe = new FrameworkElement();
            MouseBehaviour.SubscribeToMouseUp(_fe);

            Console.WriteLine("1st GC started");
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, blocking: true);
            GC.WaitForPendingFinalizers();
            Console.WriteLine("1st GC ended\r\n");

            Console.WriteLine("Last reference to FrameworkElement is cleared " +
                "(e.g. if WPF isn't using it anymore)\r\n");
            _fe = null;

            Console.WriteLine("2nd GC started");
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, blocking: true);
            GC.WaitForPendingFinalizers();
            Console.WriteLine("2nd GC ended");

            Console.ReadLine();
        }
    }

    public static class MouseBehaviour
    {
        private static FrameworkElement _fe = null;

        public static void SubscribeToMouseUp(FrameworkElement fe)
        {
            fe.MouseUp += MouseUpHappened;
            Console.WriteLine("MouseBehaviour is now subscribing to " +
                "FrameworkElement.MouseUp\r\n");

            // If you uncomment the following line, FrameworkElement won't be finalized.
            // Or to be precise:
            // It will only be finalized after the above Console.ReadLine().
            //_fe = fe;
        }

        private static void MouseUpHappened()
        {
            Console.WriteLine("MouseUp happened (never called)");
        }
    }

    public class FrameworkElement
    {
        public event Action MouseUp;

        public void FireMouseUp()
        {
            if (MouseUp != null)
                MouseUp();
        }

        ~FrameworkElement()
        {
            Console.WriteLine("FrameworkElement is now finalized.");
        }
    }
}


The output is:
MouseBehaviour is now subscribing to FrameworkElement.MouseUp

1st GC started
1st GC ended

Last reference to FrameworkElement is cleared (e.g. if WPF isn't using it anymore)

2nd GC started
FrameworkElement is now finalized.
2nd GC ended


Edit: Just applied some formatting to the text.

modified 12-May-15 8:34am.

GeneralRe: Garbage Collection? Pin
Matt Searles20-Jun-15 14:02
Matt Searles20-Jun-15 14:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.