Friday, April 17, 2020

Intra-program messaging

I wrote just over a month ago: "For fun, I started work on Friday evening, writing a program [the bakery manager] in which every screen is non-modal (this should be an interesting experiment), continued on Saturday and finished yesterday evening with a few reports". This program, like the OP's management program, is composed mainly of pairs of screens: one screen displays records about an entity (customers, sales items, orders, nicknames) and the other screen in the pair allows a given record to be edited. Normally, this second, edit, screen is invoked by using the ShowModal method, but this time around I wanted to try something different.

The edit screens are non-modal MDI son screens, as are their parent screens. The method which I originally chose to allow an edit screen to communicate with its parent was to invoke a procedure in the main form which then iterated through all the currently displayed MDI son screens in order to find a form which could be updated. When walking the dog the other day, I thought of a different and faster method (no pun intended) to achieve this.

The parent screen invokes the edit screen via a 'Create' call instead of 'Execute': this is simply using a more modern technique and has nothing to do with the intra-program messaging. In the past, normally one parameter was passed to the edit screen, the index number of the tuple to be edited (for a new tuple, -1 is passed). In the new scheme of things, two parameters are now passed: the index number as before along with the handle of the calling screen. When the edit screen finishes, it sends a message to this handle with the index number of the tuple which has been edited (if -1 was originally passed then a new value will be returned). No more calling the main screen, etc.

In the calling screen, I added one new procedure, DefaultHandler, which handles all messages sent to this screen. I'm only interested in one, so all the others are passed onto the inherited handler. As this procedure handles messages with a high frequency, my handler should be as short as possible, so all it does is save the passed index number then sets a timer to execute in 500 ms. It is the timer which does the actual updating - previously this was in the procedure which was called by the main form.

Following is an example of the new (for me) code paradigm
procedure TDoCustomers.DefaultHandler(var Message); begin  with TMessage (Message) do   if msg = WM_UpdateCust then    begin     newcust:= wparam;     timer1.enabled:= true;    end   else inherited DefaultHandler (Message); end; procedure TDoCustomers.Timer1Timer(Sender: TObject); begin  timer1.enabled:= false;  try   qCustomers.disablecontrols;   if qCustomers.locate ('id', newcust, []) then qCustomers.delete;   qGetOne.params[0].asinteger:= newcust;   qGetOne.open   if qCustomers.state in [dsInactive] then qCustomers.open;   qCustomers.append;   qCustomersID.asinteger:= newcust;   qCustomersCustName.asstring:= qGetOneCustName.asstring;   qCustomersTaksiv.asinteger:= qGetOneTaksiv.asinteger;   qCustomers.post;   qGetOne.close;   qCustomers.locate ('id', newcust, []);  finally   qCustomers.enablecontrols;  end; end;
This code also shows one of my discoveries from six years ago: instead of closing the dataset then retrieving all the records again, I simply add the new tuple.

This new technique works for adding customers, sales items and nicknames, but not yet for orders: in the first three cases, there is one parent screen calling one edit screen, but with orders, there are a couple of parent screens which can call the 'edit order' screen, but only one of them should be updated (an order can be made from the customers screen and from the orders screen). The customers screen does not know (at the moment) the handle of the orders screen so this can't be passed to the edit screen. At the moment, I've used the old technique for updating this type of datum; I'm sure that I'll think of a solution when I'm showering or walking the dog.

No comments: