WPF grouped combobox scrolls groups not items - wpf

We are attempting to implement simple grouping of items within a ComboBox using the ICollectionView. The grouping and sorting being used in the CollectionView works correctly. But the popup items list created by the ComboBox does not function as intended because the scroll bar scrolls the groups not the items.
eg: If there were 2 groups of 25 items, then the scroll bar would have two positions/points to scroll through rather than the desired 50.
Can someone explain why the scroll bar scrolls the groups not the items within the groups, and how we might change this behavior?
Viewmodel setting up the ICollectionView:
public ViewModel()
{
CurrenciesView.Filter = CurrencyFilter;
CurrenciesView.SortDescriptions.Add(new SortDescription("MajorCurrency", ListSortDirection.Descending));
CurrenciesView.SortDescriptions.Add(new SortDescription("Code", ListSortDirection.Ascending));
CurrenciesView.GroupDescriptions.Add(new PropertyGroupDescription("MajorCurrency", new CurrencyGroupConverter()));
public ICollectionView CurrenciesView { get { return CollectionViewSource.GetDefaultView(currencies); } }
private ObservableCollection<Currency> currencies = new ObservableCollection<Currency>();
public ObservableCollection<Currency> Currencies
{
get { return this.currencies; }
set
{
if (this.currencies != value)
{
this.currencies = value;
this.PropertyChanged(this, new PropertyChangedEventArgs("Currencies"));
}
}
}
XAML UserControl hosting the ComboBox
<UserControl x:Class="FilterView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:ViewModels">
<UserControl.Resources>
<ResourceDictionary>
<DataTemplate x:Key="CurrencyItem">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Code}" FontWeight="ExtraBold"/>
<TextBlock Text="{Binding Description}" Margin="10,0,0,0"/>
</StackPanel>
</DataTemplate>
<Style TargetType="ComboBox">
<Setter Property="MinWidth" Value="100"/>
<Setter Property="Margin" Value="0,5,0,5"/>
</Style>
<DataTemplate x:Key="GroupHeader">
<TextBlock Text="{Binding Name}" Padding="3"/>
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0">Currency</Label>
<ComboBox Grid.Column="1" Grid.Row="0" Name="Currency"
ItemsSource="{Binding Path=CurrenciesView, Mode=OneWay}"
ItemTemplate="{StaticResource CurrencyItem}"
SelectedValuePath="Code"
IsSynchronizedWithCurrentItem="True">
<ComboBox.GroupStyle>
<GroupStyle HeaderTemplate="{StaticResource GroupHeader}"/>
</ComboBox.GroupStyle>
</ComboBox>
</Grid>
</UserControl>

Fixed by setting CanContentScroll = false on PART_Popup template for the child ScrollViewer.
<UserControl x:Class="FilterView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:ViewModels">
<UserControl.Resources>
<ResourceDictionary>
<DataTemplate x:Key="CurrencyItem">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Code}" FontWeight="ExtraBold"/>
<TextBlock Text="{Binding Description}" Margin="10,0,0,0"/>
</StackPanel>
</DataTemplate>
<Style TargetType="ComboBox">
<Setter Property="MinWidth" Value="100"/>
<Setter Property="Margin" Value="0,5,0,5"/>
</Style>
<DataTemplate x:Key="GroupHeader">
<TextBlock Text="{Binding Name}" Padding="3"/>
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0">Currency</Label>
<ComboBox Grid.Column="1" Grid.Row="0" Name="Currency"
ItemsSource="{Binding Path=CurrenciesView, Mode=OneWay}"
ItemTemplate="{StaticResource CurrencyItem}"
SelectedValuePath="Code"
IsSynchronizedWithCurrentItem="True"
**ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.CanContentScroll="True"**>
<ComboBox.GroupStyle>
<GroupStyle HeaderTemplate="{StaticResource GroupHeader}"/>
</ComboBox.GroupStyle>
</ComboBox>
</Grid>
</UserControl>
Or in ComboBox derived control:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Popup popup = GetTemplateChild("PART_Popup") as Popup;
if (popup != null)
{
ScrollViewer scrollViewer = GetVisualChild<ScrollViewer>(popup.Child);
if (scrollViewer != null)
{
scrollViewer.CanContentScroll = false;
}
}
}

