Why right click event not worked in WPF? - c#

I want to delete my listbox item on right click. But, right click event not worked in my case.
Below is the code which I tried.
In constructor:
listBox1.MouseDown += new MouseButtonEventHandler(listBox1_MouseRightClick);
Right Click:
private void listBox1_MouseRightClick(object sender, MouseButtonEventArgs e)
{
if (sender is ListBoxItem)
{
ListBoxItem item = (ListBoxItem)sender;
Harvest_TimeSheetEntry entryToDelete = (Harvest_TimeSheetEntry)item.DataContext;
MessageBoxResult Result = System.Windows.MessageBox.Show("Are you sure?", "Delete Confirmation", System.Windows.MessageBoxButton.YesNo);
if (Result == MessageBoxResult.Yes)
{
Globals._globalController.harvestManager.deleteHarvestEntry(entryToDelete);
}
else
{
System.Windows.MessageBox.Show("Delete operation Terminated");
}
}
}
In xaml:
<ListBox x:Name="listBox1" ItemsSource="{Binding}" Margin="0,131,0,59" ItemTemplateSelector="{StaticResource templateSelector}" SelectionMode="Single" MouseRightButtonDown="listBox1_MouseRightClick">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter Event="MouseDown" Handler="listBox1_MouseRightClick"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Please suggest, how should I use right click event so it could work?

Your original code seems redundant and verbose. MouseRightButtonDown isn't working because there is already an event handling listbox item selection and the ListBoxItem datacontext is simply the SelectedItem of listBox1.
Get rid of overriding the style and just declare the listbox with the preview event. This will tunnel MouseRightButtonDown instead of bubble it.
<ListBox x:Name="listBox1"
ItemsSource="{Binding}"
ItemTemplateSelector="{StaticResource templateSelector}"
Margin="0,131,0,59"
SelectionMode="Single"
PreviewMouseRightButtonDown="listBox1_MouseRightClick" />
In the constructor, get rid of this
listBox1.MouseDown += new MouseButtonEventHandler(listBox1_MouseRightClick);
Now in the event handler, sender is your listbox1 but if you're not tying this event to other listboxes, simply get the selectedItem from listbox1 and cast it to the appropriate object. Otherwise if you decide you want the functionality on multiple listboxes cast sender to ListBox
private void listBox1_MouseRightClick(object sender, MouseButtonEventArgs e)
{
Harvest_TimeSheetEntry entryToDelete = (Harvest_TimeSheetEntry)listBox1.SelectedItem;
if(entryToDelete != null)
{
//Do work
}
}

Deleting records on right click is not a good design and it leads users make more confuse the functionality. Still if you want to do something, then you can go for the PreviewMouseRightButtonDown event. Please see the below snippet
ListBox1.PreviewMouseRightButtonDown += new MouseButtonEventHandler(ListBox1_MouseRightButtonDown);
Change your XAML as follows
<ListBox x:Name="listBox1"
ItemsSource="{Binding}"
Margin="0,131,0,59"
ItemTemplateSelector="{StaticResource templateSelector}"
SelectionMode="Single">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter Event="PreviewMouseRightButtonDown"
Handler="ListBox1_PreviewMouseRightButtonDown" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

Related

Deselect on single click in a ListBox with Extended SelectionMode

I have a ListBox with SelectionMode="Extended". You can only deselect the last item by holding down ctrl while clicking on it. I would like to be able to deselect the item by just clicking on it while not changing the behavior of the Extended selection mode other than that.
I only found one question about this topic and it actually has a different goal (being able to deselect all items by clicking outside of the ListBox).
If I understand your requirement correctly you could handle the PreviewMouseLeftButtonDown event for the ListBoxItem container and de-select it if it's already selected:
<ListBox SelectionMode="Extended">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="OnMouseLeftButtonDown"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBoxItem>1</ListBoxItem>
<ListBoxItem>2</ListBoxItem>
<ListBoxItem>3</ListBoxItem>
</ListBox>
private void OnMouseLeftButtonDown(object sender, MouseEventArgs e)
{
ListBoxItem lbi = sender as ListBoxItem;
if (lbi != null)
{
if (lbi.IsSelected)
{
lbi.IsSelected = false;
e.Handled = true;
}
}
}
This should allow you to be able to de-select an item without using the CTRL key.

PreviewKeyDown of checkbox in Multi Select ComboBox in WPF

