views:

691

answers:

1

In one part of the application I'm working on, there is a form control that does validation on reception of the CMExit message, which is exactly how the Delphi documentation says to do it (this code sample is from the Delphi Help files):

procedure TDBCalendar.CMExit(var Message: TWMNoParams);
begin
 try
  FDataLink.UpdateRecord;                          { tell data link to update database }
 except
  on Exception do SetFocus;                      { if it failed, don't let focus leave }
 end;
 inherited;
end;

The purpose of this is to perform the validation as soon as the control loses focus. So, for example, if I were to click on the OK button, the form control would lose focus, this method would run and on an exception would set the focus back to that form control. (Thus the "click" event on the OK button would never go through and the dialog would never close).

The problem I'm having is that this form control is inside a modal dialog window. Clicking OK does indeed send the CMExit message and causes the record to update (and validation to occur). However, pressing Enter while in the form control causes the modal dialog to close without sending the CMExit message. It's as if the form control never "loses focus". This means that not only does the dialog close without the form actually validating the data, but the data set isn't updated, either.

Given this problem, where is the best place for me to place my dataset updating/validation code? I could move it up to the dialog form itself and implement it in the OnCloseQuery handler, but this would mean that the logic is duplicated in both the form control and on the form itself. (The form control is used in other places, and I want to avoid changing its behaviour).

(I speculate that CMExit isn't triggered because the control never actually does lose focus. The form is closed, but the form control still "has focus" on the closed form.)

+7  A: 

Closing a form does not necessarily fire the on-exit event of a TControl. The user could hit Alt-F4, for example.

I'd suggest moving the validation to a separate proc, and call that separate proc from both the on-exit and on-close events.

The below code should work without too much modification:

function TDBCalendar.UpdateSuccessful: boolean;
begin
  { tell data link to update database }
  { if successful, return True, else return False }
  { this function must be Public if a form is gonna check this value }
  Result := True;
  try
    FDataLink.UpdateRecord;
  except on Exception do
    Result := False;
  end;
  inherited;
end;

procedure TDBCalendar.CMExit(var Message: TWMNoParams);
begin
  //if not valid, then don't let them leave
  if not(UpdateSuccessful) then begin
    SetFocus;
  end;
end;

///////////////////////////////////////////
//on the form that contains your control...
///////////////////////////////////////////

procedure TMyForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  //if not valid, then don't let them close the form
  if not(dbcal.ControlIsValid) then begin
    Action := caNone;
  end
  else begin
    inherited;
  end;
end;
JosephStyons