Reputation: 1215
Currently I try to merge different ToolStripMenuItems
into one MenuStrip
. Therefor i've created an Interface called IMenu
. This include a ToolStripMenuItem
from the MEF Plugin and should be load into the Main-Window-Menustrip. The Problem is that if i run the application i get in the MenuStrip
two DropDown elements with the same name. But what i want is that i can bind the menu of the MEF plugin into the MenuStrip of the application without double entries e.g.
How can i make a good menu structure for MEF Plugins?
Example: I have from the main application this entries
File
|--> New
|--> Save
|--> Import
|--> Export
And then i create a plugin for Import / Export a specific type. Therefor i must add into the menu dynamicly entries under Import / Export. But how? How is your solution?
File
|--> New
|--> Save
|--> Import
|-----> To Word
|--> Export
|-----> From Word
Here my code: First the interface for the Plugin
public interface IMenu
{
ToolStripMenuItem ToolStripItem { get; }
}
This is a plugin example for menu item
Help
|--> Update
[Export(typeof(IMenu))]
class UpdateMenuItems : IMenu
{
System.Windows.Forms.ToolStripMenuItem helpMenuItem;
System.Windows.Forms.ToolStripMenuItem updateMenuItem;
public System.Windows.Forms.ToolStripMenuItem ToolStripItem
{
get { return helpMenuItem; }
}
public UpdateMenuItems()
{
InitializeComponent();
}
private void InitializeComponent()
{
this.updateMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.updateMenuItem.Name = "Update";
this.updateMenuItem.Size = new System.Drawing.Size(94, 20);
this.updateMenuItem.Text = "Update";
this.updateMenuItem.MergeIndex = 1;
this.updateMenuItem.MergeAction = System.Windows.Forms.MergeAction.Insert;
this.updateMenuItem.Click += updateMenuItem_Click;
this.helpMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.helpMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.updateMenuItem});
this.helpMenuItem.Name = "aboutToolStripMenuItem";
this.helpMenuItem.Size = new System.Drawing.Size(44, 20);
this.helpMenuItem.MergeAction = System.Windows.Forms.MergeAction.Insert;
this.helpMenuItem.Text = "Help";
}
void updateMenuItem_Click(object sender, EventArgs e)
{
UpdateController update = new UpdateController();
update.Execute();
}
This is another MEF Plugin which also has the root element "Help"
[Export(typeof(IMenu))]
class MenuItem : IMenu
{
public ToolStripMenuItem ToolStripItem
{
get { return testItem; }
}
private System.Windows.Forms.ToolStripMenuItem testItem;
private System.Windows.Forms.ToolStripMenuItem unterpunk1;
private System.Windows.Forms.ToolStripMenuItem unterpunk2;
public MenuItem()
{
InitializeComponent();
}
public void InitializeComponent()
{
this.testItem = new System.Windows.Forms.ToolStripMenuItem();
this.testItem.Name = "aboutToolStripMenuItem";
this.testItem.Size = new System.Drawing.Size(94, 20);
this.testItem.Text = "Help";
this.testItem.Click += testItem_Click;
unterpunk1 = new ToolStripMenuItem();
unterpunk1.Text = "Speichern";
unterpunk1.MergeAction = MergeAction.Insert;
unterpunk1.MergeIndex = 1;
testItem.DropDownItems.Add(unterpunk1);
unterpunk2 = new ToolStripMenuItem();
unterpunk2.Text = "Prüfen";
unterpunk2.MergeAction = MergeAction.Insert;
unterpunk2.MergeIndex = 2;
testItem.DropDownItems.Add(unterpunk2);
}
void testItem_Click(object sender, EventArgs e)
{
//
}
In my main form i add all to the MenuStrip.
foreach (var item in MenuItems)
{
this.menuStrip1.Items.Add(item.ToolStripItem);
}
The ToolStripMenuItems
don't merge automatically?
Upvotes: 2
Views: 2874
Reputation: 12319
I doubt very much that there is an automatic feature that would do that, so here is how I'd approach the manual solution. There is one pitfall, which is that a ToolStripItem
can at any time only have one parent. So adding it to the existing menu will remove it from the other, you need to take that into account (done in my method by checking if the item already had an owner). If that is a problem, you would need to clone the subitem first.
EDIT: I realized you will probably need a recursive function, so I replaced the code. Class MenuItem2 contains the Help/Speichern/Save As/About items.
public Form1()
{
InitializeComponent();
List<IMenu> menuItems = new List<IMenu>() { new UpdateMenuItems(), new MenuItem(), new MenuItem2() };
MergeMenus(this.menuStrip1.Items, menuItems.Select(m => m.ToolStripItem));
}
/// <summary>
/// Recursive function that merges two ToolStripItem trees into one
/// </summary>
/// <param name="existingItems">Collection of existing ToolStripItems</param>
/// <param name="newItems">Collection of new ToolStripItems. IEnumerable instead of ToolStripItemCollection to allow for items without an owner</param>
private void MergeMenus(ToolStripItemCollection existingItems, IEnumerable<ToolStripItem> newItems)
{
int count = newItems.Count();
int removedFromCollection = 0; // keep track of items that are removed from the newItems collection
for (int i = 0; i < count; i++)
{
ToolStripItem newItem = newItems.ElementAt(i - removedFromCollection);
bool merged = false;
string key = newItem.Name; // the items are identified and compared by its name
if (existingItems.ContainsKey(key))
{
ToolStripItem existingItem = existingItems[key];
if (existingItem != null && existingItem.GetType().Equals(newItem.GetType()))
{
// check if the matching items are ToolStripMenuItems. if so, merge their children recursively
if (newItem is ToolStripMenuItem)
MergeMenus(((ToolStripMenuItem)existingItem).DropDownItems, ((ToolStripMenuItem)newItem).DropDownItems.Cast<ToolStripItem>());
// do not add this particular item (existing item with same name and type found)
merged = true;
}
}
if (!merged) // newItem does not exist in existingItems (or not as the same type)
{
// if there was an owner, the item will be removed from the collection and the next element's index needs to be adjusted
if (newItem.Owner != null)
removedFromCollection++;
// add item to the existing tree
existingItems.Add(newItem);
}
}
}
Tested and seems to work:
Upvotes: 3
Reputation: 7951
I implemented a Main Menu, using MVVM and Unity through an IMenuService. I posted a rather complete answer to my own question on this same topic (but for menus instead of toolstrips). I think you can do something extremely similar for a ToolStrip to what I did in my code.
Please see the following answer:
DataTemplate to generate Menu with MVVM
Upvotes: 2