RAD Studio |  |

Retrieving Data from Modal Forms
See Also
Collapse All

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:

Copy Code

    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;

Copy Code

    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:

Copy Code

    constructor TColorForm(Value: Pointer; Owner: TComponent);
    begin
      FColor := Value;
      String(FColor^) := '';
    end;

Copy Code

    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:
Copy Code

    procedure TColorForm.SelectButtonClick(Sender: TObject);
    begin
      with ColorListBox do
        if ItemIndex >= 0 then
          String(FColor^) := ColorListBox.Items[ItemIndex];
      end;
      Close;
    end;

Copy Code

    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:

Copy Code

    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;

Copy Code

    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

TForm

Copyright(C) 2009 Embarcadero Technologies, Inc. All Rights Reserved.
What do you think about this topic? Send feedback!