which event get fired when sub-menu items open - c#

I have designed a simple xaml which has a Menu called "File" in menubar. And sub-menu items are "New", "Open", "Save". Which event get fired when menu will expand / or open sub-menu popup windows? Please help me.

There is an event called SubmenuOpened https://msdn.microsoft.com/en-us/library/system.windows.controls.menuitem.submenuopened(v=vs.110).aspx. Refer below code.
<Menu>
<MenuItem Header="File" SubmenuOpened="MenuItem_SubmenuOpened">
<MenuItem Header="Open" />
<MenuItem Header="Save" />
</MenuItem>
</Menu>
private void MenuItem_SubmenuOpened(object sender, RoutedEventArgs e)
{
}

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.

Get if something can be copied in my application

In WPF I can define a Command for copying content like this:
<MenuItem Header="Copy" Command="ApplicationCommands.Copy" >
Works great and the menu item is only enabled if there is something to copy.
I want to make another MenuItem that is also only enabled if there is something to copy in my app. How can I copy that behavior?
If I'm reading your question correctly, then the two menu items have the same criteria for if they are enabled. If that is the case, I think you could just bind your second menu item's IsEnabled property to the first menu item's IsEnabled property.
(Just confirmed that this works for me).
As an example:
<MenuItem x:Name="CopyItem" Header="Copy" Command="ApplicationCommands.Copy"/>
<MenuItem Header="Something Else" IsEnabled="{Binding ElementName=CopyItem, Path=IsEnabled}"/>
You can use the same command "Copy", and distinguish between two different menu items by CommandParameter:
<MenuItem Header="Copy" Command="ApplicationCommands.Copy" CommandParameter="PlainCopy">
<MenuItem Header="TrickyCopy" Command="ApplicationCommands.Copy" CommandParameter="TrickyCopy">
Then in Command handler check parameter:
private void OnCopy(object sender, ExecutedRoutedEventArgs e)
{
if (e.Parameter == 'PlainCopy')
{
...
}
else
{
...
}
}

Generic Context Menu for multiple controls in MVVM

I have a WPF window with 4 read-only TextBoxes in it, to all of which I need to enable a context menu with copy option. Currently I'm doing with code behind. But I heard it is not a good approach.
<TextBox Name="StepsTextBox"
Text="{Binding Steps, Mode=OneWay}"
IsReadOnly="True"
Click="Copy_click"/>
Code-Behind:
private void Copy_click(object sender, RoutedEventArgs e)
{
StepsTextBox.Copy();
}
I tried using MVVM as follows:
<TextBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy" Command="{Binding OnCopyButtonClick}" CommandParameter="{Binding ElementName=StepsTextBox}"/>
</ContextMenu>
</TextBox.ContextMenu>
But how do I access this text box from the code if I pass it as parameter. And also how can I keep this code generic for all the textboxes?. Could anyone help?. Thanks in advance.
private void OnCopyButtonClick(TextBox textBox)
{
//??
}
You can use the build-in ApplicationCommands.Copy. No need to implement anything, the copy functionality is already implemented.
<TextBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy" Command="Copy" />
</ContextMenu>
</TextBox.ContextMenu>
You will still have to select the text before copying it, but that's to be expected when copying text.

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
}
}

Categories

Resources