Dynamically Created Menu Item with attached code? - c#

I'm reading 2 values from an INI File: The Section Name, & The value of the key named "Path"
[Game Name]
Path=C:\Game\Game.exe
What i'm trying to do is create a Context Menu Item with the caption being the Section "Game Name" and have it launch the application from the value of the "Path" key. I haven't worked much with dynamically created controls in the past, so i'm wondering if there's an easy way to create it, and assign a single line of code to it to launch the application.
Is it possible, or is there an easier way of going about it?
foreach (string SecHead in SectionHeader)
{
string[] Entry = myINI.GetEntryNames(SecHead);
if (Entry != null)
{
foreach (string EntName in Entry)
{
ArrayList row = new ArrayList();
row.Add(SecHead);
row.Add(myINI.GetEntryValue(SecHead, EntName));
DGV.Rows.Add(row.ToArray());
string filePath = Convert.ToString(myINI.GetEntryValue(SecHead, EntName));
//Create ContextMenu Entry from Data Above
Icon newIcon = Icon.ExtractAssociatedIcon(filePath);
}
}
}

I'll assume you know how to read the INI values and that they are in String variables. Let's also assume you are doing this in an event handler that supplies mouse coordinates, such as a MouseUp event:
var section = "section";
var gameExe = "c:\\somepath\\mygame.exe";
var menu = new ContextMenu();
var item = new MenuItem(section);
item.Click += (s, args) => { Process.Start(gameExe); };
menu.Items.Add(item);
menu.Show(this, new Point(e.X, e.Y));
Not tested live, but this should give you the general idea.

Related

How to get click event handler names of ToolStripDropDownItem?

