How to insert multiple GroupDescription? - c#

I've made an application that allow to get the live result of a match.
Now the organization of each match is this:
Nation->League->Match list
So I've the Nation that is the container of the League, and the League is the container of the Match. What I did firstly was create a XAML structure that allow me to achieve a container result as said before:
<Window x:Class="GroupBox_Header.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:GroupBox_Header"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid Margin="10">
<ListView Name="lvUsers">
<ListView.View>
<GridView>
<GridViewColumn Header="Home Team" Width="50" DisplayMemberBinding="{Binding HomeTeam}" />
<GridViewColumn Header="Away Team" Width="50" DisplayMemberBinding="{Binding AwayTeam}" />
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="Gray" FontSize="22" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding ItemCount}" FontSize="22" Foreground="Green" FontWeight="Bold" FontStyle="Italic" Margin="10,0,0,0" VerticalAlignment="Bottom" />
<TextBlock Text=" Items" FontSize="22" Foreground="Silver" FontStyle="Italic" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding League}" FontWeight="Bold" Foreground="Gray" FontSize="22" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid>
How you can see there is a ListView that have a GroupStyle definition, in this I save all the Nation. You can try the XAML in your demo solution and check the result.
After this, I've created the struct that allow me to store the value:
public struct League
{
public string Name { get; set; }
}
public struct Country
{
public string Name { get; set; }
public League League;
}
Is very pretty simple, a League struct for save all the leagues and a Country struct for the nations. I've valorized the Country in this way:
List<Country> items = new List<Country>();
items.Add(new Country() { Name = "Italy", League = { Name = "Serie A"} });
items.Add(new Country() { Name = "Italy", League = { Name = "Serie B" } });
items.Add(new Country() { Name = "England", League = { Name = "Premiere League" } });
items.Add(new Country() { Name = "Spain", League = { Name = "Primeira Division" } });
lvUsers.ItemsSource = items;
So I actually have in list Italy (2), England, Spain. I then inserted items in the ItemsSource of the ListView (the XAML control).
For create the header group as the Nation name, I've used a CollectionView:
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(lvUsers.ItemsSource);
PropertyGroupDescription groupNations = new PropertyGroupDescription("Name");
This code allow me to create multiple headers, and the result is exactly what I want. I also did the same for the League, 'cause what I need is insert as a sub-header the League name in Nation container (header):
view.GroupDescriptions.Add(groupNations);
PropertyGroupDescription groupCompetions = new PropertyGroupDescription("League");
view.GroupDescriptions.Add(groupNations);
but actually this is the result:
How you can see only the nation header appear, but the League header doesn't appear inside the Nation header. What am I doing wrong?

You can "fix" it by making League a property (add getter and setter - WPF bindings attach to properties, not fields), making the second group description group by "League.Name" instead of just "League" and make the second group style TextBlock binding bind to Name, not League.
Then you get this:
But I would say you are approaching this the wrong way and modeling your data in a really weird manner. If the ListView is intended to list Matches, then you should bind to a collection of Match objects (or a view over them), and each Match should reference its parent League, and each League should reference its parent Country.
Alternatively, use a tree instead of a list, and then have a Country with a collection of child Leagues, and each League with a collection of child Matches.
Or have both - a League that contains a collection of Matches, but each match has a parent League reference, and similar for Country/League.
The way it is in your code is kind of weird.
Also, I don't understand why you use structs for your objects instead of classes.
Changes to code behind (note, I am not saying this is a good way to do it, see above):
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<Country> items = new List<Country>();
items.Add(new Country() { Name = "Italy", League = new League { Name = "Serie A" } }); // Added "new League"
items.Add(new Country() { Name = "Italy", League = new League { Name = "Serie B" } });
items.Add(new Country() { Name = "England", League = new League { Name = "Premiere League" } });
items.Add(new Country() { Name = "Spain", League = new League { Name = "Primeira Division" } });
lvUsers.ItemsSource = items;
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(lvUsers.ItemsSource);
PropertyGroupDescription groupNations = new PropertyGroupDescription("Name");
view.GroupDescriptions.Add(groupNations);
PropertyGroupDescription groupCompetions = new PropertyGroupDescription("League.Name"); // Changed "League" to "League.Name"
view.GroupDescriptions.Add(groupCompetions); // Fixed the variable name here
lvUsers.ItemsSource = view; // Added, you probably have it but didn't post it
}
}
public struct League
{
public string Name { get; set; }
}
public struct Country
{
public string Name { get; set; }
public League League { get; set; } // added getter and setter
}
Changes to XAML, just 1 line:
<TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="Gray" FontSize="22" VerticalAlignment="Bottom" />
{Binding Name} is {Binding League} in your original code.

Related

How to add subitem in dropbox

