Why should you make your lists observable?
NOTICE!!!!! This is preety much
advanced feature to understand, but really, really basic feature to
use. If you know how to use IList classes, all knowledge needed is
already in your posession
So.. why?
For one reason. Old study showed that there is less than 3% of
wrinting, all the rest is reading. But with using engine like data
binding you lose reading quantity control. By my tests, writing
accounts for less than 0.03%.
And since quantity of writing is so small, last thing wanted here is
bogging down CPU with unprepared data. If you often sort or filter
these actions become really expensive against rest of the software.
Best approach is having prepared data which is accessible on the fly.
All sorting should be done on the fly as soon as item is added (yes,
you also need to be aware you are not propagating any expensive events
while doing that, as it will be shown in later chapter List providing widgets). And it all comes down to efficiency of your filter and sorting.
System.Data.Bindings applies sorting trough AVL sorting tree, with one
really efficient difference. You never lose index access. Finding
345632th in 1 million of them. object is taking exactly the same time
as finding Key. No more no less. It always gets to maximum of 20
iterations for 1 million elements. And having indexed access provides
one to treat AVL sorting tree as IList instead of some list collection
like all other solutions do.
Filtering on the other hand just takes reindexing of original list by only exposing valid items.
So, if you are well prepared and plan things you can do something like
this example bellow (I personally don't use DataTable/DataView as I
really don't find them neither usefull, nor efficient, but hey...
everything is in the eye of the beholder):
... well for starters, I have this extension method. People who need/want to use DataTable/DataView should simply imagine any other data filling process.
public static IList ReadToList (this SqlCommand command);
this method reads directly into real objects instead of data rows (but more on this in description attributes which provide really functional descriptive access to classes)
which enables me to do this:
IList firstList =
new ObservableSortListView (
new ObservableFilterListView (SomeSqlCommand.ReadToList(), filterDelegate),
sortDelegate, DuplicateHandling.CollectDuplicates);
or generic version of it
IList<MyObject> newList =
new GenericObservableSortListView<MyObject> (
new GenericObservableFilterListView<MyObject> (SomeSqlCommand.ReadToList(), filterDelegate),
sortDelegate, DuplicateHandling.CollectDuplicates);
And I already get new sorted/filtered view, only filtered view and
master view with one single command. Need another verion of sorted
items? Simply... do this
IList secondList =
new ObservableSortListView ((newList as IObservableListClient).ParentView,
someOtherSortDelegate, DuplicateHandling.Drop);
All wrapper lists here are implementing interface IObservableListClient, which is always providing you with reference to ParentView.
And also takes ObservableSortListView as its base which only declares
basic notifications by implementing INotifyPropertyChanged and
IListEvents.
This is again much more efficient when writing, as you can control
writing cache however you want. By signing up for modification events
in original master list, cache can also track changes and store them
back based on your request. Ok,... this is how I'm doing this. I never
used DataAdaptor to get the same functionality as DataAdaptor provides
it. But, at the same time I get full control over application data
engine by using simple IList approach.
Need to be notified when count changes in your list? There is
OnPropertyChanged. Need to know when items changed, there are
IListEvents provided events.
But, since this more than sure still is not enough to persuade hardcore DataTable/DataView lover, I'm throwing DbObservableList on the table. DbObservableList is again wrapper only class, but it wrapps around DataTable or DataView and provides at the same time complete IList approach and access to wrapped object trough Table or DefaultView.
And now you get everything compatible with before mentioned list view
wrappers. This is simply accessed trough invoking something like this
IList myTableList = new DbObservableList (myTable);
DbObservableList simply makes event messaging in classic .Net objects
more featurfull as I will now throw even better trick on the table.
Since IListEvents avoids IBindingList flaw you can make hierarchical
table... you can do something like this
public class MyNamedList : ObservableList
{
private string name = "";
public
string Name {
get {return
(name); }
set
{
if (name.Equals(value))
return;
name = value;
OnPropertyChanged
("Name");
}
}
public MyNamedList (string aName, IList parent)
: base (parent)
{
name = aName
}
}
here we got simple
ObservableArrayList which provides Name property. And lol, look at this
(this is not really needed as you could simply derive such class
extensions from previously mentioned views)
IList myBaseOfTheTree = new ObservableArrayList();
myBaseOfTheTree.Add (new MyNamedList ("Results of the first filter", firstList));
myBaseOfTheTree.Add (new MyNamedList ("Results of the second filter", secondList));
myBaseOfTheTree.Add (new MyNamedList ("Results of the Table", myTableList));
myBaseOfTheTree can now show whole hierarchy inside of any list based
widget in Gtk.DataBindings (even ComboBox or DataEntry in its
completition)
You just specify
myTreeView.ItemsDataSource = myBaseOfTheTree;
myTreeView.ColumnMappings = "{MyNamespace.MyObject} Name[Items name]; ... add any additional columns here"
and here it is. Perfect hierarchical TreeView. Always tracking changes
in all lists needed in his model structure. Even if you would add or
remove data to original lists or directly to the table, although
ignoring changes which fall outside of specified filters. Change is
reflected on the TreeView.
On the other hand... For those who are still naysayers...
Gtk.DataBindings is more than happy to work with DataTable, DataView
and DataRow.