2

Summary

I've a DropDownButton that contains all leagues of a specific nations. So essentially the user select a Nation from a ComboBox and the Leagues of the selected Nation will be added inside the DropDownButton. Until here no problem.

What I need to do is: organize the Leagues inside the DropDownButton for Nation. So imagine that inside the DropDownButton I've this organization:

Italy 
     Item1
     Item2
     Item3
England
     Item1
     Item2
Spain 
     Item1
     Item2
... etc ...

Item type

How you can see the item are organized by nation. Before explain what I did for achieve this result I have to say that I'm using a CheckedListItem class, that is essentially a class that bind a CheckBox to the item of the DropDownButton. This class is pretty simple:

public class CheckedListItem<T> : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private bool isChecked;
    private T item;

    public CheckedListItem() { }

    public CheckedListItem(T item, bool isChecked = false)
    {
        this.item = item;
        this.isChecked = isChecked;
    }

    public T Item
    {
        get { return item; }
        set
        {
            item = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item"));
        }
    }

    public bool IsChecked
    {
        get { return isChecked; }
        set
        {
            isChecked = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsChecked"));
        }
    }
}

Essentially make available the Item<T> and if is selected. For make this working in XAML I wrote this code in the Window.Resources:

<DataTemplate x:Key="NormalItemTemplate">
    <CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Item.Name}" 
              x:Name="Leagues" Checked="Leagues_Checked" Unchecked="Leagues_Unchecked" />
</DataTemplate>

<DataTemplate x:Key="SelectionBoxTemplate" >
    <TextBlock Text="{DynamicResource displayedNation}"/>
</DataTemplate>

<DataTemplate x:Key="CombinedTemplate">
    <ContentPresenter x:Name="Presenter" Content="{Binding}" ContentTemplate="{StaticResource NormalItemTemplate}" />
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, Controls:DropDownButton, 1}}"
                     Value="{x:Null}">
            <Setter TargetName="Presenter" Property="ContentTemplate"
                    Value="{StaticResource SelectionBoxTemplate}" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

<!--This is for the group item !-->

<Style TargetType="{x:Type GroupItem}" x:Key="containerStyle">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type GroupItem}">
                <!--Here, we tell that each group of item will be placed under Expander control,
                    and this expander control will by default will have style we defined in above code.-->
                <Expander IsExpanded="False" x:Name="ComboExpander" Header="{TemplateBinding Content}"
                          HeaderTemplate="{TemplateBinding ContentTemplate}">
                    <ItemsPresenter />
                </Expander>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Explain the problem

Now for achieve the grouping in the DropDownButton I've created first a CollectionViewSource in the Window.Resource like this:

<CollectionViewSource Source="{Binding Leagues}" x:Key="GroupedData">
    <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="Item.Country" />
    </CollectionViewSource.GroupDescriptions>
</CollectionViewSource>

<DataTemplate x:Key="GroupHeader">
    <TextBlock Text="{Binding Name}" Margin="10,0,0,0" Foreground="#989791"/>
</DataTemplate>

in the DropDownButton I set this structure:

<Controls:DropDownButton Content="Leagues" x:Name="LeagueMenu"
                          ItemsSource="{Binding Source={StaticResource GroupedData}}"
                          ItemTemplate="{StaticResource CombinedTemplate}" >
    <Controls:DropDownButton.GroupStyle>
        <GroupStyle ContainerStyle="{StaticResource containerStyle}"
                    HeaderTemplate="{StaticResource GroupHeader}">
        </GroupStyle>
    </Controls:DropDownButton.GroupStyle>
</Controls:DropDownButton>

