Executing a Command when a ListView is DoubleClicked. (WPF - MVVM) - c#

I am having some difficulties binding a command (ICommand) to the MouseBinding of a ListView.
I used this piece of XAML code to test the different mouse gestures:
<ListView.InputBindings>
<MouseBinding Command="{Binding OpenSOACommand}" Gesture="LeftClick" />
<MouseBinding Command="{Binding OpenSOACommand}" Gesture="MiddleClick" />
<MouseBinding Command="{Binding OpenSOACommand}" Gesture="LeftDoubleClick" />
</ListView.InputBindings>
The LeftClick and LeftDoubleClick gestures aren't triggered, yet the MiddleClick mouse binding works perfect (I have tested the mouse bindings one at a time as well...).
Is there a difference in the way the LeftDoubleClick and MiddleClick Gesture is handled? And if there is, how can I bind my ICommand to the LeftDoubleClick gesture?
Thanks!

The default Click event for the ListView is marking the event as handled. Try using PreviewLeftClick and PreviewLeftDoubleClick instead
EDIT
Since MouseBindings does not contain a PreviewLeftClick or PreviewLeftDoubleClick, try using the AttachedCommandBehavior code found here which allows you to attach a Command to just about any Event
For example,
<ListView local:CommandBehavior.Event="PreviewMouseDown"
local:CommandBehavior.Command="{Binding OpenSOACommand}" />

This is because your ListViewItems of your ListView will swallow your LeftClick events and convert them into nice SelectionChanged events. Since the ListViewItems will not respond to MiddleClick, this will work as expected.
You might want to get 'in front' of this click by handling the matching Preview equivalent of the event.
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<EventSetter Event="MouseDoubleClick" Handler="OnItemDoubleClick"/>
</Style>
</ListView.ItemContainerStyle>
And invoke the command in the handler:
private void OnItemDoubleClick(object sender, MouseButtonEventArgs e)
{
OpenSOACommand.Execute(null, this);
}

Related

DataGrid ContextMenu MenuItems are sometimes disabled

I have a DataGrid and I do not know, why the MenuItems of ContextMenu are sometimes enabled and sometimes disabled.
<DataGrid ItemsSource="{Binding Values}">
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Command="Copy" />
<MenuItem Command="Paste" />
<MenuItem Command="Delete" />
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
What can be the cause for that? I did not find any code, which is responsible for setting the ICommand.CanExecute or the MenuItem.IsEnabled.
Please tell me which information I still need to provide.
#Maverik: I do not wrote any code for those three standard .NET commands:
ApplicationCommands.Delete
ApplicationCommands.Copy
ApplicationCommands.Paste
Your MenuItems are built-in WPF commands. Accordingly to MSDN documentation their implementation depends on control where commands were triggered and in your case from the state of DataGrid(row selected or not etc.).
...The implementation logic is bound to the command with a
CommandBinding. For example, if the Close command is executed on a
control, the logic which performs the Close command may not be
provided by the control, so the application writer will be responsible
for writing the logic that determines how the control will handle the
command.
Many controls do provide implementation logic for many of the commands
in the command library. For example, the TextBox class provides logic
for the Paste, Cut, Copy, Undo, and Redo commands.
See ApplicationCommands Class.
You can impact your ContextMenu by putting in XAML:
<DataGrid.CommandBindings>
<CommandBinding Command="Copy" CanExecute="CommandBinding_CanExecute"/>
</DataGrid.CommandBindings>
and in code behind:
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = false;//put here your logic
e.Handled = true;
}

Binding custom events of custom elements in MVVM Pattern

