System.Data.Bindings & Gtk.DataBindings
((data!=database) && (data>database))

Version 2 release candidate in svn
Development cookbook (not finished)
NEW!!! Development blog
Quick tutorial on design with monodevelop and stetic
Quick tutorial on Adaptors and DataSources
Quick tutorial on Notificator
Video tutorials and screencasts (yes, they exist)
Database bindings???
Download and development information
Requirements explanation:
Patched gtk-sharp >= 2.12.9
Mono 2.4 or Ms.Net framework 3.5
Monodevelop 2.0 (preferably patched)

*** p.s. yes, I suck in web page design, any nicer page contribution is appreciated



A little history

How it all happened

Personally, I wouldn't even think about creating System.Data.Bindings if there wasn't example for some trivial data mapping on gtk-sharp mailing list by Philip van Hoof.

First name wasn't Gtk.DataBindings, but Gtk.DataBondage (I even had logo semi drawn where few widgets were tied in extreme almost absurd level of bondage).

I first finished few things on that example and then continued. But as times changed that poc was just poc and nothing else. Lacking in everything, wrong in many things. But, it was a starting point for Gtk.DataBindings in which I soon got involved until my car crash. The state of Gtk.DataBindings before the crash was fully featured widget set without a place to live in. They couldn't be used in stetic, no matter what I did. I even patched monodevelop for my self to be able to use them.

But then, ... car crash. Three months hospitalized at home got me thinking. Monodevelop and stetic still didn't support what I needed, which was another downer for me to get back with work. Until one day (4-5 months later) when I simply tried again and it worked. My long time bugging of plugging another assembly inside stetic in monodevelop finally worked.

Meanwhile, while not working on Gtk.DataBindings I simply started feeling like Gtk.DataBindings simply can't be the end of it and there has to be more. In this way System.Data.Bindings were started as separation of engine from Gtk.DataBindings.

Now Gtk.DataBindings are just the thing they started from... poc example. Poc for System.Data.Bindings which on the other hand has become much more robust engine to handle notifications between different types of clients.

And remember!
System.Data.Bindings is the rocking part of this project, it is completely pluggable data-gui or data-anything transfer system. Gtk.DataBindings is probably just most interesting plug toolkit.
 

p.s. As far as monodevelop and stetic support goes in I have to note that this is mostly for one reason. Lluis Sanchez is a great guy. Quite a few times so far he fixed things in a matter of hours.




Quick tutorial on design with monodevelop and stetic

Toolbox


As soon as project assigns reference to gtk-databind-lib stetic toolbox shows exported widgets which are allowed for project to use.

Simply drag and drop control on window as any other and that's it.

There are two kinds of widgets just as in original gtk-sharp: Widgets and Containers. 
toolbox
Properties palette


Every widget in gtk-databind-lib has additional property group named Data Binding. Editing this group is all that is to make your widget alive.

Editable properties are different from widget to widget, but they are kept under strict constraints in a matter of not confusing the work with them.

file:///home/matooo/Projects/web/gtk-databind/basicproperties.png
Data Binding properties


Example control that contains all 5 of them is DataTreeView since it has list, columns, data and boundary properties. Exposed properties differ from widget to widget.

Quick explanation of properties:

InheritedDataSource - widget will simply look for first in line data aware container and connect to its adaptor

Mappings - string or value representing the way control should talk with assigned DataSource

InheritedBoundaryDataSource - same as InheritedDataSource, but intended for layouting of this widget.

BoundaryMappings - same as InheritedDataSource, but intended for layouting of this widget.

ColumnMappings -  probably the most complex thing in complete System.Data.Bindings library. It defines the default (cached) type, while other types will be resolved on the fly, it provides description about columns, groupings and editing. But more help when it is done.
file:///home/matooo/Projects/web/gtk-databind/fulldatabindingproperties.png



Quick tutorial on Adaptors and DataSources

Adaptors

Simplest way of explaining what Adaptor is, is probably using old fashioned pointers. Those simply pointed on specific memory location and that was it. Triggering data change was as simple as just by redirecting typed pointer and data inside swapped for data from new location. But, .Net is different in this because it doesn't support direct memory handling and completely manages memory on its own. Difference between old fashioned pointers and Adaptor types is that old fashioned pointers still required a lot of after management. Adaptors and its clients are data-aware which means that complete messaging about data notifications is handled by it self without one single need to trigger the event.