<Combobox ScrollViewer.CanContentScroll="False"></Combobox>
Use above code it will solve problem.
Regards,
Ratikanta

Related

How to avoid selecting DataGridRow in ParentDataGrid by Clicking in DataGridDetailsPresenter?

I have a DataGrid (ParentDataGrid) with a DataGridDetailsPresenter. In the DataGridDetailsPresenter I have another DataGrid (ChildDataGrid). If I select a Row in the ChildDataGrid the surrounding DataGridRow of the ParentDataGrid is selected too.
I get that both the ParentDataGrid and the ChildDataGrid throws the SelectionChanged event. I tried to deselect selection in ParentDataGrid. But then the primary selection in the ParentDataGrid is changed. Therefore I want to avoid selecting DataGridRow in ParentDataGrid.
How to avoid selecting DataGridRow in ParentDataGrid by Clicking in DataGridDetailsPresenter?
This is my DataTemplate
<DataTemplate DataType="{x:Type MyViewModel}">
<DataTemplate.Resources>...
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridRow}">
<Border ...>
<SelectiveScrollingGrid>
<SelectiveScrollingGrid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</SelectiveScrollingGrid.ColumnDefinitions>
<SelectiveScrollingGrid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</SelectiveScrollingGrid.RowDefinitions>
<DataGridRowHeader Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" .../>
<DataGridCellsPresenter Grid.Row="0" Grid.Column="1" .../>
<DataGridDetailsPresenter Grid.Row="1" Grid.Column="1" .../>
</SelectiveScrollingGrid>
</Border ...>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataTemplate.Resources>
<Grid ...>
<DataGrid x:Name="ParentDataGrid" ...>
<DataGrid.Columns>...</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<Grid ...>
<DataGrid x:Name="ChildDataGrid"...>...</DataGrid>
</Grid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
</Grid>
</DataTemplate>
Handle SelectedCellsChanged event in your parent DataGrid.
private void DataGrid_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
DataGridRow row = (DataGridRow)DgrdParent.ItemContainerGenerator.ContainerFromItem(DgrdParent.SelectedItem);
if (row != null)
row.IsSelected = false;
}
And if you want to get the selected child DataGrid, you can use its SelectionChanged event.
<DataGrid x:Name="DgrdParent" ItemsSource="{Binding .}" CurrentCellChanged="DgrdParent_CurrentCellChanged_1" RowDetailsVisibilityMode="Visible" SelectionChanged="DgrdParent_SelectionChanged_1" SelectedCellsChanged="DataGrid_SelectedCellsChanged_1" >
<DataGrid.Resources>
<Style TargetType="DataGridDetailsPresenter">
</Style>
</DataGrid.Resources>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid x:Name="DgrdChild" ItemsSource="{Binding Employees}" SelectionChanged="DataGridInner_SelectionChanged_1" />
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
Handler :
private void DataGridInner_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("DataGridInner_SelectionChanged_1 > Original source = " + ((DataGrid)sender).Name);
}

Textbox two way binding not triggering

I have a tab control with 3 objects, 2 lists and a textbox. The text box is bound two way :
<TabControl x:Name="tcTabs" ItemsSource="{Binding Rooms, UpdateSourceTrigger=PropertyChanged}" Margin="5" BorderThickness="1" IsSynchronizedWithCurrentItem="True">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="22"/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding ReceivedMessages}" DisplayMemberPath="Raw" Grid.Row="0" Grid.Column="0" BorderThickness="0" />
<ListBox ItemsSource="{Binding Users}" DisplayMemberPath="Nick" Visibility="{Binding Type, Converter={StaticResource UserListVisibilityConverter}}" Grid.Row="0" Grid.Column="1" BorderThickness="1,0,0,0" BorderBrush="#FFBBBBBB" Width="130" />
<TextBox Text="{Binding CurrentInput, Mode="TwoWay"}" Grid.Row="1" Grid.ColumnSpan="2" BorderThickness="0,1,0,0" BorderBrush="#FFBBBBBB" Height="22" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
Backing object :
public string CurrentInput
{
get
{
return _currentInput;
}
set
{
if (value != _currentInput)
{
_currentInput = value;
OnPropertyChanged();
}
}
}
Problem is, when I change the text and click another tab it does not update the backing field (does not even hit the setter), however if I change then click the listbox it does...
Any reason for this odd behaviour?
That is not an odd behaviour and has been asked multiple times before. Read about Binding.UpdateSourceTrigger, also see the remarks of the respective property you bind.
I've solve this problem (Twoway Binding) by manual trigger the databinding engine using
DataContext = this;

