 |
|
 |
Very helpful function. Thank you for sharing.
|
|
|
|
 |
|
|
 |
|
|
 |
|
|
 |
|
 |
I had an similar Problem. I created an MDI-App with multiple child Forms with FormBorderStyle.None and ControlBox = false. I docked them with DockStyle.Fill to the MDI-Client. (If you wonder - this was a special Touchscreen app).
But when the Form was activated for the first time, I saw the title bar and the ControlBox, and the Form wasn't docked, for a moment. After some fiddling I came up with this solution: If I disabled the ControlBox before CreateControl fires, everything was fine, and no flickering occured...
/// <summary>
/// Base Form for MDI-child windows.
/// </summary>
public class BaseChildForm : Form
{
protected override void OnCreateControl()
{
ControlBox = false;
base.OnCreateControl();
}
}
|
|
|
|
 |
|
 |
Google says it's a known bug in the framework, I was searching for the solution and finally yours worked! Thanks buddy!
A man's got to know his limitations
Harry Callahan
|
|
|
|
 |
|
 |
Maybe you are interested in another solution I found somewhere while working on the same project.
This is to let the border of the MdiClient disapear (if you want to draw a nice background for yourself etc...) :
public partial class Form1 : Form
{
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
public static extern int SetWindowLong(IntPtr hwnd, int iIndex, int iValue);
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
public static extern int GetWindowLong(IntPtr hwnd, int iIndex);
[System.Runtime.InteropServices.DllImport("user32.dll", ExactSpelling = true)]
public static extern int SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
/// <summary> Wrapper enum for the Win32 WS_(...) constants. </summary>
public enum EWindowStyle : int
{
Border = 0x00800000,
ClientEdge = 0x00000200
}
/// <summary> Wrapper enum for the Win32 GWL_(...) constants. </summary>
public enum EGetWindowLongFlag : int
{
Style = -16,
ExStyle = -20
}
/// <summary> Wrapper enum for the Win32 SWP_(...) constants. </summary>
public enum ESetWindowPosFlag : uint
{
NoSize = 0x0001,
NoMove = 0x0002,
NoZOrder = 0x0004,
NoRedraw = 0x0008,
NoActivate = 0x0010,
NoCopyBits = 0x0100,
NoOwnerZOrder = 0x0200,
NoSendChanging = 0x0400,
FrameChanged = 0x0020,
ShowWindow = 0x0040,
HideWindow = 0x0080,
}
public Form1()
{
InitializeComponent();
this.IsMdiContainer = true;
#region Adjust MdiClient
// Get MdiClient
MdiClient mdiclient = null;
foreach (Control control in Controls)
{
mdiclient = control as MdiClient;
if (mdiclient != null)
{
// Set some properties
mdiclient.BackColor = SystemColors.Control;
// mdiclient.BackgroundImage = etc.
// Adjust BorderStyle
int iStyle = GetWindowLong(mdiclient.Handle, (int)EGetWindowLongFlag.Style);
int iExStyle = GetWindowLong(mdiclient.Handle, (int)EGetWindowLongFlag.ExStyle);
// ... to BorderStyle.None
iStyle &= ~((int)EWindowStyle.Border);
iExStyle &= ~((int)EWindowStyle.ClientEdge);
SetWindowLong(mdiclient.Handle, (int)EGetWindowLongFlag.Style, iStyle);
SetWindowLong(mdiclient.Handle, (int)EGetWindowLongFlag.ExStyle, iExStyle);
// Previouse changes were made to the NonClient area of the Form. So calling Invalidate() won't redraw the borders.
// Calling SetWindowPos does an implicit redraw of the NonClient area - thats the trick.
SetWindowPos(mdiclient.Handle, IntPtr.Zero, 0, 0, 0, 0,
(uint)(ESetWindowPosFlag.NoActivate | ESetWindowPosFlag.NoMove | ESetWindowPosFlag.NoSize | ESetWindowPosFlag.NoZOrder | ESetWindowPosFlag.NoOwnerZOrder | ESetWindowPosFlag.FrameChanged));
break;
}
}
#endregion // Adjust MdiClient
}
}
|
|
|
|
 |
|
 |
"if you want to draw a nice background for yourself etc..."
please rovide more details
thanks
Yosi
|
|
|
|
 |
|
 |
Hmm, maybe you tell me what you want to do, so I can try to help you.
The above statement means: If you want to draw the MdiClient for yourself in the Paint EventHandler it's maybe useful to get rid of the Border!
Try for yourself. Add a Paint-EventHandler to the MdiClient in the above code and draw something, then uncomment the adjustment of the border and look the difference.
As I mentioned in my original post, I did a Touchscreen-Application, where a "seamless" integration of child forms was essential - the MdiClient-Border disturbed this.
|
|
|
|
 |
