How to bind a Command to a MenuItem in C# WPF - c#

I've been reading posts for most of the day and can't find this or figure it out. Pretty much all the questions and answers build the menu in XAML but I'm trying do this in code.
The menu builds fine (there's more to it, just abbreviated here) and enables or disables properly but I can't get the command to execute. There is a button on the menu ribbon that resubmits fine so I know the code on the back end works I just can't seem to figure out how to invoke it from the menu. Any ideas would be nice.
Mouse event to build and display menu from MainView.xaml.cs. There are **'s on the line that needs fixed.
private void MainDataGrid_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
ContextMenu m = new ContextMenu();
MenuItem mi = new MenuItem();
m.Items.Add(new MenuItem());
mi = (MenuItem)m.Items[0];
mi.Header = "Resubmit";
**mi.CommandBindings.Add(new CommandBinding(ApplicationCommands.New, ResubmitCommand));**
mi.IsEnabled = MainViewModel.CurrentSelectedItems.Count > 0;
.
.
.
m.IsOpen = true;
}
The ResubmitCommand from my MainViewModel.cs:
public ICommand ResubmitCommand
{
get
{
return _resubmitCommand ??
(_resubmitCommand = new RelayCommand(
o => Resubmit(),
o => CanResubmit));
}
}
Thanks.

You can just set the menu item's Command property:
mi.Command = ResubmitCommand;
By default, ApplicationCommands.New only supports the Ctrl+N input gesture and is usually bound to the main window. You can bind it to a left-click on the menu item:
mi.CommandBindings.Add(new CommandBinding(ApplicationCommands.New,Resubmit));
var leftClickGesture= new MouseGesture(MouseAction.LeftClick);
mi.InputBindings.Add(new InputBinding(ApplicationCommands.New, leftClickGesture));

The lines:
m.Items.Add(new MenuItem());
mi = (MenuItem)m.Items[0];
should be changed to just:
m.Items.Add(mi);

Related

MenuItem AutoClose = false on dynamically generated Toolstrip Menu

