0

I have a Canvas that contains a Rectangle. On that canvas, I bind a mousedown event to a command on the ViewModel. In that command, I am being passed the MouseEventArgs, but there the Target element is either the Canvas or the Rectangle. Where can I find in the MouseEventArgs the Canvas this event was fired from?

My code is more or less:

<Canvas Background="White">
    <i:EventTrigger EventName="MouseLeftButtonDown">
        <local:InteractiveCommand Command="{Binding CmdMouseLeftButtonDown}"/>
    </i:EventTrigger>
    <Rectangle Width="50" Height="50" />
</Canvas>

And in the ViewModel:

ICommand CmdMouseLeftButtonDown => new DelegateCommand<MouseEventArgs>(e => 
{
    e.??? // <= Where do I find the Canvas here, whether I click on the Rectangle or Canvas?
}

Please do not answer with some hackish solution like e.MouseDevice.Target.Parent. This needs to work however complicated the element in the canvas is. It could contain another canvas for instance.

edeboursetty
  • 5,669
  • 2
  • 40
  • 67

3 Answers3

1

A view model is not supposed to have a reference to a UI element such as a Canvas or a Rectangle at all in the first place. This effectively breaks the MVVM pattern and that's why it makes no sense to pass the sender argument to the command.

You might as well get rid of the EventTrigger and invoke the command programmatically from the code-behind of the view:

<Canvas Background="White" MouseLeftButtonDown="Canvas_MouseLeftButtonDown">
    <Rectangle Width="50" Height="50" Fill="Red" />
</Canvas>

private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    var yourViewModel vm = DataContext as YourClass;
    vm.CmdMouseLeftButtonDown.Execute(sender as Canvas); //<-- pass the Canvas as a command argument or create a new command argument type that holds a reference to the Canvas
}

This is certainly not any worse than your current approach as far as the MVVM pattern is concerned. You are still invoking the very same command from the very same view and MVVM is not about eliminating code. It is about separation of concerns.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • If a ViewModel is not supposed to use the MouseEventArgs, how am I to make a control that uses the position of the mouse on the canvas? For instance if I make a custom control that's a X-Y input pad. Where does the code is supposed live? I can't any f-ing tutorial that makes any sense. I'm loosing my mind with this stupid wpf f-ing garbage. – edeboursetty Feb 24 '17 at 11:55
  • I guess you didn't even try my suggestion then? It should be pretty simple to implement. – mm8 Feb 24 '17 at 11:59
  • I want to do it right. In my understanding, if mvvm was consistent, I should be able to bind the mouse position from xaml to a ViewModel property. Something like ` – edeboursetty Feb 24 '17 at 12:46
  • No, you can't bind the current mouse position to a source property. – mm8 Feb 24 '17 at 12:48
  • what do you think about this answer, is that the right way? http://stackoverflow.com/questions/30047415/how-do-i-get-mouse-positions-in-my-view-model – edeboursetty Feb 24 '17 at 12:49
  • Yes, if you want to pass a Point to the VM. I am not sure how this related to your original question about the Canvas though. Please ask a new question if you have another issue. – mm8 Feb 24 '17 at 12:50
  • I'll give that a shot, perhaps then the event will have a proper Source property... Thanks for your help – edeboursetty Feb 24 '17 at 12:52
0

Your MouseEventArgs.Source will reference to the Canvas in any case but the MouseEventArgs.OriginalSource will referece to the Rectange if you have clicked on its area. It will be the control determined by pure hit testing.

Hamlet Hakobyan
  • 32,965
  • 6
  • 52
  • 68
0

Set <Canvas Background="Transparent" ... />

as answered in the following question by @Rob Fonseca-Ensor:

WPF: Canvas mouse events not firing on empty space

DrMarbuse
  • 804
  • 11
  • 30