Popup-Event wird im Context-Menu nicht ausgeloest


Thomas Wölfer
Thomas Wölfer

18. September 2004


Wenn man ein Menu dynamisch zusammenbaut, dann braucht man dringend das Popup-Ereignis: Wenn das eintritt, dann kann man die MenuItems das 'gleich' angezeigten Menus manipulieren. Man kann zum Beispiel den 'Enabled' State verändern, den Text setzen, oder aber auch neue Items hinzufügen oder vorhandene entfernen.

Das klappt ganz wunderbar - nur leider nicht bei Kontextmenus, zumindest nicht immer. Angenommen man hat ein ContextMenu mit Untermenüs, und man zeigt das Menü selbst per ContextMenu.Show() an. In diesem Fall gibt es zwar ein Popup-Event fürs Menü selbst, nicht aber für die Untermenüs.

Es gibt einen Workaround, allerdings einen ärgerlichen: Man muss den Code umstrukturieren und darf sich nicht länger selbst um die Anzeige des ContextMenus kümmern. Wenn man statt dessen dem Control über dem das Menü angezeigt werden soll per

control.ContextMenu = new ContextMenu();

ein solches Menü zuweist, dann wird dieses Menü von selbst geöffnet - und es wird auch das Popup-Event für Untermenüs darin ausgelöst.

Das macht zunächst nichts weiter aus, wenn man aber selbst schon Code hat der vor ContextMenu.Show() dafür zuständig ist das Menü zu manipulieren oder erst 'richtig' zusammenzusetzen, dann muss man den umbauen und die Menümanipulation im Popup-Event vornehmen.

Bei der Gelegenheit ist mir noch was anderes bei MainMenus aufgefallen: Wenn man in ein Untermenü eines MainMenus MenuItems reintut, dann führt das zu sehr unschönen Flicker-Reaktionen und die sind relativ schwer wegzubekommen: Menus haben kein BeginUpdate()/EndUpdate(), und SuspendLayout() / ResumeLayout() der zugehörigen Form bringen auch keinerlei Verbesserung.

Und das flackern wegzubekommen habe ich nur zwei Möglichkeiten gefunden. Beide gehen nur mit Interop: Im einen Fall verwendet man LockWindowUpdate() für die Form in der das MainMenu ist. Laut Raymond sollte man das aber besser bleiben lassen.

Ganz gut funktioniert das folgende: Man hält das malen der Form (und das zugehörige verarbeiten von Messages) an, fügt die MenuItems hinzu, und startet die malerei dann wieder. Das geht in etwas so:

private const int WM_SETREDRAW = 0x000B;
private const int
WM_USER = 0x400;
private const int
EM_GETEVENTMASK = (WM_USER + 59);
private const int
EM_SETEVENTMASK = (WM_USER + 69);

[DllImport("user32", CharSet = CharSet.Auto)]
private extern static IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam);

// hier ist dann die methode die die aenderung durchfuehren soll...
void foo()
{
   IntPtr eventMask = IntPtr.Zero;

   try
   {
     
SendMessage( TheApplication.MainWindow.MainForm.Handle, WM_SETREDRAW, 0, IntPtr.Zero);
     
eventMask = SendMessage( TheApplication.MainWindow.MainForm.Handle, EM_GETEVENTMASK, 0, IntPtr.Zero);
      // hier fuegt man dann die neue MenuItems ein....
  
}
  
finally
  
{
     
SendMessage( TheApplication.MainWindow.MainForm.Handle, EM_SETEVENTMASK, 0, eventMask);
     
SendMessage( TheApplication.MainWindow.MainForm.Handle, WM_SETREDRAW, 1, IntPtr.Zero);
  
}
}