ABL.NET basic questions

Posted by Piotr Ryszkiewicz on 23-Oct-2017 04:16

Hi,

I just started experimenting with ABL.NET GUI programming (and OOP as well), and now stumbling with few quite basic questions.

1. I have class constructor which draws my window. But before it does so, it should run some business controls to check if the window can be really started. If the result of such control is false, how can I prevent the window from starting ? I discovered that RETURN ERROR from constructor does the job, but is it the only and recommended option ?

2. How to simulate RETURN NO-APPLY in leave trigger of textbox to force user to enter correct value ?

3. How to implement business validation on pressing "Save" or "OK" button. Or specifically - how to stop window from write data if the result of validation is false ?

4. Is there something like {&SELF-NAME} which I can use inside the trigger ?

Any help would be appreciated :)

Piotr

Posted by Laura Stern on 25-Oct-2017 09:36

I would forget about this Form:Validate() method.  I tired in in C# and I don't see that it is working either. I'm sure I'm doing something wrong.

But you don't need it.  You need to subscribe to the Validating event on each control that you want to validate in this way.

As I thought, the Validating event fires automatically when a control loses focus - so that you can validate and then set the Cancel property of the eventArgs if you want to prevent the user from leaving the control.  

If you then want to do cross-field validation, you can just do it in the Click handler of the OK button.  You don't need to call Validate().  Just do the validation that you need to do right there by examining the values in whatever controls you need to look at.

Posted by Laura Stern on 24-Oct-2017 16:07

#2 - Mike Fechner answered this already:  Use the Validating event and set e:Cancel to TRUE (where e is the eventArgs parameter to the event handler).  I remember having the same Cancel button problem years ago when I first did GUI programming.  I can't remember what I did to handle that. I think there should be a property on the Cancel button.  For example, try setting the CausesValidation property to false.

#3 - The window does not write data.  The window does nothing automatically.  If you just have a bunch of text boxes, for example, it is up to your code to do something with the values.  You could have an event handler for the OK button which will do whatever work you need to do.  Mike was talking about a Validate event, not a Validate method.  But I think just subscribing to the Click event of the OK button would work.

If you are using data binding, that is a different story.  With data binding, updates that are made through a control will either be automatically propagated back to the data source (depending on the BindingSource settings), or it will use an event mechanism so that your code can make changes at the time of the user change.  If you don't know what I mean by data binding, then you don't need to worry about it right now!

#4 - When an event handler runs, the first argument will always be the instance of the control that is sending the event.  You can always get its Type (obj:GetType()), and then access the Name (or FullName) property from that.

#5 - Yup.  That is a .NET thing.  You cannot call Application:Run() a 2nd time after you have been kicked out of it.  Sorry.

All Replies

Posted by Mike Fechner on 23-Oct-2017 04:46

  1. I would check from the outside – before you launch the Form – if this is o.k. to be started. Throwing an error from the constructor of a hybrid class – may prevent you from starting it later in the same session.
  2. Use the Validating event and set e:Cancel to TRUE.
  3. Look up for the Validate() method of the Form or UserControl. This will basically be raising the Validating events of the Controls to allow for validation.
  4. THIS-OBJECT:GetClass():TypeName (ABL). or THIS-OBJECT:GetType():FullName (.NET). Both works for hybrids.
 
Von: Piotr Ryszkiewicz [mailto:bounce-piotrryszkiewicz@community.progress.com]
Gesendet: Montag, 23. Oktober 2017 11:18
An: TU.OE.Development@community.progress.com
Betreff: [Technical Users - OE Development] ABL.NET basic questions
 
Update from Progress Community
 

Hi,

I just started experimenting with ABL.NET GUI programming (and OOP as well), and now stumbling with few quite basic questions.

1. I have class constructor which draws my window. But before it does so, it should run some business controls to check if the window can be really started. If the result of such control is false, how can I prevent the window from starting ? I discovered that RETURN ERROR from constructor does the job, but is it the only and recommended option ?

2. How to simulate RETURN NO-APPLY in leave trigger of textbox to force user to enter correct value ?

3. How to implement business validation on pressing "Save" or "OK" button. Or specifically - how to stop window from write data if the result of validation is false ?

4. Is there something like {&SELF-NAME} which I can use inside the trigger ?

Any help would be appreciated :)

Piotr

View online

 

You received this notification because you subscribed to the forum.  To stop receiving updates from only this thread, go here.

Flag this post as spam/abuse.

 

Posted by Piotr Ryszkiewicz on 23-Oct-2017 07:02

Hi Mike,
 
Thanks for answers. But, as always, anwers generate more questions ;)
 

