Dynamically filtering a menu (ContextMenuStrip) in C# without recreating the menu?
-
16-03-2021 - |
Question
Is this possible?
I plan to have 10 menu items where these are going to have sub-menu items (1 level deep only). I want to be able to filter them when the user types into my TextBox
control. I know I can filter items upon opening the menu for the first time, but I want to be able to continually filter it as the user types and hide categories on the fly when the category menu item has no subitems applicable for the current filter (by name filtering).
Any ideas?
Solution
I added a context menu strip (menuStrip1). To this I added the following:
File
Exit
Edit
Copy
Paste
Further Down
Help
Arghhhh!
I then added a text box (FilterMenuText), and, on the OnTextChanged event, do the following:
private void FilterMenuText_TextChanged(object sender, EventArgs e)
{
foreach (ToolStripMenuItem menuItem in menuStrip1.Items)
{
if (menuItem.DropDownItems.Count > 0)
{
bool matchFound = false;
foreach (ToolStripMenuItem childMenuItem in menuItem.DropDownItems)
{
if (childMenuItem.Text.ToUpper().Contains(FilterMenuText.Text.ToUpper()))
{
matchFound = true;
break;
}
}
menuItem.Visible = matchFound;
}
}
}
This will hide and show the top level MenuItems as appropriate based on the content of the child menu items. If your menu has more than one level of drop down, put the foreach into a recursive function, like:
private void FilterMenuText_TextChanged(object sender, EventArgs e)
{
foreach (ToolStripMenuItem menuItem in menuStrip1.Items)
{
menuItem.Visible = MenuItemHasChildWithName(menuItem, FilterMenuText.Text);
}
}
private bool MenuItemHasChildWithName(ToolStripMenuItem menuItem, string name)
{
if (!menuItem.HasDropDownItems)
{
return false;
}
bool matchFound = false;
foreach (ToolStripMenuItem childMenuItem in menuItem.DropDownItems)
{
if (childMenuItem.Text.ToUpper().Contains(name.ToUpper()))
{
matchFound = true;
break;
}
if (childMenuItem.HasDropDownItems)
{
matchFound = MenuItemHasChildWithName(childMenuItem, name);
if(matchFound) { break; }
}
}
return matchFound;
}
OTHER TIPS
This is what I used for conditionaly showing menu items on a context menu. If you use the same menu and don't want to show it, you merely set each item in the same loop to true;
private void dgViewData_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
if (dgViewData.HitTest(e.X, e.Y).Type != DataGridViewHitTestType.ColumnHeader)
{
foreach (ToolStripMenuItem menuItem in conMicImport.Items)
{
menuItem.Visible = menuItem.Text.ToString().Contains("Add") == true ? false : true;
}
conMicImport.Show(dgViewData, e.Location);
ctxtDG = dgViewData;
}
}
}
Set the toolstrip object's Visible property to false when you don't want it to appear.
for example purposes I have done this using a web forms application
foreach (Control c in Page.Form.Controls)
{
//Response.Write("WORD2" + c.GetType());
if (c is Panel)
{
foreach (Control p in c.Controls)
{
if (p is CheckBoxList)
{
foreach (ListItem li in ((CheckBoxList)p).Items)
{
li.Selected = false;
}
}
}
}
}