Here step in adaptors. Every Adaptor in System.Data.Bindings has few notable properties, which define accessing data in completely new manner.


Target - Assigning target is the same as assigning old fashioned memory location since Adaptor can point to another Adaptor, which points to another Adaptor and so on until one finally points to real data. And since all adaptors are data aware they notify all connected parties about data change that happened inside the last link in chain.


FinalTarget - Resolves the final Target in chain and provides a nice way to access currently edited data. Changing the FinalTarget behaviour is as simple as overriding method DoGetFinalTarget in Adaptor subclass like in MappedAdaptor class as example. Just to show practical value of MappedAdaptor here is example.

class Company {
public Person Manager { get; set; }
}

By default it would mean a lot of hoops and tricks to update widgets to new Manager values in specific windows which need to handle that information. Or,... it was...

Here comes Adaptor, MappedAdaptor combo attack.

// Create MappedAdaptor with mapping to Manager
MappedAdaptor manager = new MappedAdaptor();
manager.Mappings = "Manager";

// Create adaptor which will serve as pointer to currently edited company
Adaptor company = new Adaptor();

// Point mapped adaptor to company adaptor
manager.Target = company;

Ok, now this is where impressive starts to get mind blowing. Every widget or System.Data.Bindings client connected to manager is notified whenever company changes. In this way, anytime code specifies something like:

company.Target = somecompany;

all connected widgets and other clients get updated on the fly. And if company Adaptor points to object which doesn't have Manager property, its Target will simply be null and notify widgets accordingly.

MappedAdaptor could as well point to another adaptor which would look something like this inside of class declaration

private IAdaptor ManagerAdaptor = new Adaptor();
public Person ManagerAdaptor {
get { return (managerAdaptor); }
}

public Person Manager {
get { return ((Person) managerAdaptor.Target); }
set {
if (value != managerAdaptor.Target)
managerAdaptor.Target = value;
}
}

which would mean a lot more flexibility inside of the class since now there is active Adaptor for that property and MappedAdaptor can simply assign Mappings = "ManagerAdaptor"


Mappings - By default every Adaptor does not use Mappings. DataCursor enabled adaptors don't handle them because they don't really need them. But whenever Adaptor is invoked with DataCursor disabled, it handles Mappings differently. Every value mapped is accessible and assignable through its Values[] property if that is possible.


Conclusion:
That mostly concludes this overly simplified view of Adaptor types, except to point out again that there are two modes Adaptor can handle, CursorMode and WidgetMode.

CursorMode is simply data pointer
WidgetMode (CursorMode=false) on the other hand handles all widget related operations.

Another important feature is that Adaptor class is being specified with every possible quirk in mind. For example, gtk needs all widget updates to be handled in Gtk.Application.Invoke, which invokes that code in gtk thread. So instead of having being carefull about threads GtkAdaptor, GtkControlAdaptor take care of this. Data in DataSource can be assigned in just about any thread, but in the end handling will always be in correct one.

The nicest example of CursorMode is CurrentSelection in item based widgets like TreeView. By pointing Adaptor or DataSource to CurrentSelection you get instant selection following and automated updates of controls.

DataSource

Now, after Adaptor types are explained, explanation why ((data!=database)&&(data>database)) is much easier. Since Adaptors provide complete framework where any object can become living organism of its self.

All it is needed is providing correct chain of information and DataSource specifies data which is to be used. DataSource can be anything, except that I would strongly suggest against using simple types as string, etc (currently simple types are disabled, but if anyone provides simple reason why they can be enabled again).

DataSource can:
Point to Adaptor
Be inherited from parent widgets
Help with handing what is possible and what is not
Help with resolving client states

Probably the only and most usable feature for explanation of Adaptor and DataSource combo is inheritance. Imagine a window with panel which consists of few sub panels and those have few widgets each. A lot of work to assign correct DataSource to every widget in picture... or not...

In simplest way all controls would access the same DataSource and all it is needed is specifying sub panels to inherit DataSource from master panel, widgets again the same. Since widgets connect to first adaptable parent they will be connected to their master sub panel, which will be connected to panel, which means swapping data in whole form is as easy as:

