I have listbox with context menu, when i press "Application Key" its not showing up context menu, it has to work like windows.
<ListBox.ContextMenu>
<ContextMenu Name="ZoneIformationList" StaysOpen="true" Background="WhiteSmoke">
<ContextMenu.BitmapEffect>
<BitmapEffectGroup/>
</ContextMenu.BitmapEffect>
<MenuItem Header="Edit" Name="EditNutritionContextMenu" />
<MenuItem Header="Remove" Name="RemoveNutritionContextMenu" />
</ContextMenu>
</ListBox.ContextMenu>
You have a couple of choices on how to achieve your goal. Either you can create a Command that opens the ContextMenu, then add a KeyBinding with the command and a KeyGesture of ALT + CTRL to the InputBindings of your main window.
You can find out more about the KeyBinding class from the KeyBinding Class page and the KeyGesture class from the KeyGesture Class page at MSDN.
The other option is to add a handler for the PreviewMouseDown (or similar) event and monitor whether the ALT and CTRL keys are being pressed, then programmatically launch the ContextMenu control if they are:
public void ListBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt &&
(e.KeyboardDevice.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
{
// Open ContextMenu
}
}
Now all that is left is to open the ContextMenu from code:
if (element.ContextMenu != null )
{
element.ContextMenu.PlacementTarget = element;
element.ContextMenu.IsOpen = true;
}
If the ContextMenu is not placed correctly, you can set the placement using the ContextMenu.HorizontalOffset and ContextMenu.HorizontalOffset properties.
Related
I'm trying to get a "Copy" context menu to display when clicking on the advanced options icon of any property. What do I need to add to make it work?
<xctk:PropertyGrid
x:Name="PropertyGrid"
Grid.Column="1" Margin="8"
ShowSummary="False"
AutoGenerateProperties="True"
HideInheritedProperties="False"
SelectedObject="{Binding InspectedObject}"
SelectedObjectName="{Binding InspectedObject, Converter={StaticResource PropertyGridPropertyNameConverter}}"
SelectedObjectTypeName="{Binding InspectedObject, Converter={StaticResource PropertyGridPropertyTypeConverter}}"
SelectedObjectChanged="PropertyGrid_OnSelectedObjectChanged"
ShowAdvancedOptions="True"
>
<xctk:PropertyGrid.AdvancedOptionsMenu >
<ContextMenu>
<MenuItem Header="Copy" Click="MenuItem_OnClick"></MenuItem>
</ContextMenu>
</xctk:PropertyGrid.AdvancedOptionsMenu>
</xctk:PropertyGrid>
I'd like the "Copy" context item to display and take a click whether or not the property is read only.
In the MenuItem click event handler, you can access property data by property DataContext in sender object.
private void MenuItem_OnClick(object sender, RoutedEventArgs e)
{
MenuItem menuItem = sender as MenuItem;
if (menuItem != null && menuItem.DataContext is PropertyItem)
{
Clipboard.SetData(DataFormats.Text, ((PropertyItem)menuItem.DataContext).Value);
}
}
In the following link, you can find more information about this topic:
https://kmatyaszek.github.io/2018/08/22/extended-wpftoolkit-propertygrid-copybutton.html
I have noticed that this solution works when you clicked (left mouse button) at AdvancedOptionsMenu icon, there is a problem with the right mouse click on the property item. To solve this problem I forked the original repo and I have added copy value menu item to AdvancedOptionsMenu. Check it out: https://github.com/kmatyaszek/wpftoolkit
I have a wrap panel that is populated with Image controls at run time. I want to use a context menu to remove images I want to delete.
<toolkit:WrapPanel x:Name="wrap_Panel">
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu IsZoomEnabled="false" x:Name="ContextMenu" >
<toolkit:MenuItem x:Name="Delete" Header="Delete" Click="DeleteImage"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</toolkit:WrapPanel>
This allows me to get the menu up but how do I actually delete the image I pressed?
private void DeleteImage( object sender, RoutedEventArgs e )
{
MenuItem menuItem = sender as MenuItem;
if ( menuItem != null )
{
Image imageItem = menuItem.DataContext as Image;
...
find the imageItem in your collection or list and delete it.
I have the following MenuItem inside of a Context Menu in my WPF application:
<MenuItem Header="Email">
<MenuItem Command="Commands:CommandRepository.GenerateUserEmailCommand"
CommandParameter="{Binding Path=SelectedItems}"
Header="Email User">
</MenuItem>
<MenuItem Command="Commands:CommandRepository.GenerateManagerEmailCommand"
CommandParameter="{Binding Path=SelectedItems}"
Header="Email Manager">
</MenuItem>
</MenuItem>
The issue is that when both of the Email commands return CanExecute = false, and therefore both commands get disabled, the top-level MenuItem "Email" remains enabled. I know I could probably bind the IsEnabled of the top menu item to its Children property and then use a converter to decide when it should be disabled, but it seems like this should be happening automatically. Isn't this the whole point of using CommandBindings (i.e. they take care of IsEnabled for you)? Any better way to accomplish this?
I can think of two ways to do this:
1) If you don't mind code behind (I try to avoid it with WPF) you could just create an event handler for IsEnabledChanged that checks if the IsEnabled Property was set to false. If it was you could then do something like
ParentMenuItem.IsEnabled = ParentMenuItem.Items.Count( c => c is MenuItem && (c as MenuItem).IsEnabled == true) > 0
2) Create a ViewModel for the Parent MenuItem and Child MenuItem types and bind the is Enabled of the Parent MenuItem View to a IsEnabled property of the view model. The view model would return false using a similar
this.Children.Count(c => c.IsEnabled == true) > 0
why should your "Email" MenuItem become disabled? just because the childrens are?
i think your approach to use multibinding and a converter is a good way to do what YOU want.
Define a RootMenu command then add it to the root menu
Header="Email" Command="Commands:CommandRepository.RootMenu" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=.}"
Bind the command to the following RootMenuCanExecute
public static void DropDownMenuCanExecute(object sender, CanExecuteRoutedEventArgs e) {
e.CanExecute = false;
ItemsControl parent = e.Parameter as ItemsControl;
if (parent.Items.Count == 0) { return; }
foreach (var i in parent.Items) {
ICommandSource cs = i as ICommandSource; if (cs==null) { continue; }
if (cs.Command == null) { continue; }
if (cs.Command.CanExecute(cs.CommandParameter)) { e.CanExecute = true; return; }
}
}
it is somewhat cpu expensive but it works.
Whatch out not to have too many MenuItem children
I have a ListView with ContextMenu on each ListViewItem that has Click event,
how can I detect in the event handler which Item was clicked in this ContextMenu?
I need the item ID.
<Style TargetType="{x:Type ListViewItem}">
.
.
.
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="tv:TreeListViewItem">
<Grid>
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Header="Open in current tab" Click="MenuItemCurrentTab_Click"/>
<MenuItem Header="Open in new tab" Click="MenuItemNewTab_Click"/>
</ContextMenu>
</Grid.ContextMenu>
See this thread..
Following the same way as the answer from the link you would
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Header="Open in current tab"
Click="MenuItemCurrentTab_Click"
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Parent}"/>
...
private void MenuItemCurrentTab_Click(object sender, RoutedEventArgs e)
{
MenuItem menuItem = sender as MenuItem;
if (menuItem != null)
{
ContextMenu parentContextMenu = menuItem.CommandParameter as ContextMenu;
if (parentContextMenu != null)
{
ListViewItem listViewItem = parentContextMenu.PlacementTarget as ListViewItem;
}
}
}
UPDATE
Add this to get the parent ListViewItem from the Grid
public T GetVisualParent<T>(object childObject) where T : Visual
{
DependencyObject child = childObject as DependencyObject;
while ((child != null) && !(child is T))
{
child = VisualTreeHelper.GetParent(child);
}
return child as T;
}
private void MenuItemCurrentTab_Click(object sender, RoutedEventArgs e)
{
MenuItem menuItem = sender as MenuItem;
if (menuItem != null)
{
ContextMenu parentContextMenu = menuItem.CommandParameter as ContextMenu;
if (parentContextMenu != null)
{
Grid grid = parentContextMenu.PlacementTarget as Grid;
ListViewItem listViewItem = GetVisualParent<ListViewItem>(grid);
}
}
}
private void MenuItemCurrentTab_Click(object sender, RoutedEventArgs e)
{
MenuItem menuItem = (MenuItem)e.Source;
ContextMenu menu = (ContextMenu)menuItem.Parent;
ListViewItem item = (ListViewItem)menu.PlacementTarget;
// do something with item
}
But it's probably better idea to create single ContextMenu, give it proper name, and use it for all list view items.
A recurring problem, with many attempts to solve but all have their drawbacks. The accepted answer here, for instance, supposes that each ListViewItem has its own ContextMenu. This works but, especially with a larger number of list items, has a considerable cost in XAML complexity and can be slow. And really isn't necessary at all. If we only use a single ContextMenu on the ListView itself, some other solutions suggest to use
<MenuItem CommandParameter="{Binding PlacementTarget.SelectedItem, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
which seems to solve the problem at first sight (PlacementTarget points to the ListView, its SelectedItem points to the list item, so the menu item handler can use the CommandParameter to get the originating list item), but, unfortunately, fails if the ListView has multiple selection enabled (SelectedItem will point to one of the items selected but not necessarily the one currently clicked) or if we use ListView.PreviewMouseRightButtonDown to disable the selection on right-click (which is, arguably, the only logical thing to do with multiple selections).
There is, however, an approach that has all the benefits:
single ContextMenu on the ListView itself;
works with all selection schemes, single, multiple, disabled;
even with multiple selection, it will pass the currently hovered item to the handler.
Consider this ListView:
<ListView ContextMenuOpening="ListView_ContextMenuOpening">
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Menu1" Click="Menu1_Click" CommandParameter="{Binding Parent, RelativeSource={RelativeSource Self}}" />
</ContextMenu>
</ListView.ContextMenu>
</ListView>
The CommandParameter is used to pass the parent of the MenuItem, ie. the ContextMenu itself. But the main trick comes in the menu opening handler:
private void ListView_ContextMenuOpening(object sender, ContextMenuEventArgs e) {
var menu = (e.Source as FrameworkElement).ContextMenu;
menu.Tag = (FrameworkElement)e.OriginalSource;
}
Inside this handler, we still know the original source of the event, the root FrameworkElement of the list item DataTemplate. Let's store it in the Tag of the menu for later retrieval.
private void Menu1_Click(object sender, RoutedEventArgs e) {
if (sender is MenuItem menu)
if (menu.CommandParameter is ContextMenu context)
if (context.Tag is FrameworkElement item)
if (item.DataContext is DataType data) {
//process data
}
}
In the menu click handler, we can look up the original ContextMenu we stored in the command parameter, from that we can look up the root FrameworkElement of the list item that we stored just before, and finally get the object stored in the list item (of type DataType).
ListViewItem item = myListView.SelectedItem as ListViewItem;
Seems to work just fine as the item is selected when you right-click it.
I have a ListView in my WPF UserControl using an ItemTemplate to display the items. Within the template is a button. When I select one item and then click on the button of another item, the previously selected item is still selected. I wonder how to automatically select the item the button is in when the button is clicked.
Xaml
<UserControl.Resources>
<DataTemplate x:Key="ItemTemplate">
<Border>
<Grid>
<!-- lots of stuff go here -->
<Button Click="MyButton_Click">Clickme</Button>
</Grid>
</Border>
</DataTemplate>
</UserControl.Resources>
<ListView x:Name="_listView"
ItemTemplate="{StaticResource ItemTemplate}">
</ListView>
C# Code behind
void MyButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show( string.Format( "clicked on {0}",
this._listView.SelectedItem.ToString() ) ) ;
}
I would do it by getting the data context of the sender object. Assuming your listview is a list of objects of type MyObject... then something like this would allow you to reference the selected object.
void MyButton_Click(object sender, RoutedEventArgs e)
{
Button b = sender as Button;
if (b == null)
{
return;
}
MyObject o = b.DataContext as MyObject;
if (o != null)
{
// Put stuff for my object here
}
}
When you press the button your click / mouse down event is handled by the button and therefore does not route through to the ListView control.
A possible way to solve this is to manually set the listview.SelectedItem in the button click event.