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

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.

Related

Transition from C#/WPF to C++/Qt: dinamically generate a button list

I'm trying to learn some Qt programming (C++) and for everything UI-related I come from C# with WPF/MVVM. The problem is that I'm having problems switching the reasoning process behind my choices.
What I'm trying to do is to link a list (or vector or some kind of ObservableCollection) of Objects to a list of Buttons so that the UI will display a button for each element in the list.
Let's say that I have a Customer class (which in C# would be my Model) with 2 variables (plus get/ set methods, or "Properties" as they are called in C#): Name and Type. Each button will display the corresponding Name and the click will send the Type to the method that handles the call.
Now, I cannot have access to a machine with VS these days but in C# I would do something like creating a View and a ViewModel (i.e. the Controller). In the VM I would create an ObservableCollection of Customers that raises the RaisePropertyChanged event when it's modified and in the View I would create an ItemsControl binded to the ObservableCollection with as DataTemplate the Button. The button would have its Content property binded to the Name of the Customer and with CommandParameter (i.e. the parameter sended with the click event) the Model itself or the Customer Type, to make different choices based on its value. This way changing the ObservableCollection would modify the number of Buttons showed.
Now my problem is: how to do the same in Qt? The best I came up with is to create a vector of this Customer class and in a for cycle:
for (unsigned int i = 0; i < model_vector.size(); ++i)
{
QPushButton* btn = new QPushButton(this);
btn->setText(model_vector[i].Name);
ui->verticalLayout->addWidget(btn);
connect(btn, SIGNAL (released()),this, SLOT (handleButton(model_vector[i])));
btn->show();
}
I would put this cycle in a method that is called to update the model_vector so I would clear the verticalLayout and re-add all the Buttons that are contained in the vector at the moment.
Anyhow this doesn't seem to me a real Model/View approach (in the sense I read on the Qt docs), but maybe I'm just misunderstanding how Qt works.
Is what I'm doing correct or is there a better way?
Thanks!
Your original approach sounds a lot like what you would do with a QtQuick based UI.
A model would provide the data, a QtQuick ListView (or similar) would provide the general view capability and mediate between the model and the actual entry visualizations, called delegates.
The delegate in this case would be a button.

Windows Forms : Custom Events vs. Exposed Properties for composite controls

Say I have a composite control in which I have a ListBox control which lists, for example, employees, and another control which contains multiple TextBox controls that will contains the employee's details.
When an employee is selected from the ListBox control, I need to pass an Employee object to the "detail" control, then assign the Text property of every TextBox control to their relative property of the Employee object.
I have two solutions to pass the Employee object to the "detail" control but I am not sure of which is the best.
Solution 1 : Expose a Employee property in the "detail" control so when an employee is selected, I could do detailControl.Employee = selectedEmployee.
Solution 2 : Expose an event in the main control and trigger it when an employee is selected. The "detail" control would subscribe to that event and receive the Employee object through the event's eventargs.
I know that both method will work just fine. I am just unsure of what is best to use.
Some say that you should pass data between controls with custom events, that it is a best loosely-coupled approach.
Some say that implementing custom events takes more time because you have to create them, add the properties to it, etc. so going with exposed properties is faster and simpler.
What do you suggest ?
Well, in my ideal world you would have a controller of some kind.
The main control would raise an event to which the controller would listen. The controller would then decode who should be notified, i.e the detail control.
This decouples the UI components entirely, providing the usual benefits, including facilitated unit testing.

INotifyCollectionChanged -- How often does it fire (and how do they make it so efficient/fast)?