I'm trying to bind the "DataClick" event of LiveChart's Cartesian Chart element using MVVM pattern.
I have my Charts.xml like this:
<ContentControl Grid.Row="0">
<lvc:CartesianChart x:Name="ContrastChart" Series="{Binding ContrastSeriesCollection}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="DataClick">
<i:InvokeCommandAction Command="{Binding ChartDataClick}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</lvc:CartesianChart>
</ContentControl>
This is my ICommand ChartDataClick on my ViewModel:
public ICommand ChartDataClick {
get
{
if(_dataClickCommand == null)
{
_dataClickCommand = new DelegateCommand(
() =>
{
MessageBox.Show("Data Clicked!");
}
);
}
return _dataClickCommand;
}
}
If I switch e.g "DataClick" for "MouseEnter" I get my command fired.
So I'm assuming that the problem is that the DataClick is a custom event.
Anybody knows a workaround for this?
I really tried everything I could find on Google that could help, but nothing so far...
LiveCharts Events: Events Documentation
The EventTrigger doesn't discriminate.
We can check this by implementing MyButtonSimple which has a custom Routed Event Tap.
We can go from handler in code behind
<custom:MyButtonSimple
x:Name="mybtnsimple" Tap="mybtnsimple_Tap"
Content="Click to see Tap custom event work">
</custom:MyButtonSimple>
To a ViewModel ICommand
<custom:MyButtonSimple
x:Name="mybtnsimple"
Content="Click to see Tap custom event work">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<i:InvokeCommandAction Command="{Binding Command}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</custom:MyButtonSimple>
And everything works as expected
The shortcoming of these triggers is that they have to be placed on the UIElement raising the event.
In other words, they ignore Bubbling or Tunneling events. That's why there is no Interaction.Triggers alternative for:
<Grid custom:MyButtonSimple.Tap="mybtnsimple_Tap">
<custom:MyButtonSimple
x:Name="mybtnsimple"
Content="Click to see Tap custom event work">
</custom:MyButtonSimple>
</Grid>
To sum it up, the DataClick event isn't raised on the CartesianChart (but further down the Logical Tree) and therefore you can't handle it this way.

Unable to focus ListView

Situation: In MVVM pattern, I have some inputbindings on a listview which work only when the listview is focused. However, whenever user clicks, the listview goes out of focus and user is unable to execute the inputbindings.
Problem: I want to bring the focus on the listview (on button click) in a way that the inputbindings work.
What I tried:
I tried using attached property IsFocused (where I focus using UIElement.Focus() and/or Keyboard.Focus()) and binding it to a bool variable in the ViewModel which I would set using an ICommand.
I also tried a separate example where I can use the System.Windows.Input.Keyboard.Focus(item) method in the code behind (I mean the .xaml.cs file with the same name) to focus the listview and it works! But, I don't know how to implement the similar thing in a ViewModel which is connected using a d:DesignInstance attribute.
I believe that the mouseclick event is bubbled up and handled somewhere else which causes the list to unfocus as soon as I click it. Like, if I find a way to set the event as handled that will help, but again I don't know how to do that in a viewmodel. Here is my attached property :
FocusExtension.cs
public static class FocusExtension {
public static bool GetIsFocused(DependencyObject obj) {
return (bool)obj.GetValue(IsFocusedProperty);
}
public static void SetIsFocused(DependencyObject obj, bool value) {
obj.SetValue(IsFocusedProperty, value);
}
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached(
"IsFocused", typeof(bool), typeof(FocusExtension),
new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));
private static void OnIsFocusedPropertyChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e) {
var uie = (UIElement)d;
if ((bool)e.NewValue) {
uie.Focus();
}
}
}
XAML File:
<ListView
x:Name="lv"
Grid.Column="2" Margin="2" MinWidth="250" Height="400" ToolTip="the List"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding ListBindingInVM}"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.CanContentScroll="False"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.DropHandler="{Binding }"
behaviour:ListViewAutoScroll.AutoScrollToEnd="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
>
<ListView.Style>
<Style TargetType="ListView" >
<Setter Property="ViewModels:FocusExtension.IsFocused" Value="{Binding ListFocused, Mode=TwoWay}"></Setter>
<!--The one below is not clean, but worked. However, list goes out of focus on click. -->
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="ViewModels:FocusExtension.IsFocused" Value="True"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListView.Style>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<!--This command sets the ListFocused to true-->
<i:InvokeCommandAction Command="{Binding BringListToFocus }"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListView.InputBindings>
<!-- Bindings that don't work when list is not focused-->
<KeyBinding Modifiers="Control" Key="C" Command="{Binding CopyCommand}"/>
<KeyBinding Modifiers="Control" Key="V" Command="{Binding PasteCommand}"/>
</ListView.InputBindings>
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy" Command= "{Binding CopyCommand}"></MenuItem>
<MenuItem Header="Paste" Command= "{Binding PasteCommand}"></MenuItem>
</ContextMenu>
</ListView.ContextMenu>
The focus behavior that you describe is easily implemented from the codebehind, and doing so does not violate the MVVM pattern. Consider Josh Smith's post, below:
https://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090097
The use of a ViewModel here makes it much easier to create a view that
can display a Customer object and allow for things like an
"unselected" state of a Boolean property. It also provides the ability
to easily tell the customer to save its state. If the view were bound
directly to a Customer object, the view would require a lot of code to
make this work properly. In a well-designed MVVM architecture, the
codebehind for most Views should be empty, or, at most, only contain
code that manipulates the controls and resources contained within that
view. Sometimes it is also necessary to write code in a View's
codebehind that interacts with a ViewModel object, such as hooking an
event or calling a method that would otherwise be very difficult to
invoke from the ViewModel itself.