|
 |
I have the same problem, I try
Protected Overrides Sub OnCreateControl()
controlbox=false (or formborderstyle=none)
MyBase.OnCreateControl()
End Sub
and found that the painting of the childform (clientsize) would go wrong. so I track the source code of the form.cs, and found they call MyBase.UpdateStyles(), so the following would be better to remove the controlbox.
Protected Overrides Sub OnCreateControl()
MyBase.UpdateStyles()
MyBase.OnCreateControl()
End Sub
|
|
|
|
 |
|
 |
I copy the code to Parent form but it's not work for me the controle button show every time. the source code is :
using System;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace GainDrug
{
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
public void ActivateMdiChild(Form childToActivate)
{
if (this.ActiveMdiChild != childToActivate)
{
MdiClient mdiClient = GetMDIClient();
int count = this.MdiChildren.Length;
Control form = null; // next or previous MDIChild form
int pos = mdiClient.Controls.IndexOf(childToActivate);
if (pos < 0)
throw new InvalidOperationException("MDIChild form not found");
if (pos == 0)
form = mdiClient.Controls[1]; // get next and activate previous
else
form = mdiClient.Controls[pos - 1]; // get previous and activate next
// flag indicating whether to activate previous or next MDIChild
IntPtr direction = new IntPtr(pos == 0 ? 1 : 0);
// bada bing, bada boom
SendMessage(mdiClient.Handle, WM_MDINEXT, form.Handle, direction);
}
}
public MdiClient GetMDIClient()
{
foreach (Control c in this.Controls)
{
if (c is MdiClient)
return (MdiClient)c;
}
throw new InvalidOperationException("No MDIClient !!!");
}
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
public const int WM_MDINEXT = 0x224;
private frmMyMDIParent fCus;
private void testToolStripMenuItem1_Click(object sender, EventArgs e)
{
if ((fCus != null))
{
if (!fCus.IsDisposed)
{
fCus.WindowState = FormWindowState.Normal;
fCus.BringToFront();
}
else
{
fCus = new frmMyMDIParent();
fCus.MdiParent = this;
fCus.Show();
}
}
else
{
fCus = new frmMyMDIParent();
fCus.MdiParent = this;
fCus.Show();
}
if (fCus == null)
{
MessageBox.Show("Is Nothing");
}
if (fCus.IsDisposed)
{
MessageBox.Show("Is Disposed");
}
}
private void frmMain_Load(object sender, EventArgs e)
{
}
}
}
Where are wrong please ?
Tospon
|
|
|
|
 |
|
|
 |
|
 |
The strange behaviour does indeed look bad expecially if the form takes a fraction of a second to draw itself.
Unfortunately I see this most often when accessing the MDI window list menu to change active window so I would be very interested to see a solution for that - the only one I can think of is to build the MDI window menu myself and then use your code to select the desired form...
Anyone have any better ideas?
|
|
|
|
 |
|
 |
The solution is easy, just subclass the MDIClient
window and handle the WM_MDIACTIVATE message, and
perform the activation yourself via the WM_MDINEXT
method used by the code postted, rather tha letting
the MDIClient window do it.
If you do that, you also eliminate the need to call
the dedicated ActivateMdiChild method provided by
the code, to activate the child window without the
flicker. IOW, you can use Activate() or Select(),
or any framework API to activate the window, as they
all generally work by sending WM_MDIACTIVATE to the
MDIClient.
-- modified at 20:42 Saturday 18th August, 2007
|
|
|
|
 |
|
 |
I added the following code to my ChildForm.
There is no flickering anymore, but there is also no menu merging,
no drawing of minimize/maximize/close button
and the menu where the selected child is shown is also not updated ...
I think there needs a little bit more to be done ... any ideas
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0222)
{
if (this.WindowState == FormWindowState.Maximized)
{
NoFlickerActivate();
return;
}
}
base.WndProc(ref m);
}
public void NoFlickerActivate()
{
if (this.MdiParent == null) return;
MdiClient mdiClient = GetMDIClient();
if (mdiClient == null) return;
int ct = this.MdiParent.MdiChildren.Length;
Control form = null; // next or previous MDIChild form
int pos = mdiClient.Controls.IndexOf(this);
if (pos < 0) throw new InvalidOperationException("MDIChild form not found");
if (pos == 0)
{
if (ct == 1) form = this;
else form = mdiClient.Controls[1]; // get next and activate previous
}
else
form = mdiClient.Controls[pos - 1]; // get previous and activate next
// flag indicating whether to activate previous or next MDIChild
IntPtr direction = new IntPtr(pos == 0 ? 1 : 0);
// bada bing, bada boom
SendMessage(mdiClient.Handle, WM_MDINEXT, form.Handle, direction);
}
public MdiClient GetMDIClient()
{
if (this.MdiParent == null) return null;
foreach (Control c in this.MdiParent.Controls)
{
if (c is MdiClient)
return (MdiClient)c;
}
return null;
}
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
public const int WM_MDINEXT = 0x224;
|
|
|
|
 |
