Reputation: 31
I am new in the WPF world, and I am trying to implement an application according to the MVVM design pattern. I am having some problems when it comes to using treeviews.
My Model is a list of log messages (from different applications) with the following properties: message, severity, application. I have one collection with 6 items that belong to two different applications. I want my tree view to look as follows:
App A
Error
First Error message (aaa)
Second Error message (bbb)
Warning
First Warning message (ccc)
App B
Warning
First Warning message (ddd)
Info
First Info message (eee)
Second Info message (fff)
My current understanding is that the expects to have an item with a list of children, so in order to create the view I want I will need to create a list that contains a string (application name) and a list of children (different severities) that will contain list of children( display messages).
That doesn't make sense to me because I am creating a dependency between my View and my Model, let's say in the future I will need to add another hierarchical layer, I will need to change my data structure in order to support it.
Is there a way to use only one list with different groupings?
Thanks.
Upvotes: 3
Views: 2101
Reputation: 185057
You can easily group your data with a CollectionView
, even in a nested way:
ObservableCollection<LogEntry> data = new ObservableCollection<LogEntry>(new LogEntry[]
{
new LogEntry("App1", "Warning", "Msg1"),
new LogEntry("App1", "Error", "Msg2"),
new LogEntry("App1", "Warning", "Msg3"),
new LogEntry("App2", "Error", "Msg4"),
new LogEntry("App2", "Info", "Msg5"),
new LogEntry("App2", "Info", "Msg6"),
});
ListCollectionView view = (ListCollectionView)CollectionViewSource.GetDefaultView(data);
view.GroupDescriptions.Add(new PropertyGroupDescription("Application"));
view.GroupDescriptions.Add(new PropertyGroupDescription("Severity"));
Data = view;
(You could also create the CollectionView view in XAML, this is just an example)
Now you can display this using DataTemlates:
<TreeView ItemsSource="{Binding Data.Groups}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type CollectionViewGroup}" ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:LogEntry}">
<TextBlock Text="{Binding Message}"/>
</DataTemplate>
</TreeView.Resources>
</TreeView>
Upvotes: 4
Reputation: 64098
You are going to have to create a view, in the logical sense, which will create the nesting required for the hierarchical data templates. If I understand you correctly, you have a collection which represents the timeline of log events received by your application.
You could utilize some nested CollectionViewSource
s with a nested TreeView
if you did not feel like altering the basic collection (adapted from this post). However, I do not know if this will respect online updates to the underlying collection I've decided is a property Messages
on the data context:
<!-- First level grouping on the application name -->
<CollectionViewSource x:Key="ApplicationGroups"
Source="{Binding Messages}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Application" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
Then you set up the template for the application and severity levels:
<HierarchicalDataTemplate x:Key="ApplicationTemplate">
<!-- Nested tree view for the severity -->
<TreeView>
<TreeView.Resources>
<!-- Since this TreeView will receive a Group as its DataContext
we will bind to its Items property, containing the objects
which are a member of its group
-->
<CollectionViewSource x:Key="SeverityGroups"
Source="{Binding Items}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Severity" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<!-- Message Template -->
<DataTemplate x:Key="MessageTemplate">
<TextBlock Text="{Binding Message}" />
</DataTemplate>
<!-- Severity Hierarchy Template -->
<HierarchicalDataTemplate x:Key="SeverityTemplate"
ItemsSource="{Binding Items}"
ItemsTemplate="{StaticResource MessageTemplate}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
<!-- Application sub-Tree View -->
<TreeViewItem Header="{Binding Name}"
ItemsSource="{Binding Groups, Source={StaticResource SeverityGroups}}"
ItemTemplate="{StaticResource SeverityTemplate}" />
</TreeView>
</HierarchicalDataTemplate>
You then have your TreeView
set its ItemsSource
to the Groups
property of the collection view, like so:
<TreeView ItemsSource="{Binding Groups, Source={StaticResource ApplicationGroups}}"
ItemTemplate="{StaticResource ApplicationTemplate}" />
Usually, rather than go through this hassle, I will create a translation layer which converts to a natural ViewModel
hierarchy, however, if your severity levels may change this may be a better option.
Some commercial data grid solutions provide decent flexibility for nested grouping, however, I do not have enough experience with these to recommend one over the other.
Upvotes: 1