Conditionally show WPF Treeview levels - c#

I'm using a TreeView and HierarchicalDataTemplates in WPF and need to use some logic to decide which level of my data structure should be the root node in the tree.
My data structure that the TreeView is bound to is something like this:
|Root
|---|Sublevel1
|------|SubLevel2
|---------|Sublevel3
Based on the role that the user is logged in as, I need to make the root of the tree one of the sublevels, for example:
If I'm an Admin, show the entire tree as listed above.
If I'm a Power User, start the tree at Sublevel1.
If I'm a Normal User, show the tree starting at SubLevel2.
What would be the best approach for doing this? I basically have 4 user roles so I've toyed with the idea of simply creating 4 versions of the tree and selectively showing the correct one based on the user role, but I'm sure there's a better way.

If the tree views essentially display the same data, then do not create four separate tree views. That is redundant, keep it DRY. Populate a single TreeView according to the role that is curretly active.
Let us assume that you have a collection in your view model that contains the hierarchical tree data structure. Usually there is a root node or a collection of nodes of a common node data type, which contains an ObservableCollection of child nodes.
private MyNodeType _root;
This root contains the hierarchical data. Expose a property that represents the top level collection of your tree data structure for binding to ItemsSource of your single TreeView.
private ObservableCollection<MyNodeType> _roles;
public ObservableCollection<MyNodeType> Roles
{
get => _roles;
set
{
if (_roles == value)
return;
_roles = value;
OnPropertyChanged();
}
}
It is important to implement INotifyPropertyChanged, if you can change roles at runtime, because then you would assign a different collection and without raising the PropertyChanged event, the bindings and therefore the associated controls would not update their value.
Now, if you change the role in your application, you can search for matching role in your _root member and directly assign the corresponding child collection to the Roles property, e.g. Sublevel1 (assuming that the child collection property is Children).
Roles = _root.Children;
Of course you could also remove the _root backing property and load, search and assign the right collection on each user change, that depends on your requirements.

Related

Show records belonging to treeview's selected last node

I have a treeview that displays a few nodes, say 5 or so. When the final node is selected, I want to display records belonging to that final node (linked via a foreign key) inside of a listbox.
So the structure would be
Treeview Listbox
-1 -Object belonging to 5
--2 -Object belonging to 5
---3
----4
-----5
My question is if such a comperation between these two controls is even possible and if I'm going about it the smartest way.
I can't find anything about it (getting actual data from the last selected node in the treeview is already pretty hard on how to find a how to). Any tips in the right direction would be very appreciated.
It is possible to do this exactly the way you're trying to by using attached properties, but it's a bit of a clumsy way of going about it. What you really should be doing is using data binding.
Your TreeView is, presumably, bound to some kind of data structure in your view model (data context), and what class that is should (again, presumably) be able to easily ascertain whether or not a particular given item is the deepest/last one in the tree. So create a property in that class and bind the TreeView's SelectedItem to that, so that it gets updated whenever the user selects an item in the Tree. Next, create another property for your ListBox to bind to. When your first property gets set, it either sets this second property to the currently selected item if it's the last in the list, or sets it to null if it isn't.
By doing this you de-couple your logic from your view and you make something that's much easier to debug, test and modify in future.

Implement WPF TreeView of the same structure as another TreeView

I have a TreeView which data source is generated in runtime through code to which I don't have access. Its hierarchical data, nodes of tree with 2, 3 or 4 depth levels. I have to make the same structured tree with RadioButtons corresponding to every object in first tree. Is there a way to iterate through every element of TreeView or another method to do my task?
In WPF, we use DataTemplates, or in your case HierarchicalDataTemplates to define how our data should be presented. You already have the data in the first TreeView and that should be accessible from its ItemsSource or Items properties. Therefore, all you need to do is to define another HierarchicalDataTemplate to display the same data object the way that you want it. There is no need to manually iterate through all of the nodes from the original TreeView.

Custom Query Dynamic Data Application

I am trying to use a Dynamic Data Application and having issues with customizing a query. Everything scaffolds wonderfully, however, I want to be able color code the main Parent table when a Child Objects Property is a certain value. Normally I would just iterate over the data using LINQ however, all the information I've researched always filters and color codes items in the grid based on the Parent level data. Any ideas on how to do this on Child Data?
In the ItemDataBound event of the child control, if the condition is met, you can walk up the control hierarchy recursively until you reach the row/item of the parent grid. Once you get to the row/item, you can set the color to whatever you need.

Pattern / Methodology For Dynamic ContextMenu Based on Collection of Objects

