Sample for ToolStripManager.Merge

Concepts

Before delving into ToolStripManager there are a few concepts to get under your belt

Target - this is the ToolStrip you're merging into (e.g. a Main MenuStrip on your form)
Source - this is the ToolStrip with items you want to merge into the Target toolstrip. (e.g. a menu from an MDI child from)

Merge Action
The merge action should be set on items in the "Source" toolstrip - e.g. your MDI child menu strip.

  • Append
    (default) adds the Source item to the end of the Target Items collection
  • Insert
    adds the Source item to the Target Items collections as specified by the MergeIndex property set on the Source Item.
  • Replace
    finds a match (using Text property, then MergeIndex if no match), then replaces the matching Target Item with the Source Item.
    (e.g, MDI child item replaces item from MainMenuStrip)
  • MatchOnly
    finds a match (using Text property, then MergeIndex if no match), then adds all the Source Item's DropDownItems to the Target Item.
    (e.g. an MDI child wants to add a menu item to MainMenuStrip's Save As-> menu).
  • Remove
    finds a match (using Text property, then MergeIndex if no match), then removes the item from the Target ToolStrip.
    (e.g. MDI child can remove the save menu item from MainMenuStrip).

Note the usefulness of MatchOnly - it can be used to build up a menu structure to insert/add/remove into a submenu. The most frequently used MergeActions will be MatchOnly, Append, and Insert.

Notes on Automatic Merging

If you are using automatic merging from MDI, you do not need to call ToolStripManager.Merge. You will want to make sure your MainMenuStrip property in the MainForm is set AND you will want to set Visible = false on any MenuStrips in the MDI child. This is not automatically done for you as it is with the old MainMenu stuff.

Regarding the sample

Create a new windows forms application in VS 2.0 and add this as an existing item to your project.

The sample below shows most MergeActions, except Remove which is pretty straight forward.

---

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WindowsApplication1 {

// This sample covers most MergeActions except MergeAction.Remove, which is pretty self explanitory.

public enum MergeSample {
None,
Append,
InsertInSameLocation,
InsertInSameLocationPreservingOrder,
ReplacingItems,
MatchOnly
}
public class Form1 : Form {
ContextMenuStrip cmsBase;
ContextMenuStrip cmsItemsToMerge;

public Form1() {
InitializeComponent();

if (components == null) {
components = new Container();
}
cmsBase = new ContextMenuStrip(components);
cmsItemsToMerge = new ContextMenuStrip(components);

// cmsBase is our base context menu strip
cmsBase.Items.Add("one");
cmsBase.Items.Add("two");
cmsBase.Items.Add("three");
cmsBase.Items.Add("four");

// cmsItemsToMerge contains the items you might want merge in
cmsItemsToMerge.Items.Add("one");
cmsItemsToMerge.Items.Add("two");
cmsItemsToMerge.Items.Add("three");
cmsItemsToMerge.Items.Add("four");

// distinguish the merged items by setting the shortcut display string.
foreach (ToolStripMenuItem tsmi in cmsItemsToMerge.Items) {
tsmi.ShortcutKeyDisplayString = "merged item!";
}

// associate the context menu strip with the form so it shows up on right click.
this.ContextMenuStrip = cmsBase;

CreateCombo();

}

#region ComboBox switching stuff
private void CreateCombo() {
// handy combo for flipping between the samples
ComboBox sampleSelectorCombo = new ComboBox();
sampleSelectorCombo.DataSource = Enum.GetValues(typeof(MergeSample));
sampleSelectorCombo.SelectedIndexChanged += new EventHandler(comboBox_SelectedIndexChanged);
sampleSelectorCombo.Dock = DockStyle.Top;
this.Controls.Add(sampleSelectorCombo);

TextBox textBox = new TextBox();
textBox.Multiline = true;
textBox.Dock = DockStyle.Left;
textBox.DataBindings.Add("Text", this, "ScenarioText");
textBox.ReadOnly = true;
textBox.Width = 150;
this.Controls.Add(textBox);
this.BackColor = ProfessionalColors.MenuStripGradientBegin;
this.Text = "right click on the blue to see!";
}
void comboBox_SelectedIndexChanged(object sender, EventArgs e) {
ComboBox sampleSelectorCombo = sender as ComboBox;
if (sampleSelectorCombo.SelectedValue != null) {
CurrentSample = (MergeSample)sampleSelectorCombo.SelectedValue;
}
}

private string scenarioText;

public string ScenarioText {
get { return scenarioText; }
set {
scenarioText = value;
if (ScenarioTextChanged != null) {
ScenarioTextChanged(this, EventArgs.Empty);
}
}
}

public event EventHandler ScenarioTextChanged;

#endregion

private void RebuildItemsToMerge() {
// cases where we've changed the items collection for the sample.
cmsItemsToMerge.SuspendLayout();
cmsItemsToMerge.Items.Clear();
cmsItemsToMerge.Items.Add("one");
cmsItemsToMerge.Items.Add("two");
cmsItemsToMerge.Items.Add("three");
cmsItemsToMerge.Items.Add("four");
// distinguish the merged items by setting the shortcut display string.
foreach (ToolStripMenuItem tsmi in cmsItemsToMerge.Items) {
tsmi.ShortcutKeyDisplayString = "merged item!";
}
cmsItemsToMerge.ResumeLayout();
}
#region switching current samples
private MergeSample currentSample = MergeSample.None;
private MergeSample CurrentSample {
get { return currentSample; }
set {
if (currentSample != value) {
bool resetRequired = false;

if (currentSample == MergeSample.MatchOnly) {
resetRequired = true;
}
currentSample = value;
// back out previous merge, if any
ToolStripManager.RevertMerge(cmsBase, cmsItemsToMerge);

if (resetRequired) {
RebuildItemsToMerge();
}

switch (currentSample) {
case MergeSample.None:
return;
case MergeSample.Append:
ScenarioText = "This sample adds items to the end of the list using MergeAction.Append.\r\n\r\nThis is the default setting for MergeAction. Typical scenarios for this are adding addional menu items to the end of the menu when some part of the program is activated.";
ShowAppendSample();
break;
case MergeSample.InsertInSameLocation:
ScenarioText = "This sample adds items to the middle of the list using MergeAction.Insert.\r\n\r\nNotice here how the items are added in reverse order - four,three,two,one. This is because they all have the same merge index - we place one at index 2, then two at index 2... etc.\r\n\r\nTypical scenarios for this are adding addional menu items to the middle/beginning of the menu when some part of the program is activated. ";
ShowInsertInSameLocationSample();
break;
case MergeSample.InsertInSameLocationPreservingOrder:
ScenarioText = "This sample is the same as InsertInSameLocation, except we've corrected the problem where the items add backwards by increasing the MergeIndex of \"two merged item!\" to be 3, \"three merged item!\" to be 5, etc.\r\n You could also add the original items backwards to the source context menu strip";
ShowInsertInSameLocationPreservingOrderSample();
break;
case MergeSample.ReplacingItems:
ScenarioText = "This sample replaces a menu item altogether using MergeAction.Replace - good for MDI scenario where your File->Save does something completely different\r\n\r\nNOTE matching is based on Text, if there is no text match, it will resort to MergeIndex.";
ShowReplaceSample();
break;
case MergeSample.MatchOnly:
ScenarioText = "This sample adds the sub items ONLY from the child to the target (original) context menu strip.\r\n\r\nThis is good for cases where you want to add an additional menu item to a child menu like \"Save As\"->\"My new fancy filetype\", just below the \"Save As\"->\"My old filetype\" item. In this case you would create a Save As item in your source context menu strip, switch it to MatchOnly, and leave the My Fancy New Filetype as MergeAction.Append - this will add it to the end.";
ShowMatchOnlySample();
break;

}
// re-apply with new settings
ToolStripManager.Merge(cmsItemsToMerge, cmsBase);
}
}
}
#endregion

#region MergeSample.Append
/* Example 1 - Add all to the end of the list
* one
* two
* three
* four
* merge-one
* merge-two
* merge-three
* merge-four
*/
public void ShowAppendSample() {
foreach (ToolStripItem item in cmsItemsToMerge.Items) {
item.MergeAction = MergeAction.Append;
}
}
#endregion

#region MergeSample.InsertInSameLocation
/* Example 2 - Place all in the same location
* one
* two
* merge-four
* merge-three
* merge-two
* merge-one
* three
* four

*/
public void ShowInsertInSameLocationSample() {
// notice how they're all backwards!
// this is because "merge-one" gets applied, then we look for the new 2nd position
// to place "merge-two"...
foreach (ToolStripItem item in cmsItemsToMerge.Items) {
item.MergeAction = MergeAction.Insert;
item.MergeIndex = 2;
}
}
#endregion

#region MergeSample.InsertInSameLocationPreservingOrder
/* Example 3, inserting in the right order
* one
* two
* merge-one
* merge-two
* merge-three
* merge-four
* three
* four
*/
public void ShowInsertInSameLocationPreservingOrderSample() {

// back out previous merge, if any
ToolStripManager.RevertMerge(cmsBase, cmsItemsToMerge);

// this is the same as above, but increases the MergeIndex so that
// subsequent items will be placed afterwards.
int i = 0;
foreach (ToolStripItem item in cmsItemsToMerge.Items) {
item.MergeAction = MergeAction.Insert;
item.MergeIndex = 2 + i++;
}

// re-apply with new settings
ToolStripManager.Merge(cmsItemsToMerge, cmsBase);
}
#endregion

#region MergeSample.ReplacingItems
/* Example 4 -
* merge-one
* merge-two
* merge-three
* merge-four
*/
public void ShowReplaceSample() {

// NOTE: MergeAction.Replace uses Text comparison,
// if matching text not found, it will fall back to MergeIndex.

foreach (ToolStripItem item in cmsItemsToMerge.Items) {
item.MergeAction = MergeAction.Replace;
}

}
#endregion

#region MergeSample.MatchOnly
/* Example 5 matching to add subitems to a menu item.
* Add items to the flyouts for the original collection
* one -> subitem from "one merged item!"
* two -> subitem from "two merged item!"
* three -> subitem from "three merged item!"
* four -> subitem from "four merged item!"
*/
public void ShowMatchOnlySample() {

foreach (ToolStripMenuItem item in cmsItemsToMerge.Items) {
item.MergeAction = MergeAction.MatchOnly;
item.DropDownItems.Add("subitem from \"" + item.Text + " " + item.ShortcutKeyDisplayString + "\"");
}

}

#endregion

/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;

/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}

#region Windows Form Designer generated code

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Text = "Form1";
}

#endregion
}
}