So fügen Sie MenuItems (mit einer Kopfzeile) dynamisch zu einem WPF-Menü hinzu

8

[Edit # 3] - Jedem, der diese Frage liest: Tun nicht unter keinen Umständen den in dieser Frage beschriebenen Ansatz. Es ist ein Coding Horror . Ich gebe das freimütig zu und weiß, dass alle Programmierer sich in der Vergangenheit in eine Ecke gearbeitet haben und (vor allem beim Erlernen einer neuen Technologie) von anderen, wohlmeinenden Entwicklern auf der Interweb in die Irre geführt wurden. Lesen Sie zuerst die Antwort von Robert und lesen Sie dann diese Frage. Bitte.

[Bearbeiten # 2b]

Ich entschuldige mich für die Länge dieser Frage - es gibt eine Frage hier (am Ende!), aber ich wollte sicherstellen, dass der Quellcode explizit war. Wie auch immer.

[Bearbeiten # 2] - Der Titel der Frage wurde geändert, um die Frage ... genauer wiederzugeben.

[Bearbeiten] - Ich habe etwas mehr über den Verlauf des Entwurfs / Codes, den ich hier gemacht habe, aktualisiert: Obligatorischer Blogbeitrag . Wenn es hilft, die Frage unten zu klären, fühlen Sie sich frei, es zu lesen ...

Ursprüngliche Frage

Die Anwendung, an der ich arbeite, verwendet Prism und WPF mit einer Reihe von Modulen (derzeit 3), von denen eine das Anwendungsmenü enthält. Ursprünglich war das Menü statisch mit Hooks in CompositeCommand / DelegateCommands, was für das Weiterleiten von Tastendrucken an den entsprechenden Moderator hervorragend funktionierte. Jedes MenuItem verwendete ein StackPanel in seiner Kopfzeile, um den Inhalt als eine Kombination aus einem Bild und einer Textbeschriftung anzuzeigen - das war das Aussehen, nach dem ich suchte:

%Vor%

Leider ist die Anwendung ein bisschen komplexer geworden und es ist wünschenswert, dass sich andere Module mit dem Menü registrieren - daher habe ich versucht, das Menü dynamisch zu machen. Das Ziel besteht darin, dass andere Module (über einen Dienst) dem Menü Befehle hinzufügen können - zum Beispiel fügt Modul A einen Menüeintrag im Toolbar-Modul hinzu, der einen Handler in Modul A aufruft. Es gibt ein paar hervorragende Artikel da draußen zu diesem Thema - die beiden, die ich mir angeschaut habe, sind Erstellen eines datengebundenen WPF-Menüs mithilfe einer HierarchicalDataTemplate und WPF-Beispielserie - Databound HierarchicalDataTemplate-Menübeispiel . Dem Ratschlag des Artikels folgend, habe ich es geschafft, ein dynamisch aufgebautes Menü ohne offensichtliche Datenbindungsprobleme zu erstellen - es kann ein Menü mit verknüpften Elementen zu meinem Präsentationsmodell erstellen, das die Struktur einer ObservableCollection im Präsentationsmodell widerspiegelt >

Momentan sieht mein XAML wie folgt aus:

%Vor%

Der dahinter stehende Code für die View macht das Heavy Lifting in der Methode ContentPresenter_Loaded:

%Vor%

Wie Sie sehen können, versuche ich, die StackPanel / Bild / Name-Kombination des ursprünglichen Menüs neu zu erstellen, und zwar einfach im Code dahinter. Der Versuch, dies zu tun, hat nicht so gut geklappt - während die Menüobjekte sicherlich erstellt werden, erscheinen sie nicht als etwas anderes als leere, anklickbare Objekte - das StackPanel, das Bild, der Name usw. werden nicht gerendert . Interessanterweise wird auch der Originaltext im ContentPresent in der HierarchicalDataTemplate gelöscht.

Die Frage ist dann, gibt es eine Möglichkeit, eine Header-Eigenschaft von MenuItem im Load-Ereignis so zu setzen, dass es ordnungsgemäß auf dem UserControl angezeigt wird? Ist die Tatsache, dass die Elemente in der Kopfzeile nicht angezeigt werden, ein Hinweis auf ein DataBinding-Problem? Wenn ja, was wäre die richtige Methode, um den Header an ein transientes Objekt (das StackPanel, das im Load-Ereignishandler erstellt wurde) zu binden?

Ich bin offen dafür, irgendetwas im obigen Code zu ändern - das ist alles eine Art von Prototyping und versucht herauszufinden, wie man mit der dynamischen Menüerstellung am besten umgehen kann. Danke!

    
Matt Jordan 16.09.2010, 03:52
quelle

2 Antworten

9

Ich gestehe, dass ich nicht ganz so tief in Ihr Beispiel eingedrungen bin, wie ich vielleicht sollte, aber wenn ich Code-Behind sehe, der den visuellen Baum durchsucht, denke ich, könnte dies in einem View-Modell expliziter gehandhabt werden ?

Mir scheint, dass Sie in diesem Fall ein ziemlich direktes Ansichtsmodell entwickeln könnten - ein Objekt, das zum Beispiel die Eigenschaften Text , Image , Command und Children enthält - und dann ein einfache Datenvorlage, um sie als MenuItem darzustellen. Dann manipuliert alles, was den Inhalt Ihrer Menüs ändern muss, dieses Modell.

Bearbeiten:

Nachdem Sie sich genauer angeschaut haben, was Sie vorhaben, und die beiden Beispiele, die Sie in Ihrem Blogpost verlinkt haben, hämmere ich meinen Kopf gegen den Schreibtisch. Beide Entwickler scheinen zu glauben, dass die Eigenschaften der Menüelemente, die von der Vorlage generiert werden, über die visuelle Struktur im Ereignis ContentPresenter.Load nach ihrer Erstellung gesucht werden. Nicht so. Dafür steht ItemContainerStyle .

Wenn Sie das verwenden, ist es ziemlich einfach, dynamische Menüs des von Ihnen beschriebenen Typs zu erstellen. Sie benötigen eine MenuItemViewModel -Klasse, die INotifyPropertyChanged implementiert hat und diese öffentlichen Eigenschaften verfügbar macht:

%Vor%

Verwenden Sie dies:

%Vor%

wo die ItemsSource ein ObservableCollection<MenuItemViewModel> ist und diese Vorlage verwendet:

%Vor%

Die Menüs im Fenster stellen genau dar, was sich in der Sammlung befindet, und werden dynamisch aktualisiert, wenn Elemente hinzugefügt und entfernt werden, und zwar sowohl für die Elemente der obersten Ebene als auch für die Nachkommen.

Es gibt kein Klettern im visuellen Baum, kein manuelles Erstellen von Objekten, kein Code-Behind (außer im Ansichtsmodell und in allem, was die Sammlung von vornherein bevölkert).

Ich habe ein ziemlich gründlich gearbeitetes Beispiel dafür gebaut; Sie können das Projekt hier herunterladen.

    
Robert Rossney 16.09.2010, 18:50
quelle
0

Ein anderer möglicher Ansatz könnte darin bestehen, dass das Menü eine Region ist und einer Konvention entspricht, so dass alle zu dieser Region hinzugefügten Ansichten ein ViewModel mit einer Eigenschaft namens MenuHeader aufweisen. Auf diese Weise kann der Region-Adapter den Menü-Header einfach aus dem Datenkontext der Ansicht abrufen und ihn beim Hinzufügen auf das Element setzen.

Ähnliches geschieht in Prism mit Ansichten, die zu einer Tab-Region hinzugefügt wurden. Sie können mehr hier lesen.

Ich hoffe, dies bietet eine nützliche Anleitung.

Danke, Damian

    
Damian Schenkelman 17.09.2010 19:29
quelle

Tags und Links