Freitag, 7. August 2009

Wpf ContextMenuNotEnabledBug

Gestern habe ich Probleme gehabt mit einem wpf Bug (?) – In einer Maske (mit wenig Elementen) waren die Einträge des Kontext-Menüs (dessen Items an Commands gebunden waren…) nicht aktiv. Erst Nach einem Klick auf ein Element (aber nicht auch jedes…) wurden die Einträge aktiv.. In meinen Augen ein absolut wirres Verhalten.

Nach langem Suchen habe ich eine Erklärung und ein paar Workarounds gefunden: Bei MSDN und in einem anderen Blog.

Persönlich bin eher für fool-proof (Wenn ich in einem Jahr oder in zweien wieder an den code muss denke ich nicht daran die MenuItems zu “Workarounden”….) und minimal-Invasiv.

Also so wenig wie möglich meinen Code “umbiegen”, nur damit das Kontext-Menü funktioniert. Meine Lösung ist daher:

Im code-behind des wpf-Window:

internal BenutzerVerwaltung()
{
InitializeComponent();
ContextMenuNotEnabledBugHelper.Workaround(this);
}

Dazu der ContextMenuNotEnabledBugHelper:

/// <summary>
/// Helper to Work around the ContextMenu[Not]EnabledBug of wpf.
/// see http://social.msdn.microsoft.com/forums/en-US/wpf/thread/7bd75a7c-eab4-4f3a-967b-94a9534a7455/
/// and http://www.wiredprairie.us/journal/2007/04/commandtarget_menuitem_context.html
/// regarding this...
/// </summary>
public static class ContextMenuNotEnabledBugHelper
{
public static void Workaround(Window window)
{
foreach (object child in RecurseChildren(window))
{
if (HasContextMenu(child))
{
Workaround(window, GetContextMenu(child));
}
}
}

private static bool HasContextMenu(object o)
{
return GetContextMenu(o) != null;
}

private static ContextMenu GetContextMenu(object o)
{
if (o is DependencyObject)
{
return (ContextMenu) ((DependencyObject) o).GetValue(ContextMenuService.ContextMenuProperty);
}
return null;
}

private static void Workaround(Window window, ContextMenu contextMenu)
{
foreach (object o in LogicalTreeHelper.GetChildren(contextMenu))
{
if (o is MenuItem)
{
MenuItem menuItem = (MenuItem) o;
IInputElement oldTarget = menuItem.CommandTarget;
menuItem.CommandTarget = window; // <-- this is the Workaround !
if (oldTarget != null)
{
menuItem.CommandTarget = oldTarget;
}
}
}
}

private static IEnumerable RecurseChildren(DependencyObject current)
{
foreach (object child in LogicalTreeHelper.GetChildren(current))
{
yield return child;
if (child is DependencyObject)
{
foreach (object chlidsChild in RecurseChildren((DependencyObject) child))
{
yield return chlidsChild;
}
}
}
}
}

Also: Meinungen dazu ??

Keine Kommentare:

Kommentar veröffentlichen