3

I want to show a TPanel in the middle of a form that is MDI parent for other forms. Some kind of 'splash' form, but not quite. The panel will contain links/buttons/shortcuts from where the user will call misc. functions.

The main requirement is that the TPanel should be placed below the MDI child form(s) when I click the MDI child. However, as it is, the TPanel will ALWAYS stay above the MDI child forms.

Calling Panel.SendToBack will make the panel disappear. How can I do?

Gabriel
  • 20,797
  • 27
  • 159
  • 293
  • 3
    A non-windowed control, such as a `TImage` and `TShape` will work as you wish. – Andreas Rejbrand Aug 18 '12 at 20:02
  • 2
    "The panel will contains link/buttons/shortcuts from where the user will call misc. functions" Sounds like a toolbar. Why would you want that to below the other forms? The use must minimize or close all forms to reach that functionality. Rather make this panel an MDI form itself, so it can be opened or put on top in a single click. – GolezTrol Aug 20 '12 at 09:08
  • Why not align the panel to an edge of the main form? It will reduce the client space for the MDI childs, but it sure works well without VCL hacks. Or consider using a [collapsable side bar](http://stackoverflow.com/q/7265416/757830). – NGLN Aug 20 '12 at 09:41
  • @GolezTrol - I think that will work too if I get rid of the form's caption. Thanks. – Gabriel Aug 21 '12 at 16:14
  • @AndreasRejbrand That will work, but you cannot place TButtons on a TShape. It's not about the panel, but about the controls it should contain. Resorting to handle-less controls (TSpeedButton?) is an option, but you are very limited in your choice of controls and will lack keyboard input. – GolezTrol Aug 21 '12 at 23:04

2 Answers2

6

You will need to override the Panel's WindowProc so that the panel will always be behind the MDI children e.g.:

TMainForm = class(TForm)
...
private
  FPanelWndProc: TWndMethod;
  procedure PanelWndProc(var M: TMessage);
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  Windows.SetParent(Panel1.Handle, ClientHandle);
  // Override Panel1 WindowProc
  FPanelWndProc := Panel1.WindowProc;
  Panel1.WindowProc := PanelWndProc;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  // Restore Panel1 WindowProc
  Panel1.WindowProc := FPanelWndProc;
end;

procedure TMainForm.PanelWndProc(var M: TMessage);
var
  P: ^WINDOWPOS;
begin
  if M.Msg = WM_WINDOWPOSCHANGING then
  begin
    P := Pointer(M.LParam);
    // Always place panel at bottom
    P.hwndInsertAfter := HWND_BOTTOM;
  end;
  FPanelWndProc(M);
end;

Note: To quickly test the code, you can create a MDI application via File -> New -> MDI Application


EDIT: The code above dose infact answers your initial question. If you want your "Panel to behave somehow as a MDI child" (your comment quote), then simply (...hmmmm...) use a MDI Child form. i.e. create a new form with .FormStyle = fsMDIChild, and then use something like:

SetWindowLong(Child.Handle, GWL_STYLE, 
   GetWindowLong(Child.Handle, GWL_STYLE) and not (WS_BORDER or WS_DLGFRAME or WS_SIZEBOX));

To remove it's border (since simply setting .BorderStyle = bsNone does not work).
Put whatever you need on that form, and it will move above other MDI forms once you click it.

kobik
  • 21,001
  • 4
  • 61
  • 121
  • 1
    This prevents the panel from coming up at all. I expect OP actually want's to bring the panel to the front when using its buttons, and let it automatically send to the back when done. – NGLN Aug 20 '12 at 09:36
  • 4
    @NGLN, I answered exactly what the OP asked for. OP might or might not want to bring the panel to the front when using its buttons. that was not the question. – kobik Aug 20 '12 at 09:45
  • 2
    So far as I can tell, OP asked for panel always to be behind the MDI children. – David Heffernan Aug 20 '12 at 16:45
  • Hi kobik. I want to Panel to behave somehow as a MDI child. To have it on top of other forms when the user clicks it, or below when the user clicks a MDI child form. – Gabriel Aug 21 '12 at 16:26
  • 3
    That's exactly how an MDI child behaves!! – David Heffernan Aug 21 '12 at 20:23
1

The MDI system works by having a single window that is the parent of all the MDI child windows, known as the client window. That client window is, in turn, a child of the MDI form. The VCL implementation of MDI creates the child window for you. You can gain access to its window handle through the ClientHandle.

Since the client window is a child of the main form, and parents all the MDI forms, the only solution for you is to make this panel part of the client window.

You could take control of the painting of the client window. You can do this by replacing the window proc of the client window with one of your own. You'll also need to handle button clicks etc. But that's pretty messy.

Now, perhaps you could make your panel a child of the client window. But I'm pretty sure that will screw up your MDI, which indeed you confirm to be the case.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • 4
    _But I'm pretty sure that will screw up your MDI._ No, it doesn't. ;) +1 – NGLN Aug 19 '12 at 08:28
  • @NGLN Thanks, I updated my answer. Not quite sure why I doubted parenting. I imagined that it would mess up tiling etc. But I guess my instincts were wrong there. – David Heffernan Aug 19 '12 at 09:22
  • Hi David. You were right. Changing the order of the windows won't work correctly anymore. See the comment I left for NGLN. – Gabriel Aug 19 '12 at 11:23
  • 3
    I've seen similar behaviour in my MDI app. I had to add some message processing to fix it. Unfortunately I can't get at the code for another week. Sorry I can't help sooner. Perhaps somebody else will chip in. However, I'm not that motivated to go the extra mile to help you after the way you behaved in this question: http://stackoverflow.com/questions/11853111/resize-wont-execute-untill-i-manually-call-clientheigh-or-until-i-manually-resi – David Heffernan Aug 19 '12 at 12:02
  • Sorry, I was too quick: it's not as easy as I imagined. I'm still interesting in your code though. – NGLN Aug 20 '12 at 08:17