Why are Groups in WPF ListView empty? - c#

I have a class in my application with three properties GroupName, ItemName, and Data. I am have a ListView to group a collection of these classes by GroupName and display their ItemName property in a text box. The problem is that when I run the code, the groups appear correctly but none of them display any members.
Here is the xaml code:
<ListView x:Name="MyList">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<TextBlock FontWeight="Bold" Text="{Binding Name}"/>
</Expander.Header>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type testProgram:MyClass}">
<TextBlock Text="{Binding ItemName}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And here is the code behind:
public partial class MyListView
{
public MyListView(ObservableCollection<MyClass> items)
{
InitializeComponent();
Items = items;
var v = CollectionViewSource.GetDefaultView(Items);
v.GroupDescriptions.Add(new PropertyGroupDescription("GroupName"));
MyList.ItemsSource = v;
}
public ObservableCollection<MyClass> Items { get; set; }
}
When I remove the <ListView.GroupStyle>... and set MyList.ItemsSource = Items; then everything shows up properly.
I suspect that the issue is between ItemsSource = v, DataType = "{x:Type testProgram:MyClass}", and {Binding ItemName}, but I don't know what is breaking or how to fix it.

Your ControlTemplate is missing an ItemsPresenter. WPF uses an ItemsPresenter in the GroupItem template to mark the location where the actual items will be placed when the template is expanded. Since you have no presenter, the detail items are not displayed.
Try changing your template to this:
<Expander IsExpanded="True">
<Expander.Header>
<TextBlock FontWeight="Bold" Text="{Binding Name}"/>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>

Related

How to set template for listbox in WPF(Windows 10 Weather)

