WPF ContextMenu disappears when clicked - c#

I have a WPF application and I added a ContextMenu for a grid. The user makes a selection on the grid and thereafter; a ContextMenu appears. I have some textboxes on the ContextMenu where the user can enter some values but if the user clicks the ContextMenu itself (and not inside the textbox) the dialog disappears. I want to prevent this and I tried to get some event that dictate when the ContextMenu has been clicked.
private void CreateContextMenu()
{
detectionInfoContextMenu = new ContextMenu(); //create an instance of the class
//selectionBoxCanvas.ContextMenu = detectionInfoContextMenu;
playVideoGrid.ContextMenu = detectionInfoContextMenu;
detectionInfoContextMenu.MouseDown += detectionInfoContextMenu_MouseDown;
}
void detectionInfoContextMenu_MouseDown(object sender, MouseButtonEventArgs e)
{
if(e.LeftButton == MouseButtonState.Pressed)
MessageBox.Show("You clicked me!");
}
I am trying to get the Mouse button events to determine if the Left mouse button was clicked. This seems to work very well on other control e.g. canvas etc but does not seem to work here on the ContextMenu. Am I using the wrong event?

You can use StaysOpenOnClick to prevent the closing of the menu:
<MenuItem StaysOpenOnClick="True">Test</MenuItem>
Or you use a Popup as Ivan Zub suggested in the comments.
And here an example with a TextBox inside the menu:
<ContextMenu>
<MenuItem StaysOpenOnClick="True">
<MenuItem.Header>
<TextBox Width="100" />
</MenuItem.Header>
</MenuItem>
</ContextMenu>

Related

Using ContextMenu in TreeView

There is an application I want to make using C#. This is part of it's UI:
The white area below the menu bar represents a TreeView. I want the Tree_View menu to appear everywhere in the white area when right-clicking with the mouse.
This is the XAML code for the Tree_View object (mind the foo function there):
<MenuItem Name="Menu_Tree" Header="_Tree_View">
<MenuItem Header="_New_Scene" IsCheckable="false" Click = foo/>
<MenuItem Header="_Copy_This_Scene" IsCheckable="false"/>
<MenuItem Header="_Remove_This_Scene" IsCheckable="false"/>
<Separator />
<MenuItem Header="_New_Shot" IsCheckable="false"/>
<MenuItem Header="_Copy_This_Shot" IsCheckable="false"/>
<MenuItem Header="_Remove_This_Shot" IsCheckable="false"/>
<Separator />
<MenuItem Header="_Move_This_Shot_Up" IsCheckable="false"/>
<MenuItem Header="_Move_This_Shot_Down" IsCheckable="false"/>
</MenuItem>
and this is the XAML code for the Tree_View object:
<Grid Name="TreeHolder" Column="0" Margin="20,10,10,10" Background="DimGray">
<TreeView Name="myTree" MouseRightButtonDown="something" ToolTip="Right Click to Add or Remove Scenes and Scots.">
</TreeView>
</Grid>
This is the something function which should be triggered by right-click:
private void something(object sender, MouseButtonEventArgs e)
{
ContextMenu cm = new ContextMenu();
//cm.Items.Add("Add a New Scene ?", ... );
Menu_Tree.ContextMenu = cm;
}
Since nothing works there, I want to ask the following:
a) How to make the Tree_view menu items appear in the Tree_View object white area as well?
b) If so, how will I make it an enabled menu, for example being able to trigger the foo function?
(In other worlds, make an exact copy of the menu list, make it visible with right click and make it work as well)
I strongly believe that it has to do with the ContextMenu which I am not able to use fairly, so any help would be highly appreciated.
The items to add are MenuItems. You can create each item as you would with any other object, set its Header property and assign a Click event handler. The sender is the TreeView itself.
private void something(object sender, MouseButtonEventArgs e)
{
var cm = new ContextMenu();
var newSceneMenuItem = new MenuItem { Header = "_New_Scene" };
newSceneMenuItem.Click += OnNewSceneClick;
cm.Items.Add(newSceneMenuItem);
var treeView = (TreeView) sender;
treeView.ContextMenu = cm;
}
private void OnNewSceneClick(object sender, RoutedEventArgs e)
{
MessageBox.Show("I am a Message Box.", "New Scene clicked");
}
However, you can do this much easier in XAML without the right click handler.
<Grid Name="TreeHolder" Column="0" Margin="20,10,10,10" Background="DimGray">
<TreeView Name="myTree" ToolTip="Right Click to Add or Remove Scenes and Scots.">
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Header="_New_Scene" Click="foo"/>
<MenuItem Header="_Copy_This_Scene"/>
<MenuItem Header="_Remove_This_Scene"/>
<Separator />
<MenuItem Header="_New_Shot"/>
<MenuItem Header="_Copy_This_Shot"/>
<MenuItem Header="_Remove_This_Shot"/>
<Separator />
<MenuItem Header="_Move_This_Shot_Up"/>
<MenuItem Header="_Move_This_Shot_Down"/>
</ContextMenu>
</TreeView.ContextMenu>
</TreeView>
</Grid>
You do not need to set the IsCheckable to false, that is already the default value. Instead of assigning a Click event handler you could use commands, but I guess this would be a too advanced topic for you question, since it does not look like you are employing the MVVM pattern.

