WPF contextmenu is slow with many items - how can I speed it up?
I have found the context menu takes over 10 seconds to display when I have 10,000 items in it, I'd like to make this faster (e.g. less than 1 second)
Here is my test code that shows the context menu slowness directly:
private void button1_Click(object sender, RoutedEventArgs e)
{
ContextMenu cm = new ContextMenu();
for (int i = 0; i < 1000; i++)
{
MenuItem mi = new MenuItem();
mi.Header = "test"; // this is HOT - 3%
mi.Tag = this; // cold
for (int j = 0; j < 10; j++)
{
MenuItem mi2 = new MenuItem(); // this is HOT - 1%
mi2.Header = "test"; // this is HOT - 12%
mi2.Tag = this; // cold
mi.Items.Add(mi2); // this is HOT - 6%
}
cm.Items.Add(mi); // this is HOT - 3%
}
开发者_JS百科 cm.IsOpen = true; // this is HOT - 72%
}
According to performance profilers I've used, the cm.IsOpen is taking most of the time - but setting the mi.Header is also significant.
It's going to be a poor user experience having 1k items at a single level, but how to make context menus with lots\expensive items more responsive, is a fair question.
The general pattern I use is to initially create my menu with dummy sub-menus. The dummy sub-menus have a single place-holder menu item and I hook the sub-menu opening event. In the event handler I remove the place-holder menu item and add the real items in.
This pattern allows sub-menus to be created on demand and only if the sub-menu is actually opened. In other words it makes your menu dynamic meaning you do not have to create everything up-front, nor create hundreds of sub-menus which will not be opened.
This pattern would reduce your initial menu item count from 10k items to 1k for the example above. This should get you down to ~1 second.
Again 10k menu items with 1k in the root is pretty much unusable in terms of UX. I hope this is just a thought experiment! :)
Example
ContextMenu cm = new ContextMenu();
for (int i = 0; i < 1000; i++)
{
MenuItem mi = new MenuItem();
mi.Header = "test";
mi.Tag = this;
object dummySub = new object();
mi.Items.Add(dummySub);
cm.Items.Add(mi);
mi.SubmenuOpened += delegate
{
mi.Items.Clear();
for (int j = 0; j < 10; j++)
{
MenuItem mi2 = new MenuItem();
mi2.Header = "test";
mi2.Tag = this;
mi.Items.Add(mi2);
}
};
}
cm.IsOpen = true;
精彩评论