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.
}
Related
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;
};
});
}
I have a toolstrip that will contain toolstripdropdownbuttons that represent folders that are found in a directory. I would like a dropdown menu to each toolstripdropdownbutton to contain subfolders that are found. An example of this would be the Internet Explorer Links bar I have tried the following code but I'm not quite sure how to go about it (see picture)
Links Bar Example
Code I have tried:
private void populateLinks()
{
linksToolStrip.Items.Clear();
DirectoryInfo linksFolder = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Favorites) + "\\Links");
foreach (DirectoryInfo linksDirectory in linksFolder.GetDirectories())
{
Image favImage = Properties.Resources.Folder;
ToolStripDropDownButton button = new ToolStripDropDownButton();
button.Text = Truncate(linksDirectory.Name, 22);
button.ToolTipText = linksDirectory.Name + "\nDate Created: " + Directory.GetCreationTime(linksDirectory.FullName);
button.Image = favImage;
button.Tag = linksDirectory.FullName;
linksToolStrip.Items.Add(button);
populateLinksFolders(linksDirectory, button.DropDown);
}
and
private void populateLinksFolders(DirectoryInfo subdirectory, ToolStripDropDown tsdd)
{
foreach (DirectoryInfo directory in subdirectory.GetDirectories())
{
populateLinksFolders(directory, ?) //<- Everything tried here fails
}
}
How can I accomplish this?
You need to pass the ToolStripDropDownButton, then use the DropDownItems property to add subitems.
populateLinksFolders(linksDirectory, button);
Then:
private void populateLinksFolders(DirectoryInfo subdirectory, ToolStripDropDownButton tsddb)
{
foreach (DirectoryInfo directory in subdirectory.GetDirectories())
{
ToolStripDropDownButton button = new ToolStripDropDownButton(directory.Name);
tsddb.DropDownItems.Add(button);
populateLinksFolders(directory, button);
}
}
Take a look on the code ,i hope you will understand what I'm trying to do:
private void btnOpen_Click(object sender, EventArgs e)
{
string[] Folders = Directory.GetDirectories(txtFolder.Text);
string foldername;
int count=0;
foreach (string f in Folders)
{
foldername = Path.GetDirectoryName(f);
Label newlabe = new Label();
newlabe.Location = new Point(12, 58);
newlabe.Text = foldername;
count++;
}
}
as you can see i insert a Directory Path into text box , then I opened an array which contains the sub Directories,the next step is to open labels which contains the sub directories names from the directory i insert to the text box, that's not working , what should i do ?
Use some kind of container and stack/add the labels into it. You don't need to assign a location to the label, as the container (depending on the container layout algorithm) will layout them for you.
I dont know whether you use WinForms or WPF or something else, so I will not write any sample code.
But here is some pseudocode:
create a container and add it to the form
for each folder
create a label for the folder
add the label to the container
by the way, have you tried a TreeView control?
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?
I've been having a problem with the following code:
namespace Viewer
{
public partial class Form1 : Form
{
int count = 0;
LinkLabel[] linkLabel = new LinkLabel[200];
string filename;
string extension;
string filepath;
private void btnLoad_Click(object sender, EventArgs e)
{
// Creates a Directory for the Movies Folder
DirectoryInfo myDirectory = new DirectoryInfo(#"C:\Users\User\Movies");
// Creates a list of "File info" objects
List<FileInfo> ls = new List<FileInfo>();
// Adds filetypes to the list
ls.AddRange(myDirectory.GetFiles("*.mp4"));
ls.AddRange(myDirectory.GetFiles("*.avi"));
// Orders the list by Name
List<FileInfo> orderedList = ls.OrderBy(x => x.Name).ToList();
// Loop through file list to act on each item
foreach (FileInfo filFile in orderedList)
{
// Creates a new link label
linkLabel[count] = new LinkLabel();
// Alters name info for display and file calling
filepath = filFile.FullName;
extension = filFile.Extension;
filename = filFile.Name.Remove(filFile.Name.Length - extension.Length);
// Write to the textbox for functional display
textBox1.AppendText(filename + "\r\n");
// Alters link label settings
linkLabel[count].Text = filename;
linkLabel[count].Links.Add(0, linkLabel[count].Text.ToString().Length, filepath);
linkLabel[count].LinkClicked += new LinkLabelLinkClickedEventHandler(LinkedLabelClicked);
// Adds link label to table display
tblDisplay.Controls.Add(linkLabel[count]);
// Indexes count up for arrays
count = count + 1;
}
}
private void LinkedLabelClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
System.Diagnostics.Process.Start(filepath);
}
}
}
My goal is to generate a table of links to all of the media files that I add at launch, and have the links open the files in their respective players.
As of right now, it generates all of the links properly, but whenever I click on any of them, it launches the last item in the list.
For example, if the list contains "300", "Gladiator", and "Top Gun", no matter which link I click, it opens "Top Gun".
I assume that this has to do with it calling the variable "filepath" in the click event, which is left in it's final state. However, I'm not exactly clear on how to create a static link value or action on each individual link, as all of the answers I've researched are in regards to single linklabel situations, not dynamic set-ups.
Any help/advice would be appreciated!
Try as below:
In foreach loop add one line more like:
linkLabel[count].Tag = filepath;
then in click event get this path as blow,
private void LinkedLabelClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
string filepath = ((LinkLabel)sender).Tag.tostring();
System.Diagnostics.Process.Start(filepath);
}