MrFantastic
MrFantastic

Reputation: 87

How to access to controls inside a GroupItem?

I'm using a CollectionView to group my item as following:

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

the full XAML structure is this:

<ComboBox x:Name="CompetitionCombo"
          ItemsSource="{Binding Source={StaticResource GroupedData}}"
          ItemTemplate="{StaticResource CombinedTemplate}">
           <ComboBox.GroupStyle>
            <GroupStyle ContainerStyle="{StaticResource containerStyle}"
                        HeaderTemplate="{StaticResource GroupHeader}">
            </GroupStyle>
</ComboBox.GroupStyle>

<DataTemplate x:Key="CombinedTemplate">
        <ContentPresenter x:Name="Presenter" 
                          Content="{Binding}"
                          ContentTemplate="{StaticResource NormalItemTemplate}" />
</DataTemplate>

 <DataTemplate x:Key="GroupHeader">
            <DockPanel>
                <Image Source="{Binding Name.ISO, 
                       Converter={StaticResource CountryIdToFlagImageSourceConverter}}"
                       Stretch="None" Width="23" Height="18" RenderOptions.BitmapScalingMode="HighQuality" />
                <TextBlock Text="{Binding Name.Name}" Margin="10,0,0,0" Foreground="Black" FontWeight="Bold"/>
                <CheckBox Margin="5,0,0,0" HorizontalAlignment="Right" IsChecked="True" x:Name="PART_DisplayLeague" 
                          Unchecked="CheckBoxCountry_Unchecked" Checked="CheckBoxCountry_Checked" />
            </DockPanel>
        </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>

as you can see in the GroupHeader I've a CheckBox, I'm trying to access to this CheckBox in the following way:

//current value of league.Country = "England";

//Iterate the available groups
foreach(CollectionViewGroup gp in CompetitionCombo.Items.Groups)
{ 
  if(league.Country.Name == (gp.Name as Country).Name)
  {
       //Get the container
       GroupItem container = CompetitionCombo.ItemContainerGenerator.ContainerFromItem(gp) as GroupItem;

      //Get the control
      var control = container.Template.FindName("PART_DisplayLeague", container);

      control.IsChecked = true;
  }
}

I get correctly the groups, but the control variable is even Null, seems that the code cannot find PART_DisplayLeague: the CheckBox associated to the Country in the header of the container.

Note:

the league is a simple implementation of the following:

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

public class Country
{
   public string Name { get; set; } 
   public string ISO { get; set; } 
}

what am I doing wrong?

UPDATE

Forgot to add the collection implementation, in particular Competition:

public List<CheckedListItem<League>> Competitions = new List<CheckedListItem<League>>();

for add element to the collection:

Competitions.Add(new CheckedListItem<League>
{
    IsChecked = true,
    Item = new League { Name = "foo" } 
});

is possible find an implementation of CheckedListItem here

as you can see Item is part of CheckedListItem.

UPDATE #2

Practice example of the situation:

Suppose that Competitions contains the following Items:

Competitions.Add(new CheckedListItem<League>
{
    IsChecked = true,
    Item = new League { Name = "foo", Country = new Country { Name = "Italy"}} 
});

Competitions.Add(new CheckedListItem<League>
{
    IsChecked = true,
    Item = new League { Name = "foo2", Country = new Country { Name = "Italy"}} 

});

Competitions.Add(new CheckedListItem<League>
{
    IsChecked = true,
    Item = new League { Name = "foo", Country = new Country { Name = "England"}} 
});

the UI Structure will this:

[] Italy     <- this is the GroupHeader sort by xaml
    [] foo   <- Items of the container
    [] foo2  
[] England
    [] foo3

now what I'm asking for: When I check foo2 and foo the Italy CheckBox need to be Checked, also when I uncheck an Item contained in the Italy container, then, Italy must be unchecked.

This mean two thing:

  1. if all items in the container are checked, then also the header checkbox need to be checked.
  2. if almost one item in the container isn't checked, then the checkbox in the container must be unchecked 'cause this mean that not all the items associated to this country are checked.

My problem's that I can't find the CheckBox associated to the Country 'cause is generated by the Xaml, that's why I can't use a Binding property as suggested.

