0

How can I show topmost a button clicked child form in my parent form (with a tabcontrol which docked as fill)? It always shows the form at the back of the tabcontrol of the parent form, I've even used:

frm.TopMost = true;
frm.BringToFront();

Still shows at the back.

diiN__________
  • 7,393
  • 6
  • 42
  • 69
office puppy
  • 67
  • 2
  • 8
  • When you say "child form" you mean an MDI child? – Jcl Apr 20 '16 at 07:58
  • yes sir, I'm trying to show in-front of the tab control in my parent form – office puppy Apr 20 '16 at 08:00
  • You're using `TopMost` *for an MDI child*? The two are not compatible. Also, you're still using MDI in 2016? Why? It's really kind of obsolete nowadays, and unfamiliar. Maybe a simple `Owner` relationship would work better? – Luaan Apr 20 '16 at 08:47
  • @Luaan I don't agree with MDI being obsolete (not more obsolete than Winforms anyway). I'm doing a new (a rewrite, basically) LoB app and it's MDI (tabbed MDI which you can undock, but anyway)... I've tried many approaches and found MDI to be the best design for this specific app, and the one users liked the most – Jcl Apr 20 '16 at 09:07
  • @Jcl Sure, but "tabbed MDI with undocking" most likely doesn't use the Windows MDI infrastructure, does it? That's the obsolete part, not the interface itself (though plenty of people don't understand it anymore). I love Visual Studio's "tabbed-pseudo-MDI" interface, but it doesn't use "real" MDI - it doesn't even use "real" windows anymore. – Luaan Apr 20 '16 at 09:16
  • @Luaan I'm not sure of the implementation (I'm using devexpress' tabbed mdi and docking controls, not reinventing the wheel), but I'd say they internally use MDI. I like VS's interface too, but everything I try (be it doing it myself, or trialing third-party controls) in WPF gets messy very soon (*disclaimer:* I hate developing WPF and hate all the time I've had to spend doing so, so my oppinion might be biased ;-) ). I wished there was any modern good *and completely baked* desktop UI library, but seems there's none (and yes, I think WPF is half-baked, and don't get me started on UWP :-) ) – Jcl Apr 20 '16 at 09:20
  • 1
    @Jcl Yup, DevExpress' is absolutely totally not real MDI - you can see it easily in Spy++. They fake it all the way because "real MDI" hasn't been updated in so long it really sucks today :). WPF is actually pretty great, but it doesn't interface with C# as well as Winforms does, and it changes too many things to make it attractive for most Winforms (and native Windows) developers. Oh well, we'll have to die out before it really gets traction :) – Luaan Apr 20 '16 at 09:30
  • 1
    @Luaan this would be a great conversation to have in front of a beer :-) I've done my share of WPF, have released apps with it, and I hate it for anything that doesn't need to be "fancy looking"... love the binding and MVVM, but XAML (a declarative language where you can write functionality?) and the visual tree implementation, I hate it. I do a mixture of Winforms using some kind of self-baked binding (not winforms databinding) and use self-implemented ICommands (kind of a mix of MVVM and MVP) these days. Wished I'd have found any UI system that fit me better, but I didn't – Jcl Apr 20 '16 at 09:31

3 Answers3

2

What you want is not possible. MDI children of a control get shown on a control (which you can't directly select) called MdiClient, which is not transparent (and can't be) and by default, goes always to the back of other controls in the parent form.

So the only way to do this, would be getting the MdiClient over the controls in the parent form: this would do what you expect, but it would also hide the parent controls when there are no child forms displayed (since again, the MdiClient is not, and can't be transparent).

So the only reasonable way would be having a maximized child form with the TabControl, instead of having that TabControl directly on the parent.

Or you could have your TabControl only shown when there are no child windows. For that, make a timer in the parent form, and check this at every interval:

if(MdiChildren.Length > 0)
   myTabControl.SendToBack();
else
   myTabControl.SendToFront();

This will only work if the MDI children are always maximized: your TabControl will not be visible when there are any children (no matter if they cover it or not)

Update

As remarked in the comments, you can have "your own MDI", by having a host control (a Panel, for example) in the parent form and loading the child forms in that control:

var form = new ChildForm();
form.TopLevel = false;
form.Parent = myHostPanel;
form.Show();

This would show the form inside the panel (which you can locate and zorder where you want)... you lose all the MDI management though, and you'll have to keep track of your children (and take care of the forms' events if needed) yourself.

I'd not use this solution, it's pretty hacky and can get messy for big applications (unless you do a correct system)

As a summary

Since we're discussing these methods in the comments

You can hack your way to do what you want, but you'll come into all sorts of problems with any approach.

If I was you, I'd redesign my application so that what you want to achieve is not needed. If you can't do that, the only sane way would be just not having those controls in the parent form, have an always-maximized, non-closable MDI child form with those controls, and skip that window everytime you need to work in the MDI children collection.

Jcl
  • 27,696
  • 5
  • 61
  • 92
  • Thank you sir for the answer. Is there other way to make other forms not be visible in taskbar or inside another form? – office puppy Apr 20 '16 at 08:35
  • You can make your windows not show in taskbar setting the property `ShowInTaskbar` to `false`. You can also make the parent of a form any control inside your "parent" form (just set the `Parent` property, instead of `MdiParent`, and set the forms `TopLevel` property to `false`) – Jcl Apr 20 '16 at 08:37
  • maybe using the timer have performance issues, consider using event like `MdiChildActivate`. – mostafa8026 Apr 20 '16 at 08:38
  • 1
    @mostafa8026 that's right... using a timer with a reasonable interval should be not a performance problem, but trapping `MdiChildActivate` could be better: you'd need to handle the closing or hiding of the child forms too though, there's no `MdiChildDeactivate` – Jcl Apr 20 '16 at 08:40
  • @Jcl, it can be done with creating an event for the main form. this event can be called when `mdiChildren.Count()` has been changed. – mostafa8026 Apr 20 '16 at 08:45
  • The `MdiChildren` collection is not observable, so in any way or other, you'll need to keep track of it via polling... you can do so in many events, but the easiest way would be just having a timer. A 100/200 milliseconds timer should be good enough for winforms (most redraws in complex winforms apps take a lot of time anyway), and should not be a performance problem at all. – Jcl Apr 20 '16 at 08:46
  • Note that non-`TopLevel` windows don't quite behave as forms. For example, you're not going to get activation (`WM_ACTIVATE` is only sent to top-level windows) - you need to write your own. It basically becomes a `Panel` you can drag around, and little else. Performance may also suffer, since they're not rendered to their own surface on DWM, so you don't get hardware-accelerated movement (i.e. the parent needs to be redrawn when you drag the child). It's not *hacky*, really - that's just how windows work; .NET just has a "special" window that's called a `Form`, which is usually top-level. – Luaan Apr 20 '16 at 08:52
  • @Jcl Or you can just ensure that all MDI children are added through your own method :) – Luaan Apr 20 '16 at 08:53
  • 1
    @Luaan that's totally right... it also has bugs ([I even filled one in connect](https://connect.microsoft.com/VisualStudio/feedback/details/1059444/in-windows-8-drawtobitmap-on-a-form-draws-the-windows-7-chrome) -*marked as won't fix, btw*) regarding the window chrome in Win8+, since the form is rendered as a bitmap and for some reason the chrome gets rendered in the Win7 non-aero style, even on Win8 or 10... you'd basically have to make your own full windowing system :-) – Jcl Apr 20 '16 at 08:54
  • Oh yes, but added -and- removed (or hidden), you'll need to subscribe to their closed, resize events, etc... I'd rather use a timer if I was to do something like this (then again, I'd never design an app like this, but that's out of the question ;-) ) – Jcl Apr 20 '16 at 08:55
  • @Jcl it works thanks, exactly what i've wanted to do. – office puppy Apr 20 '16 at 09:04
0

Please explain what components of what frameworks you are using and what you have done so far. Without that information i suggest the following solution (untested).

In the "ButtonClick" event of your ParentForm do this:

ChildForm cf = new ChildForm();
cf.MdiParent = this;
cf.Show();

If this doesn't work you may add a

cf.Focus();
Chrigl
  • 628
  • 6
  • 22
  • i've already done that sir.. still doesn't work, i'm using .net framework 4. My parent form is filled with tab control with tab pages., my concern is the child form shows at the back., – office puppy Apr 20 '16 at 08:11
0

This question make me uncomfortable :). after a lot of testing, I can't really find a solution. neither BringToFront() Function nor SendToBack() Work Properly. maybe the following approach can help you. I use IntersectWith Function of Rectangle Class and test if form intersect with tabControl or not. if so change the tab control visibility to false otherwise true. take a look at the following code:

first make form declaration public at the mdi parent form:

public partial class MdiParentForm : Form
{
    Form frm = new Form();
}

After that when you initialize your child form, add some handler to its locationChanged event, like this:

frm.MdiParent = this;
frm.LocationChanged += Frm_LocationChanged;
frm.Show();

And at the end, This is the handler:

private void Frm_LocationChanged(object sender, EventArgs e)
    {
        Rectangle tabControlRectangle = new Rectangle(tabControl1.Location, tabControl1.Size);
        Rectangle childFormRectangle = new Rectangle(frm.Location, frm.Size);
        if (tabControlRectangle.IntersectsWith(childFormRectangle))
        {
            tabControl1.Visible = false;
        }
        else
        {
            tabControl1.Visible = true;
        }
    }

Thanks to @Jcl, Problem with this is that the tab control will hide and show as long as any point of the child form touches its rectangle. Moving the child form around would be horrible :-)

mostafa8026
  • 273
  • 2
  • 12
  • 25