« Hoot With a Ribbon, MDI, and Inheritance | Switched Keys Site Conversion » |
Programmatically - Let Designer show you
The web is full of queries about how to accomplish something programmatically. This can't help with all of them, but if it has to do with C# in Visual Studio, this might guide you in the right direction. Visual Studio is robust enough to do a lot of things for you, but sometimes you need a variation of one of those automatic features. Well, it's easy to determine that than it might appear.
When VS creates an item, it does it with code. Finding how it's done may be as simple as looking at the code behind, i.e. in the form's .Designer file. Specifically, it answers the common questions:
What's the name of the class you use to create the item?
What's the name of the parent class, and how do you add an item to it?
Here's a couple of examples:
Example 1: I have a form that inherits from another form. The base form includes a complex context menu so derived forms do not have to duplicate that work repeatedly. But there are a couple of forms that need some additional items added to the list. How can I dynamically add items to that context menu for some forms?
In order to see what Designer does, add the items to a copy of the base form in design mode (or look at a previously added item). In my case, there were items name popLoadSearch, popSaveSearch, and a separator.
Then, open the .Designer file and search for the names. Each was found four times. You can verify some of the statements by looking at the properties of the previously added items. The first set were statements that create the objects and the rest configured the objects and added them to the context menu collection. This is what I found.
Create objects
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
this.popLoadSearch = new System.Windows.Forms.ToolStripMenuItem();
this.popSaveSearch = new System.Windows.Forms.ToolStripMenuItem();
Configure objects
//
// toolStripSeparator1
//
this.toolStripSeparator1.Name = "toolStripSeparator1";
this.toolStripSeparator1.Size = new System.Drawing.Size(162, 6);
//
// popLoadSearch
//
this.popLoadSearch.Name = "popLoadSearch";
this.popLoadSearch.Size = new System.Drawing.Size(165, 22);
this.popLoadSearch.Text = "Load Search";
this.popLoadSearch.Click += new System.EventHandler(this.popLoadSearch_Click);
//
// popSaveSearch
//
this.popSaveSearch.Name = "popSaveSearch";
this.popSaveSearch.Size = new System.Drawing.Size(165, 22);
this.popSaveSearch.Text = "Save Search";
this.popSaveSearch.Click += new System.EventHandler(this.popSaveSearch_Click);
Add objects to collection
this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.popLoadSearch,
this.popSaveSearch});
Declare variables
private System.Windows.Forms.ToolStripMenuItem popLoadSearch;
private System.Windows.Forms.ToolStripMenuItem popSaveSearch;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
Yes, this looks backward since variables have to be declared before they are used but in Designer the commands are contained in the InitializeComponent method, while the declarations are in the class. Just remember to move the declarations to the top, and preferably instantiate them at the same time. I also noticed the command to size the menustrip, so I figure I needed to change that too.
this.contextMenuStrip1.Size = new System.Drawing.Size(166, 484);
After removing some reference terms (this., private, Systems.Windows.Forms.), renaming the separator, and compressing some of the commands, this is what my addition looks like. I include a command in the form load method to execute this.
private void AddContextItems()
{
// Declare Variables
ToolStripSeparator topSeparator = new ToolStripSeparator();
ToolStripMenuItem popLoadSearch = new ToolStripMenuItem();
ToolStripMenuItem popSaveSearch = new ToolStripMenuItem();
// Configure objects
topSeparator.Name = "Separator";
topSeparator.Size = new System.Drawing.Size(162, 6);
// popLoadSearch
popLoadSearch.Name = "popLoadSearch";
popLoadSearch.Size = new System.Drawing.Size(165, 22);
popLoadSearch.Text = "Load Search";
popLoadSearch.Click += new System.EventHandler(popLoadSearch_Click);
// popSaveSearch
popSaveSearch.Name = "popSaveSearch";
popSaveSearch.Size = new System.Drawing.Size(165, 22);
popSaveSearch.Text = "Save Search";
popSaveSearch.Click += new System.EventHandler(popSaveSearch_Click);
//add objects to collection
this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
topSeparator,
popLoadSearch,
popSaveSearch});
// resize the menu
this.contextMenuStrip1.Size = new System.Drawing.Size(166, 534); // 484 + 22 + 22 + 6
}
Of course, I also had to create the methods assigned to the Click method of each.
Example 2: For my program Hoot I'm adding a Ribbon wrapper for C# WinForms and I need to dynamically add new recent items to the Orb menu. You can read about this Ribbon at https://www.codeproject.com/articles/364272/easily-add-a-ribbon-into-a-winforms-application-cs. Other than the basic introduction for getting started there's not much documentation since it's not officially Microsoft (like WPF), so I have to figure things out. The Orb menu includes a list of items that are commonly used in other programs to list recent items opened. I want to do that but don't know the classes or methods to use. Intellisense is not always the easiest way to figure that out, so I create an item in design mode called OrbRecent and look for it in the Designer code.
Instantiation
this.OrbRecent = new System.Windows.Forms.RibbonOrbRecentItem();
Add to collection
this.ribbonMain.OrbDropDown.RecentItems.Add(this.OrbRecent);
Configuration
//
// OrbRecent
//
this.OrbRecent.Image = ((System.Drawing.Image)(resources.GetObject
("OrbRecent.Image")));
this.OrbRecent.SmallImage = ((System.Drawing.Image)(resources.GetObject
("OrbRecent.SmallImage")));
this.OrbRecent.Text = "Recent";
Declaration
private System.Windows.Forms.RibbonOrbRecentItem OrbRecent;
Now that I know how VS created and added the item to the collection, I can create a method to do that dynamically, and adjust it to my future needs. I will add it to my Utilities class and use a database table to store the entries. Of course, I'll have to reverse sort the entries so the most recent are on top, and to keep the list at a reasonable size I will need to monitor the size and remove old items.
AddRecent(string spec) {
RibbonOrbRecentItem adder = new RibbonOrbRecentItem();
adder.Text = spec;
this.OrbRecent.Image = ((System.Drawing.Image)(resources.GetObject
("OrbRecent.Image")));
this.OrbRecent.SmallImage = ((System.Drawing.Image)(resources.GetObject
("OrbRecent.SmallImage")));
ribbonMain.OrbDropDown.RecentItems.Add(spec);
}
Visual Studio's Designer code taught me that.