none
WPF: ContextMenu Items an TabContorl Items binden RRS feed

  • Frage

  • Hallo

    Der Titel fasst meine Frage schon kurz und knackig zusammen.

    Ich habe ein ContextMenu in dem ControlTemplate einer TabControl. Wie binde ich nun die Items von der TabControl an dieses ContextMenu in den Resourcen?

    Grüße

    Mittwoch, 15. Oktober 2014 11:34

Antworten

Alle Antworten

  • Hallo,
    wenn ich dich richtig verstehe, dann willst du einfach jedem TabItem das selbe Konextmenü geben.

    Dafür ist es am einfachsten einen Style zu definieren, welcher auf die TabItems angewendet wird:

    <Style TargetType="TabItem">
        <Setter Property="TabItem.ContextMenu">
            <Setter.Value>
                <ContextMenu>
                    <ContextMenu.Resources>
                        <Style TargetType="MenuItem">
                            <EventSetter Event="Click" Handler="OnTabContextMenuItemClicked"/>
                        </Style>
                    </ContextMenu.Resources>
                    <MenuItem Header="Delete" Tag="Delete" />
                </ContextMenu>
            </Setter.Value>
        </Setter>
    </Style>
    Dieser Style kann entweder in den Resourcen (den des Window/TabControl/...) eingefügt werden. Dieses wird dann auf jedes TabItem-Control angwenet. Durch die angefügte x:Key-Eigenschaft und dem manuellen aufrufen des Resourcenkeys kannst du auch nur einzelne TabItems mit dem Style versehen.

    Die Eventhandler musst du entweder über einen Style für die Items registrieren oder durch Commands ersetzen. WPF spielt leider nicht immer mit wenn es um Events in Resourcen geht.

    Der Codebehind könnte wie folgt aussehen:

    private void OnTabContextMenuItemClicked(object sender, RoutedEventArgs e)
    {
        var contextMenu = FindParent<ContextMenu>(sender as MenuItem);
        var tabItem = contextMenu.PlacementTarget as TabItem;
        var tabControl = tabItem.Parent as TabControl;
    
        switch ((sender as MenuItem).Tag.ToString())
        {
            case "Delete":
                tabControl.Items.Remove(tabItem);
                break;
        }
    }
    
    //Hilfsmethode zum ermitteln des Parents, so funktioniert es auch mit SubItems
    T FindParent<T>(FrameworkElement fe) where T : FrameworkElement
    {
        if (fe.Parent is T)
            return fe.Parent as T;
        else
            return FindParent<T>(fe.Parent as FrameworkElement);
    }
    Der Code ermittelt das ContextMenu, das TabItem und das TabControl um entsprechend damit arbeiten zu können.


    Tom Lambert - C# MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Mittwoch, 15. Oktober 2014 12:11
    Moderator
  • Leider minimal falsch verstanden.

    Ich habe das ControlTemplate des TabPanels so verändert, dass links die Tabs sind und rechts ein Knopf, der ein ContextMenu öffnet. Dieses ContextMenu soll mit ALLEN TabItems (bzw. deren Headern) gefüllt werden. 

    Grüße

    Mittwoch, 15. Oktober 2014 12:34
  • Das wird so nicht ohne weiteres gehen. Da das ContextMenu und die Items sich nicht im selben Baum (LogicalTree/VisualTree) befindet, sind Datenbindungen nicht möglich.

    Entweder du achtest nun auf eine Änderung der Items und passt das ContextMenu manuell an, oder aber du bindest TabControl und ContextMenu an die selbe liste im Codebehind.

    Bei letzterem nimmst du am Besten einen benutzerdefinierten Typ um die Tabs darstellen zu können:

    public class MyItem : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members
    
        protected void OnPropertyChanged(string propertyName)
        {
            var evt = this.PropertyChanged;
            if (evt != null)
                evt(this, new PropertyChangedEventArgs(propertyName));
        }
        public event PropertyChangedEventHandler PropertyChanged;
    
        #endregion
    
        private string _Header;
        public string Header
        {
            get
            {
                return this._Header;
            }
            set
            {
                if (this._Header != value)
                {
                    this._Header = value;
                    this.OnPropertyChanged("Header");
                }
            }
        }
    
        private UIElement _Content;
        public UIElement Content
        {
            get
            {
                return this._Content;
            }
            set
            {
                if (this._Content != value)
                {
                    this._Content = value;
                    this.OnPropertyChanged("Content");
                }
            }
        }
    }

    Im Model/Codebehind erstellst du nun eine Auflistung davon:

    public ObservableCollection<MyItem> MyTabItemsCollection { get; set; }

    Im Konstruktor musst du diese entsprechend initialisieren.
    INotifyPropertyChanged und ObservableCollection<T> informieren die GUI über eine Eigenschaftenänderung.

    Im XAML kannst du nun das TabControl an die Auflistung binden:

    <TabControl Margin="0,56,0,0" Name="tc" ItemsSource="{Binding MyTabItemsCollection}">
        <TabControl.ItemTemplate>
            <DataTemplate >
                <TextBlock Text="{Binding Header}"/>
            </DataTemplate>
        </TabControl.ItemTemplate>
        <TabControl.ContentTemplate>
            <DataTemplate>
                <ContentPresenter Content="{Binding Content}"/>
            </DataTemplate>
        </TabControl.ContentTemplate>
    </TabControl>
    Beim ContextMenu sieht das ganz ähnlich aus:
    <ContextMenu ItemsSource="{Binding MyTabItemsCollection}">
        <ContextMenu.ItemTemplate>
            <DataTemplate >
                <TextBlock Text="{Binding }"/>
            </DataTemplate>
        </ContextMenu.ItemTemplate>
    </ContextMenu>
    Diese Art der Datenbindung funktioniert, weil sie unabhängig von den einzelnen Bäumen ist.


    Tom Lambert - C# MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    • Als Antwort markiert TZDEV Mittwoch, 15. Oktober 2014 15:00
    • Tag als Antwort aufgehoben TZDEV Mittwoch, 15. Oktober 2014 15:11
    Mittwoch, 15. Oktober 2014 13:18
    Moderator
  • Funktioniert leider nicht.

    Das ContextMenu bleibt leer und der Content wird gar nicht mehr angezeigt. Denke, dass sich da was mit meiner ControlTemplate in die Quere kommt.

    Gibt es noch eine andere Möglichkeit das Ganze zu lösen?

    Mittwoch, 15. Oktober 2014 15:18
  • Wie schon oben geschrieben kannst du die Tabs auch manuell durch gehen und hinzufügen. Allerdings wüsste ich nicht, warum das Binding nicht klappen sollte.

    Ich habe mein Testprojekt mal hoch geladen. Solange du alles richtig umgesetzt hast, sollte es auch in einem Control keine Probleme geben.

    Wenn du keinen Unterschied findest, wäre es nicht schlecht wenn du mal deinen Code zeigst.


    Tom Lambert - C# MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Mittwoch, 15. Oktober 2014 15:30
    Moderator
  • Hab rausgefunden, dass es mit der 1. Variante diverse Komplikationen gibt.

    Also hab ich jetzt das ContextMenu mit einer Schleife gefüllt.

    Ist es möglich irgendwie auf das Tab zu wechseln, wenn man auf das ContextMenu Item klickt? 

    Mittwoch, 15. Oktober 2014 15:54
  • Erstelle für weitere Fragen bitte auch einen neuen Thread damit die Übersicht gewahrt bleibt.

    In Kurzfassung geschrieben, kannst du das TabItem in der Tag-Eigenschaft des MenuItems speichern und im Click-Eventhandler über den sender-Parameter wieder heran kommen. Das Item kannst du nun der SelectedItem-Eigenschaft des TabControls zuweisen.

    PS: Wenn diese Frage soweit abgeschlossen ist, markiere bitte auch eine oder mehrere Antworten.


    Tom Lambert - C# MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    • Als Antwort markiert TZDEV Mittwoch, 15. Oktober 2014 16:10
    Mittwoch, 15. Oktober 2014 16:03
    Moderator