Upvotes: 0

Views: 1339

Answers (1)

Milan
Milan

Reputation: 616

post the code that someone can just copy paste and reproduce your problem, then people can not only solve your problem but also suggest a better solution.

in the meantime, you can find your checkbox in the following manner:

var control = FindVisualChildren<CheckBox>(container);

Here you can find the method FindVisualChildren.


Edit: a full working example

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

public class Country
{
    public string Name { get; set; }
    public string ISO { get; set; }
}
public class CheckedListItem<T> 
{
    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;

        }
    }

    public bool IsChecked
    {
        get { return isChecked; }
        set
        {
            isChecked = value;

        }
    }
}
 public partial class MainWindow : Window 
{
    public ObservableCollection<CheckedListItem<League>> Competitions { get; set; }
    public MainWindow()
    {
        InitializeComponent();


        Competitions = new ObservableCollection<CheckedListItem<League>> { (new CheckedListItem<League>
        {
            IsChecked = true,
            Item = new League { Name = "foo", Country = new Country { Name = "Italy" } }
        }),
        (new CheckedListItem<League>
        {
            IsChecked = true,
            Item = new League { Name = "foo2", Country = new Country { Name = "Italy" } }

        }),
        (new CheckedListItem<League>
        {
            IsChecked = true,
            Item = new League { Name = "foo", Country = new Country { Name = "England" } }
        }) };

        this.DataContext = this;
    }

    public void CheckBoxCountry_Checked(object sender, EventArgs args)
    {
        foreach (CollectionViewGroup gp in CompetitionCombo.Items.Groups)
        {

                //Get the container
                GroupItem container = CompetitionCombo.ItemContainerGenerator.ContainerFromItem(gp) as GroupItem;

            //Get the control
            var control = FindVisualChildren<CheckBox>(container);



        }
    }

    public IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
    {
        if (depObj != null)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(depObj, i);

                if (child != null && child is T)
                    yield return (T)child;

                foreach (T childOfChild in FindVisualChildren<T>(child))
                    yield return childOfChild;
            }
        }
    }
}

.xaml

<Window x:Class="WpfApp2.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:WpfApp2"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>
            <CollectionViewSource Source="{Binding Competitions}" x:Key="GroupedData">
                <CollectionViewSource.GroupDescriptions>
                    <PropertyGroupDescription PropertyName="Item.Country" />
                </CollectionViewSource.GroupDescriptions>
            </CollectionViewSource>

            <DataTemplate x:Key="NormalItemTemplate">
                <DockPanel>
                    <TextBlock Text="sdf" Margin="10,0,0,0" Foreground="Black" FontWeight="Bold"/>
                </DockPanel>
            </DataTemplate>
            <DataTemplate x:Key="CombinedTemplate">
                <ContentPresenter x:Name="Presenter" 
                          Content="{Binding}"
                                   ContentTemplate="{StaticResource NormalItemTemplate}"
                           />
            </DataTemplate>
            <DataTemplate x:Key="GroupHeader">
                <DockPanel>


                    <TextBlock Text="{Binding Name.Name}" Margin="10,0,0,0" Foreground="Black" FontWeight="Bold"/>
                    <CheckBox Margin="5,0,0,0" HorizontalAlignment="Right" IsChecked="True" x:Name="PART_DisplayLeague" 
                           Checked="CheckBoxCountry_Checked" />
                </DockPanel>
            </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>
        </Grid.Resources>
        <ComboBox x:Name="CompetitionCombo"
          ItemsSource="{Binding Source={StaticResource GroupedData}}"
          ItemTemplate="{StaticResource CombinedTemplate}">
            <ComboBox.GroupStyle>
                <GroupStyle ContainerStyle="{StaticResource containerStyle}"
                        HeaderTemplate="{StaticResource GroupHeader}">
                </GroupStyle>
            </ComboBox.GroupStyle>
        </ComboBox>
    </Grid>
</Window>

I added NormalItemTemplate because you didn't provide the template.

When you check the checkbox in the UI, you will call the method CheckBoxCountry_Checked in mainwindow.cs and that method will find the checkbox inside all 3 comboboxes.

Upvotes: 2

Related Questions