BackgroundI am trying to display two tables in an UltraTree control: Location and Machine. The Location table is the hierarchy; the Machine table references the Location table.The tables have the following columns (the relevant ones):Location--------LocationPKNameParentLocationPKMachine-------MachineIDNameLocationPK(lots of other columns)Each node in the tree will have one of two types of children - any number of locations *or* exactly 1 machine.I want the tree to display the Location heirarchy using just the name column, and if they expand a name and it is referenced by a machine, show the machine details (quite a few columns of information).
ProblemCurrently whenever a Location node is expanded, it shows two child nodes - "Location" and "Machine". The user then has to expand the "Location" and "Machine" columns to see the next layer of details.Is there a way to hide the "Location" and "Machine" nodes from appearing, and instead show their children, when only one of those bands has children?
Edited to add: From the documentation on the UltraTreeColumnSettings.ShowBandNodes, it says: "Regardless of the setting of ShowBandNode, band nodes will always be created to separate siblling bands. In a case where a child band has no siblings, a band node will be created for it if ShowBandNodes is set to ShowBandNodes.Always"
Is there a way to hide a band from appearing when there are no child nodes for that band?
ExampleI am seeing this:- RootLocation - Location - ThisIsALocationName - AndAnother - Location - YetAnotherLocation - Location - Machine - AMachine | All | Columns | Appear | Here... - Machine - Machinebut want to see this instead:- RootLocation - ThisIsALocationName - AndAnother - YetAnotherLocation - AMachine | All | Columns | Appear | Here...Further DetailsI have created a DataSet and populated it with two tables, Location and Machine. I have added two relationships to the DataSet like so:
dataSet.Relations.Add("Location", rv.Tables["Location"].Columns["LocationPK"], rv.Tables["Location"].Columns["ParentLocationPK"], false);
dataSet.Relations.Add("Machine", rv.Tables["Location"].Columns["LocationPK"], rv.Tables["MachineColumns["LocationPK"], false);
I have looked through the examples in the WinTree folder in the Samples directory. I have not been able to find an example that uses two tables in one tree via a bound DataSet.
We have also considered styling the screen after the sample in folder "DataBinding (Navigation) CS"; however we'd like to see if this specific problem can be overcome as we will most likely encounter this situation again in the future.
Hi Gavin,
There's no way the tree can handle this for you automatically, but it's probably possible to do what you want with a little extra effort.
I am assuming that you are probably just leaving the tree with the default setting for AutoGenerateColumnSets and so the tree is creating two ColumnSets - one for the Location table and one for the Machine table. The Location table will have five columns. Three of these are the fields of the data. The other two are chaptered columns that represent the child bands - one for Location and one for Machine.
As long as both of these columns exist in the column set and they both have IsChaptered set to true, the tree has to show the two Band Nodes to separate the sibling bands. The IsChaptered property on the column is settable, but you don't really want to set it because that will affect every node in the tree, not just the ones where there are no child Locations.
So what you should be able to do is create a new ColumnSet which is an exact copy of the Locations ColumnSet but does not include the Locations column. Then you would need to examine each node in code and determine if it has no child locations and if it does not, you can assign this ColumnSet to that node - thus eliminating the Locations under that node.
There are a couple of caveats to this. First, you probably don't want to loop through every node in the tree. So I would recommend using the InitializeDataNode event.
Second, getting child rows for a particular node through the DotNet binding model can be an expensive operation. So if, in InitializeDataNode, you check for the existance of child nodes under the Location Band node, it might slow things down. It would probably be better for you to use the DataSource itself to determine if the parent row has any child rows rather than trying to get this information through the tree. You can use the node.ListObject to get the underlying data row for a partilcluar node in the tree.
Hi Mike,
excellent topic, as I am facing exactly the same problem as the other developer.
I could reach to not to show both (as of only one child table can have data (or none of them), and never both for the same parent row - ensured by the business logic).I did it analyzing in the AfterDataNodesCollectionPopulated the given node, and if necessary, I set the node.Visible=false;Only one problem remained: there is a node visible between the parent row and the child table. How can I remove it, so the child1 or child2 table would directly connect to the parent row, and will not be a useless node between?
Have a look at the attached image, to see what I would like to remove.
Thank you, Zoltan
Hi Mike,thanks the idea! Could you please help me a little bit how/where to do these?Let's see.My ColumnSet is generated automatically when I set tree.DataSource/DataMember. (it's quite a complex data model, with lot's of tables, relations)What I have to do, when that node is expanded, then it's ColumnSet is generated on-demand, I have to make 2 copy of it, removing the not necessary columns (Band), and based on the parent data, have to assign the correct one to the Node.What is the best place to make this ColumnSet copy? In ColumnSetGenerated()?And how to make a copy of a ColumnSet? Is there any simple one-call way?How can I change the ColumnSet of a Node? Silly, but I cannot find, it has a DataColumnSetResolved, but it's read-only.And where / when is the best place to change it to my local DataSouce copy?Thank you very much
pxclient said:What is the best place to make this ColumnSet copy? In ColumnSetGenerated()?
That seems like a good place to do it. In a simple case where you have two child bands and you want to show one of the other, you really only need to make one copy and then just hide one of the child bands in the auto-generated column set. This way, the child band will always be hidden unless you apply the copied columnset to a node.
pxclient said:And how to make a copy of a ColumnSet? Is there any simple one-call way?
I'm not sure off the top of my head. See if there's a Clone method. If not, then what you could do is, instead of making a copy, you could auto-generate a second ColumnSet from the same data and modify it to remove the child band. There's a static method that allows you to pass in a DataSource and DataMember and auto-generates a column set for you.
UltraTreeColumnSet.FromDataSource(dataSource, dataMember)
pxclient said:How can I change the ColumnSet of a Node? Silly, but I cannot find, it has a DataColumnSetResolved, but it's read-only.
It's on the Override:
node.Override.ColumnSet
pxclient said:And where / when is the best place to change it to my local DataSouce copy?
I'd use the InitializeDataNode event to start with and see if that works.
Thank you very much Mike, it works!
1) In ColumnSetGenerated() you can create your own ColumSet: create a copy (with UltraTreeColumnSet.CloneSerializableObject(..)) and remove the not necessary columns.
2) In AfterDataNodesCollectionPopulated() you can analyze the Node.Cells data, and if necessary, you can set the node.Override.ColumnSet to your own (these were created in step 1))InitializeDataNode() was not good for me, as there you don't have celldata yet
Attaching a screenshot about the result. Very nice! Thanks.
Hi,
I'm implementing an identical scenario and am having problems getting the tree to display correctly (similar to your 'after' screenshot). Could you possibly provide a sample code of how you implemented the ColumnSetGenerated event (including how you used the UltraTreeColumnSet.CloneSerializableObject(..)) and the InitializeDataNode() and any other member events that I should be aware of.
Really appreciate a quick a response.
Thanks,Assad
Sorry for late answer:private void _tree_ColumnSetGenerated(object sender, ColumnSetGeneratedEventArgs e){ if (e.ColumnSet.Key == "Feature Details") { //default it has 2 children: childtable_1+ childtable_2 //but we only wanna show one of them, depends on the parent row is a child1 or child2 row //so create the 3 ColumnSet, one will contain only child1, other will contain only child2 //And later (_tree_AfterDataNodesCollectionPopulated()) we will set the appropriate ColumnSet to the node //1) make child1 ColumnSet: copy the original ,and remove the child2 node _columnSetFeatureDetailsChild1 = UltraTreeColumnSet.CloneSerializableObject(e.ColumnSet, null) as UltraTreeColumnSet; _columnSetFeatureDetailsChild1.Key = "_columnSetFeatureDetailsChild1"; int colPos = -1; bool colFound = false; foreach (UltraTreeNodeColumn col in _columnSetFeatureDetailsChild1.Columns) { ++colPos; if (col.Key == "Child2") break; } _columnSetFeatureDetailsChild1.Columns.RemoveAt(colPos); //2) make child2 ColumnSet: copy the original ,and remove the VS node _columnSetFeatureDetailsChild2 = UltraTreeColumnSet.CloneSerializableObject(e.ColumnSet, null) as UltraTreeColumnSet; _columnSetFeatureDetailsChild2.Key = "_columnSetFeatureDetailsChild2"; colPos = -1; colFound = false; foreach (UltraTreeNodeColumn col in _columnSetFeatureDetailsChild2.Columns) { ++colPos; if (col.Key == "Child1") break; } _columnSetFeatureDetailsChild2.Columns.RemoveAt(colPos);}private void _tree_AfterDataNodesCollectionPopulated(object sender, AfterDataNodesCollectionPopulatedEventArgs e){ foreach (UltraTreeNode node in e.Nodes) { if (node.DisplayColumnSetResolved.Key == "Feature Details") { bool rowChild1 = false; bool rowChild2 = false; foreach (UltraTreeNodeCell cell in node.Cells) { if (cell.Key.ToLower() == "property") if (cell.Text.ToLower() == "child1") rowChild1 = true; else if (cell.Text.ToLower() == "child2") rowChild2 = true; } if (rowChild1) node.Override.ColumnSet = _columnSetFeatureDetailsChild1; else if (rowChild2) node.Override.ColumnSet = _columnSetFeatureDetailsChild2; }}
Jay said:Why is there not an option to hide the relationship band nodes in the UltraTree. I'm not sure why anyone would want to see these empty band nodes that map one row to another.
The band nodes exist to separate child bands. Orignally, the WinTree didn't support data binding. Binding support was added later and so the data has to be displayed using the existing node structure that the tree already supports.
If you combined two sibling bands into a single island of data, the user would have no way of knowing that they were two separate islands of data, and the tree would have no way to expose two separate collections of child nodes from a single parent node.
Mike,
Thanks that was the clarification I needed. I missed the fact that there was only one child per band and that is why this worked. I was trying to remove the band nodes completely. I was able to achieve this with the UltraGrid instead although it took me some time to find out that I had to set the MaxDepth because it kept locking up.
Just curious....Why is there not an option to hide the relationship band nodes in the UltraTree. I'm not sure why anyone would want to see these empty band nodes that map one row to another.
Hi Jay,
I'm not following you. The original issue here had to do with the fact that the data source contains sibling bands. That is... a parent band with two child bands that are sibling to each other. And the idea was to remove one of the child bands so that only one child band exists and therefore the tree does not need to display the "Band Nodes" that separate the sibling child bands.
Jay said:I am not seeing in the provided code how to reassociate the child table directly to the node without the relationship node.
So this is where you lost me. There is no need to re-associate anything. It sounds to me like maybe you are trying to show both child bands in one list without the band nodes. That is not possible.
Sorry about digging up an old thread. I'm having a similar issue. I have tried to implement with the code provided but it isn't working the same.
I have my copy of the columnSet in a field variable where i have removed the "child" column that has the nested table.
I then perform the node.Override.Columnset set using this columnSet that does not contain the "child" column any more. The end result is a row without any children which is what I would expect since I did remove the "child" column when I created the columnSet clone.
I am not seeing in the provided code how to reassociate the child table directly to the node without the relationship node. Am I missing something? Maybe I am associating the ColumnSet with the wrong node somehow? Looking at the supplied code it looks like you associate the ColumnSet with the parent node which is what I am doing.
Basic recap:
ColumnSetGenerated event:
AfterDataNodesCollectionPopulated Event:
Thanks for this. It works.
Cheers,
Assad