So, following this question, I have been attempting to deal with a way to stop a drop down menu from closing when I click on an item.
In the linked question, one such answer suggested that I set the AutoClose property to false. I did so, and this did achieve what I asked. However, the way I implemented it means that the Drop Down menu is forced open.
Form Code:
public void ToolStripMenuItem_Click(object sender, EventArgs e)
{
ToolStripMenuItem item = sender as ToolStripMenuItem;
if (item != null)
item.Checked = !item.Checked;
item.DropDown.AutoClose = false;
}
I know why this is - the implementation means that there is no way to allow the AutoClose to be set to true. However, since the menuItems are dynamically generated in a different class, I don't have any events or objects to refer to.
This code copies the menu structure from the Main Form, and copies it across to recreate it in the "Profile View" (to set what users can/cannot see).
Controller Code:
private void PopulateProfileView(User_AccessProfilesView view, Menu_View mainMenu)
{
// Disabled Items are not able to be set, becasue they are either always visible for every user,
// or only visible to specific users (Administrator)
List<string> disabledMenuItems = new List<string>();
List<string> disabledSubMenuItems = new List<string>();
bool error = false;
bool subError = false;
_groupDictionary = new Dictionary<string, List<string>>();
// Populate the disallowed Menu Items from the Main Menu,
// and then add the items specific to the Profile View
disabledMenuItems.Add("File");
disabledMenuItems.Add("Administrator");
disabledMenuItems.Add("Help");
disabledMenuItems.Add("Te&rminations");
disabledMenuItems.AddRange(mainMenu.disallowedMenuItems);
// Populate the disallowed Sub Menu Items from the Main Menu,
// and then add the items specific to the Profile View
disabledSubMenuItems.Add("View All");
disabledSubMenuItems.AddRange(mainMenu.disallowedSubItems);
foreach (ToolStripMenuItem item in mainMenu.mainMenuStrip.Items)
{
ToolStripMenuItem menuItem = new ToolStripMenuItem(item.Text);
if (error == false)
{
// Add to the menu bar
view.menuStrip.Items.Add(menuItem);
menuItem.Click += new EventHandler(view.ToolStripMenuItem_Click);
foreach (ToolStripItem dropItem in item.DropDownItems)
{
if (dropItem is ToolStripMenuItem)
{
ToolStripMenuItem menuDropItem = new ToolStripMenuItem(dropItem.Text);
// Same concerns as above with regards to doing a substring check
// to decide if menu items should be excluded or not.
foreach (string s1 in disabledSubMenuItems)
{
if (!menuDropItem.Text.Contains(s1))
{
subError = false;
}
else
{
subError = true;
break;
}
}
if (!subError)
{
menuItem.DropDownItems.Add(menuDropItem);
menuDropItem.Click += new EventHandler(view.ToolStripMenuItem_Click);
}
}
else if (dropItem is ToolStripSeparator)
{ menuItem.DropDownItems.Add(new ToolStripSeparator()); }
}
How do I implement the AutoClose property correctly so that if I click on a menu item, the menu won't close, but if I click on the menu header, or move the mouse away from the menu, or select another menu (either by click or mouse over), the menu does Close?
Apologies if this is a simple issue - I have been out of the game for roughly a year, and have to jump back into this and I am having a little bit of an issue following everything properly.
To solve the problem you can follow these steps:
You should determine which menu items should keep open even after clicking on them. I'll use "keepopen" as value of Tag property for those items that should be kept open after clicking.
For the menu item which contains those items, you need to get DropDown property and and handle its ItemClicked event and in the ItemClicked event, you should check if the item which is clicked is one of those "keepopen" items, then set DropDown.AutoClose of the container menu item to false. For other items, set it to true. It will prevent closing those "keepopen" item when clicking, while let other items close by click.
You should handle CheckedChanged event of those "keepopen" items and set DropDown.AutoClose to true. While using the Click event handler we prevented the items from closing, here we enable the closing again, so if the user click outside of the menu, it will close.
Then this would be the result, look at mouse clicks:
Example
As an example, create an empty form and handle its Load event and use following code. When you click on SubMenu1, SubMenu2 or SubMenu3, they will just get checked or unchecked without closing the menu. But of you click outside the menu or on SubMenu4, it will close the menu.
const string keepopen = "keepopen";
private void Form1_Load(object sender, EventArgs e)
{
var menuStrip = new MenuStrip() { Dock = DockStyle.Top };
this.Controls.Add(menuStrip);
var menu1 = (ToolStripMenuItem)menuStrip.Items.Add("Menu1");
menu1.DropDownItems.Add(new ToolStripMenuItem("Submenu1")
{ Tag = keepopen, CheckOnClick = true });
menu1.DropDownItems.Add(new ToolStripMenuItem("Submenu2")
{ Tag = keepopen, CheckOnClick = true });
menu1.DropDownItems.Add(new ToolStripMenuItem("Submenu3")
{ Tag = keepopen, CheckOnClick = true });
menu1.DropDownItems.Add("-");
menu1.DropDownItems.Add(new ToolStripMenuItem("Submenu4"));
menu1.DropDown.ItemClicked += (obj, args) =>
{
if (args.ClickedItem.Tag == keepopen)
menu1.DropDown.AutoClose = false;
else
menu1.DropDown.AutoClose = true;
};
menu1.DropDownItems.OfType<ToolStripMenuItem>()
.Where(x => x.Tag == keepopen)
.ToList().ForEach(x =>
{
x.CheckedChanged += (obj, args) =>
{
menu1.DropDown.AutoClose = true;
};
});
}

Add key bindings to control specific context menu

I'm unable to get the keyboard shortcuts working on context menu items belonging to specific controls and I was wondering if anyone could help me.
Use case: I'm trying to write an attached behavior that can be applied to various elements in order to provide undo/redo context menu functionalists to those controls. The context menu part is working but the key bindings don't seem to work.
What I've tried so far:
Attempt 1:
var menu = new ContextMenu();
var undoCommand = new DelegateCommand(Undo);
var undoMenuItem = new MenuItem
{
Header = "Undo",
Command = undoCommand,
InputGestureText = "Ctrl+Z",
};
menu.Items.Add(undoMenuItem);
AssociatedObject.ContextMenu = menu; // AssociatedObject is a textbox as an example
Attempt 2:
var menu = new ContextMenu();
var undoCommand = new DelegateCommand(Undo);
var undoMenuItem = new MenuItem
{
Header = "Undo",
Command = undoCommand,
};
undoMenuItem.InputBindings.Add(new InputBinding(undoCommand,
new KeyGesture(Key.Z, ModifierKeys.Control)))
menu.Items.Add(undoMenuItem);
AssociatedObject.ContextMenu = menu; // AssociatedObject is a textbox as an example
Neither of these seem to be working. The context menu is selectible from the UI (right click on a text box for instance and select "Undo") but the keyboard shortcut is not fired.
Is there a way to do this in an attached behavior? I would like to avoid having access to the underlying Window element inside my attached behavior if possible and would like the keys to work within their respective bounds (for instance, only if the text box is focused should Ctrl+Z cause an Undo on that text box).
Many thanks

Adding/Removing events based on context

I am new to C# and WinForms. I have some objects with the right click (context menu) events. However, depending on the context (for example depending on whether the user is in the wizard screen or the main screen), I want to alter what the right click does. I googled a bit and found that I can use += and -= operators but I still could not achieve what I want to do. Given the code below, for example,
Any ideas ?
EDIT: I want the OnClickCard behave differently in different places.
Sammple Code:
public override ContextMenuStrip GetContextMenuStrip(GoView view)
{
if (Selectable)
{
ContextMenuStrip contextMenu = new ContextMenuStrip();
if (!Empty)
{
// this is just for example so not showing the implementation
contextMenu.Items.Add(new ToolStripMenuItem(
"Delete",
null,
new EventHandler(OnClickDelete)));
}
// Empty
else
{
ToolStripMenuItem addCard = new ToolStripMenuItem("Add");
foreach (..some data..)
{
ToolStripMenuItem card = new ToolStripMenuItem(
data,
null,
new EventHandler(OnClickCard));
addCard.DropDownItems.Add(card);
}
}
}
else
{
return null;
}
}
private void OnClickCard(object sender, EventArgs e)
{
ToolStripMenuItem cardItem = (ToolStripMenuItem)sender;
if (cardItem.Text.Contains("ABC"))
{
Common.Forms.FormMMUSettings f = new FormMMUSettings(cardItem.Text,ParentMagazine.NextSite);
f.Show();
}
SetCard(new MagazineCard(2, cardItem.Text));
}
Are you saying you want to change the contents of the context menu depending on the circumstances when it's clicked? If so, the easiest way is to create multiple context menus, and just use an event to set whichever context menu you want.
ContextMenu menu1 = new ContextMenu();
MenuItem menu1Item1 = new MenuItem();
menu1Item1.Header = "Menu 1 Item 1";
menu1Item1.Click += new RoutedEventHandler(menu1Item1Clicked);
menu1.Items.Add(mnu1Item1);
MenuItem menu1Item2 = new MenuItem();
menu1Item2.Header = "Menu 1 Item 2";
menu1Item2.Click += new RoutedEventHandler(menu1Item2Clicked);
menu1.Items.Add(menu1Item2);
ContextMenu menu2 = new ContextMenu();
MenuItem menu2Item1 = new MenuItem();
menu2Item1.Header = "Menu 2 Item 1";
menu2Item1.Click += new RoutedEventHandler(menu2Item1Clicked);
menu2.Items.Add(menu2Item1);
MenuItem menu2Item2 = new MenuItem();
menu2Item2.Header = "Menu 2 Item 2";
menu2Item2.Click += new RoutedEventHandler(menu2Item2Clicked);
menu2.Items.Add(menu2Item2);
public void menu1Item1Clicked(object sender, EventArgs e)
{
}
etc..
Now you can just set whichever menu you need using:
myForm.ContextMenu = menu1;
Hope this helps.
+= and -= should work just fine but I'd suggest you to use a kind of handlers repo where you'd switch which handler to use. This should work like a strategy pattern where different wizard steps would be different strategies.
It would be easier to help you if you'll show us some code and point us which code parts do not work properly.
You seem to suggest that you would like to attach different handlers depending on context:
if(that)
obj.event += HandleThat;
else
obj.event += HandleSomethingElse;
That should work, but you can also do it in one handler:
obj.event += HandleAll;
void HandleAll(object sender, EventArgs arg) {
if(that)
HandleThat();
else
HandleSomethingElse();
}
EDIT: ok, your edited question really meant something else.
First obvious problem is that you add OnClickDelete as a handler, and show the implementation of OnClickCard which will not be called in your example. If it is a typo, then you just need to implement the handler method as you need. What part exactly is not working?

DevExpress DropDownButton Problems

I'm trying to create a DevEx drop down button. Unfortunately, I'm running into two problems I can't figure out:
1) I can't get the popup menu to skin correctly, i.e. it doesn't skin as "Office 2010 Blue". The code I'm using is shown below:
private void InitializeSendToPricingSheetButton()
{
var barManager = new BarManager();
if (barManager.Controller == null) barManager.Controller = new BarAndDockingController();
barManager.Controller.PaintStyleName = "Skin";
barManager.Controller.LookAndFeel.UseDefaultLookAndFeel = false;
barManager.Controller.LookAndFeel.SkinName = "Office 2010 Blue";
barManager.ItemClick += HandleSendToPricingSheetClick;
barManager.Items.AddRange(new[] { new BarButtonItem(barManager, "Foo"), new BarButtonItem(barManager, "Bar"), new BarButtonItem(barManager, "Baz") });
var popupMenu = new PopupMenu { Manager = barManager };
foreach (var barItem in barManager.Items) popupMenu.ItemLinks.Add((BarItem)barItem);
popupMenu.ItemLinks[1].BeginGroup = true;
dropDownButtonSendToPricingSheet.DropDownControl = popupMenu;
}
2) This button is on a form. If the form loses focus (e.g. I click on Firefox), the pop-up menu still remains on-top. It won't go away until clicked.
Any suggestions would be much appreciated. Thanks for helping me deal with DevEx insanity.
I have solution to your second question.
You should add drop down button event handler as below:
dropDownButton1.LostFocus += new EventHandler(HidePopUp);
Handler method should be as below:
private void HidePopUp(object sender,object e)
{
dropDownButton1.HideDropDown();
}
For your second question, you should assign value to the bar manager property as:
BarManager manager = new BarManager();
manager.Form = this; // refers to current form
Find below link for reference
https://www.devexpress.com/Support/Center/Question/Details/Q274641
It is probably simpler to use DefaultLookAndFeel
Add this comp to your form and set the theme you'd like to use.
There is no need to set the theme for individual components.
defaultLookAndFeel1.LookAndFeel.SetSkinStyle("Office 2010 Blue");

Setting ToolStripMenuItem.Visible to true doesn't work

I have a TreeView control for which each node in it I want to share a ContextMenuStrip which has two ToolStripMenuItems ie:
this.BuildTree = new MyApp.MainForm.TreeView();
this.ItemMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
this.DeleteMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ShowLogMenuItem = new System.Windows.Forms.ToolStripMenuItem();
...
this.ItemMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.DeleteMenuItem,
this.ShowLogMenuItem});
So I show and hide these to items according to certain criteria on a right click in a MouseUp event. When both are hidden I hide the ContextMenuStrip itself. Problem is when I hide the ContextMenuStrip it seems the next time I want to show one of the menu items I have to click twice on the node. The strange thing is on the first click to reshow one or both of the the items I have the following code:
ItemMenuStrip.Visible = true;
ShowLogMenuItem.Visible = true;
The two lines above don't seem to do anything ie both remain false in the debugger view after stepping over each line.
I don't think I've got any events on these values being set at least I don't have any events attached.
What am I doing wrong?
I suggest you to set:
this.BuildTree.ContextMenuStrip = this.ItemMenuStrip;
to make the menu automatically open on tree right-click.
Then subscribe ItemMenuStrip.Opening event to change the visibility of items and the contextmenu itself:
void ItemMenuStrip_Opening(object sender, CancelEventArgs e)
{
if (something)
{
e.Cancel = true; // don't show the menu
}
else
{
// show/hide the items...
}
}
If you need to know the current position of the clicked point (e.g. to check if a tree node is clicked), you can use Control.MousePosition property. Note that MousePosition is a point in screen coordinates, so you need to call treeView1.PointToClient(position) to get the tree coordinates e.g. :
private void ItemMenuStrip_Opening(object sender, CancelEventArgs e)
{
var pointClicked = this.BuildTree.PointToClient(Control.MousePosition);
var nodeClicked = this.BuildTree.GetNodeAt(pointClicked);
if (nodeClicked == null)
{
// no tree-node is clicked --> don't show the context menu
e.Cancel = true;
}
else
{
// nodeClicked variable is the clicked node;
// show/hide the context menu items accordingly
}
}
So figured out what was going wrong I was setting Visible on this.ItemMenuStrip rather than the this.BuildTree.ContextMenuStrip.
This seems rather strange to me as I would have thought BuildTree.ContextMenuStrip was just a direct reference to the ItemMenuStrip but apparently not.

Categories

Resources