I use this article to create multi-select ComboBox.
I want to select/unselect the CheckBox inside each item when pressing the Space button.
I tried to add PreviewKeyDown for the CheckBox but the event doesn't get raised.
I also tried adding PreviewKeyDown in the StackPanel but then I can't get the selected item that it's CheckBox is currently checked.
You need to handle KeyUp of the combobox and make sure that the dropdown is open.
Update:
<Grid>
<ComboBox x:Name="cbo" KeyUp="ComboBox_KeyUp" Height="30" Width="200">
<CheckBox Content="checkbox1"/>
<CheckBox Content="checkbox2"/>
<CheckBox Content="checkbox3"/>
<CheckBox Content="checkbox4"/>
<CheckBox Content="checkbox5"/>
</ComboBox>
</Grid>
///////////////////////////
private void ComboBox_KeyUp(object sender, KeyEventArgs e)
{
if (cbo.IsDropDownOpen)
{
// select first and second
(cbo.Items[0] as CheckBox).IsChecked = true;
(cbo.Items[1] as CheckBox).IsChecked = true;
}
}
I use EventSetter.
<ComboBox.Resources>
<Style TargetType="{x:Type ComboBoxItem}">
<EventSetter Event="PreviewKeyDown" Handler="EventSetter_OnHandler" />
</Style>
</ComboBox.Resources>
and in code behind.
private void EventSetter_OnHandler(object sender,KeyEventArgs e)
{
var item=((ComboBoxItem)sender).DataContext as Node;
item.IsSelected=!item.IsSelected;
}

WPF TreeView restores its focus after double click

I have a WPF TreeView with XAML shown below:
<TreeView x:Name="twElements">
<TreeView.Resources>
<v8r:IconTypeConverter x:Key="IconConverter"/>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="twElements_MouseDoubleClick" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding ChildItems}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Icon, Converter={StaticResource IconConverter}}"/>
<TextBlock Text="{Binding Text}" Margin="3,0,0,0"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
The task is to open some form after double click on a child item.
code-behind for DoubleClick event:
private void twElements_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (e.Source is TreeViewItem && ((TreeViewItem)e.Source).IsSelected)
{
e.Handled = true;
var twi = (TreeViewItem)e.Source;
var Editable = twi.Header as IEditable;
if (Editable != null)
{
Window Editor = Editable.GetEditor();
Editor.Show(); // Editor is a WPF.Window
}
}
}
The problem: After desired window is opened, form with a treeview activates itself, making new window to go background.
How to make new window to remain active?
You probably need to let WPF finish the job of handling the current mouse click event(s) before you open the new Window. Let the new window be the next UI job by adding it to the current Dispatcher's queue like this:
(...)
//Editor.Show();
Action showAction = () => Editor.Show();
this.Dispatcher.BeginInvoke(showAction);
in constructor of new form set
this.Focus();
Also, does your new form should be Modal window? if yes use Editor.ShowDialog() instead of Editor.Show(); It will automatically solve issue with focus

Determining which ListViewItem was clicked on in a ListView when executing a ContextMenu MenuItem

