0
procedure TForm1.Button2Click(Sender: TObject);
var
  Button: TButton;
  Example : String;

begin

  if {Example = ''} InputQuery('Put a question/request here', Example) then
  Repeat                                                           

    InputQuery('Put a question/request here', Example);

if InputQuery = False then
 Abort
 else
  Until Example <> ''; //or InputBox.


  Button := TButton.Create(self);
  Button.Parent := self;
  //Button properties go here
  Button.Caption := (Example);
  //Any procedures can go here

end;

This procedure continues after the repeat loop even if the user presses cancel. I've tried using the GoTo function using the CancelCreateButton label if InputQuery = False but I just get errors(so i removed some of the code).

How can i make it so that if the user clicks cancel on the inputquery it cancels the procedure and doesn't create a button?

3 Answers3

2

If the Cancel button of the input query form is pressed, then the call to InputQuery returns False. In that scenario you should call Abort, the silent exception, to skip the rest of the event handler.

if not InputQuery(...) then
  Abort;

If you want to perform validation in a loop then that would look like this:

repeat
  if not InputQuery(..., Name) then
    Abort;
until NameIsValid(Name);
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • The rest of the procedure carries on and the button is still produced –  Feb 10 '17 at 10:25
  • No it does not. Of course, I assume that you have not defined your own version of `Abort` that hides `System.Abort`. – David Heffernan Feb 10 '17 at 10:27
  • trust me im not lying –  Feb 10 '17 at 10:29
  • Create a simple program to test the code in my answer. Once you realise that what I am telling you is correct, you will then have to look in the rest of your code to see what you did wrong. – David Heffernan Feb 10 '17 at 10:29
  • I get an error with NameIsValid(Name), I have set it as a boolean in the variables, what could the problem be? It says missing operator or semicolon. –  Feb 13 '17 at 10:39
  • 1
    That's something that you need to provide. I just put it in there as a placeholder. – David Heffernan Feb 13 '17 at 10:44
  • When you say provide, do you mean I have to set it to true somewhere? Or assign it a the value name? –  Feb 13 '17 at 10:52
  • Oh dear, this isn't really working out is it. I had assumed that you were looping because you wanted to check the input for validity, and if not valid, ask the user to input again. Am I wrong? – David Heffernan Feb 13 '17 at 10:57
  • I wanted it to loop if they entered nothing, but abort if they clicked cancel. I tried using a Boolean variable so i could distinguish between the two but it didn't work. –  Feb 13 '17 at 11:00
  • I have managed to fix it, I rearranged the code as i realized it contradicted itself. I made it so the ''if not inputQuery'' came before the ''if inputquery'' –  Feb 13 '17 at 11:11
  • 1
    It works perfectly the way it's written in my answer. I suggest you slow down and think a bit harder. Replace `NamesIsValid(Name)` with your test for the name being valid. If you can't do that then you are in deep trouble. – David Heffernan Feb 13 '17 at 11:12
  • David, I may have to use your code, as new problems have arisen (See edited question), unless there is a solution to the new problem –  Feb 13 '17 at 11:36
  • 1
    I don't understand why you *insist* on `Abort`. an `Exit` is just fine in this case. if you want to extract the code into another method, then extract the whole code from the event handler into a new method. – kobik Feb 13 '17 at 11:59
  • @kobik `exit` is fine here. Once you extract the method though, it becomes more troublesome. That's when the choice of using `Abort` pays off. Let me turn this around. What is the downside of using `Abort`? – David Heffernan Feb 13 '17 at 12:07
  • *"What is the downside of using Abort"* - Code flow. if I use a method that calls `Abort`, or method (or even a function) that calls a method that calls `Abort`, It can be very difficult to later remember (and debug) that there is a silent exception that breaks then whole code flow. *personally*, I try to avoid `Abort` at all cost. – kobik Feb 13 '17 at 12:14
  • @kobik By that argument you don't use exceptions then? Your proposal of using `exit` in an extracted method is very risky. Only works if your code does nothing at all after any call to that method returns. `Abort` is designed as the thing that you call when the user cancels an operation. I can't see what you describe as a downside, merely just a lack of appreciation for exceptions. – David Heffernan Feb 13 '17 at 12:20
  • @DavidHeffernan, I am using exceptions for it's purpose: to report errors. I just don't use `Abort` in my code flow. A user canceling an operation is not an exception in my view. If you extract the entire method from the event handler, you wont need `Abort` either. – kobik Feb 13 '17 at 12:28
  • @kobik So in your opinion `Abort` should never be called, and methods should never call other methods? That's the only reasonable conclusion of your argument. – David Heffernan Feb 13 '17 at 12:54
  • @DavidHeffernan, "and methods should never call other methods" I give up. Your conclusions are ridiculous. – kobik Feb 13 '17 at 13:05
  • @kobik Not at all ridiculous. You say "extract the entire method" but that is the ridiculous part. Who lives by that rule? Never extract part of a method? Code gets pulled around here and there. A method is not an indivisible unit, never ever to be broken up. Once you start doing that, exit is no good, because you have no way to avoid the code that comes after. That's why Abort was invented. – David Heffernan Feb 13 '17 at 13:15
  • @David, You continue with your conclusions... "Never extract part of a method?". Did I say that? You can extract a method to billion other methods without using or depending on `Abort`, that silently pulls the rugs under your feet. there are other means of normal code flow. No other language I know uses a silent *exception* to abort code flow. Funny thing, I up-voted Your answer, I think it's a good solution. but I just can see why You *insist* on `Abort`. Are you saying we cannot program code without `Abort`? I would love you to prove me wrong. – kobik Feb 13 '17 at 13:32
  • @kobik StopIteration in Python is an example of the same technique used in Python. Without a silent exception, how would you abort an operation when deep down the call stack. For instance how would you abort a file copy operation with a 10 deep call stack? Isn't that exactly what exceptions are designed for? Sure you can code it without exceptions but it is much more verbose and error prone. – David Heffernan Feb 13 '17 at 13:39
  • @DavidHeffernan, "how would you abort a file copy operation" - I would probably use a callback from that method. maybe `Abort` was the thing I was missing whole my life. I'll return here to buy you a beer the first time `Abort` saves my day. seriously. – kobik Feb 13 '17 at 14:05
  • @kobik Sure you'd use a callback. But how would get from there back to the top of the call stack without an exception? You'd return a boolean success flag all the way to the top? – David Heffernan Feb 13 '17 at 14:10
  • @DavidHeffernan, I can't imagine such situation. Maybe I'll ask my own question so you can illustrate how you abort a 10 deep (!) call stack copy operation. P.S: I would make an update to also show that `Exit` works fine in this situation. and explain a bit more why you insisted on `Abort` for future readers. – kobik Feb 14 '17 at 10:42
  • @DavidHeffernan, http://stackoverflow.com/questions/42242107/using-abort-to-improve-simplify-code-in-some-situations :) – kobik Feb 15 '17 at 06:27
