Adding/Removing events based on context - c#

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?

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

C# Winforms Dynamic menu entries

I'm attempting to create a dynamic menu strip in my program. Here's an example of what it looks like right now:
Creating the ToolstripMenuItems dynamically itself is easy. What I plan to do is to check if the current month already exists in the folder paths that my program works with, and if it doesn't then it will create an extra menu strip with the date (for example once we hit August, it should create August 2014, with sub-items "NL" & "PL").
However the part that i am stuck at is how to link functionality to these dynamically created sub-items. As I've been unable to find a way to do this, both the "NL" and "PL" tabs open a specific .TXT file of that specific month (which is created elsewhere in the program). However when I make them dynamically, I can't find a way to make them do this and they just don't have any functionality.
At this point I find myself manually creating new menu items & code every month for this. And I would very much prefer the program to run itself.
Any tips on how to make dynamic menuitems functional?
Added code:
private void CreateMenu()
{
ToolStripMenuItem item = new ToolStripMenuItem();
item.Text = "Logs";
DirectoryInfo dir = new DirectoryInfo(#"Y:\Heineken\Tools\Logs\");
foreach (DirectoryInfo directory in dir.GetDirectories())
{
ToolStripMenuItem dateItem = new ToolStripMenuItem(directory.Name);
ToolStripMenuItem NLMenu = new ToolStripMenuItem("NL"); // <--- This needs to open a specific text file on a network share
ToolStripMenuItem PLMenu = new ToolStripMenuItem("PL"); // <--- This needs to open a specific text file on a network share
dateItem.DropDownItems.Add(NLMenu);
dateItem.DropDownItems.Add(PLMenu);
item.DropDownItems.Add(dateItem);
}
menuToolStripMenuItem.DropDownItems.Add(item);
}
It would help if you would post some code snippet of how you are dynamically creating your menu items. In general, you can link functionality to dynamic menu entries by simply passing a delegate into the ToolStripMenuItem constructor, like so:
var entry = new ToolStripMenuItem("NL", null, delegate
{
//TODO: do something
});
owner.DropDownItems.Add(entry);
This assumes the variable "owner" is your parent menu entry.
Edit: Given the code you supplied, you could do it like this
private void OpenTextFile(string id)
{
//TODO: logic for opening the shared file
}
private void CreateMenu()
{
ToolStripMenuItem item = new ToolStripMenuItem();
item.Text = "Logs";
DirectoryInfo dir = new DirectoryInfo(#"Y:\Heineken\Tools\Logs\");
foreach (DirectoryInfo directory in dir.GetDirectories())
{
ToolStripMenuItem dateItem = new ToolStripMenuItem(directory.Name);
ToolStripMenuItem NLMenu = new ToolStripMenuItem("NL", null, (sender, e) => OpenTextFile("NL"));
ToolStripMenuItem PLMenu = new ToolStripMenuItem("PL", null, (sender, e) => OpenTextFile("PL"));
dateItem.DropDownItems.Add(NLMenu);
dateItem.DropDownItems.Add(PLMenu);
item.DropDownItems.Add(dateItem);
}
menuToolStripMenuItem.DropDownItems.Add(item);
}
You need to hook up the click event handler to the dynamically created ToolstripMenuItems. In the click event handler you can then cast the sender to ToolstripMenuItem and access any properties you may need to work out which text file you need to load.
For example this click handler will give a message box with the text of the menu item:
private void toolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show(((ToolStripMenuItem)sender).Text);
}
To add a new dynamic menu item and link it to the click handler you can do this:
ToolStripMenuItem newItem = new ToolStripMenuItem("Dynamic Option");
newItem.Click += toolStripMenuItem_Click;
existingToolStripMenuItem.DropDownItems.Add(newItem);
Edit after code posted
You can store the file that is supposed to be opened when the menu item is clicked in the Tag property. When the menu item is then clicked you can open the file based on the path in the Tag.
For example, in your CreateMenu method you would need to do something like this:
ToolStripMenuItem NLMenu = new ToolStripMenuItem("NL");
//store the filename here for later
NLMenu.Tag = Path.Combine(dir.FullName, "nl.txt");
//attach the click handler
NLMenu.Click += toolStripMenuItem_Click;
//repeat for PLMenu...
Then in the click handler you can do this:
private void toolStripMenuItem_Click(object sender, EventArgs e)
{
string filename = ((ToolStripMenuItem)sender).Tag;
//do something with the file.
}

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");

TreeView Event keeps firing

I'm using the WPF version of TreeView (System.Windows.Controls.TreeView) and need to assign several events. As these are WPF most of the events are either different or behave in a different way hence my confusion as AfterExpand is not available
For now I need 2 events:
SelectedItemChanged which is at TreeView level
ExpandedEvent which is at TreeViewItem level
So far I have
private void DisplayGetEventTypes(UXEvent.GetEventTypesResp resp, CustomAsyncStateContainer state)
{
navBarControl.Groups.Clear();
if (resp.eventTypeItems != null)
{
UXEvent.EventType[] eventItems = resp.eventTypeItems;
int nodeCount = eventItems.Length;
for (int i = 0; i < nodeCount; i++)
{
UXEvent.TryEvent eventItem = new UXEvent.TryEvent();
eventItem.eventName = eventItems[i].name;
eventItem.eventId = eventItems[i].id;
NavBarGroup group1 = new NavBarGroup();
group1.Header = eventItems[i].name;
group1.Tag = eventItem;
group1.IsExpanded = false;
//Add dummy treeview to fill later if expanded
System.Windows.Controls.TreeView treeview = new System.Windows.Controls.TreeView();
treeview.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(myTreeView_SelectedItemChanged);
AddHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(myTreeView_ItemExpanded));
group1.Content = treeview;
group1.DisplaySource = DisplaySource.Content;
navBarControl.Groups.Add(group1);
}
}
}
and the following two draft event handlers for testing
void myTreeView_SelectedItemChanged(object sender,
RoutedPropertyChangedEventArgs<object> e)
{
// Set the data context of the text block to the selected value.
var node = (ItemsControl)e.OriginalSource;
System.Windows.Forms.MessageBox.Show("Selected!");
}
void myTreeView_ItemExpanded(object sender, RoutedEventArgs e)
{
// Set the data context of the text block to the selected value.
var node = (TreeViewItem)e.OriginalSource;
System.Windows.Forms.MessageBox.Show("Opening! - " + node.Header);
}
The problem I'm having is that myTreeView_ItemExpanded is firing multiple times when any treeviewItem is expanded. I think it fires multiple times due to the event bubbling .
Firstly, can anyone point me to some good resources where I can read up on this behaviour?
Secondly, how can I amend my code to make AddHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(myTreeView_ItemExpanded)); a single event only?
Thank you
I spotted the problem. The line
AddHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(myTreeView_ItemExpanded));
should read
treeview.AddHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(myTreeView_ItemExpanded));
I assume the first line must have added the event handler to all available controls and all fired when the event triggered.

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