I'm trying to use the context menu in a listview to run some code that requires data from which item it originated from.
I initially just did this:
XAML:
<ListView x:Name="lvResources" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListView.Resources>
<ContextMenu x:Key="resourceContextMenu">
<MenuItem Header="Get Metadata" Name="cmMetadata" Click="cmMetadata_Click" />
</ContextMenu>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ContextMenu" Value="{StaticResource resourceContextMenu}" />
</Style>
</ListView.ItemContainerStyle>
...
C#:
private void cmMetadata_Click(object sender, RoutedEventArgs e)
{
// code that needs item data here
}
But I found that the originating listview item was not accessible that way.
I've read some tactics about how to get around this, like intercepting the MouseDown event and setting a private field to the listviewitem that was clicked, but that doesn't sit well with me as it seems a bit hacky to pass data around that way. And WPF is supposed to be easy, right? :) I've read this SO question and this MSDN forum question, but I'm still not sure how to really do this, as neither of those articles seem to work in my case. Is there a better way to pass the item that was clicked on through to the context menu?
Thanks!
Similar to Charlie's answer, but shouldn't require XAML changes.
private void cmMetadata_Click(object sender, RoutedEventArgs e)
{
MenuItem menu = sender as MenuItem;
ListViewItem lvi = lvResources.ItemContainerGenerator.ContainerFromItem(menu.DataContext) as ListViewItem;
}
Well in the cmMetadata_Click handler, you can just query the lvResources.SelectedItem property, since lvResources will be accessible from the code-behind file that the click handler is located in. It's not elegant, but it will work.
If you want to be a little more elegant, you could change where you set up your ContextMenu. For example, you could try something like this:
<ListView x:Name="lvResources" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListView.Style>
<Style TargetType="ListView">
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<TextBlock Text="{TemplateBinding Content}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Get Metadata" Name="cmMetadata" Click="cmMetadata_Click"
DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
</ListView.Style>
<ListViewItem>One Item</ListViewItem>
<ListViewItem>Another item</ListViewItem>
</ListView>
What this does is plug in a template for your ListViewItem, and then you can use the handy TemplatedParent shortcut to assign the ListViewItem to the DataContext of your menu item.
Now your code-behind looks like this:
private void cmMetadata_Click(object sender, RoutedEventArgs e)
{
MenuItem menu = sender as MenuItem;
ListViewItem item = menu.DataContext as ListViewItem;
}
Obviously the downside is you will now need to complete the template for a ListViewItem, but I'm sure you can find one that will suit your needs pretty quickly.
So I decided to try and implement a command solution. I'm pretty pleased with how it's working now.
First, created my command:
public static class CustomCommands
{
public static RoutedCommand DisplayMetadata = new RoutedCommand();
}
Next in my custom listview control, I added a new command binding to the constructor:
public SortableListView()
{
CommandBindings.Add(new CommandBinding(CustomCommands.DisplayMetadata, DisplayMetadataExecuted, DisplayMetadataCanExecute));
}
And also there, added the event handlers:
public void DisplayMetadataExecuted(object sender, ExecutedRoutedEventArgs e)
{
var nbSelectedItem = (MyItem)e.Parameter;
// do stuff with selected item
}
public void DisplayMetadataCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
e.Handled = true;
}
I was already using a style selector to dynamically assign styles to the listview items, so instead of doing this in the xaml, I have to set the binding in the codebehind. You could do it in the xaml as well though:
public override Style SelectStyle(object item, DependencyObject container)
{
ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(container);
MyItem selectedItem = (MyItem)item;
Style s = new Style();
var listMenuItems = new List<MenuItem>();
var mi = new MenuItem();
mi.Header= "Get Metadata";
mi.Name= "cmMetadata";
mi.Command = CustomCommands.DisplayMetadata;
mi.CommandParameter = selectedItem;
listMenuItems.Add(mi);
ContextMenu cm = new ContextMenu();
cm.ItemsSource = listMenuItems;
// Global styles
s.Setters.Add(new Setter(Control.ContextMenuProperty, cm));
// other style selection code
return s;
}
I like the feel of this solution much better than attempting to set a field on mouse click and try to access what was clicked that way.

listview.selectionchanged, can I make it fire everytime I click an item?

Is there a way to make the selectionchanged event fire every time a selection in the listview is clicked, instead of only when it changes?
For example, lets say i have a listview with only one object in it. The user clicks that object, and that object contains information that populates some textboxes below. The user starts changing some of the values in these textboxes (which are not bound to the object). They then decide that they dont want what is in those text boxes so they'd like to reset everything to what is in the object in the listview. But when they click the one object in the listview, nothing happens, because the selection has not changed.
Hope that makes sense. Anyone know how I can get around this?
The ListView.SelectionChanged and ListViewItem.Selected events are not going to re-fire if the item is already selected. If you need to re-fire it, you could 'deselect' the item when the event fires.
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (var item in e.AddedItems.OfType<ListViewItem>())
{
Trace.WriteLine("ListViewItem Selected");
item.IsSelected = false;
}
}
Thus allowing you to re-select it ad nauseum. However, if you don't need the actual selection then you should be using an ItemsControl.
If you do want to maintain the select-ability of the item(s) then you should look at registering to a different event than ListView.SelectionChanged, or ListView.Selected. One that works well for this is PreviewMouseDown, as like the initial item selection we want it to occur on both left and right clicks. We could attach it to the single ListViewItem, but since the list may at some point gain more items, we can assign it to all items by using the ItemContainerStyle property of the ListView.
<ListView SelectionChanged="ListView_SelectionChanged">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="PreviewMouseDown"
Handler="ListViewItem_PreviewMouseDown" />
</Style>
</ListView.ItemContainerStyle>
<ListViewItem>Item 1</ListViewItem>
<ListViewItem>Item 2</ListViewItem>
<ListViewItem>Item 3</ListViewItem>
<ListViewItem>Item 4</ListViewItem>
</ListView>
private void ListViewItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Trace.WriteLine("ListViewItem Clicked: " + (sender as ListViewItem).Content);
}
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(ListView.SelectedIndex != -1)
{
//to do staff
}
ListView.SelectedIndex = -1;
}
also we can use this one!
<ListView x:Name="ListView"
Height="Auto" SelectionChanged="ListView_OnSelectionChanged"
Width="260"
Margin="0,-12,0,-25">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding name_to_show_menu,Mode=TwoWay}" Tapped="UIElement_OnTapped"></TextBlock>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
and in code behind
private void UIElement_OnTapped(object sender, TappedRoutedEventArgs e)
{
//this fire every time
}

Categories

Resources