MainPanel.DataSource = somesource;

Another trick which can be used in this simple example. Creating non-data-aware sub panels, this way widgets can't connect to them and they will connect to master panel. As easy as this.

Ok, now more complex example. Imagine one of sub panels is showing sub object or different object. What now? Well, in first case just assign MappedAdaptor in something like this:

MappedAdaptor ma = new MappedAdaptor();
ma.Mappings = "SomeField";

// optimized version, which will be simplified in future with support for ControlAdaptor as well
ma.Target = MainPanel.Adaptor.Adaptor;

// simple version, but not as good. unless your practice is to handle data trough Adaptors
// and always point widgets to those adaptors
ma.Target = MainPanel.DataSource;

// assign DataSource to sub panel
subpanel.DataSource = ma;

live data with sub panel using different flow of data is finished forever during application needs. Now every time MainPanel.DataSource or Adaptor it possibly points to will reflect changes in it.

In second example sub panel is just needed to be treated as MainPanel.

Just as another example of ((data!=database)&&(data>database)), providing basic support for databases in System.Data.Bindings is about 300 lines.


Quick tutorial on Notificator

Notificator

Notificator class is just a convenience class which distributes messages across System.Data.Bindings engine. Simply calling

System.Data.Bindings.Notificator.ObjectChangedNotification (this);

from wherever inside that object. This triggers notification about data being updated externally, and thats it. As soon as this step is finished all clients connected to this object have been updated.
(code can be found in sample testsample1)
Example of use inside property:

private string someProperty = "";
public string SomeProperty {
get { return (someProperty); }
set {
if (someProperty == value)
return;
someProperty = value;
System.Data.Bindings.Notificator.ObjectChangedNotification (this);
}
}

public string ReformatProperty {
get {
return ("<b>" + SomeProperty + "</b>");
}
}

As soon as ObjectChangedNotification was called both properties are updated inside widgets if they are connected. What does this simple call allow?

Simply calling:
obj.SomeProperty += " something";

and directly assigning object without the smallest need to update GUI or other clients (System.Data.Bindings already did that).



Video tutorials

Video tutorials will be replaced with newer ones based on the feedback I got on these ones which were made more in hurry
than with care.
- I plan to add final effect movie separated so one can see what he is waiting for
- faster typing, (seems I was more than too carefull not to type too fast)

p.s. For those who can't wait to see the best, simply skip to stopwatch example screencast


This screencast simply shows how easy is to create gui object handling across different controls. Another displayed feature is handling objects properties without even touching the gui, but even in that case gui still reflects all changes without single trouble. (code can be found in sample testsample1 - SVN Link)
 
testsample1 (19.5MB)

Feature worth mentioning about this example is the ease how System.Data.Bindings in combination with Gtk.DataBindings handles multi thread situations. Gtk it self is very particular when and how the changes to gui can happen. This is one of the things that is needed to be like that in order to have a really responsive applications.

Second notable fact about copyprogress example is that it provides nice way of showing how not even once copy routine touched anything gui related besides Notificator. (code can be found in sample copyprogress - SVN Link)

copyprogress (24.7MB)

Sample shows how System.Data.Bindings can be used between different types of clients, in this case ConfigFile posing as Adaptor. (code can be founf in config_sample1 - SVN Link)

config_sample
(7.8MB)

Example shows how to use the same data across two or more windows. Second thing which it does is probably explanation how and why to use Notificator object. (code can be found in sample3 - SVN Link)

cursoreddata
(14.7MB)

Example shows how lazy load is not needed to be done in your application when using System.Data.Bindings. Everything needed for lazyload is already a part of SDB and as such applications always run smoothly (code can be found in sample4 - SVN Link)

lazyload
(11.9MB)

System.Data.Bindings in its best or at least at 20% of what they offer. The things done here are as normal for SDB as walk in the park and should not be considered as magic. The difference from magicis that everyone can do these tricks at home. (code can be found in stopwatch sample - SVN Link)

stopwatch
(33.6MB)

And when you think 20% was overblowing... watch this. This screencast is just showing a little addition on stopwatch sample. Little addition with a big bang. (code for this is not available online as it won't ever be a part of stopwatch)
seriousmagic
(5.8MB)