I've got unfinished project and my first steps is to modify main menu which is based on combobox and looks like in GitHub desktop application
So now task is to group similar items into subgroups.
For example Log View, Cities View and Stores View should be placed under the group Administration. And years 2017...2020 should be placed under one group.
Unfortunately I can't find solution for this task, maybe ComboBox is not the best solution for this. Maybe someone can give me direction where my solution is? It can either collapsible group or like menu-style, when nested items appears on right of the group.
P.S Administration and Year Table Administration is just empty combobox items.
So, my solution is based on thies https://stackoverflow.com/a/3585244/7283900
I added Expander to my ComboBox, so entire solution now looks like
XAML:
<Window x:Class="WpfApp1_TEST.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_TEST"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid>
<Grid.Resources>
<Style x:Key="GroupItem" TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander x:Name="expander"
ExpandDirection="Down">
<Expander.Header>
<DockPanel>
<TextBlock Text="{Binding Name}" FontWeight="Bold" Margin="2,5,0,2" FontSize="14"/>
</DockPanel>
</Expander.Header>
<Expander.Content>
<Border Margin="5,0,0,0">
<ItemsPresenter >
</ItemsPresenter>
</Border>
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<ComboBox Height="27" HorizontalAlignment="Left"
ItemsSource="{Binding Item}"
Margin="162,109,0,0" VerticalAlignment="Top"
Width="195" Name="cboGroup" >
<ComboBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource GroupItem}"/>
</ComboBox.GroupStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</Grid>
And CodeBehind of this page
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<Item> items = new List<Item>();
items.Add(new Item() { Name = "Item1", Category = "Category_1" });
items.Add(new Item() { Name = "Item2", Category = "Category_1" });
items.Add(new Item() { Name = "Item3", Category = "Category_1" });
items.Add(new Item() { Name = "Item4", Category = "Category_2" });
items.Add(new Item() { Name = "Item5", Category = "Category_2" });
ListCollectionView lcv = new ListCollectionView(items);
lcv.GroupDescriptions.Add(new PropertyGroupDescription("Category"));
//this.comboBox.ItemsSource = lcv;
this.cboGroup.ItemsSource = lcv;
}
public class Item
{
public string Name { get; set; }
public string Category { get; set; }
}
}

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:
if all items in the container are checked, then also the header checkbox need to be checked.
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.
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.

Add GroupDescription dynamically?

I'm looking for a way to add GroupDescriptions dynamically, in particular in the viewmodels, I actually add GroupDescription behind code like this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<Country> items = new List<Country>();
items.Add(new Country() { Name = "Italy", League = new League { Name = "Serie A" } }); // Added "new League"
items.Add(new Country() { Name = "Italy", League = new League { Name = "Serie B" } });
items.Add(new Country() { Name = "England", League = new League { Name = "Premiere League" } });
items.Add(new Country() { Name = "Spain", League = new League { Name = "Primeira Division" } });
lvUsers.ItemsSource = items;
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(lvUsers.ItemsSource);
PropertyGroupDescription groupNations = new PropertyGroupDescription("Name");
view.GroupDescriptions.Add(groupNations);
PropertyGroupDescription groupCompetions = new PropertyGroupDescription("League.Name"); // Changed "League" to "League.Name"
view.GroupDescriptions.Add(groupCompetions); // Fixed the variable name here
lvUsers.ItemsSource = view;
}
}
public struct League
{
public string Name { get; set; }
}
public struct Country
{
public string Name { get; set; }
public League League { get; set; } // added getter and setter
}
and this is my xaml:
<Window x:Class="GroupBox_Header.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:GroupBox_Header"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid Margin="10">
<ListView Name="lvUsers">
<ListView.View>
<GridView>
<GridViewColumn Header="Home Team" Width="50" DisplayMemberBinding="{Binding HomeTeam}" />
<GridViewColumn Header="Away Team" Width="50" DisplayMemberBinding="{Binding AwayTeam}" />
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="Gray" FontSize="22" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding ItemCount}" FontSize="22" Foreground="Green" FontWeight="Bold" FontStyle="Italic" Margin="10,0,0,0" VerticalAlignment="Bottom" />
<TextBlock Text=" Items" FontSize="22" Foreground="Silver" FontStyle="Italic" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding League}" FontWeight="Bold" Foreground="Gray" FontSize="22" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid>
what I want to do is Bind in the ListView a collection that have the group description automatically.
If I understand your question right, you could either control the GroupDescriptions directly from the ViewModel, from code behind or create a custom ListView which let you bind to an ObservableCollection.
ViewModel
Use CollectionViewSource to get a hold of the CollectionView from the ViewModel. You could argue that this is an anti-pattern, but I think it is okay.
Code behind
Create a dependency property in you code behind that on change will update the group descriptors for the view. Then in you xaml bind that property to you ViewModel.
Custom ListView
Inherit from ListView and create a dependency property that let's you bind to an ObservableCollection.
Either way, you need to control different scenarios like, property
changed and collection changed.

WPF stackpanel does not show the whole content

