Link to home
Start Free TrialLog in
Avatar of Ole Kullmann
Ole KullmannFlag for Denmark

asked on

Visibility of toolbox out of control

I am working on an application that has a toolbox (a small window that stays on top of, or even outside the main form).

There are three wishes to the visibility of this toolbox:
1) The user must be able to open and close (show and hide) the toolbox as s/he wishes. The toolbox can be opened AND closed via a menu item which uses the Checked flag to indicate the current state. The toolbox can also be closed via the X button of the toolbox itself.
2) When the user switches to a different application, the toolbox (if visible), must be hidden until the user switches back to the application in question. This is important because the user may have more instances of the application open and switching between them must also switch toolbox so that a visible toolbox is assured to be linked to the active application. The toolbox must only be shown again if it was visible before the application switch.
3) If the application is minimized, the toolbox (if visible), must be hidden until the application is restored. The toolbox must only be shown again if it was visible before the minimize.

Now, that's three wishes at the same time. Maybe I should have bought a Kinder Egg...

Opening and closing via a menu item is easy. I can simply ask for the current state of visibility, in order to determine whether to Show or to Hide. However, the nasty user can also close via the X button of the toolbox, so I cannot just set the menu item Checked property when processing the menu click. To solve that, I added OnShow and OnHide (could be OnClose) handlers in the toolbox, which call back to the main form, in which the code now is something like:

procedure TForm1.ViewToolboxClick(Sender: TObject); // Menu item click
begin
  if Form2.Visible then // or: if ViewToolbox.Checked then
    Form2.Hide
  else
    Form2.Show;
end;

procedure TForm1.ToolboxShow(Sender: TObject); // Form2.OnShow callback
begin
  ViewToolbox.Checked := True;
end;

procedure TForm1.ToolboxHide(Sender: TObject); // Form2.OnHide callback
begin
  ViewToolbox.Checked := False;
end;

Switching application is a little more tricky. I cannot use FormActivate / FormDeactive as they are also called for switches between main form and toolbox. Instead, I use some event handlers in the global Application object:

procedure TForm1.FormShow(Sender: TObject);
begin
  Application.OnActivate := AppActivate;
  Application.OnDeactivate := AppDeactivate;
  Form2.OnShow := ToolboxShow;
  Form2.OnHide := ToolboxHide;
  FShowToolbox := True; // Toolbox will be shown in AppActivate
end;

And as you can see, I have introduced a variable to control whether AppActivate should open the toolbox or not.

procedure TForm1.AppActivate(Sender: TObject);
begin
  if FShowToolbox then
    Form2.Show;
end;

procedure TForm1.AppDeactivate(Sender: TObject);
begin
  if FShowToolbox then // could also be testing Form2.Visible
  begin
    Form2.Hide;
    FShowToolbox := True; // To make it visible again at AppActivate
  end;
end;

But what is that last setting of FShowToolbox? It seems that this variable is never set False. Well, it is. I extended the previous code a little:

procedure TForm1.ToolboxShow(Sender: TObject);
begin
  FShowToolbox := True;
  ViewToolbox.Checked := True;
end;

procedure TForm1.ToolboxHide(Sender: TObject);
begin
  FShowToolbox := False;
  ViewToolbox.Checked := False;
end;

So the "Form2.Hide" statement in TForm1.AppDeactivate sets the FShowToolbox to False and I need to set it back to True because I'm only closing temporarily - it's not a close of toolbox requested by the user.

That's about how much I had done to that for a start. I had NOT considered the third wish at all. I thought it was a simple case of "application switch".

But one day last week I happened to minimize the application. To my surprise, the toolbox remained visible. Or rather, it was minimized and immediately redisplayed. Only the main form was effectively minimized.

I changed a little and tested again. Now minimizing worked, but application switch did not. No matter what I did, I couldn't get the program to fulfil all three wishes.

I then decided to isolate the problem in a small test program instead of trying wild things in my large project.

