Container Control Communication Best Design Method

Hi,

On a main window I have several container controls in a scrollable canvas. The main window loads a record from the local database and saves it’s record id in a property on the window. I update each of the container controls with the record’s fields for editing. I’ve read about several ways for the window to communicate with the container controls, but I’m wondering what is the best design method.

Is it best to:

  1. Load the record in the main window and then assign data to the containers simply by referencing them:
sql =  "SELECT first_name, last_name from Table WHERE id = RecordID"
...
cc1.fieldFirstName.text = rs.Field(“first_name”).StringValue
cc2.fieldLastName.text = rs.Field(last_name).StringValue[/code]

2) Create methods on the container controls and basically do the same as above, except using the container control’s methods.

Container control:
[code]Sub FirstName(assigns fn as String)
  fieldFirstName.text = fn
End Sub

Main window:

sql =  "SELECT first_name, last_name from Table WHERE id = RecordID"
...
cc1.FirstName = rs.Field(“first_name”).StringValue[/code]

3) Same as above except have the container control use computed properties instead of methods.

4) Have each container use a single "Init" method which takes the record id, loads the record from the database and sets the container control's fields.  Then register it as an observer of the main window and have the main window loop through all the container control observers and call their Init methods.

[code]Sub Init(RecordID as String)
 sql =  "SELECT first_name, last_name from Table WHERE id = RecordID"
...
fieldFirstName.text = rs.Field(“first_name”).StringValue
fieldLastName.text = rs.Field(last_name).StringValue
End Sub

Thank you

We prefer to create the Recordset in the window, Load the window specific control and have a Load method in each of the containers. Same with Save and Validate methods.

Do what makes sense to you. But, if you want truly modular containers, that could be used in multiple places, let them deal with their own controls and just provide an interface for the parent control to load/save/validate.

If you’ve having the parent reference the child controls of the container what have you gained? You might as well just put those controls on their own canvas if all you’re doing is grouping them.

So then you pass the RecordSet into the container’s load method?

my windows have a special class inheriting from window
in the open event, I loop for all the container controls inside that have a database class interface,
and then I send each of them the recordset of the current record.
if any of the containercontrol is modified, it tells the window that it needs to be saved
at the cancelclose event of the window I ask the user (or not) if he wants to save or not

Sure, why not? Obviously this doesn’t work if it’s NEW data because you’d have to use the DatabaseRecord but it’s possible.

FWIW, we (BKeeney Software) don’t use the standard Recordset and DatabaseRecord plain. We prefer to put things in a Data class and the class handles the reads and writes to the database. That way all database code is in one spot.

We created ActiveRecord for Xojo to do most of the db work for us. You can get a download and example at http://www.bkeeney.com/rbinto/activerecord/. We also have a commercial utility to create the Data classes for you (though you can do it on your own).

Thanks Bob and Jean-Yves. I understand why you developed that class now, Bob. However, I would like to refrain from using ActiveRecord at this time for the sake of first learning as much as I possibly can.

I’m still a little confused on how to get the parent window to save the container control’s data.

Would I define a different ‘Save’ event definition in each of the container controls with a parameter of a data object that’s based on the container’s fields? Then Raise that event when I’m ready to save a container control and have the parent window’s handler of that event do all the SQL? Is there a better design?

More experienced programmers feel free to correct me, but as I understand good OOP design the objects themselves should know how to handle themselves.

So when you want to save something then the outside should not know how it is done, it should just send the object the info that a save is in order.

So as far as I see your last post is close to the ideal way to go, though I would use a method (but that’s me … saving seems more a method to me than an event)

Sure. AR behinds the scenes is using standard Xojo db calls. However, I understand.

I recommend creating a data class that mirrors your object. The properties are the fields. Then have a standard Load, Save methods where the Save can do the Save_new (with databaseRecord) and Save_Existing (with Recordsets). Then you create a new instance of this class. Call the load method (which does an SQL Select and loads the properties) and then pass that object around.

In the window you have this Data object that you pass into the various containers. The containers get/set the data object properties as needed in the Load/Save methods. Then finally the window would call the Save method (maybe after checking a Validate method and giving appropriate error messages if a fail).

That’s effectively what we did for many years before ActiveRecord. 100% Xojo database coding but in an OO world.