I am using a Combobox to show the contents of two different Lists, the List<Production> productions and the List<Seasons> seasons. Below is the XAML code
<StackPanel Grid.Column=" 1" Grid.Row="2">
<ComboBox x:Name="comboBox" ItemsSource="{Binding ShowProductionsSeasonsNames}" SelectedItem="{Binding SelectedProductionsSeasons}">
<ComboBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontWeight="Bold" FontSize="14"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ComboBox.GroupStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
And this is the code in the viewmodel.
public ListCollectionView ShowProductionsSeasonsNames
{
get
{
List<ProductionsSeasonsNames> items = new List<ProductionsSeasonsNames>();
foreach (var production in this.Productions)
{
items.Add(new ProductionsSeasonsNames() { Name = production.DisplayName, Category = "Productions" });
}
foreach (var seasons in this.Seasons)
{
items.Add(new ProductionsSeasonsNames() { Name = seasons.DisplayName, Category = "Seasons" });
}
ListCollectionView lcv = new ListCollectionView(items);
lcv.GroupDescriptions.Add(new PropertyGroupDescription("Category"));
return lcv;
}
}
public class ProductionsSeasonsNames
{
public string Name { get; set; }
public string Category { get; set; }
}
My issue is that it partly works. Although my productions and seasons lists contain around 100 items each, in the stackpanel only 15 from each list appear. Furthermore when I scroll down or up the focus goes directly to the top of each header without letting me to actually scroll (I am sorry I cant explain this part better). Does anyone have any ideas on this?
Use Grid instead of Stackpanel and set grid height="*" or you can directly put your combobox into Grid.Column=" 1" Grid.Row="2"

Can I have different classes at the same level of a TreeView hierarchy?

I have a hierarchy of classes that I want to display in a WPF TreeView.
Task
Person
Items
Days
The classes include string properties and collections properties. It looks like this:
Laundry
John
Items Collection
Clothes
Washing Powder
Days Collection
Sunday
Thursday
Shopping
Millie
Items
Money
List
Bags
Car
Days
Saturday
I would like to display the strings (Person) as leaves and be able to drill into the collections (Items, Days) to reach the strings inside.
I found this explanation http://social.msdn.microsoft.com/forums/en-US/wpf/thread/e40e0a8f-7758-4b69-80f6-1c657294d019/ that works well for this layout:
Task
Person
Items
Days
But I can't work out how to adjust it to represent different classes at the same hierarchical level. All help appreciated, especially an example.
I've worked it out, I think. Let me know if there's a more sensible approach than this.
This blog post helped a lot, but unfortunately the code is riddled with typos:
David Sackstein's - HierarchicalDataTemplate and TreeView
The key is that every class that is bound to the TreeView is derived from a baseclass. This allows you to get the different types into the TreeView by binding it to a collection of their base class. Then in the XAML you can create a HierarchicalDataTemplate for each DataType and everything just works.
<Window x:Class="HierarchicalDataTemplateAndTreeView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:HierarchicalDataTemplateAndTreeView"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Task}" ItemsSource="{Binding Path=Children}">
<StackPanel Orientation="Horizontal" Margin="3">
<TextBlock Text="{Binding Path=Name}" FontSize="16" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Person}" ItemsSource="{Binding Path=Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" Foreground="Purple" FontSize="14" FontWeight="Bold" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:ItemCollection}" ItemsSource="{Binding Path=Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" Foreground="Blue" FontSize="12" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Item}" ItemsSource="{Binding Path=Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" Foreground="Blue" FontSize="11" FontStyle="Italic" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:DayCollection}" ItemsSource="{Binding Path=Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" Foreground="RosyBrown" FontSize="12" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Day}" ItemsSource="{Binding Path=Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" Foreground="RosyBrown" FontSize="12" FontStyle="Italic" />
</StackPanel>
</HierarchicalDataTemplate>
</Window.Resources>
<StackPanel>
<TreeView Name="treeView"/>
</StackPanel>
</Window>
using System.Collections.Generic;
using System.Windows;
using System.Windows.Documents;
namespace HierarchicalDataTemplateAndTreeView
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
treeView.ItemsSource = GetData();
}
public List<Composite> GetData()
{
List<Composite> list = new List<Composite>()
{
new Task
{
Name = "Laundry", Children = new List<Composite>()
{
new Person { Name = "John" },
new ItemCollection
{
Name = "Items", Children = new List<Composite>()
{
new Item { Name = "Clothes" },
new Item { Name = "Washing Powder" }
}
},
new DayCollection
{
Name = "Days", Children = new List<Composite>()
{
new Day { Name = "Sunday" },
new Day { Name = "Thursday" }
}
}
}
},
new Task
{
Name = "Shopping", Children = new List<Composite>()
{
new Person { Name = "Millie" },
new ItemCollection
{
Name = "Items", Children = new List<Composite>()
{
new Item { Name = "Money" },
new Item { Name = "List" },
new Item { Name = "Bags" }
}
},
new DayCollection
{
Name = "Days", Children = new List<Composite>()
{
new Day { Name = "Saturday" }
}
}
}
}
};
return list;
}
}
}
using System.Collections.Generic;
namespace HierarchicalDataTemplateAndTreeView
{
public class Composite
{
public string Name { get; set; }
public List<Composite> Children { get; set; }
}
public class Task: Composite
{
}
public class Person : Composite
{
}
public class ItemCollection : Composite
{
}
public class Item : Composite
{
}
public class DayCollection : Composite
{
}
public class Day : Composite
{
}
}

Categories