1.       Honesty, I don’t like this idea, it would force me to create some wrapper around the form with part of the logic there. It can be done, but it’s not nice I think. Isn’t there something better ? BTW, I did not observe the effect you described, but it reminds me one question I forgot to ask previously (see point 5 below)

2.       It does work, but has unwanted side effect – it fires also when pressing Cancel button. How to avoid that ?

3.       I am afraid I don’t understand L. I know there is VALIDATE method which is called when „OK“ button is pressed. But how can I influence what is it doing ? I see it can’t be overridden. I thought that I could use Validating or Validated methods on form level, but these does not seem to fire when I call this-object:validate().  Moreover, the ‚e‘ argument to these is of different type, which does not provide Cancel property.

4.       This gives me he name of the form. But I wanted the name of the screen object on which the current running trigger was fired.

5.       If STOP event happens inside the form I can’t start this form again without restarting the session. The only workaround I found is to create ON STOP block around the piece of code inside the form which may cause STOP event. Can it be done in better way ? ON STOP around the code which calls the form does not work.

 
Piotr
 
 

Posted by Piotr Ryszkiewicz on 23-Oct-2017 07:58

I will add 6th question:

6. How to implement pessimistic locking with ABL.NET ? I assume it can't be done (or at least can't be done in the nice way) if BindingSource is bound with dataset. But what if it is bound with buffer handle on real table ? If I GET-CURRENT(EXCLUSIVE-LOCK) on the buffer handle in the form constructor it does lock the record and keep the lock till the form is closed. But this is not what I want. I'd like to lock it when specific button (Edit) is pressed and keep the lock till button Save or Cancel is pressed. When I GET-CURRENT on keypress of Edit it keeps the lock only during Click trigger is running, then it releases the lock.

I can define another buffer on the same table, lock record in this second buffer when Edit is pressed and release the lock when Cancel or Save is pressed - but is this the only way ?

Piotr

Posted by Thomas Mercer-Hursh on 23-Oct-2017 09:16

Mike seems to be covering the specifics, but let me add a couple of general suggestions.  I would recommend *not* drawing the window in the constructor since, if it fails, you are left with a class that has not been realized.  Much better to do basic, predictable setup things in the constructor, return with the class realized, and then create the window.  I think this will also solve your problem.

Why in the world would you want to use pessimistic locking?  Especially across UI?!?  This is very much not good practice.

Posted by jquerijero on 23-Oct-2017 09:48

For no. 1, the standard approach in WinForm programming is setting a class level flag inside the constructor then checking the flag during the Load event where you can call THIS-OBJECT:Close().

Posted by Piotr Ryszkiewicz on 23-Oct-2017 10:22

[mention:eec949bfa7364ddbb70465982d500e0f:e9ed411860ed4f2ba0265705b8793d05] : Thank you, no. 1 solved :)

[mention:6911e6cc8725416dba58ae08a80faffd:e9ed411860ed4f2ba0265705b8793d05] : You are right. This was my lack of understanding how ABL.NET really works. Now I went through it with debugger and saw that it's not constructor which draws the window. I am sorry.

Regarding pessimistic locking: this is something which customer is used to. We are not going to develop completely new GUI, I am just experimenting with ABL.NET to provisionally create few new dialogs in old ABL application which will use controls not available in classic ABL.

Posted by Laura Stern on 23-Oct-2017 12:46

Re #1: Seems you got it working, but really your model is not correct.  Mike and Thomas had it right.  The form is a form.  It is a UI component.  It's job is not decide if it should be created while you are in the middle of constructing it.  Once you are in the constructor it should get created barring some unforeseen circumstance.  It is the caller who should decide if creating the form is OK.  

And Close() on a Form that is in the process of loading?  Huh?  Since when is that standard WinForm programming?  The Load happens when the form is being visualized - the constructor will have been done already.  Why would you go so far as to try and show it and then blow it away.  That is complete bizarre.  So bizarre, that I feel I must be misunderstanding what you're saying.

Posted by jquerijero on 23-Oct-2017 13:07

Excuse me, didn't you read he wants to encapsulate the decision inside his form? Unlike most people, I don't question people's intention on what they want to achieve, be it wrong or right. Who the hell I am to know or guess exactly what some people are trying to do? The query was answered regarding an encapsulated decision and that's what the solution I presented. 

Posted by Laura Stern on 23-Oct-2017 13:21

Yes, I read it.  I'm just giving my opinion, not a mandate.  Regardless, I still don't get calling Close() on a form that is in the middle of being loaded.  If you want to make the decision in the constructor, throwing an error from the constructor makes more sense to me.   Another way is to return an output parameter yes/no and have the caller delete the object if it's not wanted.

