<< Observable lists
Adaptors - Advanced >>
Chapter 3
Adaptors - basics


Well, for the start first thing that should be explained is the difference and commonality between Adaptor and DataSource terms. Like first DataSource is commonly used property which specifies object that will provide data. And Adaptor? Adaptor is a class which specifies object that will provide data. Those two are the same? Well, yes and no. DataSource is property and Adaptor is class. Which means, DataSource always points to exact reference, but if it points to Adaptor object, then his target is always redirected as soon as Adaptor target changes.

But before I focus on Adaptor insides, let me introduce why this feature is probably one of the best and most usable in System.Data.Bindings. Common flaw of all MVC solutions is strain posed on developer to keep track from where his widgets get their data. By pointing your widgets on class reference you actually have to update that reference when you want to swap original data source object. And here comes adaptor. Instead of pointing widgets to class reference you rather create blank adaptor.

Adaptor currentDataAdaptor = new Adaptor();

and point adaptor to that exact data source you would point widgets instead.

currentDataAdaptor.Target = currentData;

point all widgets to that same adaptor you just created. (and this is probably something what will never be done. GUI features also provide nifty and really simple shortcuts)

myWidget1.DataSource = currentDataAdaptor;
myWidget2.DataSource = currentDataAdaptor;
myWidget3.DataSource = currentDataAdaptor;
myWidget4.DataSource = currentDataAdaptor;
myWidget5.DataSource = currentDataAdaptor;

so what...? Most of the people are probably asking them selves here why additional variable and additional 2 lines if they could do it simply do what last part of code does it, but do that in beginging.

Well... yes... you could. But so could you take off your shoes and put them back on for every step you make. So why don't you do that? If you follow the steps with adaptor... changing all widgets to new data is as simple as this.

currentDataAdaptor.Target = someDifferentData;

and everything is updated? Meaning... you can use Adaptor not only localy, but even across assemblies which never reference directly. Data binding doesn't care about that. Provide it with source object, specify mappings and that's it. With a nifty trick using IVirtualObject you don't even have to know which properties will be needed or what you want to share across your assembly. But that lesson is one of the most advanced usage cases of data bindings and will be covered in "Advanced usage of data bindings" not here.

But really best part of this is the fact one doesn't need to track records what shows what. Even remotely good Adaptor map can simply provide you with completely alive software where you actually forgot how it is mapped. Beside single control point for multiple destinations you actually need to remember less.

Although I promised that first part will not involve GUI, I have to break that promise to be more explanatory. One nice example where and how to use adaptors is simple window where you just want to show Name of currently used object in background in status bar (like for example file copy) and at the same time you provide dialog with actual progress (which is optionally shown based on users preferences) and you simply do following steps:
- create new Adaptor
- create label and put it on main window, set created adaptor as its datasource, set label Mappings to Name
- create dialog and set its widgets datasource to the same adaptor and now set all widgets in that dialog with correct mappings

Ok. this is it... from now you can simply ignore GUI (you are done with it, no more and never again)
- Now... This step can be done anywhere (thread, timer, blocking thread, main application). Start method to copy files in queue. The only change from console copy without any feedback is you settting

myCreatedAdaptor.Target = currentFile;

at the start of file copy (if you mapped progress that one will be shown constantly too). You don't even care if progress is shown or not, you don't care if gui is doing different jobs, you don't care if thread you executed method in was blocking. Form is constantly responsive with its progress feedback. Without one single Application.Invoke or one single line of setting data besides setting adaptor.

This is a bit different, but at least it shows how progress bar can be made. And be carefull how it never touches or cares about GUI. Well, for now this video was just blindly recorded, so it will help you if you skip a little.
How to make copy progress window (video)
And for actual Adaptor reference you can simply run sample3 provided in tree.

Now to explanation how adaptors work in more technical terms (not needed, but really good to know):

Remember old fashioned pointers pointer can point to pointer, which can point to another pointer which points to data. A simple linked list. We could always access end of the chain. Like every pointer list, Adaptor list can have multiple start points all converging at the same finish Target. But, in the need to be explanatory, I need to explain basic Adaptor innards.

For end user (and even here, probably the only cases of use are writing custom widget or time when developer needs to resolve end of the chain for some special reason) only two existing properties impose them selves as "good to know how to use it" are Target and FinalTarget.

  • FinalTarget returns exactly that. End of chain or null if end of chain is IAdaptor type with (Target == null).
  • Target on the other hand is world writeable and specifies next in chain line.
  • When resolving FinalTarget, Target simply checks if Target is IAdaptor and if it is...

    return ((Target as IAdaptor).FinalTarget)

    and does that until it reaches end of the adaptor chain, otherwise it simply returns its own Target as result.
    !!! FinalTarget CAN NEVER be IAdaptor as it will simply return null. unless one used Advanced approach

    Now, the harder to understand part. Widgets actually don’t care what type its DataSource is (Just to brag a little, even list widgets like DataTreeView don’t care about that. It has optimized property transfer for default type, but if type processed is of another type, it will simply use what it can and avoid what it can't, but more on this latter when explaining DataTreeView). Doesn’t even care if Property with mapped name exists in fact. By default Gtk part of System.Data.Bindings simply disables that widget if (DataSource == null) or property with mapped name doesn’t exists in object mapped as DataSource, which simplifies form handling.

    As widgets DataSource you can specify any object including Adaptor and as you probably saw before, you will probably only gain by specifying Adaptor as DataSource, as it offers something static data sources can’t. Flexibility and by flexibility, I really mean FLEXIBILITY.

    Adaptor posts changes trough event messages to all interested parties which also includes all in chain connected Adaptors. Messages as in normal language are “Hey, data changed” and “Hey, my target changed”. Which simply means that all Adaptors in chain will resolve their FinalTarget imidietly after TargetChanged message has been dispatched. And since this is chain link it automatically sends notification that his Target or Data changed.

    Next adaptor type in line (and last for 99.99% if use) is MappedAdaptor. MappedAdaptor resolves its data target differently. Instead of providing FinalTarget like usual Adaptor, MappedAdaptor resolves the usual way and then after that resolves mapped property. Here is a nice example of such data classes:

    class Person {
      ....
    }

    class Company {
      ....
      Person Manager { .... }
    }

    Which is following the mapping code:

    Adaptor a = new Adaptor();
    MappedAdaptor ma = new MappedAdaptor();
    ma.Mappings = "Manager";
    ma.Target = a;

    So, what do we have here now? Lets say we have company1 and company2 objects pointing at two different data.
    By simply pointing
    a.Target = company1;
    all widgets connected to either a or ma adaptors will reflect change and display/edit new data. As easy as that. Practically same as “xxx.xxx” type of property mapping, except that it is all part of one same engine, which means it works the same as everything else Adaptor based.

    There is one another big difference between MS data bindings and System.Data.Bindings+Gtk.DataBindings. All widgets can have InheritedDataSource, which simply means it will inherit DataSource from first container specifying its own DataSource, but if that container specifies InheritedDataSource = true; then it will inherit it from its parent container and so on.

    By using Adaptor, MappedAdaptor, InheritedDataSource and Mapping correctly, you can simply map gui into one big living thing. Specify data source once and all gui will reflect it. Never connect or disconnect. All that connection/disconnection is happening automatically.

    << Observable lists
    Adaptors - Advanced >>