WPF Getting mouse coordinates within viewmodel - c#

I have a WPF project (C#, MVVM Light, Visual Studio 2010).
I have a bit of a problem regarding separation of concerns (MVVM) which basically is this: I have a command in a view model. I have a context menu that I want to call that command. So far so good. The problem is that the command needs to coordinates that the mouse was clicked.
To be a little more specific, the ContextMenu only appears if you click on a particular Canvas control, and it's the coordinates within said Canvas control that I want.
The easy way to do this is to manage it all in the code behind of the XAML document (and I have been able to do it that way), but I'd rather have it within my ViewModel if I can do so. The reason is that there are calls to my data model within this command so we end up with a problem of separation.
I am aware of PassEventArgsToCommand, and I'm aware that it's a bad practise, however in this case I'm not sure I can see a way around it. So for the moment I did try that, and it looks like this:
<ContextMenu x:Key="BackgroundMenu">
<MenuItem Header="Add new node here">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cmd:EventToCommand Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.DataContext.AddNewNodeAtLocationCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</MenuItem>
</ContextMenu>
So now my command, within the view model, looks like this:
void AddNewNodeAtLocationExecute(RoutedEventArgs e)
{
return;
}
Within that method I'd like to get those mouse coordinates, but I don't know if it's possible. e.OriginalSource is 'MenuItem', which doesn't help much.
So how can I do this? Can I do this? Or should I just have this one command handled by the code behind? Said code will involve a call to the database, which is why I'm being so particular about the separation.
Thanks in advance.

Well I stumbled across this question which speaks about separation of concerns and what not.
In the end I did a merging of the two ideas I had. Firstly, the ContextMenu simply links to the code behind. At that point I get the coordinates I want. Then that code behind gets the DataContext of the view (where the command I want is) and calls the Execute method (having first checked the 'can' method).
I suppose it's as ideal as you're going to get.

Related

Why does this DataGrid mouse input binding work in one project, but not in another?

We are building a WPF application containing a DataGrid, which should call a function on the currently selected row if that row is double-clicked. We are aiming for an MVVM approach where possible, trying to avoid events.
Because I've done something similar for a DataGrid in a previous application, I thought this would work:
<DataGrid.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick" Command="{Binding ShowDetailsCommand}"/>
</DataGrid.InputBindings>
In this working application, ShowDetailsCommand points to a method which accesses the currently selected DataGrid item via a data binding to SelectedItem.
Trying this same approach in the new project does not seem to work at all. The method which the command points to is never called (tested with a breakpoint and console output), and there is also no error message about the command not being found in the DataContext. We have also tried to move the <InputBindings> block directly into the Window object just to test if the DataContext may have changed further down, but double-clicking still produced no reaction.
As far as I can tell, the only major differences between the two DataGrids are:
The old one's ItemsSource was manually bound to a collection of objects, while the new one's Binding was created by dragging a DataSet onto it in the designer
The DataContext for the old project was a separate ViewModel class, while the new one's is the window's own Codebehind class (DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}", declared on the Window)
Could either of these be the cause for an InputBinding not working? If not, is there anything else we could be doing wrong? Apologies if this is not enough information, I am just very unsure where the problem might be. I will try to supply more information about the code if needed.
Try to bind to the ShowDetailsCommand of the DataContext of the parent window using a RelativeSource:
Command="{Binding DataContext.ShowDetailsCommand,
RelativeSource={RelativeSource AncestorType=Window}}"

Context menu binding never gets executed

I have ListView control in my view with it's own viewmodel A. I have made a seperate UserControl to use as ListViewItem, because it's styling takes a lot of space. In this ListViewItem I have a button, which is binded to viewmodel A and it works fine.
As the context menu has it's own visual tree and cannot bind via ancestor, I have used binding proxy, to solve this issue. I have tweaked it a little so it worked for my particular case, because if it just used {Binding} it would bind to item's model, not listview's viewmodel.
<helpers:BindingProxy x:Key="proxy" Data="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}"/>
To check if the binding is correct I've used a converter as a way just to have a breakpoint to check source. Everything was good and I was getting my viewmodel right there.
Now, when I try to bind to this in my context menu
<UserControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Open"
Command="{Binding DataContext.OpenChatCommand, Source={StaticResource proxy}, Converter={StaticResource DataBindingDebugConverter}}"
CommandParameter="{Binding}"/>
</ContextMenu>
</UserControl.ContextMenu>
The command never gets called. I added converter to see if something is wrong, but it turns out, I never get to my converter, which in turn means this code never gets executed.
Anyone with any ideas why this is happening and how to solve this is welcome.
I think it's the compiler malfunctioning though
I just did a brief readup on that "binding proxy" you mentioned, but as far as I know, DataGridTextColumn is in the same Visual Tree as its DataGrid, just that its DataContext is bound to its data.
For ContextMenu, it's totally different. This one really has a separate tree from its parent. There is no point in using a proxy object in resources, because it is from a different visual tree. When you use StaticResource, WPF will search upwards through its visual tree, level by level, inside those elements' Resource property (which is a ResourceDictionary).
One way is to make that proxy into a singleton, and use Source={x:Static helpers:BindingProxy.Instance}. Of course using this means that your proxy can only be used by a single View, or else something unexpected would happen.
The other way is to make use of PlacementTarget property of the ContextMenu.
<ContextMenu DataContext="{Binding Path=PlacementTarget.DataContext,
RelativeSource={RelativeSource Self}}">
This is the preferred way, but you need to make sure the parent's DataContext is really the VM that you need.
Edit
There is no super elegant way to do it the MVVM way. The best way is probably through the use of Tag property.
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag,
RelativeSource={RelativeSource Self}}">
ListView Control:
<MyControl:MyListViewItem .... Tag="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type MyControl:MyListViewView}}}"}" ...>