Posted by Laura Stern on 23-Oct-2017 13:27

P.S. Maybe you're thinking that a Form always needs to be closed.  But if it is never shown, that is unnecessary.

Posted by jquerijero on 23-Oct-2017 13:34

I'm not assuming anything. I wasn't trying to present other solution and was simply answering the scenario that was puzzling the poster, a self-terminating form.

Posted by Piotr Ryszkiewicz on 24-Oct-2017 03:05

I am sorry you started to fight because of my question ;)

Some more explanation: the form needs data in some specific code lists to be able to start. This is the part of it's logic. In normal circumstances these data should be there, but I should cover non-standard situations as well. So if the data is not there, the form should not start. Testing this outside the form does not make sense for me, because then the caller should know the logic which is specific to this form itself.

My current solution, based on the anserws I got till now is following: In the form constructor I just set logical variable telling me if all required data is available. Then, in ShowModalDialog method in the form I test this variable and do not WAIT-FOR ShowDialog if it is false. In Load method I call Close if variable is false.

Of course I am still open for suggestions if it can be done in better way, but putting this logic outside the form is really not the way I'd like to go.

Posted by Laura Stern on 24-Oct-2017 08:01

OK.  So the form decides for itself if it is viable.  I'll accept that.

As for the other part, I've already given my opinion.  I very strongly discourage you from this approach.  It is convoluted,  and unnecessary.  In fact, as stated, the Load event will never fire because it only fires when you show the form, which you will not be doing.  I'll say again: Have the constructor return an error as you originally proposed.  The caller can handle this and move on to do something else.  The class instance will automatically go away and never get returned from the constructor. You never need to call Close() if the form is not shown.  Clean, neat and easy.

(Sorry jquerijero.  Didn't mean to step on toes but I don't know how to state my opinion on this more diplomatically!)

And I will also throw in that it is usually unnecessary to provide a ShowModalDialog() method in the form.  Again, it is the caller that creates the form and then calls form:ShowDialog() on it directly.  

Posted by Piotr Ryszkiewicz on 24-Oct-2017 11:34

OK Laura, I got your point. You are right, solution with RETURN ERROR (or maybe better with throwing error ?) is also nice. I was originally afraid about cleaning up afterwards, if constructor did not run completely. But I already understand that it's anyway done in destructor which is called when I delete the object.

Anyway, I have two working solutions for my first issue, and none on others. Any suggestions there ?

Regarding ShowModalDialog() - this was automatically generated by PDS, I just used it.

Posted by Laura Stern on 24-Oct-2017 16:07

#2 - Mike Fechner answered this already:  Use the Validating event and set e:Cancel to TRUE (where e is the eventArgs parameter to the event handler).  I remember having the same Cancel button problem years ago when I first did GUI programming.  I can't remember what I did to handle that. I think there should be a property on the Cancel button.  For example, try setting the CausesValidation property to false.

#3 - The window does not write data.  The window does nothing automatically.  If you just have a bunch of text boxes, for example, it is up to your code to do something with the values.  You could have an event handler for the OK button which will do whatever work you need to do.  Mike was talking about a Validate event, not a Validate method.  But I think just subscribing to the Click event of the OK button would work.

If you are using data binding, that is a different story.  With data binding, updates that are made through a control will either be automatically propagated back to the data source (depending on the BindingSource settings), or it will use an event mechanism so that your code can make changes at the time of the user change.  If you don't know what I mean by data binding, then you don't need to worry about it right now!

#4 - When an event handler runs, the first argument will always be the instance of the control that is sending the event.  You can always get its Type (obj:GetType()), and then access the Name (or FullName) property from that.

#5 - Yup.  That is a .NET thing.  You cannot call Application:Run() a 2nd time after you have been kicked out of it.  Sorry.

Posted by Piotr Ryszkiewicz on 25-Oct-2017 04:22

Thank you :)

#2: Yes, setting CausesValidation to false did the trick

#3: Yes, I am using databinding. I know I can call my validation logic from Click trigger on OK/Save button before I call Assign() on databinding source. This works. But I thought that this is what Validating (or Validated ?) methods on form level should do. The problem is, that calling THIS-OBJECT:VALIDATE() do not call these methods, even if subscription is done correctly (I assume it is, this was also generated by PDS).

#4: OK. Sender:GetType() gives me the name of the class (in my case System.Windows.Forms.MaskedTextBox).

CAST(sender,System.Windows.Forms.MaskedTextBox):NAME gives me what I need - the name of my widget. I did not find the way to get it without CASTing sender to specific type. But why CAST(sender,Sender:GetType()):NAME does not compile ?