I am trying to create a Windows 10 weather application in WPF using C#. I need to have a Listbox to display recent 10 day weather section. I must set template for Listbox items. I tried this:
<ListBox Grid.Row="1" x:Name="DailyWeatherListBox">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel>
<!--...-->
</StackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
This is the recent 10 day weather section (Windows 10 Weather)
Look the Windows 10 Weather. I put this image for disuse Windows 10.
I also don't know how to set Scrollbar in the Listbox corners. I would be very thankful if you could help.
I would start off something like this:
<ListBox ItemsSource="{Binding WeeklyWeather}"
SelectedItem="{Binding SelectedDailyWeather, Mode=TwoWay}">
//Use ItemTemplate to set how item looks "inside"
//I'll leave design details for you
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Day}"/>
<Image Source={Binding WheatherPicturePath}/>
<TextBlock Text="{Binding Temperature}"/>
<TextBlock Text="{Binding Description}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
//ItemsPanel defines container for items. It can be StackPanel, Wrapanel, Grid, etc
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
//You use this place to design how container normally looks like
<Border Background="White">
//DataTemplate defined above is placed in ContentPresenter
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
//Here we catch "IsSelected" event and re-design our template to visually reflect "selected"
<Trigger Property="IsSelected" Value="true">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border Background="Gray">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Here are couple ideas how for those bindings.
public class WeatherViewModel
{
public string Day { get; set; }
public string WheatherPicturePath { get; set; }
public string Temperature { get; set; }
public string Description { get; set; }
}
public class BindedDataContext
{
public ObservableCollection<WeatherViewModel> WeeklyWeather { get; set; }
public WeatherViewModel SelectedDailyWeather { get; set; }
//...
}
Your approaches for code-behind may differ, but they need to be in place for you to use those bindings.
For such scrollbar I would look into Change scrollviewer template in listbox
You can set a simple ListBox template like this:
<ListBox Grid.Row="1" x:Name="DailyWeatherListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<!--Insert XAML for One Item-->
<Label Content="{Binding}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBoxItem>Item 1</ListBoxItem>
<ListBoxItem>Item 2</ListBoxItem>
</ListBox>
In most real world scenarios where there is more then once piece of information per item to be displayed you would define how you want your data to be displayed through a DataTemplate. For example if I wanted to display both the high temperature and the low temperature and style them separately: I would first create a DailyWeather model in c# and create a DataTemplate for it, like so:
public class DailyWeather
{
public int High { get; set; }
public int Low { get; set; }
// You Would Add All Your Other Data You Want to Display Here
}
In your page resources (or another resource dictionary like in App.xaml):
<Window.Resources>
<DataTemplate DataType="{x:Type DailyWeather}">
<Grid>
<StackPanel>
<Label FontSize="18" Content="{Binding High}"/>
<Label FontSize="14" Content="{Binding Low}"/>
</StackPanel>
</Grid>
</DataTemplate>
</Window.Resources>
On your ListBox no ItemTemplate is required ...
<ListBox Grid.Row="1" x:Name="DailyWeatherListBox"/>
... because once you set the source to a List<DailyWeather>, (or do a binding like Siim Haas's answer suggests) your program will find the DataTemplate we defined for a DailyWeather object that we included in the resources.

Not able to show grouped data correctly in WPF DataGrid

I am trying to display my EF query result (which has a group by clause) and display itinto a DataGrid. I am trying to copy the example here
http://wpftutorial.net/DataGrid.html
So here is my XAML code
<DataGrid ItemsSource="{Binding QueryResult}">
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander>
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
<TextBlock Text="{Binding Path=ItemCount}"/>
<TextBlock Text="Items"/>
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
</DataGrid>
Here is my ViewModel
internal void GenerateReport()
{
Data = Project.GetReport(); **// this is List<IGrouping<string, MyType>>**
//QueryResult is a ListCollectionView property
QueryResult= new ListCollectionView(Data);
QueryResult.GroupDescriptions.Add(new PropertyGroupDescription("train"));
}
This is what my result of query looks like
I do get the key correctly but if you look at my code, I have a collection against the key item. THat is not displaying here. How do I get that to display against each train?
EDIT
I think I have more details on this. Here is how my QueryResult collection looks like
Basically my QueryResult has 10 items where each item has a Name, Items and ItemsCounts.
Each Items itself has 1 item inside which has a key and Group. This group basically contains the items that I want to display in my DataGrid as grouped against parent 10 records that are appearing.

GroupBox Header Not Showing

I followed an this StackOverflow question, which has a link to another article, on how to group data, but my header is not showing. Can someone point me to the problem of why my header is not showing in the grouping of my data. The only thing different between the article and my markup that I see is that my data is in a different format, but I have no control on how I receive it.
Representation of my data:
<MyDetails xmlns="clr-namespace:MyCompany.Mapping;assembly=Mapping" xmlns:scg="clr-
namespace:System.Collections.Generic;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<MyDetails.Details>
<scg:List x:TypeArguments="MyAttributes" Capacity="64">
<MyAttributes Bit="0" Channel="1" Name="SomeName" IOAttribute="O" />
</scg:List>
</MyDetails.Details>
</MyDetails>
ViewModel Property I bind to:
ObservableCollection<MyDetails> Channels= new
ObservableCollection<MyDetails>();
MyDetails Class Public Property (Defined as a DataMember):
List<MyAttributes> Details = new List<MyAttributes>();
MyAttributes Class Public Properties (All defined as a DataMembers):
property string Channel {get; set;}
property string Bit {get; set;}
property string Name {get; set;}
property string Attribute {get; set;}
XAML Resource Header:
<CollectionViewSource x:Key="MyDetails" Source="{Binding Channels[0].Details}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Channel" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
XAML:
<ItemsControl ItemsSource="{Binding Source={StaticResource MyDetails}}">
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<GroupBox Margin="10" >
<GroupBox.Header>
<TextBlock Text="{Binding Channel}" FontWeight="Bold" FontSize="16" /> <!-- Does not show -->
</GroupBox.Header>
<ItemsPresenter />
</GroupBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ItemsControl.GroupStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Bit}" Width="50" />
<TextBlock Text="{Binding Path=Name}" Width="150" />
<TextBlock Text="{Binding Path=Attribute}" Width="50" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
DataContext against each GroupItem will be of CollectionViewGroup type which contains information about each group like Items or Name. You need to change binding in a group header to Name which will be the value grouped by
<GroupBox.Header>
<TextBlock Text="{Binding Name}" FontWeight="Bold" FontSize="16" />
</GroupBox.Header>

Toggling between Grouping and non grouping of datagrid in WPF