Basically, I'm wondering how it is actually efficient here.
Sample code:
void GetItems()
{
foreach (var item in items)
myObservableCollection.Add(item);
}
Won't this fire off the CollectionChanged event every time causing the UI to have to refresh everytime? Or does it do it so that it waits til the GetItems function is done?
Basically, it seems that WPF handles it very well, and I'm wondering how they did that.
Optimizing Performance: Data Binding provides some background on how data bindings are resolved, including the performance implications of different items sources. Take a look at the Binding to an ItemsSource section.
Consider a scenario in which you have a CLR List object that holds
a list of employees that you want to display in a ListBox. To create a
correspondence between these two objects, you would bind your employee
list to the ItemsSource property of the ListBox. However, suppose you
have a new employee joining your group. You might think that in order
to insert this new person into your bound ListBox values, you would
simply add this person to your employee list and expect this change to
be recognized by the data binding engine automatically.
That assumption would prove false; in actuality, the change will not
be reflected in the ListBox automatically. This is because the CLR
List object does not automatically raise a collection changed
event. In order to get the ListBox to pick up the changes, you would
have to recreate your list of employees and re-attach it to the
ItemsSource property of the ListBox. While this solution works, it
introduces a huge performance impact. Each time you reassign the
ItemsSource of ListBox to a new object, the ListBox first throws away
its previous items and regenerates its entire list. The performance
impact is magnified if your ListBox maps to a complex DataTemplate.
A very efficient solution to this problem is to make your employee
list an ObservableCollection. An ObservableCollection object
raises a change notification which the data binding engine can
receive. The event adds or removes an item from an ItemsControl
without the need to regenerate the entire list.
Update time for 1 item (ms)
To a CLR List object = 1656 ms
To an ObservableCollection = 20 ms
WPF never binds directly to a collection. If you specify a collection as a binding source, WPF actually binds to the collection's default view.
A collection view is a layer on top of a binding source collection
that allows you to navigate and display the source collection based on
sort, filter, and group queries, without having to change the
underlying source collection itself. A collection view also maintains
a pointer to the current item in the collection. If the source
collection implements the INotifyCollectionChanged interface, the
changes raised by the CollectionChanged event are propagated to the
views.
The event will fire for every change.
The GUI does not have to react and refresh every time, it can postpone that.
I know WinForms will optimize this, I think WPF has a similar approach.
If you want to see how often the UI requests the fresh results expose it as a public property and put a debug line in the get (assessor) of the public property for myObservableCollection.

Best way to manage ListViewItems in a Detailed ListView?

I've adopted the following pattern for putting ListViewItems in a ListView with multiple columns (when I want to display information about a list of MyObject types), and I'm just curious to see if this is the best way to accomplish this task, or if there's anything more efficient and readable in code:
Create an inherited ListViewItem class that takes a MyObject object in the constructor - I'll call this MyObjectListViewItem - and a Refresh() method that clears and re-populates the ListViewItem subitems.
Populate the ListView with my new MyObjectListViewItem items.
example:
public MyObject MyObject { get; set; }
public MyObjectListViewItem(MyObject myObj)
{
MyObject = myObj;
this.Refresh();
}
public void Refresh()
{
this.SubItems.Clear();
this.Text = myObj.FirstColumnProperty;
this.SubItems.Add(myObj.SecondColumnProperty); // etc...
}
Suggestions? Better ways?
Have you considered using a BindingSource, or creating your own which implements IBindingListView? This keeps concerns about the data and its state scoped to the data itself and not on any controls which consume it. Since .NET controls are already built to work with BindingSources, you can take advantage of some more robust functionality. Instead of explicitly invoking a screen refresh, the control is simply responsible for responding to events raised by the binding source, and a controller that notifies whether the control is ready to be refreshed without forcing it.
Making ListViewItems that know how to build themselves is a good idea.
If you extend that idea a little, you make the columns know how to build each subitem, which also allows them to be able to automatically sort the ListView, support grouping and copy/drag and drop rows. This is just a few of the things that ObjectListView does for you.
ObjectListView is an open source wrapper around a .NET WinForms ListView control that makes the ListView much easier to use -- as well as adding some very nice new features and getting around some annoying bugs/limitations.
If you did like #Rex's idea of using a BindingSource, the ObjectListView project also provides a data-aware DataListView which is data bindable.

Create an Event Handler whenever a new Node is added in treeview

I want to write a custom class in which whenevr I add a node to treeview it should call an event. so that I can run it recursively.
I've rarely used TreeView, but at a glance this is my first impression. Since TreeViewCollection cannot be inherited from, you may have to create a separate method on your new class that will perform the actual adding to the Nodes collection. This will allow you to tie in an event. Something like this in your inherited class:
public event EventHandler NodeAdded;
public void AddNode(TreeViewNode node)
{
Nodes.Add(node);
if (NodeAdded != null)
{
NodeAdded(this, EventArgs.Empty);
}
}
You could then create a custom EventArgs class and include any information you may want to use.
Unfortunately, since TreeNodeCollection is in fact a tree, I think you're going to be stuck needing to implement your own tree view control essentially from scratch, with a custom tree data structure with events, or finding a third-party or open source one to use.
If it were a simple collection, I'd say just wrap a framework TreeView control in a custom user control, and hide the Nodes property, exposing only the accessors and mutators you want. Then you could just fire your control's events before and/or after passing the calls through to the underlying TreeView control. However, this would only give you events on the first layer of nodes.
It's easy to make a List or Dictionary class with mutator events, because you can just wrap them like that. But trees are royal pain to have to implement, especially if you're going to distribute or sell the code that uses it. And unfortunately, we get essentially no help with them from the .NET framework. Because of all the complexities of implementing a tree structure, I would strongly recommend trying to find a pre-made solution, either third-party or open source.

Categories