WPF Button Command for right mouse button?

I am learning about MVVM and Commands in WPF. I have a couple of buttons and I want to trigger similar commands depending on the fact if the buttons are clicked with the left or right mouse button.
Until now I used Event Handlers and I was able to determine which mouse button was pressed by checking the MouseButtonEventArgs.
<Button Content="Command Test" PreviewMouseDown="Button_PreviewMouseDown"/>
Current code behind:
private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
if (e.LeftButton == MouseButtonState.Pressed) {
Debug.Print("Left");
}
else {
Debug.Print("Right");
}
}
But I don’t see anything similar if I use Commands.
How can I set different commands for a button? One command when the left mouse button is clicked and another command when the right mouse button is clicked?
<Button Content="Command Test" Command="{Binding PressLetterCommand, Mode=OneWay}"/>
Currently the Command only fires when the left mouse button is clicked. If the command would also be fired if the right mouse button is clicked and I can find out which button was clicked that would be also a solution.
I searched and I found this question and answer which uses Decorator. How do I bind a command to e.g. the right mouse button in a ControlTemplate in WPF? I tried it but as far as I understand this won’t work for my buttons.
Any suggestions?
Try this
<Button Content="Command Test">
<Button.InputBindings>
<MouseBinding Gesture="RightClick" Command="{Binding PressLetterCommand}" />
</Button.InputBindings>
</Button>

How can I change the text in a WPF menu item from the .cs file?

I'm learning WPF (this is my second day), and I'm trying to make a play/pause toggle menu item. The program starts paused, with the menu item reading "Start"; when I click "Start", I'd like the menu item to change to "Pause".
<MenuItem Header="_Server">
<MenuItem Header="Start" Click="ToggleRunningStatus" Name="toggleRunningMenuItem" />
</MenuItem>
I was hoping it'd be as simple as toggleRunningMenuItem.SetText("Pause"); but that doesn't seem to be the case. Thanks for helping me out!
You have the header in your XAML set to "Start".
You can access that same property from code-behind:
toggleRunningMenuItem.Header = "Pause";
You don't even need to set a name for the menu:
private void ToggleRunningStatus(object sender, RoutedEventArgs e)
{
var menuItem = (MenuItem)e.OriginalSource;
menuItem.Header = "Pause";
}
Try changing the Name attribute to x:Name so:
<MenuItem Header="Start" Click="ToggleRunningStatus" x:Name="toggleRunningMenuItem" />

How to bubble events from MenuItems

