Mojack
Mojack

Reputation: 144

MahApps Change LeftWindowCommands Items when changing the tab

I have a tabControl in my MainWindow and a working LeftWindowCommand

<Controls:MetroWindow.LeftWindowCommands>
    <Controls:WindowCommands >
        <Button x:FieldModifier="public" x:Name="btnOpenBanMenu" Click="btnOpenBanMenu_Click">
            <StackPanel Orientation="Horizontal">
                <Rectangle Width="20"
               Height="20"
               Fill="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}">
                    <Rectangle.OpacityMask>
                        <VisualBrush Stretch="Fill" Visual="{StaticResource bans}" />
                    </Rectangle.OpacityMask>
                </Rectangle>
                <TextBlock Margin="4 0 0 0"
               VerticalAlignment="Center"
               Text="Bans"/>
            </StackPanel>
        </Button> 
    </Controls:WindowCommands>
</Controls:MetroWindow.LeftWindowCommands>

It's all fine. But now i want to use the LeftWindowCommand as a "Sub-Menu-Bar", so if you change the selected Tab, the LeftWindowCommands-Bar should also change there items and the actions behind the buttons. I've played around with the visibility, but that's not what i want.

For better understanding:

enter image description here


You see the items "Giveaway, Losung, Songrequest". These items are inside my TabControl.

And now i want to change the "Sub Menu"-Items (described in the picture) when i select a different tab then giveaway.

Can someone guide me, how to do that?


EDIT2: Finally it works with MVVM, but i still dont know, how to bind the LeftWindowCommands.

MainModel:

class MainModel
{
    public string Header { get; set; }
    public MahApps.Metro.Controls.MetroContentControl Content { get; set; }
    public MahApps.Metro.Controls.WindowCommands LeftWindowCommands { get; set; }
}

MainViewModel:

class MainViewModel : BaseViewModel
{
    private ObservableCollection<Model.MainModel> _tabItems;
    public ObservableCollection<Model.MainModel> tabItems
    {
        get { return _tabItems; }
        set
        {
            _tabItems = value;
            OnPropertyChanged("tabItems");
        }

    }

    public MainViewModel()
    {
        _tabItems = new ObservableCollection<Model.MainModel>()
        {
            new Model.MainModel
            {
                Header = "Giveaway",
                Content = new Controls.ucGiveaway(),
                LeftWindowCommands = LeftWindowCommandsGiveaway()
            },
            ... etc 
        };
    }

    private MahApps.Metro.Controls.WindowCommands LeftWindowCommandsGiveaway()
    {
        MahApps.Metro.Controls.WindowCommands command = new MahApps.Metro.Controls.WindowCommands();
        command.Items.Add(
            new Button { Content = "MyButton #1", Foreground = Brushes.Red });

        return command;
    }
}

DataContext:

<Controls:MetroWindow.DataContext>
    <ViewModels:MainViewModel/>
</Controls:MetroWindow.DataContext>

TabControl:

            <TabControl.ItemTemplate>
            <DataTemplate>
                <TextBlock
                Text="{Binding Header}" />
            </DataTemplate>
        </TabControl.ItemTemplate>
        <TabControl.ContentTemplate>
            <DataTemplate>
                <Controls:MetroContentControl
                    Content="{Binding Content}" />
            </DataTemplate>
        </TabControl.ContentTemplate>
    </TabControl>

This works. The designer shows all tabs & content. How can i bind the WindowCommands? I want something like:

<Controls:MetroWindow.LeftWindowCommands>
    <Controls:WindowCommands ItemsSource="{Binding LeftWindowCommands}">
    </Controls:WindowCommands>
</Controls:MetroWindow.LeftWindowCommands>

Furthermore i want to be able to add more than one button in my MainViewModel. Something like:

private MahApps.Metro.Controls.WindowCommands LeftWindowCommandsGiveaway()
    {
        MahApps.Metro.Controls.WindowCommands command = new MahApps.Metro.Controls.WindowCommands();
        command.Items.Add(
            new Button { Content = "MyButton #1", Foreground = Brushes.Red });

        command.Items.Add(
            new Button { Content = "MyButton #2", Foreground = Brushes.Red });

        return command;
    }

Upvotes: 1

Views: 1446

Answers (2)

Jensen
Jensen

Reputation: 85

I think the problem is you binds WindowCommands.ItemSource to a WindowCommands object instead of a collection of controlelements (like button, textblock)

here is my solution:

MainWindow.xaml

<mah:MetroWindow x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <mah:MetroWindow.LeftWindowCommands>
        <mah:WindowCommands ItemsSource="{Binding Buttons}">
            
        </mah:WindowCommands>
    </mah:MetroWindow.LeftWindowCommands>
    <TabControl x:Name="MainTab" ItemsSource="{Binding TabItems}">
        
    </TabControl>
</mah:MetroWindow>

MainModel.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Media;

namespace WpfApp1
{
    public class MainModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private ObservableCollection<TabCustomItem> mTabItems;

        private ObservableCollection<Button> mButtons;

        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public ObservableCollection<TabCustomItem> TabItems
        {
            get { return mTabItems; }
            set 
            { 
                mTabItems = value; 
                OnPropertyChanged("TabItems");
            }
        }

        public ObservableCollection<Button> Buttons
        {
            get { return mButtons; }
            set 
            {
                mButtons = value;
                OnPropertyChanged("Buttons");
            }
        }

        public MainModel() 
        {

            ObservableCollection<Button>  Buttons1 = new ObservableCollection<Button>()
            {
                new Button()
                {
                    Content = "Press Tab #1"
                }
            };
            ObservableCollection<Button>  Buttons2 = new ObservableCollection<Button>()
            {
                new Button()
                {
                    Content = "Press Tab #2"
                }
            };

            ObservableCollection<Button> Buttons3 = new ObservableCollection<Button>()
            {
                new Button()
                {
                    Content = "Press Tab #3"
                }
            };

            Buttons = Buttons1;

            mTabItems = new ObservableCollection<TabCustomItem>
            {
                new TabCustomItem()
                {
                    Header = "Tab #1",
                    ToolItemsSource = Buttons1

                },
                
                new TabCustomItem()
                {
                    Header = "Tab #2",
                    ToolItemsSource = Buttons2
                },

                new TabCustomItem()
                {
                    Header = "Tab #3",
                    ToolItemsSource = Buttons3
                }
            };
        }

        
    }
}

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using MahApps.Metro.Controls;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : MetroWindow
    {
        private MainModel mMainModel;
        public MainWindow()
        {
            InitializeComponent();
            this.MainTab.SelectionChanged += MainTab_SelectionChanged;
            mMainModel = new MainModel();
            DataContext = mMainModel;
        }

        private void MainTab_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            TabCustomItem ti = (sender as TabControl).SelectedItem as TabCustomItem;
            mMainModel.Buttons = ti.ToolItemsSource;
        }

    }
}

TabCustomItem.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using MahApps.Metro.Controls;

namespace WpfApp1
{
    public class TabCustomItem : TabItem
    {
        public static readonly DependencyProperty ToolItemsSourceProperty = DependencyProperty.Register(nameof(ToolItemsSource), typeof(ObservableCollection<Button>),
                                                                    typeof(TabCustomItem), new PropertyMetadata(null));

        public ObservableCollection<Button> ToolItemsSource
        {
            get { return (ObservableCollection<Button>)GetValue(ToolItemsSourceProperty); }
            set { SetValue(ToolItemsSourceProperty, value); }
        }


    }
}

you can add whatever control to windowcommands by generating more general control collections. I can't reference the selected tab in data binding and that's why i use event handler to assign the current control collection to the data i bind to (variable Buttons)

maybe rewriting a tabcontrol and add a binding variable will make it simple (reference that variable in data binding)

Upvotes: 0

appa yip yip
appa yip yip

Reputation: 1454

Ideally, you would use Bindings, but since you are using code-behind, here's a simple solution (it is up to you if you want to adapt it to some patter like MVVM):

Basically what this code does is:

  • There's a List of UIElements containing all the sub-menus (they can be anything, from a simple Button to a StackPanel full of elements).

    • IMPORTANT: The items on the list must be ordered, which means index 0 => sub-menu for tab index 0.
  • Inside the WindowCommands there's a TransitioningContentControl which will be responsible for containing the sub-menu.

  • Everytime the selected tab changes, I load the n position of the List to the TransitioningContentControl (n is the selected index of the TabControl).

Output:

Output

And here's the code I've used for the example, so you can adapt it:

Code-behind:

public partial class MainWindow : MetroWindow
{
    public List<UIElement> LeftWindowCommands { get; private set; }

    public MainWindow()
    {
        InitializeComponent();

        LeftWindowCommands = new List<UIElement>();

        var StackPanelForTab1 = new StackPanel() { Orientation = Orientation.Horizontal };
        var StackPanelForTab2 = new StackPanel() { Orientation = Orientation.Horizontal };
        var StackPanelForTab3 = new StackPanel() { Orientation = Orientation.Horizontal };

        // You can add as many children as you want
        StackPanelForTab1.Children.Add(new Button { Content = "MyButton #1", Foreground = Brushes.Red });
        StackPanelForTab2.Children.Add(new Button { Content = "MyButton #2", Foreground = Brushes.Black });
        StackPanelForTab3.Children.Add(new Button { Content = "MyButton #3", Foreground = Brushes.Blue });

        // MUST add items in the right order on the list
        // MUST have the sabe amount of tabs on the TabControl and items on the list
        LeftWindowCommands.Add(StackPanelForTab1);
        LeftWindowCommands.Add(StackPanelForTab2);
        LeftWindowCommands.Add(StackPanelForTab3);
    }

    private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.Source is TabControl)
        {
            MyContentControl.Content = LeftWindowCommands[MyTabControl.SelectedIndex];
        }
    }
}

Window:

<Controls:MetroWindow x:Class="WpfTests.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
        xmlns:local="clr-namespace:WpfTests"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

    <Controls:MetroWindow.LeftWindowCommands>
        <Controls:WindowCommands>
            <Controls:TransitioningContentControl x:Name="MyContentControl" />
        </Controls:WindowCommands>
    </Controls:MetroWindow.LeftWindowCommands>

    <TabControl SelectionChanged="TabControl_SelectionChanged" x:Name="MyTabControl" >
        <TabItem Header="Tab #1">
            <Label>#1</Label>
        </TabItem>
        <TabItem Header="Tab #2">
            <Label>#2</Label>
        </TabItem>
        <TabItem Header="Tab #3">
            <Label>#3</Label>
        </TabItem>
    </TabControl>
</Controls:MetroWindow>

You'll need this:

using MahApps.Metro.Controls;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

If you try to adapt it to MVVM and have any troubles I'm here to help.

Upvotes: 2

Related Questions