views:

275

answers:

7

What method is considered the "standard" for keeping data structures within GUI controls synchronized with the data structures that are maintained by the application?

For example: In WinForms, if one creates a ListView instance, rather than pointing it to a data structure that represents the items to appear within the list, one must programmatically instantiate ListViewItem(s) and call an .Add method to manually replicate them, one by one, into a data structure that is internal to the ListView itself. This makes sense from a threading standpoint, and it also makes sense within the context of rendering that a control should require a specialized data structure to exist for which the control alone knows the details regarding maintenance.

However, this creates two issues:

Redundancy: If the client class manages its own list of entities, to allow the user to select among them from the WinForms UI, this entire list must be read, converted and then recreated inside of the UI control via methods such as: .Add(ListViewItem item) Lists now occupy twice as much memory.

Complexity: Since two lists now exist, one must programmatically ensure that they remain synchronized. This can be achieved with events that are fired from the client class's collection object, or a programmer can simply be careful to always add/remove from one list when they add/remove from the other.

I have seen many instances where programmers will take the shortcut of using a UI element like a ListView as the actual collection object used to maintain the list. For example, each user entered item will be immediately inserted into the ListView, and then when it comes time to access to user's entires, the application just iterates through the ListView. This method fails to apply when you are properly seperating business/application logic from UI logic.

Overall, something just doesn't seem right about storing application data within a data structure that is internal to a GUI control. Likewise, storing two lists and keeping them programmitically synchronized doesn't seem like an elegant solution either. Ideally, one would need only to supply a UI element with a reference to a list that resides within the scope of the client.

So, what is the "right" way to approach this problem?

A: 

The problem with accessing the ListView (for example) directly, is that you are modifying the "master" copy of the data in memory. This means that if your user decides to cancel their changes you need to be able roll back any changes they might have made.

So you have the choice, the added memory of two copies of the data structure or the added complexity of having to roll back on cancel.

ChrisF
A: 

Check out this article on data binding. For things like ListViews, you can bind to a custom class that implements one of the list interfaces. By adding support to your custom class for events you can automatically update the GUI whenever a change is made to the list (or vice versa).

TLiebe
A: 

I just went through this mess with a List(Of MyObject) and the ListView. The ListView is deficient in that it doesn't offer a .DataSource property like many other object containers, such as the ComboBox, ListBox, and DataGridView. I chose to drop the ListView in favor of a DataGridView, simply because I the requirements didn't require a ListView (I added a custom first column to the DataGridView to render an image, which is why I originally chose the ListView. But you are right, the extra code to keep things in synch was too much of a headache. As to your question, do what I did. Figure out if you really require a ListView by considering if you can use a DataGridView instead and meet the requirements.

HardCode
+2  A: 

Standard DataBinding might make your life a whole lot simpler here. Using a BindingList<T>, you can easily achieve two-way binding. Two-way binding makes it easy to update the UI with changes made programmatically (i.e. by the model) or by the user. The list will remain synchronized.

However, you might want to trade off your ListView for a (readonly) DataGridView if you can afford to. It will make your like a whole lot easier with DataBinding.

dataGridView.DataSource = new BindingList<T>(initialList);
Bryan Menard
+1  A: 

DataBinding is the way to go. Take a look at this article, it is very helpful:

Data Binding in .NET / C# Windows Forms

Data binding provides a way for developers to create a read/write link between the controls on a form and the data in their application (their data model). Classically, data binding was used within applications to take advantage of data stored in databases. Windows Forms data binding allows you to access data from databases as well as data in other structures, such as arrays and collections.

alt text

Philip Wallace
+1  A: 

I think there isn't a right approach for this problem. Generally, I'd strongly support separating the data source collection from the UI by all means. Several data binding solutions exists to put the two together.

Anyways, let me make some considerations on the points you exposed.

On Redundancy: You indeed would have 2 lists for the essentially the same entities in the mind of the user. The lists are not at all equivalent.

For a start, there won't be duplication of memory. The list on the UI side may have a reference to the original object in the data source list, but it's nothing more then a reference. The total memory consumption wouldn't be much larger than having a single collection object in the UI

Secondly, the two lists (UI vs. data source) may not have the same number of items. If you use paging in your interface, the UI list may be much smaller than the data source list.

On Complexity: Using a good data binding solution may greatly reduce the complexity of synchronizing UI with its data source. There are some cases where having the two lists separated will actually simplify your code.

Consider the possibility of having a separate window/page/user control/whatever that is responsible for editing (new or old) objects. If you only have a single collection of objects directly on your UI, this separate component would have to reference to the same UI control that holds the list. This simply doesn't makes sense.

jpbochi
+2  A: 

Every UI control needs some state of its own. With complex controls (like ListView) that state is correspondingly complex. The trick is to make the maintenance of the controls state as simple as possible. With a standard ListView, that's not possible -- the programmer has to do the work.

That's one reason I wrote ObjectListView (an open source wrapper around .NET WinForms ListView). It allows you to use a ListView at a higher level where the maintenance of the controls state is invisible. An ObjectListView operates on your model objects directly:

this.objectListView1.Objects = listOfModelObjects;
this.objectListView1.SelectedObject = aPerson;

Once you can work at this level, data binding itself is no so useful. But, if you really want to use it, you can use the data-bindable DataListView from the ObjectListView project. It gives you the best of both worlds.

With an ObjectListView, there is no reason to switch to the far less interesting DataGridView. An ObjectListView gives you the ease of DataGridView with the nice UI features of a ListView, then plus some more:

alt text

Grammarian
@grammarian haven't used your creation (ObjectListView) but it looks cool (have checked CP article)
TheVillageIdiot