I have currently populated a datagrid with certain amount of rows and grouped the rows based on department.
So ,all I can see in my output is a "Department-based Grouped Datagrid".
Is it possible to toggle between grouping and non grouping of datagrid rows ?
For Example : If a user doesnt wants to see records based on groups, he'll click on radiobutton and datagrid will populate rows without grouping and vice-versa.
Thanks in advance.
Here is sample code inside DataGrid.GroupStyle :
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander Margin="15 0 0 0" IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="white" Text="{Binding Path=emp}" FontWeight="Bold" Margin="5 0 0 0"/>
<TextBlock Foreground="white" Text="{Binding Path=empCount}" Margin="10 0 3 0"/>
<TextBlock Foreground="white" Text="emps"/>
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
Sorry if this is old, but I might as well answer.
The edit you need to do are not in your GroupStyle but in the code of your ViewModel. You probably bind your listview ItemSource to an ObservableCollection<>. Internally A view of your collection is made, and this collectionView is the one that is actually bound to your ListView. You can explicitly obtain that view in this way:
public ObservableCollection<T> HeldCollection{ get; set; }
#region properties for the view
CollectionView _HeldView;
public CollectionView HeldView
{
get
{
if (_HeldView == null)
{
_HeldView = (CollectionView)CollectionViewSource.GetDefaultView(HeldCollection);
//_HeldView.GroupDescriptions.Add(GroupDescription);
}
return _HeldView;
}
}
You can then edit the GroupDescription since it is a property of the CollectionView, for example in response to a button toggle.

WPF Treeview HierarchicalDataTemplate ItemTemplateSelector

I am trying to create a simple 2 level Treeview in WPF (MVVM approach). For my first level I have a standard datatemplate, for my second level I want to use a Template Selector so that I can change the appearance of each item based on one of its properties.
Below is my Treeview xaml
<Treeview ItemsSource={Binding ListA}>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding ListB}" ItemTemplateSelector={StaticResource TemplateSelector}>
<Textblock Text={Binding Name}/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
My first level is
<Textblock Text={Binding Name}/>
will just display a name
For my second level the TemplateSelector is returning a datatemplate which is something like
<DataTemplate x:Key="SomeKey">
<StackPanel Orientation="Horizontal">
<ViewBox>
-----
</ViewBox>
<TextBlock Text={Binding Name}/>
</StackPanel>
</DataTemplate>
But all I see for my second level is my second level ViewModel name. I double checked the template selector and it is definitely returning the correct data template but it is just not getting displayed.
Can anyone please point me in the right direction?
Edit -- Added more code as per request
this is my template selector
public class DataFieldsDataTemplateSelector : DataTemplateSelector
{
public DataTemplate AlphaTemplate { get; set; }
public ------
public ------
public DataFieldsDataTemplateSelector()
{
//This is getting the template from my ResourceDictionary
AlphaTemplate = (DataTemplate)dDictionary["alphaTemplate"];
}
public override DataTemplate SelectTemplate(object item,DependencyObject container)
{
//Somecode
return AlphaTemplate;
}
}
my template for AlphaTemplate in my dictionary is
<DataTemplate x:Key="alphaTemplate">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Viewbox IsHitTestVisible="False">
<Path Data="M0,0L56.622002,0 56.622002,14.471 35.715,14.471 35.715,64 20.715,64 20.715,14.471 0,14.471z" Stretch="Uniform" Fill="{DynamicResource ButtonForegroundNormal}" VerticalAlignment="Center" Width="15" Height="15" Margin="0,0,0,0" RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<TransformGroup>
<TransformGroup.Children>
<RotateTransform Angle="0" />
<ScaleTransform ScaleX="1" ScaleY="1" />
</TransformGroup.Children>
</TransformGroup>
</Path.RenderTransform>
</Path>
</Viewbox>
<textBlock Text="{Binding Name}/>
</Grid>
</DataTemplate>
my class TypeB contains a Name(Text) and DataType(Text) Fields
if the DataType is Alpha I return AlphaTemplate in my templateSelector and so on
I have an action(dragDrop) on the window which adds items to the second level. And I want the template selector should pick up the correct datatemplate for that dropped item based on its DataType
My main ViewModel contains ICollectionView of TypeA Objects and Each TypeA ViewModel contains ICollectionView of TypeB ViewModels.
Let me know if you need anything
I dont know what is wrong with this as this will require to debug the code, but what you wanted to achieve can be done by defining the default DataTemplate for your TypeB and switching the content depending on the binding like this:
<DataTemplate DataType="{x:Type TypeB}">
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate">
<Setter.Value>
<!-- Default template here for your item -->
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding XYZ}" Value="true">
<Setter Property="ContentTemplate">
<Setter.Value>
<!-- Different template for your item -->
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
Thanks

Categories

Resources