Mega Code Archive

 
Categories / Delphi / System
 

How to mimic MS Word or MS IE free floating windows using VCL

Title: How to mimic MS Word or MS IE free-floating windows using VCL Question: In this article I will show how to relatively simply mimic the multiple free-floating windows concept of MS applications. The advantage of this concept is that there is only one application running but the individual windows have an taskbar icon so that they can be minimized, maximized, organized into tiles, etc. In each window a separate instance is running, so it is possible to work independently in each window as for example in MS Word. The core idea is to create a hidden main form which contains a list of child windows (i.e., the actual application). The whole application is terminated after all child windows are closed. Answer: I will describe how to make a simple multiple free-floating windows application. Using this example it is possible to rewrite an existing application to use the multiple free-floating windows concept. The sample application is able to create a new child window, hide or close a child window. Two a bit "dirty" tricks are involved: hiding of the main application form and forcing the child windows to appear on the task bar. First we will create a new VCL project. The main application form TMainForm will contain a FWndList which will contain all the child windows. The FWndList is a classical TObjectList. Next, we make two public methods - AddForm and RemoveForm - which will be used to add and remove some a child window from the FWndList. These methods are called when a new child window is created or when a child window is closed. We also make a method which will signal to the child windows that the window list was changed, so that the child windows can update their menus, list boxes, etc. After the application is executed it is convenient to immediately open one child window. It is done by the method Init in the listing which is called from the project file (.dpr). type TMainForm = class(TForm) procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private FWndList: TObjectList; protected procedure UpdateChildWindows; public procedure AddForm(const F: TForm); procedure RemoveForm(const F: TForm); property WndList: TObjectList read FWndList; procedure Init; end; implementation {$R *.dfm} uses Child; { TMainForm } procedure TMainForm.AddForm(const F: TForm); // add a new child window to the window list begin // and the form FWndList.Add(F); // signal to other child windows to update their listboxes UpdateChildWindows; end; procedure TMainForm.RemoveForm(const F: TForm); // remove the child window F from the window list var idx: Integer; begin // find the child window F in the FWndList idx:=FWndList.IndexOf(F); if (idx = -1) then raise Exception.Create('ERROR: form not found'); // remove the element FWndList.Delete(idx); // signal to other child windows to update their listboxes UpdateChildWindows; end; procedure TMainForm.Init; // creates the first form var F: TChildWindow; begin // creates the form F:=TChildWindow.Create(Application); // show the form F.Show; // add the form to the windows list AddForm(F); // a "dirty" trick to hide the main application window from the task bar ShowWindow(Application.Handle, SW_HIDE); end; procedure TMainForm.FormCreate(Sender: TObject); begin // create the object list so that the elements of the list are not *owned* by // the list FWndList:=TObjectList.Create(false); // hides the main application window Application.ShowMainForm:=false; end; procedure TMainForm.FormDestroy(Sender: TObject); begin // dispose the list, but not the elements of the list FWndList.Free; end; procedure TMainForm.UpdateChildWindows; // signal to the child windows to update their menus, listboxes, etc. var i: Integer; begin for i:=0 to FWndList.Count-1 do (FWndList[i] as TChildWindow).UpdateState; end; Next we will create a child window which will act as an independent application window. We will create a child window which is able to open another child window, close and hide itself. In a real application the child window will be replaced by your application. Thus, the child window will have three methods - NewChildWindow, HideChildWindow and CloseChildWindow. Additionaly, we must add a method UpdateState which updates the window state after some other window is closed. Usually, in this method we update the menu with open windows. type TChildWindow = class(TForm) protected procedure CreateParams(var Param: TCreateParams); override; public procedure UpdateState; function NewChildWindow: TChildWindow; procedure HideChildWindow; procedure CloseChildWindow; end; implementation {$R *.dfm} uses Main; { TForm2 } procedure TChildWindow.CreateParams(var Param: TCreateParams); begin inherited; Param.ExStyle:=Param.ExStyle or WS_EX_APPWINDOW; Param.WndParent:=GetDesktopWindow; end; procedure TChildWindow.CloseChildWindow; // close the window begin Close; end; procedure TChildWindow.HideChildWindow; // hide the window begin Hide; end; function TChildWindow.NewChildWindow: TChildWindow; // open a new child window begin Result:=TChildWindow.Create(Application); Result.Show; MainForm.AddForm(Result); end; procedure TChildWindow.FormCreate(Sender: TObject); begin Caption:='Child Window ' + IntToStr(MainForm.WndList.Count); end; procedure TChildWindow.FormClose(Sender: TObject; var Action: TCloseAction); begin // remove the form from the child window list MainForm.RemoveForm(Self); // free the form after closing Action:=caFree; end; procedure TChildWindow.UpdateState; var i: Integer; begin for i:=0 to MainForm.WndList.Count-1 do // e.g., update menu, listbox, etc. end; Finally, we must modify the project file, so that the application is terminated after all child windows are closed. program Project1; uses Forms, Main in 'Main.pas' {MainForm}, Child in 'Child.pas' {ChildWindow}; {$R *.res} begin Application.Initialize; Application.CreateForm(TMainForm, MainForm); // show the first child window MainForm.Init; // repeat until no child window exists, this construction replaces usual // Application.Run calling repeat Application.ProcessMessages; until (MainForm.WndList.Count = 0); end.