Doing so didn't make me less confused, but it allowed me to introduce a semi-automatic memorization of what happened in the program: The test program has a memo and each method in the main program writes a line to that memo. It's being logged with a time stamp and includes the name of the method. While running, I can manually add comments in the memo. When closing, the test program saves the memo to a text file.

After having tried various things, the test program no longer has the original problem, but that doesn't mean, it's better. The current problems are:

>>> After minimizing by clicking the MINIMIZE BUTTON of Form1 and restoring the application by clicking the taskbar icon, the toolbox is displayed as it should, but clicking X on the toolbox does not close the toolbox. <<<

>>> After minimizing by clicking the TASKBAR ICON and restoring the same way, the toolbox remains invisible although it was open before. <<<

I have tested this with Delphi XE[1] and RAD Studio 10.1 Berlin Starter on Windows 7 and 10. Same results.

Is my approach to the toolbox visibility insane or what? My setting of FShowToolbox is questionable, I admit, but can you come up with something better?

Attached to this question, you should find the source of the test program. Feel free to test any changes you like, but make sure that you fulfil all three wishes and remember: There are two ways the user may close the toolbox (menu item or X button). There are - at least - two ways to switch application (clicking an app window or using Alt+TAB) and there are two ways to minimize (minimize button of main form or clicking the app icon in the taskbar). All should be working, if you are want to make me happy again.

Best Regards,
Ole Kullmann
Unit1.dfm
Unit1.pas
Unit2.dfm
Unit2.pas
Avatar of Geert G
Geert G
Flag of Belgium image

you describe the problem as this toolbox being a separate application

in reality it should only be a separate unit which you include in the uses list of the project
that alone should solve the correct toolbox being active with the active version of the app
it is possible you have multiple toolboxes open, but clicking on a another toolbox will activate that other application

you'd only need to worry about hiding the toolbox when the app is deactivated
more of a nice to have, to minimize confusion

because of this separate unit, you should not mix code of this toolbox in other units
the toolbox unit should be an independant unit
> code resusability

so what you are doing with your references inside Form1 to Form2 is completely wrong
there is some work here to prevent circular unit reference too
like a callback for the menu, or some plain global variables
or even better ... an observer pattern

this is actually a very good idea to use an observer pattern on
the menu needs to be changed based on the state of toolbox

vice versa works too ... the toolbox needs to change state (hidden) when app is deactived
so you need a few observers and subsribers

i'll see if i can create a sample with those ideas
Avatar of Ole Kullmann

ASKER

Thanks, Geert
Bad description I made, if you get that impression. The toolbox is just a form in a unit - not a separate application. Looking forward to your sample. I'm not sure what you mean by "observer pattern", but the sample will probably clarify that to me.
Ole
the first problem to solve is to be able to give an event to your toolbox unit
which gets called when an action happens on the toolbox

like closing the toolbox with the X

this is the best implementation i ever found with adding/removing events to a list
http://delphi.cjcsoft.net/viewthread.php?tid=44901
i had to fix some typos because the browser interprets the > and < symbols

unit uPatterns;

interface

uses Classes, SysUtils;

type
  TMultiEvent = class
  private
    FObservers: TList;
  protected
    function FindObserver(Observer: TMethod): integer;
    function GetObserver(Index: integer): TMethod;
    procedure SignalObserver(Observer: TMethod); virtual;
  public
    constructor Create;
    destructor Destroy; override;

    procedure Attach(Observer: TMethod);
    procedure Detach(Observer: TMethod);

    procedure Signal;
  end;

  TMultiNotifyEvent = class(TMultiEvent)
  private
    FSender: TObject;
  protected
    procedure SignalObserver(Observer: TMethod); override;
  public
    procedure Attach(Observer: TNotifyEvent);
    procedure Detach(Observer: TNotifyEvent);

    procedure Signal(Sender: TObject);
  end;

  TSubject = class
  private
    FOnChange: TMultiNotifyEvent;
    FValue: string;
    procedure SetValue(const aValue: string);
  protected
    procedure DoChange; virtual;
  public
    constructor Create(AValue: string);
    destructor Destroy; override;
    property OnChange: TMultiNotifyEvent read FOnChange;

    property Value: string read FValue write SetValue;
  end;