How to setup a grid as template for an Items control?

I'm trying to create an ItemsControl that uses a grid as its ItemsPanel in such a way that it has two columns, where the first columns width is the width of the widest item in that column, and has as may rows needed to display all the items
Basically, I want the following, but somehow within an ItemsControl so that I can bind to a collection of objects:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label Content="{Binding Items[0].Header}"/>
<TextBox Text="{Binding Items[0].Content}" Grid.Column="1"/>
<Label Content="{Binding Items[1].Header}" Grid.Row="1"/>
<TextBox Text="{Binding Items[1].Content}" Grid.Row="1" Grid.Column="1"/>
<Label Content="{Binding Items[2].Header}" Grid.Row="2"/>
<TextBox Text="{Binding Items[2].Content}" Grid.Row="2" Grid.Column="1"/>
</Grid>
Edit : Rachels answer worked great, here is a working example.
(I moved the Grid.IsSharedSizeScope="True" to the ItemsPanel, not sure if Rachel meant to put it in the ItemTemplate (which didn't work))
namespace WpfApplication23
{
public partial class Window1 : Window
{
public List<Item> Items { get; set; }
public Window1()
{
Items = new List<Item>()
{
new Item(){ Header="Item0", Content="someVal" },
new Item(){ Header="Item1", Content="someVal" },
new Item(){ Header="Item267676", Content="someVal" },
new Item(){ Header="a", Content="someVal" },
new Item(){ Header="bbbbbbbbbbbbbbbbbbbbbbbbbb", Content="someVal" },
new Item(){ Header="ccccccc", Content="someVal" }
};
InitializeComponent();
DataContext = this;
}
}
public class Item
{
public string Header { get; set; }
public string Content { get; set; }
}
}
<Window x:Class="WpfApplication23.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Grid.IsSharedSizeScope="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="ColumnOne" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Content="{Binding Header}"/>
<TextBox Text="{Binding Content}" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
There are multiple problems here for an ItemsControl:
Getting your first column to match the width of the largest item
Generating a dynamic number of rows
Generating more than one item for each iteration of the ItemsControl
The last one is really the biggest problem, because an ItemsControl wraps each ItemTemplate in a ContentPresenter, so there is no default way of creating more than one item in the panel for each Iteration of the ItemsControl. Your end result would look like this:
<Grid>
...
<ContentPresenter>
<Label Content="{Binding Items[0].Header}"/>
<TextBox Text="{Binding Items[0].Content}" Grid.Column="1"/>
</ContentPresenter>
<ContentPresenter>
<Label Content="{Binding Items[1].Header}" Grid.Row="1"/>
<TextBox Text="{Binding Items[1].Content}" Grid.Row="1" Grid.Column="1"/>
</ContentPresenter>
<ContentPresenter>
<Label Content="{Binding Items[2].Header}" Grid.Row="2"/>
<TextBox Text="{Binding Items[2].Content}" Grid.Row="2" Grid.Column="1"/>
</ContentPresenter>
</Grid>
My best suggestion would be to create an ItemTemplate that contains a 1x2 Grid, and use Grid.IsSharedSizeScope to make the width of the first column shared. (The ItemsPanelTemplate would remain the default StackPanel.)
This way, the end result would look like this:
<StackPanel>
<ContentPresenter>
<Grid IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="ColumnOne" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Content="{Binding Header}"/>
<TextBox Text="{Binding Content}" Grid.Column="1"/>
</Grid>
</ContentPresenter>
<ContentPresenter>
<Grid IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="ColumnOne" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Content="{Binding Header}"/>
<TextBox Text="{Binding Content}" Grid.Column="1"/>
</Grid>
</ContentPresenter>
...
</StackPanel>
You can use a ListView
<ListView ItemsSource="{Binding MyList}">
<ListView.View>
<GridView>
<GridView.ColumnHeaderContainerStyle>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="Visibility" Value="Collapsed" />
</Style>
</GridView.ColumnHeaderContainerStyle>
<GridViewColumn
Header=""
Width="Auto"
DisplayMemberBinding="{Binding Header}"/>
<GridViewColumn
Header=""
DisplayMemberBinding="{Binding Value}"/>
</GridView>
</ListView.View>
</ListView>
the ColumnHeaderContainerStyle hides the GridViewHeader

Stretching the items in a WPF ListView within a ViewBox

I have a frustrating problem that I would much appreciate some help with. I have a ListView within a ViewBox and I can't get the items within the ListView to stretch horizontally.
Here is the XAML:
<Window x:Class="WpfApplication3.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3"
Title="Window1">
<Window.Resources>
<local:Inning x:Key="inning">
<local:Inning.Skins>
<local:Skin SkinNumber="1"></local:Skin>
<local:Skin SkinNumber="2"></local:Skin>
<local:Skin SkinNumber="3"></local:Skin>
</local:Inning.Skins>
</local:Inning>
</Window.Resources>
<Grid DataContext="{StaticResource inning}">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Background="Black"
Text="SKINS"
Foreground="White"
FontSize="80"
FontWeight="Bold"
HorizontalAlignment="Center"></TextBlock>
<Grid Margin="2"
Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Viewbox>
<ListView Name="lvSkinNumbers"
ItemsSource="{Binding Skins}"
HorizontalAlignment="Stretch"
Background="Black"
VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="250"
VerticalAlignment="Stretch"
LineStackingStrategy="BlockLineHeight"
Margin="2"
TextAlignment="Center"
HorizontalAlignment="Stretch"
Background="Black"
Foreground="White"
Text="{Binding SkinNumber}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Viewbox>
<Viewbox Grid.Column="1">
<ListView Name="lvFirstInningSkins"
ItemsSource="{Binding Skins}"
Grid.Column="1"
HorizontalContentAlignment="Stretch"
Background="Black">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="250"
VerticalAlignment="Stretch"
LineStackingStrategy="BlockLineHeight"
Margin="2"
TextAlignment="Center"
HorizontalAlignment="Stretch"
Background="Green"
Foreground="White"
Text="{Binding SkinNumber}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Viewbox>
<Viewbox Grid.Column="2"
HorizontalAlignment="Stretch">
<ListView Name="lvSecondInningSkins"
ItemsSource="{Binding Skins}"
Grid.Column="2"
HorizontalAlignment="Stretch"
Background="Black">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="250"
VerticalAlignment="Stretch"
LineStackingStrategy="BlockLineHeight"
Margin="2"
TextAlignment="Center"
HorizontalAlignment="Stretch"
Background="Green"
Foreground="White"
Text="{Binding SkinNumber}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Viewbox>
</Grid>
</Grid>
</Window>
Here is the code behind with the definitions of the Skin and Inning objects:
using System;
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace WpfApplication3
{
public class Inning
{
private ObservableCollection<Skin> _skins = new ObservableCollection<Skin>();
public ObservableCollection<Skin> Skins
{
get { return _skins; }
set { _skins = value; }
}
}
public class Skin : INotifyPropertyChanged
{
public Skin()
{
}
public Skin( int skinNumber, Inning inning )
{
this.SkinNumber = skinNumber;
this.Inning = inning;
}
public Inning Inning { get; set; }
public int SkinNumber { get; set; }
public int SkinCount
{
get { return this.Inning.Skins.Count; }
}
public void Notify( string propertyName )
{
if ( PropertyChanged != null )
PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
}
public event PropertyChangedEventHandler PropertyChanged;
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
}
The number of skins that can occur in an inning can change, and can be changed by the user, so I've put the ListViews into ViewBoxes so they automatically resize accordingly when the number of skins change. The resulting window can be seen here: http://i52.tinypic.com/244wqpl.jpg
I've tried all sorts of combinations of HorzontalAlignment="Stretch" and HorizontalContentAlignment="Stretch" and tried modifying the ItemsPanel template but I can't for the life of me seem to figure out how to get the ListView to stretch horizontally. Is what I'm trying to do impossible without some code behind to alter the width of the ListView dynamically? Or am I missing something really simple?
Any help that anyone can offer would be most appreciated.
Thanks,
Matthew
Try setting the ItemContainerStyle for your ListView to something like:
<ListView>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
You also might need to set <Viewbox Stretch="Fill"/>.
After this, I think you can remove all those other "HorizontalAlignment = Stretch" and "HorizontalContentAlignment = Stretch" setters in your code since it probably won't be necessary anymore.
Unexpectedly setting ScrollViewer.HorizontalScrollBarVisibility="Disabled" also worked for me:
<ListView ItemsSource="{Binding SourceList}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">

WPF items not visible when grouping is applied

I'm having this strange issue with my ItemsControl grouping. I have the following setup:
<ItemsControl Margin="3" ItemsSource="{Binding Communications.View}" >
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander>
<Expander.Header>
<Grid>
<Grid.ColumnDefinitions >
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ItemCount, StringFormat='{}[{0}] '}" FontWeight="Bold" />
<TextBlock Grid.Column="1" Text="{Binding Name, Converter={StaticResource GroupingFormatter}, StringFormat='{}Subject: {0}'}" FontWeight="Bold" />
</Grid>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ItemsControl.GroupStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock FontWeight="Bold" Text="{Binding Inspector, Converter={StaticResource NameFormatter}, StringFormat='{}From {0}:'}" Margin="3" />
<TextBlock Text="{Binding SentDate, StringFormat='{}{0:dd/MM/yy}'}" Grid.Row="1" Margin="3"/>
<TextBlock Text="{Binding Message }" Grid.Column="1" Grid.RowSpan="2" Margin="3"/>
<Button Command="vm:CommunicationViewModel.DeleteMessageCommand" CommandParameter="{Binding}" HorizontalAlignment="Right" Grid.Column="2">Delete</Button>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In my ViewModel, I expose a CollectionViewSource named 'Communications'. I proceed to adding a grouping patter like so:
Communications.GroupDescriptions.Add(new PropertyGroupDescription("Subject"));
Now, the problem i'm experience is the grouping work fine, but I can't see any items inside the groups. What am I doing wrong? Any pointers would be much appreciated.
I can't seem to reproduce the problem - I assume you are using a CollectionViewSource? It might be because you bound to the View property directly.
Here's the C# code I used:
public class Communication
{
public string Subject { get; set; }
public string Body { get; set; }
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
var source = (CollectionViewSource)Resources["Communications"];
source.Source = new List<Communication>()
{
new Communication { Subject = "WPF 4.0", Body = "I love what's happening with 4.0"},
new Communication { Subject = "WPF 4.0", Body = "I hear the text rendering is the best feature"},
new Communication { Subject = "Blend 3.0", Body = "Behaviors in Blend 3 change everything"}
};
source.GroupDescriptions.Add(new PropertyGroupDescription("Subject"));
}
}
Here is the XAML - it's the same as yours but with a couple of things removed since I don't have your converters or commands:
<Window
x:Class="GroupStyleDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
>
<Window.Resources>
<CollectionViewSource x:Key="Communications" />
</Window.Resources>
<Grid>
<ItemsControl Margin="3" ItemsSource="{Binding Source={StaticResource Communications}}" >
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander>
<Expander.Header>
<Grid>
<Grid.ColumnDefinitions >
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ItemCount, StringFormat='{}[{0}] '}" FontWeight="Bold" />
<TextBlock Grid.Column="1" Text="{Binding Path=Name}" FontWeight="Bold" />
</Grid>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ItemsControl.GroupStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Body }" Grid.Column="1" Grid.RowSpan="2" Margin="3"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
After hitting this problem myself I discovered the cause: the template for ItemsControl directly including a panel with IsItemsHost="true".
You must insert an ItemPresenter into your template and set the ItemsControl.ItemsPanel property instead.

Resources