Search code examples
c#wpfdatagridcollectionview

How to split two groups that are being grouped by title, also by time of occurrence in C# WPF CollectionView?


So I am utilizing CollectionView to group similar data together. From my code below, you can see that I have a class that has an identifier and another class in it which holds the TimeStamp, Title, and Message.

So if there is an empty collection, and I generate Title A at 07:30:01 AM, this will create a group. If I generate right after that, another Title A at 07:30:03 AM, then it should group together with the previous one because they are the same title. Now if I generate Title B at 07:30:10 AM, then it should create another group because the existing title and this title are different. And if I generate another Title B right after that, it will be grouped with Title B.

My intention is that while I want to continue to group it based on title, if the last entry was Title B at 07:30:15 AM and the new entry is Title A at 07:31:00 AM, I want this entry to be a new group instead of grouping it to the existing Title A's. I was somewhat able to achieve this by ordering it through identifier which you can see below:

enter image description here

But as you can see, I do not want those 0,1,2,3 written there, I want the Title's that is grouped by written there. Is there a way to do that?

Here is my code:

Xaml:

<Window.Resources>
        <local:LogStatusCollection x:Key="logKey" />
        <CollectionViewSource x:Key="logCollection" Source="{DynamicResource logKey}">
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="TimeStamp" />
            </CollectionViewSource.SortDescriptions>
            <CollectionViewSource.GroupDescriptions>
                <PropertyGroupDescription PropertyName="Identifier" />
            </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>
    </Window.Resources>
    <Grid>
        <DataGrid x:Name="dataGrid1" 
                  DataContext="{DynamicResource logCollection}" ItemsSource="{Binding Path=.}"
                  CanUserAddRows="False" IsReadOnly="True" AutoGenerateColumns="False">

            <DataGrid.Columns>
                <DataGridTextColumn Header="Time Stamp" Binding="{Binding Path=StatusLog.TimeStamp}" Width="*"/>
                <DataGridTextColumn Header="Title" Binding="{Binding Path=StatusLog.Title}" Width="*"/>
                <DataGridTextColumn Header="Message" Binding="{Binding Path=StatusLog.Message}" Width="*"/>
            </DataGrid.Columns>

            <DataGrid.GroupStyle>
                <!-- Style for groups at top level. -->
                <GroupStyle>
                    <GroupStyle.ContainerStyle>
                        <Style TargetType="{x:Type GroupItem}">
                            <Setter Property="Margin" Value="0,0,0,5"/>
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type GroupItem}">
                                        <Expander IsExpanded="True" Background="#FF112255" BorderBrush="#FF002255" Foreground="#FFEEEEEE" BorderThickness="1,1,1,5">
                                            <Expander.Header>
                                                <DockPanel>
                                                    <TextBlock FontWeight="Bold" Text="{Binding Path=Name}" Margin="5,0,0,0" Width="100"/>
                                                    <TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount, StringFormat=Count: {0}}"/>
                                                </DockPanel>
                                            </Expander.Header>
                                            <Expander.Content>
                                                <ItemsPresenter />
                                            </Expander.Content>
                                        </Expander>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </GroupStyle.ContainerStyle>
                </GroupStyle>
            </DataGrid.GroupStyle>
            <DataGrid.RowStyle>
                <Style TargetType="DataGridRow">
                    <Setter Property="Foreground" Value="Black" />
                    <Setter Property="Background" Value="White" />
                </Style>
            </DataGrid.RowStyle>
        </DataGrid>
    </Grid>

C#:

namespace WpfApp2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public LogStatusCollection logCollection { get; }                                                                                

        public MainWindow()
        {
            InitializeComponent();

            logCollection = (LogStatusCollection)this.Resources["logKey"];
            logCollection.Add(new LogStatus
            {
                Identifier = 0,
                StatusLog = new Status { Title = "Title A", Message = "Message A", TimeStamp = DateTime.Now.ToString()}
            });
            logCollection.Add(new LogStatus
            {
                Identifier = 1,
                StatusLog = new Status { Title = "Title B", Message = "Message B", TimeStamp = DateTime.Now.AddMinutes(1).ToString() }
            });
            logCollection.Add(new LogStatus
            {
                Identifier = 1,
                StatusLog = new Status { Title = "Title B", Message = "Message B", TimeStamp = DateTime.Now.AddMinutes(5).ToString() }
            });
            logCollection.Add(new LogStatus
            {
                Identifier = 2,
                StatusLog = new Status { Title = "Title A", Message = "Message A", TimeStamp = DateTime.Now.AddMinutes(7).ToString() }
            });
            logCollection.Add(new LogStatus
            {
                Identifier = 3,
                StatusLog = new Status { Title = "Title B", Message = "Message B", TimeStamp = DateTime.Now.AddMinutes(12).ToString() }
            });
            logCollection.Add(new LogStatus
            {
                Identifier = 3,
                StatusLog = new Status { Title = "Title B", Message = "Message B", TimeStamp = DateTime.Now.AddMinutes(15).ToString() }
            });

        }



    }

    public class LogStatusCollection : ObservableCollection<LogStatus> {

    }

    public class LogStatus : INotifyPropertyChanged
    {
        private int _identifier = 0;
        private Status _status;

        public int Identifier
        {
            get { return _identifier; }
            set
            {
                if (_identifier != value)
                {
                    _identifier = value;
                    NotifyPropertyChanged("Identifier");
                }
            }
        }

        public Status StatusLog
        {
            get { return _status; }
            set
            {
                if (_status != value)
                {
                    _status = value;
                    NotifyPropertyChanged("Status");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    public class Status : INotifyPropertyChanged
    {
        private string _timestamp;
        private string _title;
        private string _message;

        public string TimeStamp 
        {
            get { return _timestamp; }
            set 
            {
                if(_timestamp != value)
                {
                    _timestamp = value;
                    NotifyPropertyChanged("TimeStamp");
                }
            }
        }

        public string Title
        {
            get { return _title; }
            set
            {
                if (_title != value)
                {
                    _title = value;
                    NotifyPropertyChanged("Title");
                }
            }
        }

        public string Message
        {
            get { return _message; }
            set
            {
                if (_message != value)
                {
                    _message = value;
                    NotifyPropertyChanged("Message");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

Solution

  • Instead of binding to the Name do bind to the first element of Items, all of them have the same Title:

    <Expander.Header>
        <DockPanel>
            <TextBlock FontWeight="Bold" Text="{Binding Path=Items[0].StatusLog.Title}" Margin="5,0,0,0" Width="100"/>
            <TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount, StringFormat=Count: {0}}"/>
        </DockPanel>
    </Expander.Header>