|
 |
As tonyt said the definitive solution is subclass the MDIClient. This way the MDI menu works perfectly. The main problem subclassing MDIClient is that this class is sealed. You can use MDIClientController by Jacob Slusser http://www.codeproject.com/KB/dialog/mdiclientcontroller.aspx[^]. In the WndProc overrided method of MDIClientController add the tonyt code:
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MDIACTIVATE)
{
if (this.parentForm.ActiveMdiChild.Handle != m.WParam)
{
int pos = -1;
for (int i = 0; i < this.mdiClient.Controls.Count; i++)
{
if (mdiClient.Controls[i].Handle == m.WParam)
{
pos = i;
break;
}
}
if (pos < 0)
throw new InvalidOperationException("MDIChild form not found");
Control form = null; if (pos == 0)
form = mdiClient.Controls[1]; else
form = mdiClient.Controls[pos - 1];
IntPtr direction = new IntPtr(pos == 0 ? 1 : 0);
SendMessage(mdiClient.Handle, WM_MDINEXT, form.Handle, direction);
}
return;
}
base.WndProc(ref m);
}
and define the constant:
const int WM_MDIACTIVATE = 0x0222;
Attach the MDIClientControler to your parent MDI form by dragging the component in the designer or by code in the MDI form constructor:
private MDIClientController mdiClientController;
public MDIForm()
{
InitializeComponent();
this.mdiClientController = new MdiClientController(this);
}
|
|
|
|
 |
|
 |
Works a treat -- Thank you.
Bertram
|
|
|
|
 |
|
 |
Hi, I believe I have this working now in VB. For some reason, specifying a 1 or 0 for the direction (last parameter of SendMessage) didn't seem to work - it always did the same thing, regardless of direction. So I made a small adjustment, and it appears to work great now. Here's the pertinent VB code in case anyone is interested. BTW, if anyone sees a problem with this code, I'd appreciate it if you reply to this! Thanks for putting me on the right track! David Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, _ ByRef lParam As Integer) As Integer Const WM_MDINEXT As Integer = &H224 Then, in the procedure to actually do the work (note that moMDIClient I get the same way as this article, but maintain it for the life of the mdiparent form instead of getting it every time): Dim iPos As Integer = moMDIClient.Controls.IndexOf(oFrm) Dim frmTemp As Form Dim iLastIdx As Integer = moMDIClient.Controls.Count - 1 If iPos = iLastIdx Then frmTemp = moMDIClient.Controls(0) Else frmTemp = moMDIClient.Controls(iPos + 1) End If SendMessage(moMDIClient.Handle, WM_MDINEXT, frmTemp.Handle, 0)
|
|
|
|
 |
|
 |
I couldn't get it to work initially, but with playing around with it i came up with the following - in a module put:
Private Const WM_MDINEXT As Integer = &H224
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByRef lParam As Integer) As Integer
Public Sub ActivateChild(ByVal chldForm As Form)
On Error Resume Next
Dim moMDIClient As MdiClient, iPos As Integer, frmTemp As Form, iLastIdX As Integer
moMDIClient = GetMDIClient(chldForm)
iPos = moMDIClient.Controls.IndexOf(chldForm)
iLastIdX = moMDIClient.Controls.Count - 1
If iPos = iLastIdX Then
frmTemp = moMDIClient.Controls(0)
Else
frmTemp = moMDIClient.Controls(iPos + 1)
End If
SendMessage(moMDIClient.Handle, WM_MDINEXT, frmTemp.Handle, 0)
End Sub
Private Function GetMDIClient(ByVal chldForm As Form) As MdiClient
On Error Resume Next
Dim c As Control
For Each c In chldForm.MdiParent.Controls
If c.ToString = "System.Windows.Forms.MdiClient" Then
GetMDIClient = c
Exit For
End If
Next
End Function
|
|
|
|
 |