#5. OK, I will have to live with that it seems

Posted by Mike Fechner on 25-Oct-2017 04:34

On #4, you might also try:
 
CAST (sender, System.Windows.Forms.Control):Name
 

Posted by Piotr Ryszkiewicz on 25-Oct-2017 05:09

#4: Yes, this works, thank you.

Posted by Laura Stern on 25-Oct-2017 08:46

Re #3: How would the form's Validate method know what to call?  It is not omniscient! It is not the form's job to handle your data.  It is your job, as I said.  So it sounds like you've already got it under control.

And I hadn't looked it up before but now have:  The Validate() method on the form simply causes the appropriate validate event to fire for the control that currently has focus:

"Verifies the value of the control losing focus by causing the Validating and Validated events to occur, in that order."

So that is another way to go - but you still have to subscribe to those events and do any necessary validation yourself.  I also think these events fire automatically when a control loses focus so that you can validate input as the user is entering data, and not wait until the end.  But sometimes there are cross-field dependencies and you do have to wait until OK is hit to validate these kinds of things.

All this is pretty standard UI programming - the same as it is in the ABL.  There are just different properties/events in .NET.  There is no magic!

Posted by Piotr Ryszkiewicz on 25-Oct-2017 09:10

Yes, I know it's my job to handle and validate the the data :)

What I didn't understand is, why validating method did not fire.

Currently it looks like this:

=============================================

METHOD PRIVATE VOID tvdlg_Validating( INPUT sender AS System.Object, INPUT e AS System.ComponentModel.CancelEventArgs ):

 MESSAGE "Inside validating method" VIEW-AS ALERT-BOX.

/* Validation logic goes here */

RETURN.

/*.......*/

THIS-OBJECT:Validating:Subscribe(THIS-OBJECT:tvdlg_Validating).

=============================================

The code above was generated by PDS.

And then in the click trigger of OK button I have:

THIS-OBJECT:Validate().

The above did not cause tvdlg_Validating to run.

But I think I start to understand. "Verifies the value of the control losing focus"

The form as such does not have the focus - only it's widgets have, so there is no meaning to have validating event on form level ? Was my mistake that in PDS I clicked on Validating event and created method for that ? Do I understand it correctly ?

Posted by Laura Stern on 25-Oct-2017 09:36

I would forget about this Form:Validate() method.  I tired in in C# and I don't see that it is working either. I'm sure I'm doing something wrong.

But you don't need it.  You need to subscribe to the Validating event on each control that you want to validate in this way.

As I thought, the Validating event fires automatically when a control loses focus - so that you can validate and then set the Cancel property of the eventArgs if you want to prevent the user from leaving the control.  

If you then want to do cross-field validation, you can just do it in the Click handler of the OK button.  You don't need to call Validate().  Just do the validation that you need to do right there by examining the values in whatever controls you need to look at.

Posted by Piotr Ryszkiewicz on 25-Oct-2017 09:41

OK. So my suspicion was right.

Thank you all !

Posted by tbergman on 25-Oct-2017 10:30

You could try the ValidateChildren() method on the form.
 

Posted by jquerijero on 25-Oct-2017 14:20

I'm refraining from responding more to this thread, but if you are curious when to use a self-terminating form that is also fully encapsulated functionally;  

There is no control flow difference when the form terminates prematurely or continues on normally. This is normally a case when you have to display a form in the middle of a certain process where the form could be shown or not shown. Using the approach of handling the termination inside the Load event allows the caller code to be simplified to just NEW and SHOW (again, if there are no difference in control flow between normal and early termination). This way you don't have to special case the creation and showing of the form by checking additional condition.

This should also handle SHOWDIALOG, if you assign the DialogResult property of the form to Cancel/No inside the Load event, it will have an effect of closing the dialog-box. This way your calling program will just need to do NEW, SHOWDIALOG, IF DialogResult = OK THEN for both normal and early termination.

Note: I've used the first case. The ShowDialog, I have not.

Posted by jquerijero on 25-Oct-2017 15:06

The PDS ABL Dialog Box template includes the ShowModalDialog() method.

Posted by Laura Stern on 25-Oct-2017 15:58

Got it re: ShowModalDialog.

As to the rest, I would think the caller WANTS to know the difference between putting up a dialog that the user cancels out of vs, having a form that was never appropriate to show at all.  Writing code that obscures the fact that errors have happened doesn't seem good to me.  You did add the caveat " if there are no differences in control flow between normal and early termination".  But that would not seem like the normal case to me.

But I will leave it at that.  It is of course up to Piotr to decide which way he wants to go.

This thread is closed