0

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.

MrFantastic
  • 87
  • 10
  • 2
    What you're doing wrong is trying to do this in code behind. Instead, you should be binding the `IsChecked` property of the CheckBox to a boolean property of the DataContext object. Whatever that is. Who has a `Name.Name` property? It needs to implement `INotifyPropertyChanged`, though. – 15ee8f99-57ff-4f92-890c-b56153 Jan 06 '18 at 14:27
  • Oh right, the group. Thanks. Not clear what the rest of that means though. "the container will generate different header based on the grouping of Country.Name": Can you clarify that? How many classes are involved? Where do the different headers come from? I only see one header template. What's in `Competitions`? – 15ee8f99-57ff-4f92-890c-b56153 Jan 06 '18 at 15:20
  • `BindingExpression path error: 'Item' property not found on 'object' ''League'`. Sorry, I don't think I'll be able to help you. – 15ee8f99-57ff-4f92-890c-b56153 Jan 06 '18 at 15:48
  • If anything should be a checked list item (nothing should use the `CheckedListItem` template; viewmodels should model concepts, not the UI), it's Country, because that's what's next to the checkbox. What is the meaning of the checkbox? – 15ee8f99-57ff-4f92-890c-b56153 Jan 06 '18 at 16:31
  • Can I see your Item class? Where is it shown in the ui? How does the user know which Item each check box refers to? – 15ee8f99-57ff-4f92-890c-b56153 Jan 06 '18 at 19:30

1 Answers1

2

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.

Milan
  • 616
  • 5
  • 11
  • i copied your example, and i was able to find the checkbox inside my groupitem. what do you mean with "I've already this method"? you are not using this method in your code above, you are trying to get it with ".Template.FindName" instead – Milan Jan 06 '18 at 20:38
  • 1
    i edited the answer. the above code is my attempt to copy paste what you did, and i can find the checkbox inside all 3 comboboxes. – Milan Jan 06 '18 at 21:22