In this application I placed a Menu inside a Usercontrol.
<UserControl>
<Grid>
<Menu x:Name="mainMenu">
<MenuItem Header="File">
<MenuItem Header="Open Analysis">
<MenuItem Header="Load all" />
<MenuItem Header="Select from tracks" />
</MenuItem>
</MenuItem>
</Menu>
</Grid>
</UserControl>
I would like to route and bubble all "click" events on the menu to a single Click event in the Usercontrol.
So I implemented:
public static readonly RoutedEvent ClickEvent = eventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MenuItem));
public event RoutedEventHandler Click
{
add { AddHandler(ClickEvent, value); }
remove { RemoveHandler(ClickEvent, value); }
}
But that fails on the exception:
{"RoutedEvent Name 'Click' for OwnerType 'System.Windows.Controls.MenuItem'
already used."}
I suppose this is the event is registered multiple time's since there are multiple MenuItems, so it fails the second time it wants to register.
But how can I "bundle" (like a Singular handler attachment point) all those events into one single event in the usercontrol in a good mainainanble way? Since some submenus are fed by the Database thus are dynamically build-up.
In addition to what's already been said, I would look into a simple messaging solution.
Each time you click on any menu item, you can broadcast a message of type MenuItemClickedMessage.
public class MenuItemClickedMessage
{
public MenuItem MenuItemClicked { get; set; }
}
You can then have a single component that listens to that event. Once it receives the event, you can extract the menu item from MenuItemClicked property and react accordingly.
This will require some research into simple messaging solutions: http://rachel53461.wordpress.com/2011/05/28/switching-between-viewsusercontrols-using-mvvm/
Contrary to intuition, the error you are receiving is not due to multiple menu items, but due to the Click event already existing. The event registration is static, and it is already used by the WPF Framework.
How would you solve this?
You can create a style for your menu; styles can contain EventSetters in WPF. This allows you to register a click handler for any menu item in your container:
<Grid>
<Grid.Resources>
<Style TargetType="MenuItem">
<EventSetter Event="Click" Handler="OnMenuItemClick" />
</Style>
</Grid.Resources>
<Menu x:Name="mainMenu">
...
</Menu>
</Grid>
Then in your handler make sure to set the Handled property to true, to stop bubbling:
private void OnMenuItemClick(object sender, RoutedEventArgs e)
{
e.Handled = true;
}
Update
You should consider not firing the 'Click' event on the UserControl for this, since it could be a design issue: when someone uses your UserControl and the Click event is fired on the user control, you expect a 'Click', not a click on a menu item inside the control.
Consider using a custom event name instead, something like MenuItemClick, where you can supply the identifier of the menu item in the event args.
Although I would strongly suggest not to do this, but instead declare a distinct new event as Bas has pointed out, you could simply reuse the existing RoutedEvent from class MenuItem:
public partial class YourUserControl : UserControl
{
...
public event RoutedEventHandler Click
{
add { AddHandler(MenuItem.ClickEvent, value); }
remove { RemoveHandler(MenuItem.ClickEvent, value); }
}
}
MenuItemClick - is a routed event, and use RoutedEventArgs.OriginalSource, rather than sender. This points to the control that originally fired the event.
<Grid>
<Menu x:Name="mainMenu">
<MenuItem Header="File" Click="OnFileMenuItemClicked">
<MenuItem Header="Open Analysis">
<MenuItem Header="Load all" />
<MenuItem Header="Select from tracks" />
</MenuItem>
</MenuItem>
</Menu>
</Grid>
private void OnFileMenuItemClicked(object sender, RoutedEventArgs e)
{
MenuItem item = e.OriginalSource as MenuItem;
if(null != item)
{
// Handle the menuItem click here
}
}

Alterning default Menu(Item) mouse reactions

I am making a customized menu, with customized menu Items. Changing control templates and styles have been oke, but now I would like to change the way mouse events effect the menuItems visibility.
By default, when you click a MenuItem within the pop up of a parent menu item, the pop up will collapse. When the mouse leaves the pop up stays open. I would like to reverse this, so that the pop up would only collapse when the mouse leaves, and that a sub menu Item can be clicked multiple times sequentially.
Is this possible ? and ifso how could I be able to do this ?
Any information providing me with more insight, or leading to a solution is welcome!
Thanks
Use StaysOpenOnClick property on menuitems to keep the menu open on click.
A menu will close when it looses focus which is fine for me. If you want to automatically close the menu when the mouse is no longer on it you need to do this in code behind. Below code does this for the main menu by checking when the mouse is outside the area of the context menu. It only works for a menu with no submenu. If you want to use submenus you need to figure out when the mouse is over a sub menu and when not.
<Window x:Class="MenuTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<Grid Background="Red">
<Grid.ContextMenu>
<ContextMenu MouseMove="ContextMenu_MouseMove">
<MenuItem Header="Menu Item" StaysOpenOnClick="True" />
<MenuItem Header="Menu Item" StaysOpenOnClick="True" />
<MenuItem Header="SubMenu">
<MenuItem Header="Menu Item" StaysOpenOnClick="True" />
</MenuItem>
</ContextMenu>
</Grid.ContextMenu>
</Grid>
</Window>
Event handler:
private void ContextMenu_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
ContextMenu contextMenu = sender as ContextMenu;
Point p = e.GetPosition(contextMenu);
contextMenu.IsOpen = p.X >= 0 && p.X <= contextMenu.ActualWidth && p.Y >= 0 && p.Y <= contextMenu.ActualHeight;
}

Categories

Resources