(Please, note that I'm using MahApp)

So essentially I added a template for the CheckedListItem and as ItemSource the GroupedData that how you can see from code above bind the ObservableCollection of Leagues.

Data population

When a Nation is selected a list of Leagues available for the selected Nations will be added in the DropDownButton, the code for achieve this:

private ObservableCollection<CheckedListItem<League>> _leagues = new ObservableCollection<CheckedListItem<League>>();
public ObservableCollection<CheckedListItem<League>> Leagues
{
    get
    {
        return _leagues;
    }
    set
    {
        _leagues = value;
        OnPropertyChanged();
    }
}

the method that's called for add the League of the selected Nation is this:

public void GetLeagues(string link, string nation)
{
    Task.Run(() =>
    {
        var leagues = //get some collection

        foreach (var league in leagues)
        {
            var championship = new CheckedListItem<League>();
            championship.Item = new League { Name = league.Name, Link = league.Link, Country = nation };
            _leagues.Add(championship);
        }
    });
}

Essentially I create a CheckedListItem of League that as model contains this property:

public class League
{
    public string Name { get; set; }
    public string Link { get; set; }
    public string Country { get; set; }
}

The result that should be appear is something like this:

enter image description here

Taken from this tutorial.

But instead I get this result:

enter image description here

As you can see I doesn't have the header grouping with the Nation name. I also get no error in XAML or in code, so I don't know why the header doesn't appear.

I know that the problem could be a bit complicated, so for any question or more details ask me and I'll try to answer.

Demo solution:

https://mega.nz/#!g4YlTL4A!G4WXy1t64Q4yImYNvzgwGbieX1mhKnXO2OiuAO3FDg0

Pieter Witvoet
  • 2,773
  • 1
  • 22
  • 33
AgainMe
  • 760
  • 4
  • 13
  • 33
  • I don't have MahApp here, but what I usually do in situations like these is adding different background colors to various elements. That makes it easier to see which templates and elements are being displayed. – Pieter Witvoet Oct 12 '16 at 13:14
  • @PieterWitvoet The problem is on `combinedTemplate` that not working in the grouping. – AgainMe Oct 12 '16 at 13:35
  • Seems like a duplicate of [this question](http://stackoverflow.com/questions/39943204/organize-items-in-dropdownbutton/39967087). – Grx70 Oct 12 '16 at 16:11

1 Answers1

4

Instead of trying to get the third party control to work, I'd alter the working ComboBox to suit your needs:

Champ

MainWindow.xaml

<Controls:MetroWindow x:Class="WpfApplication1.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:Controls="http://metro.mahapps.com/winfx/xaml/controls"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="700" Width="525">
    <Window.Resources>
        <ResourceDictionary>

            <CollectionViewSource Source="{Binding Leagues}" x:Key="GroupedData">
                <CollectionViewSource.GroupDescriptions>
                    <PropertyGroupDescription PropertyName="Item.Country" />
                </CollectionViewSource.GroupDescriptions>
            </CollectionViewSource>

            <DataTemplate x:Key="GroupHeader">
                <TextBlock Text="{Binding Name}" Margin="10,0,0,0" Foreground="#989791"/>
            </DataTemplate>

            <Style TargetType="{x:Type GroupItem}" x:Key="containerStyle">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type GroupItem}">
                            <Expander   IsExpanded="False" x:Name="ComboExpander" Header="{TemplateBinding Content}"
                                        HeaderTemplate="{StaticResource GroupHeader}">
                                <ItemsPresenter />
                            </Expander>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>

            <DataTemplate x:Key="NormalItemTemplate">
                <CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Item.Name}" x:Name="Leagues" />
            </DataTemplate>
            <DataTemplate x:Key="HeaderTemplate">
                <TextBlock Text="Campionati"/>
            </DataTemplate>
            <DataTemplate x:Key="CombinedTemplate">
                <ContentPresenter x:Name="Presenter" 
                   Content="{Binding}"
                   ContentTemplate="{StaticResource NormalItemTemplate}" />
                <DataTemplate.Triggers>
                    <DataTrigger
                        Binding="{Binding RelativeSource={RelativeSource FindAncestor, ComboBoxItem, 1}}"
                        Value="{x:Null}">
                        <Setter TargetName="Presenter" Property="ContentTemplate"
                            Value="{StaticResource HeaderTemplate}" />
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </ResourceDictionary>

    </Window.Resources>    
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <ComboBox x:Name="LeagueMenuComboBox" 
                  ItemsSource="{Binding Source={StaticResource GroupedData}}"
                  ItemTemplate="{StaticResource CombinedTemplate}"
                  HorizontalContentAlignment="Center">
            <ComboBox.GroupStyle>
                <GroupStyle ContainerStyle="{StaticResource containerStyle}"
                            HeaderTemplate="{StaticResource GroupHeader}">
                </GroupStyle>
            </ComboBox.GroupStyle>
        </ComboBox>
    </Grid>
</Controls:MetroWindow>
Funk
  • 10,976
  • 1
  • 17
  • 33
  • Thanks for the time that you've dedicated. I've few question before accept your answer: 1. Is possible set a default text in the combobox independently from the selected item? 2. Is possible change the color of GroupBox text (I mean the country name color)? Thanks. – AgainMe Oct 12 '16 at 16:34
  • @AgainMe You're welcome, to answer your questions: 1. That's what the `CombinedTemplate` is for, it makes sure the default text stays fixed no matter what. 2. You can get blue country names setting the `GroupHeader` content to: `` – Funk Oct 12 '16 at 18:01
  • Thanks, I need to wait stackoverflow time limit to send you the bounty. Just one thing: I get no default text displayed in combobox unfortunately.. – AgainMe Oct 13 '16 at 07:57
  • @AgainMe The default text is set in the `HeaderTemplate`. Note that the Ancestor in the DataTrigger Binding is now of type `ComboBoxItem` (instead of `DropDownButton`). If that doesn't help try replacing the xaml file in your linked project with the code I've posted. – Funk Oct 13 '16 at 09:56
  • I understood why not working. So when I start the app I've no text on the ComboBox, the same case if I check an item, the text only will appear if I select the item itself on the combox. Is not possible keep the text when the application run? I though to set selectedindex = 0 to fix this, but maybe you know another solution. – AgainMe Oct 13 '16 at 10:45
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/125585/discussion-between-funk-and-againme). – Funk Oct 13 '16 at 10:52