Background
I have a TreeView that follows the MVVM design pattern and supports multiple selection by recording TreeViewItem selections in a List. As it stands there are several types of TreeViewItems available for the user to select.
They are:
Two Root nodes of type WorldFolder and MyDataFodler which can contain child Folder types
Child Folder nodes of types LocationFolder, PersonFolder, CollectionFolder
Child Item nodes of type LocationItem, PersonItem
CollectionFolder can contain child nodes of Folder types
In all this works very well with very little code and supports collections of Locations and People and furthermore Collections within Collections.
Problem / Question
My top level view-model keeps track of the selection state of TreeViewItems and the current selection may be a combination of Item, Folder or even Root type nodes. Depending on the user's selection I want to create a dynamic ContextMenu. So far this works! When I select several LocationItem and/or PersonItem type nodes my view-model generates a custom ContextMenu. The problem is the complexity! My view-model is quickly turning into dozens of if/else if/else statements to capture all the possible permutations!
For example:
if (_selectedItems.All(item => item is PersonItem)) // Only people selected
{
// Create ContextMenu based on only PersonItems
}
else if( _selectedItems.All(item => item is LocationItem)) // Only Locations
{
// Create ContextMenu based only on LocationItems
}
...
Is there a better way to handle all the possible permutations of user choices and generate my ContextMenus more efficiently?
* Sorry about the code formatting, it's been giving me grief all week *
I can't remember where I read this wise saying:
The best way to work with a TreeView is not not to work with the TreeView
What does this mean? Move the functionality into the tree nodes and keep the tree view as thumb as possible. Unfortunately, by default the tree node does not many events, though, it's easy to redirect the tree view events to the nodes.
Once done, you can override the ContextMenuStrip property in your nodes. The first selected node creates the list of ToolStripItems it wants to handle and asks the tree view which are allowed (e.g. with a FilterMenuItems(desiredItems) method). The tree view asks all selected nodes which of the nodes they would be able to handle. The result is your context menu.
This should work with almost any count of different nodes and keeps the tree (nodes) easy to maintain.
Edit: Dang! Missed the WPF tag, so I cannot asses the available events, since I did not yet work with WPF

C# TreeView design - best way to display a tree structure?

I'm trying to use a TreeView to display a tree structure of objects. I have a tree of four types of object, Company (the root node), City, Store and Employee.
The interface is designed to add/remove Cities/Stores/Employees, so the TreeView has to update to reflect any changes.
I am wondering on the correct way to get the TreeView to display the tree structure, and receive updates when it changes.
I am thinking that the Company object should have events, such as company.CityAdded and company.CityRemoved, then whatever wrapper I put round the TreeView responds to those events? When the TreeView has been built up, there will be a node per city/store/employee. Each node could then respond to events of the node it represents in the tree.
Is that the right idea? Or is there a better method?
I just wanted to add that if WPF is an option for this, it becomes incredible simple using heirarchtical databinding and observablecollections. It basically does all the event handle stuff for you, and lets you just interact with your business objects.
Sounds like you are on the right path. I had to do similar thing, a few pointers I'd like to share:
Store object reference in TreeNode tag property.
Give each Treenode a unique name that can easily identify an object, for example: object hashcode, Company ID, etc.
This way you can easily find and update TreeNode when object state changes. And when user selects a node, you can grab an object it's representing from the Tag property.
Good luck.
You're on the right lines about the concept of listening to events (it's a standard publisher/subscriber pattern).
For the actual updating of the treeview I tend to have two methods: AddOrUpdateTreeItem, and RemoveTreeItem. The add or update method does what it says, looks for the tree item (based on a path) and updates it or adds it. Of course if the model is being updated on a thread other than the one the form was created on you will need to marshal the call using Control.BeginInvoke().
This approach can be a little slow if you're populating a full tree at form_load or something, so you might have a different method for an initial population, and use the concept I've described here for subsequent updates.
I do the same for listviews, here's a typical example. The main difference when adding a tree item is that you might need to add the parent nodes in order to add the node being requested, which makes it a bit recursive. Give it a try.
private void AddOrUpdateListItem(DomainModelObject item)
{
ListViewItem li = lvwListView.Items[GetKey(item)];
if (li == null)
{
li = new ListViewItem
{
Name = GetKey(item),
Tag = item
};
li.SubItems.Add(new ListViewItem.ListViewSubItem());
li.SubItems.Add(new ListViewItem.ListViewSubItem());
li.SubItems.Add(new ListViewItem.ListViewSubItem());
li.ImageIndex = 0;
lvwListView.Items.Add(li);
}
li.Text = [Itemtext];
li.SubItems[1].Text = [Itemtext];
li.SubItems[2].Text = [Itemtext];
li.SubItems[3].Text = [Itemtext];
}
Here's an example of how BeginInvoke() might be implemented:
public class MyForm : Form
{
...
void data_Changed(object sender, DataChangedEventArgs e)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new EventHandler<DataChangedEventArgs>(data_Changed), sender, e);
return;
}
AddOrUpdateListItem(e.DataItem);
}
...
}
Instead of ...
Business objects subscribe to UI events
Commands update the UI
Business objects are updated when the UI is updated
... you can also do it the other way around (i.e. commands update the tree of business objects, which results in a corresponding update to the UI).
Part of the key to the publish/subscribe pattern for updates is how to wrap the information of what to do when the event triggers.
When the object representing "Store X" is updated with a new name, and fires an event to announce this has happened, which object consumes the event?
Similarly, when City Y is added, which object should be notified of the creation?
One common approach is to have some kind of large uber-manager class that handles the entire process - it subscribes to all the events and does everything.
A different approach, one I've used to good effect, is to create much simpler wrapper/coordinator objects that handle just one part of the puzzle. Typically, I suffix the name of these classes with "Editor".
So, you could have a CityEditor class whose constructor takes both a City object, and the TreeNode that represents that object. The CityEditor would subscribe to events on both the City object and the TreeNode, and would take care of populating the TreeNode with a caption and selecting an icon.
When the City object updates, the CityEditor responds to the triggered event by updating the TreeNode. When the City object is removed, the CityEditor makes sure the node is removed from the Treeview.
When a new Store object is added to the City, the CityEditor can take care of creating a StoreEditor to coordinate updates at that level. Similarly, when an Employee is added to a Store, an instance of EmployeeEditor handles updates of the Treeview.

Categories