Wie dynamisch, hinzufügen von MenuItems (mit header) zu einem WPF-Menü

[Edit #3] - an jeden Leser die Frage: tun nicht unter keinen Umständen verwenden Sie die beschriebene Vorgehensweise in dieser Frage. Es ist ein Coding Horror. Ich gestehe dies, wohl wissend, dass alle Programmierer, die an sich gearbeitet haben, in eine Ecke, in der Vergangenheit und (vor allem, wenn das erlernen einer neuen Technologie) sind wir alle wurden in die Irre geführt von anderen, wohlmeinenden Entwickler auf der interweb. Lesen Sie die Antwort von Robert zuerst, dann Lesen Sie diese Frage. Bitte.

[Bearbeiten #2b]

Ich entschuldige mich für die Länge dieser Frage - es ist eine Frage, die hier (am Ende!), aber ich wollte sicher gehen, dass der source-code wurde explizit. Sowieso.

[Edit #2] - Frage Titel geändert, um genau das... Frage.

[Bearbeiten] - ich habe aktualisiert, ein bisschen mehr von der Geschichte, wie ich landete in den design - /code, die ich hier gemacht habe: Obligatorische Blog-Post. Wenn es hilft bei der Klärung der Frage unten, fühlen Sie sich frei, es zu Lesen...

Ursprüngliche Frage

Die Anwendung an der ich arbeite nutzt Prism und WPF, mit einer Anzahl von Modulen (derzeit 3), von denen die die Anwendung hostet Menü. Ursprünglich war das menu statisch mit Haken in CompositeCommand /DelegateCommands, die Super geklappt für routing-Taste drückt, um den zuständigen Moderator. Jedes MenuItem verwendet ein StackPanel-Element im header, um den Inhalt anzuzeigen-wie eine Kombination aus einem Bild und einem text-label - das war der Blick, den ich wollte für:

<Menu Height="48" Margin="5,0,5,0" Name="MainMenu" VerticalAlignment="Top" Background="Transparent">
                <MenuItem Name="MenuFile" AutomationProperties.AutomationId="File">
                    <MenuItem.Header>
                        <StackPanel>
                            <Image Height="24" VerticalAlignment="Center" Source="../Resources/066.png"/>
                            <ContentPresenter Content="Main"/>
                        </StackPanel>
                    </MenuItem.Header>
                    <MenuItem AutomationProperties.AutomationId="FileExit" Command="{x:Static local:ToolBarCommands.FileExit}">
                        <MenuItem.Header>
                            <StackPanel>
                                <Image Height="24" VerticalAlignment="Center" Source="../Resources/002.png"/>
                                <ContentPresenter Content="Exit"/>
                            </StackPanel>
                        </MenuItem.Header>
                    </MenuItem>
                </MenuItem>
                <MenuItem  Name="MenuHelp" AutomationProperties.AutomationId="Help" Command="{x:Static local:ToolBarCommands.Help}">
                    <MenuItem.Header>
                        <StackPanel>
                            <Image Height="24" VerticalAlignment="Center" Source="../Resources/152.png"/>
                            <ContentPresenter Content="Help"/>
                        </StackPanel>
                    </MenuItem.Header>
                </MenuItem>               
            </Menu>

Leider, die Anwendung ist mittlerweile ein wenig komplexer und es ist wünschenswert, dass noch weitere Module registrieren Sie sich mit dem Menü - also, ich habe auf der Suche zu machen, das Menü dynamisch. Das Ziel ist, andere Module (durch eine Dienstleistung) in der Lage sein, um das hinzufügen von Befehlen zum Menü wird - zum Beispiel, Ein Modul fügt einen Menüpunkt in der Symbolleiste Modul, ruft eine Prozedur im Modul A. Es gibt ein paar gute Artikel auf diesem Thema - die zwei habe ich mir angeschaut sind Aufbau einer Datenbindung in WPF-Menü Mit einem HierarchicalDataTemplate und WPF-Sample-Serie - Datenbindung HierarchicalDataTemplate-Menü-Beispiel. Die folgenden Ratschläge in dem Artikel habe ich es geschafft, ein Menü dynamisch aufgebaut, die keine offensichtliche Datenbindung Probleme - Sie können erstellen Sie ein Menü mit Elementen verknüpft, gesichert, um meine Präsentation Modell, was die Struktur der ObservableCollection in der Präsentation Modell

Derzeit ist meine XAML-Code sieht wie folgt aus:

<UserControl x:Class="Modules.ToolBar.Views.ToolBarView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:model="clr-namespace:Modules.ToolBar.PresentationModels"
    xmlns:local="clr-namespace:Modules.ToolBar">
    <UserControl.Resources>
        <model:ToolBarPresentationModel x:Key="modelData" />
        <HierarchicalDataTemplate DataType="{x:Type model:ToolbarObject}"
                                  ItemsSource="{Binding Path=Children}">
            <ContentPresenter Content="{Binding Path=Name}"
                              Loaded="ContentPresenter_Loaded"
                              RecognizesAccessKey="True"/>
        </HierarchicalDataTemplate>
    </UserControl.Resources>
    <UserControl.DataContext>
        <Binding Source="{StaticResource modelData}"/>
    </UserControl.DataContext>
        <Menu Height="48" Margin="5,0,5,0" Name="MainMenu" VerticalAlignment="Top" Background="Transparent"
            ItemsSource="{Binding}">
        </Menu>
    </Grid>
</UserControl>

Den code hinter den anzeigen tut die schwerarbeit im ContentPresenter_Loaded Methode:

   private void ContentPresenter_Loaded(object sender, System.Windows.RoutedEventArgs e)
    {
    ContentPresenter presenter = sender as ContentPresenter;
    if (sender != null)
    {
        DependencyObject parentObject = VisualTreeHelper.GetParent(presenter);
        bool bContinue = true;
        while (bContinue
            || parentObject == null)
        {
            if (parentObject is MenuItem)
                bContinue = false;
            else
                parentObject = VisualTreeHelper.GetParent(parentObject);
        }
        var menuItem = parentObject as MenuItem;
        if (menuItem != null)
        {
            ToolbarObject toolbarObject = menuItem.DataContext as ToolbarObject;
            StackPanel panel = new StackPanel();
            if (!String.IsNullOrEmpty(toolbarObject.ImageLocation))
            {
                Image image = new Image();
                image.Height = 24;
                image.VerticalAlignment = System.Windows.VerticalAlignment.Center;
                Binding sourceBinding = new Binding("ImageLocation");
                sourceBinding.Mode = BindingMode.TwoWay;
                sourceBinding.Source = toolbarObject;
                image.SetBinding(Image.SourceProperty, sourceBinding);
                panel.Children.Add(image);
            }
            ContentPresenter contentPresenter = new ContentPresenter();
            Binding contentBinding = new Binding("Name");
            contentBinding.Mode = BindingMode.TwoWay;
            contentBinding.Source = toolbarObject;
            contentPresenter.SetBinding(ContentPresenter.ContentProperty, contentBinding);
            panel.Children.Add(contentPresenter);
            menuItem.Header = panel;
            Binding commandBinding = new Binding("Command");
            commandBinding.Mode = BindingMode.TwoWay;
            commandBinding.Source = toolbarObject;
            menuItem.SetBinding(MenuItem.CommandProperty, commandBinding);                    
        }
    }
}

Wie Sie sehen können, bin ich versucht zu reproduzieren, ist das StackPanel /Bild /Name-Kombination von original-Menü, tun nur so in der code-behind. Der Versuch zu tun, das hat nicht funktioniert so gut - während der Menü-Objekte sind sicherlich geschaffen, die Sie nicht "erscheint" als etwas anderes als leere, anklickbare Objekte - StackPanel-Element, Bild, Name, etc. nicht gerendert wird. Interessanterweise, es verursacht auch den originalen text in der ContentPresent in der HierarchicalDataTemplate gelöscht werden.

Dann die Frage, gibt es eine Möglichkeit zum festlegen eines MenuItem-Header-Eigenschaft im Load-Ereignis so, dass es angezeigt wird, auf das Benutzersteuerelement richtig? Ist die Tatsache, dass die Elemente in der Kopfzeile nicht angezeigt wird, bezeichnend für ein Binding-problem? Wenn ja, was wäre die richtige Art und Weise binden Sie die Header, um ein transientes Objekt (das StackPanel-Steuerelement, das erstellt wurde, werden in die load-Ereignisprozedur)?

Ich bin offen für Veränderung nichts im code oben - dies ist alle Art von prototyping zusammen, um herauszufinden, der beste Weg, damit umzugehen dynamische Menü-Erstellung.
Danke!

  • +1 für edit #3, hat mich gerettet eine Menge zu Lesen 🙂
InformationsquelleAutor Matt Jordan | 2010-09-16
Schreibe einen Kommentar