Tree Views did not come up to scratch because they are not easy to two way bind to data and fail on being able to send notifications of changes.
With a lot of gritting teeth I managed to do it in XAML Alone (YAY).
I achieved this by creating an HierarchicalDataTemplate which refers to itself via DynamicResource. the IDE does give me a warning that a loop is detected but, other than that it works fine.
So what does it look Like?
With time it will be prettied up :-).
<UserControl.Resources>
<ResourceDictionary> <HierarchicalDataTemplate x:Key="HeiachyTemplate" DataType="{x:Type data:InventoryItemGroup}" ItemsSource="{Binding InventoryItemGroups}" > <DataGrid
ItemsSource="{Binding InventoryItemGroups,Mode=TwoWay,NotifyOnSourceUpdated=True,NotifyOnTargetUpdated=True,NotifyOnValidationError=True,UpdateSourceTrigger=PropertyChanged}" bhv:DataGridEndEditCommandBehaviour.Command="{Binding ElementName=EditInventoryItemGroupUserControl,Path=DataContext.DataGridEndEditCommand}" CanUserAddRows="True" AutoGenerateColumns="False" AreRowDetailsFrozen="False"
HorizontalAlignment="Stretch" VerticalContentAlignment="Top"
RowDetailsVisibilityMode="Visible" AlternatingRowBackground="WhiteSmoke"
HeadersVisibility="Row" RowDetailsTemplate="{DynamicResource HeiachyTemplate}" > <DataGrid.RowHeaderTemplate> <DataTemplate> <ContentControl VerticalContentAlignment="Top" VerticalAlignment="Top"> <TextBlock FontFamily="Wingdings" Text="Ä" Margin="15,0,0,0" VerticalAlignment="Top" /> </ContentControl> </DataTemplate> </DataGrid.RowHeaderTemplate> <DataGrid.Columns> <DataGridTextColumn Header="Name" Binding="{Binding InventoryItemGroupName, Mode=TwoWay, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, NotifyOnValidationError=True,UpdateSourceTrigger=PropertyChanged}" Width="*" MinWidth="400"></DataGridTextColumn> </DataGrid.Columns> </DataGrid>
</HierarchicalDataTemplate> </ResourceDictionary> </UserControl.Resources>
Now for the data grid
<DataGrid
ItemsSource="{Binding InventoryItemGroupList,Mode=TwoWay,NotifyOnSourceUpdated=True,NotifyOnTargetUpdated=True,NotifyOnValidationError=True,UpdateSourceTrigger=PropertyChanged}" CanUserAddRows="True" AutoGenerateColumns="False" AreRowDetailsFrozen="False" RowDetailsVisibilityMode="Visible" AlternatingRowBackground="LightGray" RowDetailsTemplate="{StaticResource HeiachyTemplate}" > <DataGrid.RowHeaderTemplate> <DataTemplate> <TextBlock Text=" " Margin="15,0,0,0" VerticalAlignment="Top" /> </DataTemplate> </DataGrid.RowHeaderTemplate> <DataGrid.Columns> <DataGridTextColumn Header="Name" Binding="{Binding InventoryItemGroupName, Mode=TwoWay, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, NotifyOnValidationError=True,UpdateSourceTrigger=PropertyChanged}" Width="*" ></DataGridTextColumn> </DataGrid.Columns> </DataGrid>
Now That's All she Wrote....
I cannot say if this will work for all situations nor large record sets but it worked for me..
This is my first post, so please if I am missing any information leave a comment.
Source Code
This comment has been removed by the author.
ReplyDeleteHello!
ReplyDeleteI am very keen to implement your approach of presenting a recursive table in a WPF hierarchical DataGrid.
I have a simple recursive table which has a child column and a parent column that store countries, counties, cities, streets, etc... I would like to display the entities and their attributes in an editable hierarchical DataGrid. It seems your approach can handle this better than other articles I have read on the web.
Could you please expand on the following items and how they are constructed in the example you have used? I would really appreciated your assistance. Many thanks.
I have created CountryItemGroupList as a viewmodel of the table above. Now I need to know how to generate the corresponding components in the following entries of XAML:
1)
2) What is the reference entry for bhv? and how are the rest of the components in bhv:DataGridEndEditCommandBehaviour.Command="{Binding ElementName=EditInventoryItemGroupUserControl, Path=DataContext.DataGridEndEditCommand}" are built?
3) <DataGridTextColumn Header="Name" Width="*" Binding="{Binding InventoryItemGroupName
Any possibility of a more detailed example?
Many thanks in advance.
Abbas
Sorry! Somehow Item (1) did not get displayed:
Delete1) HierarchicalDataTemplate x:Key="HeiachyTemplate" DataType="{x:Type data:InventoryItemGroup}" ItemsSource="{Binding InventoryItemGroups}"
Hi Abbas,
ReplyDelete1 ) {x:Type data:InventoryItemGroup.. data is the namespace of my data objects in the Usercontrol (or Window ) definition you need to add xmlns:data ="NameOfYourDataNamespace"
This defines what type of data the template is to be used for. in your case it may be x:Type = GountryGroup
2) the bhv is the namespace to a custom behaviour for data grids, in this case it raises an event to notify other MVVM forms that there has been a change in the data, this is optional.
3) Width="*' makes the width take up the whole grid, the Binding you will use the property that you wish to display e.g. CountryGroupName
This comment has been removed by the author.
ReplyDeleteHi Traci,
ReplyDeleteMany thanks for your prompt response; unfortunately I got side-tracked to other things.
First of all it was very silly to raise question (3); it was so trivial and I don’t know why I raised it!! (Perhaps I was excited seeing your article after a long search on the web.) I have been developing applications with DataGrid for some time (not that I am an expert though!)
Regarding the bhv, I would be like to learn more about it as I need a method of communications between MVVM forms. Could you please point me to a right direction to read more about it? Thanks.
With regard to the main point of interest, I have the following model and would like to create a hierarchical DataGrid, similar to Microsoft Project:
1) Reference table: tblMaster, with a primary key ItemID:
ItemID ItemName
1 Countries
2 AllCountries
3 Continents
5 Africa
6 America
7 Oceania
8 Europe
31 Australia
74 France
85 UnitedKingdom
167 Switzerland
179 UAE
388 London
396 Paris
398 New York
399 Melbourne
400 Birmingham
401 Sydney
402 Geneva
2) Recursive (tree) table: tblMasterTree, with foreign keys FK_NodeID and FK_ParentID that map to ItemID of tblMaster:
TreeNodeID FK_NodeID NodeName FK_ParentID ParentName
1 1 World NULL NULL
2 3 Continents 1 Countries
3 6 America 3 Continents
4 7 Oceania 3 Continents
5 8 Europe 3 Continents
6 31 Australia 7 Oceania
7 31 Australia 2 AllCountries
8 74 France 8 Europe
9 74 France 2 AllCountries
10 85 UnitedKingdom 8 Europe
11 85 UnitedKingdom 2 AllCountries
12 167 Switzerland 8 Europe
13 167 Switzerland 2 AllCountries
14 187 USA 6 America
15 187 USA 2 AllCountries
16 388 London 85 UnitedKingdom
17 396 Paris 74 France
18 398 New York 187 USA
19 399 Melbourne 31 Australia
20 400 Birmingham 85 UnitedKingdom
21 401 Sydney 31 Australia
22 402 Geneva 167 Switzerland
As you will note a child can have more than one parent.
I want to create a hierarchical DataGrid base on the tblMasterTree. I have created a MasterTreeViewModel which is also set as the DataContext of the DataGrid. The view model defines EntityCollection as a collection of tblMasterTree rows, which resembles your InventoryItemGroupList in the DataGrid ItemsSource.
I have a reference to my ViewMode namespace in my xaml (vm) and all my entities are defined in MasterTreeViewModel.
Now, how do I create CountryGroup from my tables, which resembles your InventoryItemGroup? Will this be grouping in the database as a result of creating a view? In which case how can I make my DataGrid editable? I would like to be able to add/remove rows, add/remove child and modify cells.
Also, can you please advise on implementing expanding and collapsing rows.
Please note that I have already created a TreeListView based tblMasterTree; but it is read only.
I hope I have described my requirement clearly; please let me know if there are any ambiguities or you need more details.
Again, many thanks for taking time to read and respond to me query.
Abbas
Great example. I was looking for something like this for a long time. Could you please provide the sources as zip-archive?
ReplyDeleteI have created a sample and can be accessed from the link in the article
ReplyDeleteHi,
DeleteCan't get the source code; on Chrome I get a blank page and on IE I get the "HTTP 403 Forbidden" error.
I have used my Google account to sign in.
Thanks for your help.