|
 |
I had a look at this yesterday while trying to get rid of that annoying flicker and learned some things.
When you declare SendMessage, you need to use ByVal for all your parameters. In dmbf1b5 and Jason Newland's code, the lParam argument uses ByRef, causing it to be ignored. I tried running it with ByRef, and it always activated the child with one better z-order than what I wanted, i.e. the previous child. After changing ByRef to ByVal, you can use lParam as described in the MSDN article at http://msdn.microsoft.com/en-us/library/ms644918(VS.85).aspx[^].
In my case, I have two sets of "tabs" called collections and lists. Each list has its own MDI child, but you can only see one collection of lists at a time. Sometimes the z-orders of the lists in each collection get mixed up.
For example, suppose you have tabs A, b, C, d with indices 0, 1, 2, 3 in the MDIClient.Controls collection (the index corresponds to the z-order, with 0 being the active MDI child and 3 the last child). Suppose further that b and d are hidden via the form.Visible property. If b (or d) is sent to WM_MDINEXT with lParam 0 (or 1), b (or d) will be activated in the process.
So if you want to be sure no hidden MDI children are activated, use WM_MDINEXT with a reference to an MDI child that is visible and enabled. From what I can tell, the MDI system automatically ignores hidden MDI children when WM_MDINEXT is used, provided a hidden MDI child is not passed as wParam.
Here is some code I came up with. I borrowed heavily from Jason Newland but wound up changing things a bit to fit my problem. Here, MyMDIChild is the base form I use as MDI children. I use WM_MDINEXT with the "previous" visible tab so that no hidden forms will appear. The nice thing is that you can activate an MDI child simply by calling form.ActivateMe().
Class MyMDIChild
Protected Shared moMDIClient As MdiClient
Public Sub ActivateMe()
Dim MyIndex, j, count As Integer
Dim frmTemp As Form
If (moMDIClient Is Nothing) Then moMDIClient = GetMDIClient(Me)
End If
MyIndex = moMDIClient.Controls.IndexOf(Me) count = moMDIClient.Controls.Count
If (MyIndex <> 0 And count > 1) Then j = (MyIndex - 1) Mod count frmTemp = Nothing
While (j <> MyIndex And frmTemp Is Nothing) frmTemp = moMDIClient.Controls(j)
If (frmTemp.Visible = False) Then
frmTemp = Nothing
End If
j = (j - 1 + count) Mod count
End While
If Not (frmTemp Is Nothing) Then
SendMessage(moMDIClient.Handle, WM_MDINEXT, frmTemp.Handle, 0)
End If
End If
End Sub
Private Function GetMDIClient(ByVal chldForm As Form) As MdiClient
On Error Resume Next
Dim c As Control
For Each c In chldForm.MdiParent.Controls
If c.ToString = "System.Windows.Forms.MdiClient" Then
GetMDIClient = c
Exit For
End If
Next
End Function
Private Const WM_MDINEXT As Integer = &H224
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
End Class
modified on Wednesday, July 29, 2009 9:33 AM
|
|
|
|
 |
|
 |
Hi,
This issue has been a complaint of mine for quite awhile - I'm excited to try out your idea. I'm trying to convert your code to VB.NET, but am having problems. I can find the previous or next form, but how do I call SendMessage? Even when I import all the namespaces/elements you are using, SendMessage is not a valid option. I get: "Name 'SendMessage' is not declared."
Thanks
David
|
|
|
|
 |
|
 |
here is the declaration I use in my VB.Net projects
Private Declare Auto Function SendMessage Lib "user32.dll" (ByVal hwnd As IntPtr, ByVal wMsg As Int32, ByVal wParam As Boolean, ByVal lParam As Int32) As Int32
|
|
|
|
 |
|
 |
I haven't seen any fireworks, but just curious, do you see the fireworks when you use formToActivate.Activate()?
In any case, thanks for informing us of this approach...
Kelly Leahy
Milliman, Inc.
|
|
|
|
 |
|
 |
Sorry, the original article cited the ActiveMdiChild property as the means of activating the MDI child, which was incorrect (corrected).
It is calling the MDI child form's Activate() method (or Select() method) that causes the 'fireworks'.
It only happens when the currently active MDI child form is maximized.
You can see it by building a simple MDI app with child forms and a menustrip which has one of its menus assigned to the MenuStrip's MdiWindowListItem property. Run the app and create a few MDI child forms and maximize the active form. Then, from the menu that displays the child forms, select another form and you will see it painted in the restored state before it is activated and maximized.
|
|
|
|
 |
|