I have a old project in Windows Forms which has more than 300 menus with menu click events throughout the MDI form.
Is there any way to get the click event names in strings (e.g. "toolStripMenuItem_Click")?
I tried like this,
foreach (ToolStripMenuItem menu in menuStrip.Items)
{
foreach (ToolStripDropDownItem submenu in menu.DropDownItems)
{
var _events= submenu.GetType()
.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance)
.OrderBy(pi => pi.Name).ToList();
}
}
But it always returns empty. What is correct way to achieve this?
Retrieving event handlers at runtime is not easy especially within the Forms framework where some events have special handling in the background.
A simpler approach (if you don't need the names at runtime but at design time) is to use regular expressions on your MyForm.designer.cs file to extract the names of the click handlers.
See this sample source:
private void button1_Click(object sender, EventArgs e)
{
string fileLocaton = #"C:\Users\nineb\source\repos\WindowsFormsApp37\WindowsFormsApp37\Form1.Designer.cs";
string fileContent = File.ReadAllText(fileLocaton);
// Find all menu items in the designer file
var matches = Regex.Matches(fileContent, #"System\.Windows\.Forms\.ToolStripMenuItem (.+?)\;");
foreach (Match match in matches)
{
string menuName = match.Groups[1].Value;
textBox1.AppendText("Menuitem " + menuName + Environment.NewLine);
// For each menu item, find all the event handlers
var clickMatches = Regex.Matches(fileContent,
#"this\." + Regex.Escape(menuName) + #"\.Click \+\= new System\.EventHandler\(this\.(.+?)\)\;");
foreach (Match clickMatch in clickMatches)
{
string handlerName = clickMatch.Groups[1].Value;
textBox1.AppendText("Eventhandler " + handlerName + Environment.NewLine);
}
}
}

How to add a dictionary to a ContextMenuStrip

I am currently trying to add to a context menu strip a dictionary of status values. The main issue I am having is how to pass in the Key value of the selected dictionary item to a click event.
Dictionary<int, string> statusList = getStatusList();
if (statusList.Count > 0)
{
Dictionary<int, ToolStripItem> statusMenu = new Dictionary<int, ToolStripItem>();
foreach (var keyValuePair in statusList)
{
statusMenu.Add(keyValuePair.Key, new ToolStripMenuItem() { Text = keyValuePair.Value.ToString(), Image = Resources.Refresh });
//statusMenu[statusMenu.Count - 1, statusMenu].Click += new EventHandler(MenuOption_Click_Handler); This is where I am confused
}
datagridview1.ContextMenuStrip.Items.Add(new ToolStripMenuItem("Set Status to", Resources.Refresh, statusMenu.Values.ToArray()));
}
I can easily get the array of the dictionary values. I am just wondering if its possible upon clicking a status in the contextmenu to pass the Key to a save method?
So, for example say statusList contains the following values:
{[1, Status1]}
{[2, Status2]}
{[5, Status3]}
So if I load the contextmenu, if I click Status3, I need to pass to the ClickEvent that Status Key 5 was clicked. Right now its only detecting the Value (Status3). Any help is much appreciated!
Why don't you rename your tooltip control? The id is the key of Dictionary, so it should be distinct. Anyway, any control must have name.
This is an idea. You name the control with the prefix ToolStrip(or whatever you like) + Key and get it later. Like:
var newItem= new ToolStripMenuItem() {
Text = keyValuePair.Value.ToString(),
Image = Resources.Refresh,
Name="ToolStrip" + keyValuePair.Key };
newItem.Click +=new EventHandler(MenuOption_Click_Handler);
statusMenu.Add(keyValuePair.Key,newItem);
In MenuOption_Click_Handler method:
public void MenuOption_Click_Handler(object sender,EventArgs e)
{
var id = Convert.ToInt32(((ToolStripMenuItem)sender).Name.Substring(0,9));
}

How to get GTKSharp TreeView widget to display expanders?

So I am creating a treeview selector with C#/GTKSharp. I have the basic tree view selector functionality working: The data is loaded into my model and I can click on a node to collapse/expand.
The part I can't work out is how to tell the cell renderer to display the collapse/expand toggle button. In the examples it appears as a triangle that points right or down depending on whether the node is opened or collapsed. I just have a blank space that works as expected as I click but shows nothing.
One possibility is that I have a white on white text issue but I doubt it as my labels show up fine and I have not done any formatting yet.
I tried adding code for ShowExpanders but that was already true.
TreeView = new Gtk.TreeView();
// We add the event handlers (i.e. the control part) to the tree
TreeView.RowActivated += SelectorActivated; //On double click
TreeView.Selection.Changed += SelectorSelected; // On select (single click)
// Raise a context menu here??
// Connect to the ButtonPressEvent
// Raise a popup button
// Create columns [View]
Gtk.TreeViewColumn TreeViewColumTitle = new Gtk.TreeViewColumn();
TreeViewColumTitle.Title = "Profile";
Gtk.CellRendererText NameCellTitle = new Gtk.CellRendererText();
TreeViewColumTitle.PackStart(NameCellTitle, true);
TreeViewColumTitle.SetCellDataFunc(NameCellTitle, new Gtk.TreeCellDataFunc(RenderTitle));
NameCellTitle.Mode = CellRendererMode.Activatable;
// Populate the model
// Note that we could dispense with this step if we generated an ITreeModel
// interface in the Object class.
BindModel(Model);
// Attach everything to the pane
TreeView.Model = GTKModel;
TreeView.AppendColumn(TreeViewColumTitle);
TreeView.ShowExpanders = true;
TreeView.ExpanderColumn.Visible = true;
...
private void BindModel(Model Model) {
GTKModel = new Gtk.TreeStore(typeof(Object));
foreach (Object Object in Model.Selector) {
var BindingData = new BindingDataGTK(this, Object);
BindingData.Iter = GTKModel.AppendValues(Object);
Object.BindingData = BindingData;
BindChildren(GTKModel, BindingData);
}
}
private void BindChildren(TreeStore TreeStore, BindingDataGTK ObjectBinding) {
foreach (var Child in ObjectBinding.Object) {
var BindingData = new BindingDataGTK(this, Child);
BindingData.Iter = TreeStore.AppendValues(ObjectBinding.Iter, Child);
Child.BindingData = BindingData;
BindChildren(TreeStore, BindingData);
}
}
private void RenderTitle(Gtk.TreeViewColumn Column, Gtk.CellRenderer Cell,
Gtk.ITreeModel GTKModel, Gtk.TreeIter Iter) {
Object Object = (Object)GTKModel.GetValue(Iter, 0);
(Cell as Gtk.CellRendererText).Text = Object.Title;
Console.WriteLine("Render {0}", Object.Title);
}
So far as I know this is pretty much an automatic feature, I don't think anything special is needed to make it happen (I've certainly never needed to). You might want to try using a TreeIter to construct your tree instead?
E.g. assuming you already have a TreeView on your form with 0 (zero) columns in it called "treeview" and a list of "MyObject"s called "myListOfObjects"...
treeview.AppendColumn ("Some Title", new CellRendererText(), "text", 0);
Gtk.TreeStore _ts = new TreeStore (typeof(string));
foreach (IMyObject _mo in myListOfObjects) {
Gtk.TreeIter _it = _ts.AppendValues (_mo.SomeText);
RecurseInto (_ts, _it, _mo);
}
treeview.Model = _ts;
...
void RescureInto(Gtk.TreeStore ts, Gtk.TreeIter it, IMyObject mo)
{
foreach (IMyObject _child_mo in mo.Children) {
Gtk.TreeIter _it = ts.AppendValues (it, _child_mo.SomeText);
RecurseInto (ts, _it, _child_mo);
}
}
In theory this should work fine.

Get a list of files/directories in an open Explorer window in C#

I'm trying to pull out the list of files and directories listed in an open Explorer window (in the same order as they're displayed) so that I can look through it, then set focus to a particular item.
I found this code here that allows me to get the selected items, however I'm not sure if it's possible to use this approach to get all items:
List<string> SelectedFiles() {
string filename;
List<string> selected = new List<string>();
var shell = new Shell32.Shell();
foreach (SHDocVw.InternetExplorer window in new SHDocVw.ShellWindows()) {
filename = Path.GetFileNameWithoutExtension(window.FullName).ToLower();
if (filename.ToLowerInvariant() == "explorer") {
((Shell32.IShellFolderViewDual2)window.Document).SelectItem()
foreach (Shell32.FolderItem item in items) {
selected.Add(item.Path);
}
}
}
return selected;
}
It looks like this Shell32 approach would also allow me to select an item programmatically, which is the other part I'm trying to accomplish. Instead of SelectedItems(), I would call SelectItem(), though I'm not sure how to use that function.
Anyone know of a way to get the list of files/directories from an open Windows Explorer window (and ideally set focus to an item)? Perhaps a P/Invoke kind of thing?
I was able to modify that code snippet I found to list all files/directories instead of just the selected ones.
Here's what I ended up with:
List<string> FilesAndFolders() {
string filename;
List<string> explorerItems = new List<string>();
var shell = new Shell32.Shell();
foreach (SHDocVw.InternetExplorer window in new SHDocVw.ShellWindows()) {
filename = Path.GetFileNameWithoutExtension(window.FullName).ToLower();
if (filename.ToLowerInvariant() == "explorer") {
Shell32.Folder folder = ((Shell32.IShellFolderViewDual2)window.Document).Folder;
Shell32.FolderItems items = folder.Items();
foreach (Shell32.FolderItem item in items) {
explorerItems.Add(item.Path);
}
}
}
return explorerItems;
}
Edit:
To select an item, you call:
((Shell32.IShellFolderViewDual2)window.Document).SelectItem(item, 1);
where window is a SHDocVw.InternetExplorer, and item is a Shell32.FolderItem (from folder.Items() in the above example).
To deselect, it, pass in 0 instead of 1 as the second overload.

Iterating over a List and using the objects inside an anonymous function

I have a List<string> and I want to iterate over this collection and do something with each string on a button click. I have a small example here to illustrate what I'm trying to do:
//items is a System.Collections.Generic.List<string>
foreach (string s in items)
{
Button b = new Button() { Content = s };
b.Click += (obj, ev) =>
{
MessageBox.Show(s);
}
//add b to form, container, etc...
}
As you would expect the buttons are created appropriately with the correct content, however when I click any of the buttons, the text inside the MessageBox is always the last string in items. What am I missing with this? Why is it that all the Click functions for the buttons are passed the last item in the collection?
The foreach loop is changing s, which is used in the lambda. The lambda uses the current value of s at the point of execution, not declaring it (in techspeak: "closures close over variables, not values"). You'll have to make a local variable:
foreach (string s in items)
{
string local = s;
Button b = new Button() { Content = s };
b.Click += (obj, ev) =>
{
MessageBox.Show(local);
}
//add b to form, container, etc...
}
Thus you have a reference to the instance of s at the point of decleration, not execution.
Eric Lippert has two fantastic articles about it: part 1, part 2.

Categories