1

You can also use the Exit function

Alec
  • 569
  • 2
  • 17
  • 27
  • 2
    You can but `Abort` is better. What happens is that you sometimes extract the code into another method, and then `exit` only bails out of that method. `Abort` is robust to such changes. – David Heffernan Feb 10 '17 at 10:28
  • 2
    +1 for exit & function result. Abort is ok but it's a more advanced technique, and I doubt the asker has enough knowledge of Delphi and wants to invest in it. Especially regarding this question, where InputQuery returns a bool rather than throwing an EAbort in your face. – Anton Duzenko Feb 11 '17 at 11:28
0

You have too many calls to InputQuery, three when you only need one; it is better to capture the result of InputQuery to a Boolean variable and use that for execution flow control. Try something like this instead:

procedure TForm1.Button2Click(Sender: TObject);
var
  FSeatButton: TButton;
  Name : String;
  InputOK : Boolean;
  Label
  CancelCreateButton;
begin
  InputOK := InputQuery('Enter Students Name', 'Name', Name);
    // You can add check's on the validity of the student's name 
    // (e.g, is it a duplicate) here and update the value of InputOK 
    // (to False) if you don't want the button creation to go ahead

  if InputOK then begin
    FSeatButton := TButton.Create(self);
    FSeatButton.Parent := self;
    FSeatButton.Left := 100;
    FSeatButton.Top := 100;
    FSeatButton.Width := 59;
    FSeatButton.Height := 25;
    FSeatButton.Caption := (Name);
    FSeatButton.OnMouseDown := ButtonMouseDown;
    FSeatButton.OnMouseMove := ButtonMouseMove;
    FSeatButton.OnMouseUp := ButtonMouseUp;
  end;
end;

Assuming that does what you intend for a single student, you can reinstate the repeat ... until to get the behaviour you need.

Alex James
  • 696
  • 5
  • 13
  • Does this abort the whole procedure if the user presses cancel on the inputquery? –  Feb 11 '17 at 15:32
  • @Not_Lucas: If the user presses the InputQuery Cancel button, InputQuery returns False. So the InputOK variable is set to False. So the contents of the `begin ... end` block after `if InputOK then` does not execute. Simple as that. Btw 99% of the time you should never need to use `Abort`. Your mistake in your q is to try and use it where `if ... then` blocks will serve perfectly well. – Alex James Feb 11 '17 at 15:46
  • Could you remove the // from the parts necessary, or is all the code required because I'm not sure which parts to use –  Feb 13 '17 at 10:56
  • I have removed all the commented-out code. Presumably, when the user clicks Button2 he is only expecting to enter one student's name; is that what you are intending? – Alex James Feb 13 '17 at 17:11
  • Ok so I added validation to this code where it checks if Name = ' ' and if it does it repeat loops an InputQuery until Name <> ' '. The problem is if the user clicks cancel inside the loop it doesn't work so i added an IF Not InputQuery inside but it only works once the user clicks cancel twice, is there a way around this? –  Feb 14 '17 at 10:20
  • 1
    @Not_Lucas Surely you can see that you simply need to replace `until NameIsValid(Name)` with `until Name<>''` in my answer and you are done ......... I don't believe you are thinking clearly enough, and I strongly urge you to have more faith in yourself. Don't give up so readily. – David Heffernan Feb 14 '17 at 10:39