Absolutely. As far as I’m concerned the parent window should know nothing about what’s in the container. All it has is a Load/Save method and perhaps a Validate event.

Occasionally we’ll have a data changed event (from the container) if other UI need to be notified of changes. The interface is usually pretty simple Load/Save/Validate.

While containers should be opaque about how they do things, they shouldn’t be opaque about what they do. The container should have an explicit, well-defined function on the window. Things start getting fuzzy with containers when you start to try to hide what they do as well as how they do it.

The container should have a clear API - you use these methods to tell the container to do something, and you implement these events to get its response. The container shouldn’t know anything about a recordset or a data object. It should only know that it gets certain pieces of data of specific types, and it raises events passing out data of specific types. The window shouldn’t care what the container does with the data it passes in, and the container shouldn’t care what the window does with the data it passes back out. It’s the code in the container’s events (code which is part of the Window) that should connect the wires between the container’s data and a recordset, or file, or data object, or whatever.

Thanks guys, this has been really helpful. I just want to be sure I have this right, because I’m a little confused on who exactly should have the save, load methods. The data object, the container, the window, all or some:

  1. I create a data object class that mimics my database table and add to it one Load and two Save method’s.
  2. The data object’s Load method, takes a given record id as a parameter and loads up the data object’s properties.
  3. The data object’s Save methods I’m a little fuzzy on.
  4. When the user selects a record in a listbox, I locally Dim a new data object, and call it’s Load method with a passed record id.
  5. I then pass this data object to each one of my container controls via the container control’s Load method.
  6. The data object is saved as a property of the container.
  7. The container control’s Load method, loads the text fields of the container.

Things get a little fuzzy when I want to save from the container control.

Currently I’m doing the sql save in the LostFocus event of the text fields on the container. As I understand it now, in the LostFocus event’s I should only be setting the properties of the container’s data object property and not doing any sql. After setting the properties, a SaveRecord event should be raised for the main window to handle and execute the sql. But how do I tell the main window to do an sql update on only those fields that particular container handles? What do you pass on to the main window through the SaveRecord event?

Am I on the right track here?

Thanks

What we do (and some will disagree) in the Window load is pass the data object to the container via a Load method. There it sets the controls. We stash a copy (really just a reference) of the object in the container.

In the save of the window we call the container Save method. It uses the stashed reference object and puts the data from the controls into the data object.

The saving doesn’t occur until validation occurs but when it passes, that’s in the Window, not in the container.

So to summarize:
The parent window initiate the load and save of the data object.
It passes the data object into the containers via a container.Load method
It requests that the control data gets saved via a Save method.

The drawback to the Data object approach is that it tends to be an all or nothing approach. If you want to figure out which fields are updated (rather than all of them), you can compare the loaded data to the new data and see if that particular field is dirty and only save the dirty fields. ActiveRecord does this automatically so if no data is changed a Save command does nothing.

Our containers don’t issue that command. At most they might say that ‘data changed’ so we can mark the window as dirty but that’s done from the controls when the user changes something and doesn’t really have much to do with the data object.

Hope that helps.

I dont have data objects
I have controls that inherits from textfield, popupmenu, checkbox
and have a database class interface
in that class interface, there is a method that the windows sends to each control with the recordset of the current record as a parameter at the open event
then at the cancelclose event of the windows, it sends the controls a databaserecord, and each control fills the data it has to fill
then after all these populating the databaserecord, the windows does the database commit.

I even did some updates throught 1-N and N-N links with that method.
drag and drop is implemented for 1-N or N-N links.
the drop occurs only if there is a link between the tables.

Bob, I appreciate your reply. It definitely clears up all the questions I had.

If I understand you correctly, the approach you use has the container’s Save method access the RecordSet and perform the SQL Update. However, doesn’t that break the rules of a true modular container? I’m not saying you ever claimed it was, but just for my own understanding.

I understand that passing a data object to the Load method is the OOP way to get data into a container control. And as Tim said, raising an event is the OOP way to get data out of a container control.

Is it ok to have the container raise an event in each of my fields LostFocus events, so that my main window handles the SQL update of each field?

Thank you

I need on problem with that. We’ve used that approach a few times.