Retrieving Data from Modal Forms

From RAD Studio
Jump to: navigation, search

Go Up to Retrieving Data from Forms


Just like modeless forms, modal forms often contain information needed by other forms. The most common example is when form A launches modal form B. When form B is closed, form A needs to know what the user did with form B to decide how to proceed with the processing of form A. If form B is still in memory, it can be queried through properties or member functions just as in the modeless forms example above. But how do you handle situations where form B is deleted from memory upon closing? Since a form does not have an explicit return value, you must preserve important information from the form before it is destroyed.

To illustrate, consider a modified version of the ColorForm form that is designed to be a modal form. The class declaration is as follows:

TColorForm = class(TForm)
  ColorListBox:TListBox;
  SelectButton: TButton;
  CancelButton: TButton;
  procedure CancelButtonClick(Sender: TObject);
  procedure SelectButtonClick(Sender: TObject);
private
  FColor: Pointer;
public
  constructor CreateWithColor(Value: Pointer; Owner: TComponent);
end;
class TColorForm : public TForm {
__published: // IDE-managed Components

	TListBox *ColorListBox;
	TButton *SelectButton;
	TButton *CancelButton;

	void __fastcall CancelButtonClick(TObject *Sender);
	void __fastcall SelectButtonClick(TObject *Sender);

private: // User declarations

	String* curColor;

public: // User declarations

	virtual __fastcall TColorForm(TComponent* Owner);
	virtual __fastcall TColorForm(String* s, TComponent* Owner);
};

The form has a listbox called ColorListBox with a list of names of colors. When pressed, the button called SelectButton makes note of the currently selected color name in ColorListBox then closes the form. CancelButton is a button that simply closes the form.

Note that a user-defined constructor was added to the class that takes a Pointer argument. Presumably, this Pointer points to a string that the form launching ColorForm knows about. The implementation of this constructor is as follows:

constructor TColorForm(Value: Pointer; Owner: TComponent);
begin
  FColor := Value;
  String(FColor^) := '';
end;
void__fastcall TColorForm::TColorForm(String* s, TComponent* Owner)
    : TForm(Owner) {
    curColor = s;
    *curColor = "";
}

The constructor saves the pointer to a private data member FColor and initializes the string to an empty string.

Note: To use the above user-defined constructor, the form must be explicitly created. It cannot be auto-created when the application is started. For details, see Controlling when forms reside in memory.

In the application, the user selects a color from the listbox and presses SelectButton to save the choice and close the form. The OnClick event handler for SelectButton might look like this:

procedure TColorForm.SelectButtonClick(Sender: TObject);
begin
  with ColorListBox do
    if ItemIndex >= 0 then
      String(FColor^) := ColorListBox.Items[ItemIndex];
  end;
  Close;
end;
void __fastcall TColorForm::SelectButtonClick(TObject *Sender) {
    int index = ColorListBox->ItemIndex;
    if (index >= 0)
        * curColor = ColorListBox->Items->Strings[index];
    Close();
}

Notice that the event handler stores the selected color name in the string referenced by the pointer that was passed to the constructor.

To use ColorForm effectively, the calling form must pass the constructor a pointer to an existing string. For example, assume ColorForm was instantiated by a form called ResultsForm in response to a button called UpdateButton on ResultsForm being clicked. The event handler would look as follows:

procedure TResultsForm.UpdateButtonClick(Sender: TObject);
var
  MainColor: String;
begin
  GetColor(Addr(MainColor));
  if MainColor <> '' then
    {do something with the MainColor string}
  else
    {do something else because no color was picked}
end;

procedure GetColor(PColor: Pointer);
begin
  ColorForm := TColorForm.CreateWithColor(PColor, Self);
  ColorForm.ShowModal;
  ColorForm.Free;
end;
void __fastcall TResultsForm::UpdateButtonClick(TObject *Sender) {
	String s;
	GetColor(&s);
	if (s != "") {
		// do something with the color name string
	}
	else {
		// do something else because no color was picked
	}
}

// ---------------------------------------------------------------------
void TResultsForm::GetColor(String *s) {
	ColorForm = new TColorForm(s, this);
	ColorForm->ShowModal();
	delete ColorForm;
	ColorForm = 0; // NULL the pointer
}

UpdateButtonClick creates a String called MainColor. The address of MainColor is passed to the GetColor function which creates ColorForm, passing the pointer to MainColor as an argument to the constructor. As soon as ColorForm is closed it is deleted, but the color name that was selected is still preserved in MainColor, assuming that a color was selected. Otherwise, MainColor contains an empty string which is a clear indication that the user exited ColorForm without selecting a color.

This example uses one string variable to hold information from the modal form. Of course, more complex objects can be used depending on the need. Keep in mind that you should always provide a way to let the calling form know if the modal form was closed without making any changes or selections (such as having MainColor default to an empty string).

See Also