WPF MvvM communication between view models in a single View

This title might be inappropriate for question itself but stay with me  I’ll change it if you have better suggestion. This is my first wpf application so I might missed some key concept… I did google, but I failed to find correct approach.
I am building wpf application using MvvM Light and MUI and I got into trouble with item bindings, ie communication between view models. Now, I am sure that I wouldn't have this problem if I used single View model for the Page and all user controls in it, but I think I overdid it on my first try.
I have one Main Window in application and pages as the user controls. In each page I have several other user controls and each one of them have its own view model and its own logic for doing stuff but in the end they all depend on the corresponding VM with data grid. We could think of them as poor man angular directives. Each user control have its data context defined like so:
DataContext="{Binding ViewModelName, Source={StaticResource Locator}}
Layout looks like this: Wpf Layout
Look at this way, DG1 in VM1 is Master (customer) and UC3 and UC4 are Details (orders). If I add new order to the customer, I would like it to be updated in the DG1 without refreshing entire grid.
In VM1 Data Grid 1 selection changed I am firing commands to set property values of depending user controls.
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<mvvm:EventToCommand Command="{Binding ErrorWorkflow.GetErrorWorkflowCommand, Mode=OneWay, Source={StaticResource Locator}}" CommandParameter="{Binding SelectedError.WF_REF}" />
<mvvm:EventToCommand Command="{Binding ErrorDetails.GetErrorCaseDetailsCommand, Mode=OneWay, Source={StaticResource Locator}}" CommandParameter="{Binding SelectedError}" />
</i:EventTrigger>
</i:Interaction.Triggers>
That part works ok, but when I change the value of in the depending VM3 for both VM3 and VM1, VM1 property values are not changed even though I call RaisePropertyChanged or setting property explicitly by hand like SelectedError.Status = “somethingnew”.
On the other hand, if I clear selection form the data grid, depending view models stay bound (text boxes on them preserve the value because they are referencing properties on their own VM3).
All View Models derive from ViewModelBase from mvvmLight and all models from ObservableObjects (I know that I should use Poco, but apparently I have to create each property on VM too). Example:
public const string SelectedErrorPropertyName = "SelectedError";
private ErrorLog _selectedError;
public ErrorLog SelectedError
{
get
{
return _selectedError;
}
set
{
Set(() => SelectedError, ref _selectedError, value);
}
}
I think that Messenger would be an overkill considering the size of the application (only few pages like this one).
Should I change the Page to use only one View Model and share them for each user control or am I missing something obvious here?
If you think that I am missing some key information in this example please tell me and I’ll update.
Thank you in advance for any advice, cheers!
You should never change the Page to only one View Model and Messenger is not an overkill. The MVVM Light Messenger is built to solve exact the problem (communication between VMs) you are having at the moment. You should use it.
For further information about the messaging within MVVM Light, Jesse Liberty of Microsoft has a great tutorial on how to make use of it.

execute command in ViewModel from Mouse event

Very basic mvvm cross question...
I am creating a WPF UI and want to make an image map. I have my path objects defined and now want to hook up a command from the view model to the mouse up event on the path.
Initially I thought it would just be a case of adding the event handler and calling the command from there, but the code behind does not have a reference to the view model because of IoC, and I cannot see any way of directly using a xaml property.
Are you looking for something like this?
<Path Data="M 80,200 A 100,50 45 1 0 100,50" >
<Path.InputBindings>
<MouseBinding Command="{Binding MyPathCommand}" MouseAction="LeftClick" />
</Path.InputBindings>
</Path>

Attach command with parameter to KeyUp event

I am currently transforming a medium size WPF project to MVVM and I ran into a problem that I wasn't able to solve, yet. Maybe you could help me out?
The target framework is .NET 3.5.1.
I have a list view that gets its items from the underlying view model. That view model is exposing a command to remove the selected items from the list view. Therefore the command parameter is bound to the SelectedItems property of the list view.
<ListView ItemsSource="{Binding MyItems}"
x:Name="MyListView"
SelectionMode="Extended">
</ListView>
<Button x:Name="MyRemoveButton"
Content="Remove item"
Command="{Binding RemoveItemCommand}"
CommandParameter="{Binding ElementName=MyListView, Path=SelectedItems}">
My intention is to execute this command not only when pressing a button, but also when the KeyUp event is fired on the list view and the pressed key is "delete".
I was close to finding the solution when I stumbled upon interaction triggers in this example:
http://joyfulwpf.blogspot.com/2009/05/mvvm-invoking-command-on-attached-event.html?showComment=1250325648481#c3867495357686026904
Now the problem with this demo is that the command parameter is the pressed key, but in my case I need the command parameter to be the SelectedItems property and I need the command to execute only on a specific key.
Is there any way to do this without much overhead and in the MVVM way?
Something like this would be awesome:
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyUp">
<local:CommandAction Command="{Binding RemoveItemCommand}"
CommandParameter={Binding ElementName=MyListView, Path=SelectedItems}
EventArgument="Key.Delete"/>
</i:EventTrigger>
</i:Interaction.Triggers>
To do it in the MVVM way you need to bind "SelectedItems" property of the ListView to your ViewModel, so you could use it from your commands and wouldn't need to pass it via CommandParameter.
How strict is your separation requirement? If you don't have designers using Blend, then put a call to a ViewModel method into the KeyUp or PreviewKeyUp event handler in your code-behind.

Categories

Resources