implementation

{ TEvent }

procedure TMultiEvent.Attach(Observer: TMethod);
var
  Index: integer;
begin
  Index := FindObserver(Observer);

  { A method contains two pointers:                              }
  { - The code pointer, that's where the procedure is in memory  }
  { - The data pointer, this tells Delphi what instance of the   }
  {   object calls the procedure                                 }
  { We must store both pointers in order to use that callback.   }

  if Index < 0 then
  begin
    FObservers.Add(Observer.Code);
    FObservers.Add(Observer.Data);
  end;
end;

constructor TMultiEvent.Create;
begin
  inherited Create;
  FObservers := TList.Create;
end;

destructor TMultiEvent.Destroy;
begin
  FreeAndNil(FObservers);
  inherited Destroy;
end;

procedure TMultiEvent.Detach(Observer: TMethod);
var
  Index: integer;
begin
  Index := FindObserver(Observer) * 2;

  if Index >= 0 then
  begin
    FObservers.Delete(Index); // Delete code pointer
    FObservers.Delete(Index); // Delete data pointer
  end;
end;

function TMultiEvent.FindObserver(Observer: TMethod): integer;
var
  i: integer;
begin
  { Search fails by default, if there is a match, result will be updated. }
  Result := -1;
  for i := (FObservers.Count div 2)-1 downto 0 do
  begin
    { We have a match only if both the Code and Data pointers are the same. }
    if (Observer.Code = FObservers[i * 2 ]) and (Observer.Data = FObservers[i * 2 + 1]) then
    begin
      Result := i;
      break;
    end;
  end;
end;

function TMultiEvent.GetObserver(Index: integer): TMethod;
begin
  { Fill the TMethod record with the code and data pointers. }
  Result.Code := FObservers[Index * 2];
  Result.Data := FObservers[Index * 2 + 1];
end;

procedure TMultiEvent.SignalObserver(Observer: TMethod);
begin
  { Descendants must take care to notify the Observer by themselves }
  { because we cannot know the parameters required by the event.    }

  Assert(Assigned(@Observer));
  { We could make this method Abstract and force descendants, but   }
  { I prefer to do a run-time check to validate the passe methods   }
end;


procedure TMultiEvent.Signal;
var
  i: integer;
begin
  { Call SignalObserver for each stored observers in reverse order. }

  { SignalObserver (which is declared in sub-classes) will typecast }
  { the TMethod record into whatever procedure type it handles.     }
  { See the TMultiNotifyEvent below for an example.                 }

  for i := (FObservers.Count div 2)-1 downto 0 do
  begin
    SignalObserver(GetObserver(i));
  end;
end;

{ TMultiNotifyEvent }

procedure TMultiNotifyEvent.Attach(Observer: TNotifyEvent);
begin
  inherited Attach(TMethod(Observer));
end;

procedure TMultiNotifyEvent.Detach(Observer: TNotifyEvent);
begin
  inherited Detach(TMethod(Observer));
end;

procedure TMultiNotifyEvent.Signal(Sender: TObject);
begin
  FSender := Sender;
  inherited Signal;
end;

procedure TMultiNotifyEvent.SignalObserver(Observer: TMethod);
begin
  inherited SignalObserver(Observer);
  TNotifyEvent(Observer)(FSender);
end;

{ TSubject }

constructor TSubject.Create(AValue: string);
begin
  inherited Create;
  FValue := AValue;
  FOnChange := TMultiNotifyEvent.Create;
end;

destructor TSubject.Destroy;
begin
  FOnChange.Free;
  inherited Destroy;
end;

procedure TSubject.DoChange;
begin
  { Signal is the method that notify every observers }
  OnChange.Signal(Self);
end;

procedure TSubject.SetValue(const aValue: string);
begin
  if fValue <> aValue then
  begin
    FValue := aValue;
    DoChange;
  end;
end;

end.

Open in new window

i create a sizeable toolwindow stayontop form

with this code:
unit uToolbox;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  uPatterns;

type
  TfrmToolbox = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormWindowStateChanged(Sender: TObject);
    procedure FormActivate(Sender: TObject);
  private
    fSOWindowState: TSubject;
  public
    constructor Create(aOwner: TComponent); override;
    destructor Destroy; override;

    function VisibleWindowState: string;

    property SOWindowState: TSubject read fSOWindowState;
  end;

var
  frmToolbox: TfrmToolbox = nil;

function Toolbox: TfrmToolbox;

implementation

{$R *.dfm}

function WindowStateToString(aWindowState: TWindowState): string;
begin
  Result := 'Normal';
  case aWindowState of
    wsMinimized: Result := 'Minimized';
    wsMaximized: Result := 'Maximized';
  //else
    // wsNormal
  //  Result := 'Normal';
  end;
end;

function VisibleToString(aVisible: Boolean): string;
begin
  Result := 'Hidden';
  if aVisible then
    Result := 'Visible';
end;

function Toolbox: TfrmToolbox;
begin
  if not Assigned(frmToolbox) or (frmToolbox = nil) then
    frmToolbox := TfrmToolbox.Create(Application);
  Result := frmToolbox;
end;

constructor TfrmToolbox.Create(aOwner: TComponent);
begin
  inherited Create(AOwner);
  fSOWindowState := TSubject.Create(VisibleWindowState);
end;

destructor TfrmToolbox.Destroy;
begin
  fSOWindowState.Free;
  inherited Destroy;
end;

procedure TfrmToolbox.FormActivate(Sender: TObject);
begin
  Visible := True;
end;

procedure TfrmToolbox.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caHide;
  FormWindowStateChanged(Self);
end;

procedure TfrmToolbox.FormWindowStateChanged(Sender: TObject);
begin
  fSOWindowState.Value := VisibleWindowState;
end;

function TfrmToolbox.VisibleWindowState: string;
begin
  Result := VisibleToString(Visible) + '_' + WindowStateToString(WindowState);
end;

end.

Open in new window



and this dfm:
object frmToolbox: TfrmToolbox
  Left = 0
  Top = 0
  BorderStyle = bsSizeToolWin
  Caption = 'Toolbox'
  ClientHeight = 478
  ClientWidth = 387
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  FormStyle = fsStayOnTop
  OldCreateOrder = False
  Visible = True
  OnActivate = FormActivate
  OnClose = FormClose
  OnHide = FormWindowStateChanged
  OnResize = FormWindowStateChanged
  OnShow = FormWindowStateChanged
  PixelsPerInch = 96
  TextHeight = 13
end

Open in new window

and the main form has a menu windows, which can be used to hide/show the toolbox
and a memo to display the changes of toolbox window state:
unit uMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Menus, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    MainMenu1: TMainMenu;
    mnuWindows: TMenuItem;
    mnuToolbox: TMenuItem;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure mnuToolboxClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    procedure ToolboxChangedWindowState(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

uses uToolbox;

{$R *.dfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  Toolbox.SOWindowState.OnChange.Attach(ToolboxChangedWindowState);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  // this throws an AV because the TfromToolbox is destroyed first
  // Toolbox.SOWindowState.OnChange.Detach(ToolboxChangedWindowState);
end;

procedure TForm1.mnuToolboxClick(Sender: TObject);
begin
  Toolbox.Visible := mnuToolbox.Checked;
end;

procedure TForm1.ToolboxChangedWindowState(Sender: TObject);
var temp: string;
  IsVisible: Boolean;
begin
  temp := Toolbox.VisibleWindowState;
  IsVisible := SameText(Copy(temp, 1, 7), 'Visible');
  if mnuToolbox.Checked <> IsVisible then
    mnuToolbox.Checked := IsVisible;
  memo1.Lines.Add('Toolbox window state changed to : ' + temp);
end;

end.

Open in new window


the dfm code:
object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 426
  ClientWidth = 828
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  Menu = MainMenu1
  OldCreateOrder = False
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object Memo1: TMemo
    Left = 56
    Top = 96
    Width = 545
    Height = 225
    Lines.Strings = (
      'Memo1')
    TabOrder = 0
  end
  object MainMenu1: TMainMenu
    Left = 408
    Top = 216
    object mnuWindows: TMenuItem
      Caption = 'Windows'
      object mnuToolbox: TMenuItem
        AutoCheck = True
        Caption = 'Toolbox'
        Checked = True
        OnClick = mnuToolboxClick
      end
    end
  end
end

Open in new window

now, the trick is to show the toolbox only when the app is active
Thanks again, Geert
I will be back on this after a thorough study of the material you supplied. It may take a few days, but I can assure you, that I will be back here.
Ole
when i changed the toolbox code like below...

the toolbox only remains visible when the app is active

unit uToolbox;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  uPatterns;

type
  TfrmToolbox = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormWindowStateChanged(Sender: TObject);
  private
    fSOWindowState: TSubject;
    fVisibleOnDeactive: Boolean;
    fOldAppDeactivate: TNotifyEvent;
    fOldAppActivate: TNotifyEvent;
    fDeactivating: Boolean;
    procedure OnAppDeactivate(Sender: TObject);
    procedure OnAppActivate(Sender: TObject);
  public
    constructor Create(aOwner: TComponent); override;
    destructor Destroy; override;

    function VisibleWindowState: string;

    property SOWindowState: TSubject read fSOWindowState;
  end;

var
  frmToolbox: TfrmToolbox = nil;

function Toolbox: TfrmToolbox;

implementation

{$R *.dfm}

function WindowStateToString(aWindowState: TWindowState): string;
begin
  Result := 'Normal';
  case aWindowState of
    wsMinimized: Result := 'Minimized';
    wsMaximized: Result := 'Maximized';
  //else
    // wsNormal
  //  Result := 'Normal';
  end;
end;

function VisibleToString(aVisible: Boolean): string;
begin
  Result := 'Hidden';
  if aVisible then
    Result := 'Visible';
  if not Application.Active then
    Result := 'Inactive_' + Result;
end;

function Toolbox: TfrmToolbox;
begin
  if not Assigned(frmToolbox) or (frmToolbox = nil) then
    frmToolbox := TfrmToolbox.Create(Application);
  Result := frmToolbox;
end;

constructor TfrmToolbox.Create(aOwner: TComponent);
begin
  inherited Create(AOwner);
  fVisibleOnDeactive := True;
  fSOWindowState := TSubject.Create(VisibleWindowState);
  fDeactivating := False;
  fOldAppActivate := Application.OnActivate;
  Application.OnActivate := OnAppActivate;
  fOldAppDeactivate := Application.OnDeactivate;
  Application.OnDeactivate := OnAppDeactivate;
end;

destructor TfrmToolbox.Destroy;
begin
  fSOWindowState.Free;
  inherited Destroy;
end;

procedure TfrmToolbox.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caHide;
  FormWindowStateChanged(Self);
end;

procedure TfrmToolbox.FormWindowStateChanged(Sender: TObject);
begin
  if not fDeactivating then
   fSOWindowState.Value := VisibleWindowState;
end;

procedure TfrmToolbox.OnAppActivate(Sender: TObject);
begin
  if fVisibleOnDeactive then
      Visible := True;
end;

procedure TfrmToolbox.OnAppDeactivate(Sender: TObject);
begin
  fVisibleOnDeactive := Visible;
  fDeactivating := True;
  try
    Hide;
  finally
    fDeactivating := False;
  end;
end;

function TfrmToolbox.VisibleWindowState: string;
begin
  Result := VisibleToString(Visible) + '_' + WindowStateToString(WindowState);
end;

end.

Open in new window

SOLUTION
Avatar of Geert G
Geert G
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi Geert,

Thanks again for your interest in my problems. I read the article about Observer Patterns you linked to, and I copied your edited copy of uPatterns.

I find the that uPatterns has a very interesting way of solving the problem, that you cannot assign more than one event handler at a time in Delphi (other languages allow this).

Anyway, you sent my memory back to 2013, when I created a TStackableEventHandler which signals the event handler on the top of the stack only. Possible to pick a handler off the stack, of course. In that way, a number of forms in the application might set their individual handlers in FormShow and pick them off in FormHide. Around the same time, I also created a multi-purpose TOneTimer taking care of many different timing needs through one single TTimer component. uPattern's little trick with saving both .Code and .Data from the event handler reminded me of something, I have seen before. Unfortunately, I just don't remember what it was.

But, back from memory lane...

As I said Friday, we agree upon a toolbox being a form in a unit. Bad names in my sample. Form1 should be fMain (or frmMain if you like) and Form2 should be fToolbox (or frmToolbox).

You write that "in reality it should only be a separate unit which you include in the uses list of the project  that alone should solve the correct toolbox being active with the active version of the app" and yes, we agree again, except that there must be SOME communication between the main program and the toolbox. HOW that communication is established, I will be happy to discuss further. Actually, I will come back to this a little later.

In the same paragraph, you also wrote "it is possible you have multiple toolboxes open, but clicking on a another toolbox will activate that other application". In theory - for another project - that could be reasonable, but for the large application, I am working on, this is not desired. On the contrary, I might reveal, there are going to be several DIFFERENT toolboxes available, and the user may want some open for one instance of the application, but other toolboxes open for another instance of the application. In our discussion here, let's just stick to a single toolbox connected to a single application.

You continue with "you should not mix code of this toolbox" and again, I can only agree as far as solving the case allows. I generally use f<subform name>.Show in my coding, and even though you could write f<subform name>.Visible := True or even F<subform name>.<some public method created for the purpose> to get the same result, I don't see any way to get around referencing the toolbox (the subform in question), when the main program needs to wake it up for some reason.

But was it my callbacks from OnShow and OnHide, you didn't like? Well, I will gladly accept uPatterns' TSubject in order to solve my challenges. But that doesn't change the fact, that there will be calls originated in the toolbox, arriving to the main form via event handlers.

You pinpoint "code reusability". Absolutely, reusability is a very important issue to me. You also warn me about "circular unit reference", but there are no reference from Form2 to Form1 in my sample code. Generally, I tend to avoid circular references as much as possible and I think uPatterns will minimize that need even further in the future.

So, your reference to uPatterns was inspiring, but your uToolbox sent me back to square one (or bug one, if you like), I'm afraid. Try creating an empty main form and just add uToolbox to your project. Run the program and you will see the Toolbox and the main form.

Now, click the application icon in the taskbar and see what happens: The toolbar and the main form are minimized, but the toolbar immediately pops up again.

That is exactly where my problems started, as described in my initial text above. And I still have no idea how to fix it!

I understand that your uToolbox is only created to show the principles, but I read the code as it is and have one major comment: In "constructor TfrmToolbox.Create(aOwner: TComponent)" you set Application.OnActivate and Deactivate. You do save the original values, so you intend to restore them somewhere, but is it reasonable at all, that some inferior subform should mingle with the global Application variable? Well, maybe while being active, but not permanently. You would be in trouble when creating more toolboxes or subforms based on that idea.

Due to this problem, I chose to discard your uToolbox and create one for myself.

I attach a new set of files in which I have changed names to follow my usual naming convention. I have used uPattern as I believe you wanted me to do it. I have added a button to the toolbox in order to get your opinion on the communication between toolbox and main form when the user actually uses the toolbox.

Are you still hanging on? Thanks! I will look forward to hearing from you again.
Ole
ToolboxTesting.dpr
uMain.pas
uMain.dfm
Oops , I pressed ENTER too early.  Here comes the toolbox files
uToolbox.pas
uToolbox.dfm
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
lol, lot's of memories

didn't know about the AppActivate and the taskbar button
guess i didnt really see that problem

you have multiple toolboxes in the same app ?
and you want only 1 of them active

2 ways, descend from the same parent class and implement a singleton visible in that
or add an interface to each and apply the singleton visible idea via that interface
It works!
Geert's comments were inspiring, but did not solve the problem.