context menu for listview does not fire the RightTapped event

I have been working with c# for some time now but surprisingly I have never dealt with context menus before. I have a listView control in my universal windows 8.1 app. Now I am trying to get a context menu to popup for each item in the listView (they are all the same type of object and are added to the list as the user adds entries). I have run into several problems with this and have looked at code examples and they seem to be leading in different directions. Firstly when I right click on an item in the list it does not fire the ListView_RightTapped event.
<ListView x:Name="lstvwHours" HorizontalAlignment="Left" Height="264" Margin="427,77,0,0" VerticalAlignment="Top" Width="357" RightTapped="lstvwHours_RightTapped">
Secondly in Microsoft's context menu code example they say to use the PopupMenu class but in other code I've seen it coded into the XAML.
And lastly After the one context menu button is clicked I want it to fire a delete method.
private async void lstvwHours_RightTapped(object sender,
RightTappedRoutedEventArgs e)
{
var menu = new PopupMenu();
menu.Commands.Add(new UICommand("Delete"/*do I put the method to call here?*/));
var chosenCommand = await menu.ShowForSelectionAsync(GetElementRect((FrameworkElement)sender));
}
Here's an example.
In this case you can wire-up the commands that get invoked from your menuitem onto your view-model.
<ListView>
<ListViewItem Content="One">
<ListViewItem.ContextMenu>
<ContextMenu>
<MenuItem Header="Insert"
Command="{Binding DataContext.InsertQuery, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/>
<MenuItem Header="Delete"
Command="{Binding DataContext.DeleteQuery, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/>
</ContextMenu>
</ListViewItem.ContextMenu>
</ListViewItem>
</ListView>

Context Menu on TreeViewItem Calls TreeViewItem's Method?

I have a TreeView setup so that each TreeViewItem has right-click context menu applied as a Style. Something like:
<Grid.Resources>
<ContextMenu x:Key="contextMenu">
<MenuItem Header="Save" IsEnabled="{Binding Path=Saveable}"/>
<MenuItem Header="Copy" IsEnabled="{Binding Path=Copyable}"/>
<MenuItem Header="Remove" IsEnabled="{Binding Path=Removeable}"/>
</ContextMenu>
<Style TargetType="TreeViewItem">
<Setter Property="ContextMenu" Value="{StaticResource contextMenu}" />
</Style>
</Grid.Resources>
Saveable, Copyable and Removeable are properties that come from the object that's used as the TreeViewItem.
What I'm looking for is when the user clicks on a MenuItem, it would click on the appropriate method of the selected object. So clicking on the "Save" MenuItem would call object.Save(), "Copy" calls object.Copy(), etc. But I'm not sure what the syntax would look like, or whether the idea is actually acceptable in terms of typical WPF style. I know I can just create a new event handler in the encompassing window, but I'd prefer the selected item itself to handle the event.
Thoughts?
Thanks!
Unfortunately, I don't think that there is an automated way of doing this. The closest option would be to setup a RoutedUICommand for each item in the ContextMenu, and then create a CommandBinding for each in your class. If you want those to go to the TreeViewItem, you'll probably need to subclass TreeViewItem and set up the CommandBindings there.
The one option that I thought might work would be to add an EventSetter for MenuItem.Click to the TreeViewItem style. However, that did not work - probably because the items in the ContextMenu are in a different visual tree from the TreeViewItems.

Categories