Reputation: 2499
I am facing a strange problem in WPF datagrid after grouping. The rows inside groups start reordering. I am using the .net 3.5 framework's datagrid from codeplex.
The problem has been asked in msdn forum. Please find the link below.
http://social.msdn.microsoft.com/Forums/en/wpf/thread/3f915bf9-2571-43e8-8560-3def1d0d65bb
The msdn user has says at the end of the thread that there might be no workarounds. But I badly need one !!
Upvotes: 1
Views: 1393
Reputation: 21
Have you tried doing your sorting via a CustomSort? I saw this posted somewhere and it worked for me. It took a little bit of finding, but it's available via the ListCollectionView
class. So something like:
ListCollectionView lcv =
(ListCollectionView)CollectionViewSource.GetDefaultView(YourObjectCollection);
lcv.CustomSort = new YourObjectComparer();
Where YourObjectComparer
implements IComparer
and sorts on the properties that you want to.
Upvotes: 2
Reputation: 19895
EDIT:
For this when a cell edit takes place, we remove groups and add them again. This cuases the cell to stay on its intended order. But this poses another issue that is all the expanders are collapsed. So if we remember which expander(s) remains expanded after regrouping, we achieve what you seek.
There are a few events which can help you with achieving this functionality...
DataGrid.CellEditEnding
eventExpander.Initialized
, Expander.Expanded
and Expander.Collpased
eventsICollectionView.CurrentChanging
eventAll you need to remember is the status of the expanders when they are expanded or collapsed. Each expander represents the grouped value represented by Name
property. These grouped values are unique. So a dictionary where Dictionary.Key
is this Name
value and Dictionary.Value
is the Expander.IsExpanded
flag would just suffice.
With this raw material following code does what you seek...
Model Class: I represent a simple list of Key-Value objects in the DataGrid.
public class MyKeyValuePair<TKey, TValue> : INotifyPropertyChanged
{
private TKey key;
private TValue value;
public MyKeyValuePair(TKey k, TValue v)
{
key = k;
value = v;
}
public TKey Key
{
get { return key; }
set {
key = value;
OnPropertyChanged("Key");
}
}
public TValue Value
{
get { return value; }
set {
this.value = value;
OnPropertyChanged("Value");
}
}
public void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged
(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
XAML:
<tk:DataGrid
ItemsSource="{Binding}"
CellEditEnding="MyDataGrid_CellEditEnding">
<tk: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
Initialized="Expander_Initialized"
Expanded="Expander_Expanded"
Collapsed="Expander_Expanded">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock
Text="{Binding Path=Name}" />
<TextBlock Text=" (" />
<TextBlock
Text="{Binding Path=ItemCount}"/>
<TextBlock Text="( " />
<TextBlock Text="Items"/>
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</tk:DataGrid.GroupStyle>
</tk:DataGrid>
Window.cs Code Behind:
public partial class Window8 : Window
{
private Dictionary<string, bool> _dict;
public Window8()
{
InitializeComponent();
_dict = new Dictionary<string, bool>();
var list1 = new List<MyKeyValuePair<string, int>>();
var random = new Random();
for (int i = 0; i < 50; i++)
{
list1.Add(new MyKeyValuePair<string, int>(
i.ToString(), random.Next(300) % 3));
}
var colView = new ListCollectionView(list1);
colView.GroupDescriptions.Add(
new PropertyGroupDescription("Value"));
this.DataContext = colView;
}
private void MyDataGrid_CellEditEnding
(object sender, DataGridCellEditEndingEventArgs e)
{
var dg = sender as DataGrid;
var cellInfo = dg.CurrentCell;
var mySource = dg.ItemsSource as ListCollectionView;
var oldDlg
= new CurrentChangingEventHandler((obj, args) => { return; });
var dlg = new CurrentChangingEventHandler(
(obj, args) =>
{
if (cellInfo.Item == mySource.CurrentItem)
{
var grpDescs = mySource.GroupDescriptions;
var oldGrpDescs
= grpDescs.Cast<PropertyGroupDescription>().ToList();
mySource.Dispatcher.BeginInvoke(
new Action(
() =>
{
grpDescs.Clear();
foreach (var grdpDesc in oldGrpDescs)
{
grpDescs.Add(grdpDesc);
}
mySource.CurrentChanging -= oldDlg;
}));
}
});
oldDlg = dlg;
mySource.CurrentChanging -= oldDlg;
mySource.CurrentChanging += oldDlg;
}
private void Expander_Expanded(object sender, RoutedEventArgs e)
{
var exp = sender as Expander;
var dc = exp.DataContext as CollectionViewGroup;
_dict[dc.Name.ToString()] = exp.IsExpanded;
}
private void Expander_Initialized(object sender, EventArgs e)
{
var exp = sender as Expander;
var dc = exp.DataContext as CollectionViewGroup;
if (_dict != null
&& _dict.ContainsKey(dc.Name.ToString())
&& _dict[dc.Name.ToString()])
{
exp.IsExpanded = true;
}
}
}
But there are two trade offs.
Name
key in the dictionary may / may not stay unique. E.g. Assume for employee list if you group on FirstName
and also gropup by LastName
there will be nested expanders. Now some first name grouping may match with some last name grouping such as George
. So the dictionary will fall into a trick and will not work correctly.Hope this helps.
Upvotes: 1