Determine scrolling in MDI Form MdiClient

G

Guest

I have been racking my brains trying to figure out how to know when you have
scrolled the MdiClient window on an MDI form. (Not an MdiChild Window) I
have tried monitoring the messages for both the MdiClient and the MdiForm
itself..

I put a IFilterMessage as well well as overriding the WndProc on the the
mainform to see all messages going by and Identifiying the MdiClient
Messages..

When you click on the ScrollBar you get..

MdiClient->WM_NCLBUTTONDOWN

There is not a corresponding up button message or a WM_HSCROLL message.

Form that point you get no further messages in the MdiForms WndProc or the
IMessageFilters.PreveiwMessage until you release the mouse and then you get
either:

1) If you are clicked on the scrollbar and move the cursor,either to a
status bar or other neighboring control below it before you release the
mouse the next message appears to be a WM_MOUSEMOVE on the status bar or
other control

2) If you release the mouse in the scroll bar is issue the MdiClient a
WM_Paint but it doesn't always do a complete repaint which results in any
background painting of the MdiClient being messed up when scrolling has
occurred...

3) If you release back into the MdiClient widow you get a WM_SetCursor
message and again it doesn't do a complete repaint which results in any
background painting of the MdiClient being messed up.

Any ideas on you tell when you have scolled the MdiClient on a MdiParent
form?? What I want to be able to do is when the MdiClient has been Scrolled
to completely invalidate it and repaint it so the background is intact...

I found several discussions on various forums including one where they are
saying to Invalidate it completely on the first MdiChild paint attempt,
which cause the background to paint ok but it flickers badly.

I used the code below found on the web to cause painting of the MdiChild
background..

Thanks..

// This code stops the background repainting of the MdiClient from flickering
// as it sets the Double Buffering in MdiClient when doesnt set properly
// .From an Article at
// http://www.codeproject.com/csharp/MdiClientRevisited.asp#xx948448xx

[DefaultValue(false)]
public new bool IsMdiContainer
{
get{ return base.IsMdiContainer; }
set
{
base.IsMdiContainer = value;

if( ! value) return;

for(int i = 0; i < this.Controls.Count; i++)
{
MdiClient mdiClient = this.Controls as MdiClient;
if(mdiClient != null)
{
ControlStyles styles = ControlStyles.DoubleBuffer;

try
{
// Prevent flickering, only if our assembly
// has reflection permission.
Type mdiType = typeof(MdiClient);

System.Reflection.BindingFlags flags =
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance;

System.Reflection.MethodInfo method
= mdiType.GetMethod("SetStyle",flags);
object[] param = {styles, true};
method.Invoke(mdiClient,param);
}
catch ( System.Security.SecurityException)
{
/*Don't do anything!!! This code is running under
partially trusted context*/
}

mdiClient.Paint +=new PaintEventHandler(this.MdiClient_Paint);
mdiClient.Resize +=new EventHandler(MdiClient_Resize);
break;
}
}
}
}
 
G

Guest

If anyone else is trying to to this, I came accross this excellent article..

http://www.codeproject.com/csharp/MdiClientRevisited.asp#xx948448xx

which used Native methods..

The code below seems to let me track what the heck is happening in the
MdiClient window...There is some use of internal Win32 routines for accessing
and handling Windows Messages so you would have to change these but it will
give you an idea as I have spent far too much time on this...

//To use this
MdiClientNativeWindow mdiClientNative = new MdiClientNativeWindow(this);
mdiClientNative.Scrolled
+=new EventHandler(MdiClient_Scrolled);
mdiClientNative.MdiWindowPositionChanged
+=new EventHandler(MdiClientNative_MdiWindowPositionChanged);


//***************** MdiClientNativeWindow *****************
using System;
using System.Windows.Forms;
using System.Diagnostics;
using Bon.Win32;

namespace Bon.Forms
{
/// <summary>
/// Class: Provide access to an MdiClient in a MdiForm MdiClientNativeWindow.
/// </summary>
public class MdiClientNativeWindow: System.Windows.Forms.NativeWindow
{
/// <summary>
/// Field: is MdiClient Vertical Scroll bar active
/// </summary>
private bool verticalScrollActive;
/// <summary>
/// Property: Gets if MdiClient Window is Scrolling Vertically
/// </summary>
public bool VerticalScrollActive
{
[DebuggerStepThrough()]
get { return verticalScrollActive;}
}
/// <summary>
/// Field: is MdiClient Horizontal Scroll bar active
/// </summary>
private bool horizontalScrollActive;
/// <summary>
/// Property: Gets if MdiClient Window is Scrolling Horizontally
/// </summary>
public bool HorizontalScrollActive
{
[DebuggerStepThrough()]
get { return horizontalScrollActive;}
}
private Form parentForm;
/// <summary>
/// Property: Gets the Parent Form
/// </summary>
public Form ParentForm
{
[DebuggerStepThrough()]
get { return parentForm;}
}
private MdiClient mdiClient;
/// <summary>
/// Property: Gets the MdiClient
/// </summary>
public MdiClient MdiClient
{
[DebuggerStepThrough()]
get { return mdiClient;}
}
/// <summary>
/// Event: MdiClient Window Position Changed (recieved WM_WINDOWPOSCHANGED)
/// </summary>
public event System.EventHandler MdiWindowPositionChanged;
/// <summary>
/// Event: MdiClient Set Cursor message (recieved WM_SETCURSOR)
/// </summary>
public event System.EventHandler MdiSetCursor;
/// <summary>
/// Event: MdiClient Window Activated (recieved WM_MDIGETACTIVE)
/// </summary>
public event System.EventHandler MdiGetActive;
/// <summary>
/// Event: MdiClient Window is About to Start Scrolling
/// </summary>
public event System.EventHandler ScrollingStarted;
/// <summary>
/// Event: MdiClient Window is Scrolling
/// </summary>
public event System.Windows.Forms.ScrollEventHandler Scrolling;
/// <summary>
/// Event: MdiClient Window has scrolled
/// </summary>
public event System.EventHandler Scrolled;
/// <summary>
/// Constructor: Default
/// </summary>
public MdiClientNativeWindow():this(null)
{
}
/// <summary>
/// Constructor: with Parent Form
/// </summary>
/// <param name="parentForm"></param>
public MdiClientNativeWindow(Form parentForm)
{
this.verticalScrollActive = false;
this.horizontalScrollActive = false;
this.mdiClient = null;
this.parentForm = parentForm;
InitializeMdiClient();
}

protected override void WndProc(ref Message m)
{
try
{
WM msg = Win32API.Get_WM (m);
Debug.WriteLine("MdiClientNativeWindow->"+msg.ToString() );
switch (msg)
{

//This will check if scrolling Started
case WM.WM_NCLBUTTONDOWN:
MouseLeftClickNonClient(ref m);
break;
//Scolling progress and Completion will be triggered
case WM.WM_VSCROLL:
case WM.WM_HSCROLL:
TriggerScrolling(ref m);
break;
//Consume this event to stop background Painting
case WM.WM_ERASEBKGND:
return;
case WM.WM_SYNCPAINT:
case WM.WM_PAINT:
case WM.WM_MOVE:
break;
//Need to Trap this event So Background can be refreshed
case WM.WM_MDIGETACTIVE:
TriggerMdiGetActive();
break;
case WM.WM_SETCURSOR:
TriggerMdiSetCursor();
break;
case WM.WM_WINDOWPOSCHANGED:
TriggerMdiWindowPositionChanged();
break;
}
}
catch
{
}
base.WndProc (ref m);
}
private void MouseLeftClickNonClient(ref Message m)
{
//Is Scroll Active
if ( m.WParam == (IntPtr)HT.HTVSCROLL )
{
VerticalScrollActivated();
}
else if ( m.WParam == (IntPtr)HT.HTVSCROLL )
{
HorizontalScrollActivated();
}
}
private void HorizontalScrollActivated()
{
horizontalScrollActive = true;
TriggerScrollingStarted();

}
private void VerticalScrollActivated()
{
verticalScrollActive = true;
TriggerScrollingStarted();

}
private void TriggerMdiWindowPositionChanged()
{
if (MdiWindowPositionChanged != null)
{
MdiWindowPositionChanged(this,new EventArgs() );
}
}
private void TriggerMdiSetCursor()
{
if (MdiSetCursor != null)
{
MdiSetCursor(this,new EventArgs() );
}

}
private void TriggerMdiGetActive()
{
if (MdiGetActive != null)
{
MdiGetActive(this,new EventArgs() );
}

}
private void TriggerScrollingStarted()
{
if (ScrollingStarted != null)
{
ScrollingStarted(this,new EventArgs() );
}

}
private void TriggerScrolled()
{
if (Scrolled != null)
{
Scrolled(this,new EventArgs() );
}

} /// <summary>
/// Method: Triggers a Scroll Event and Monitors the Scroll requests
/// looking for a SB_ThumbPostions so that
/// a Scrolled message can be sent
/// </summary>
/// <param name="m"></param>
private void TriggerScrolling(ref Message m)
{
//Monitor
//
//wParam The high-order word specifies the current position
// of the scroll box if the low-order word
// is SB_THUMBPOSITION or SB_THUMBTRACK;
// otherwise, this word is not used.
//
//The low-order word specifies a scroll bar value that
//indicates the user's scrolling request.
//
//This parameter can be one of the following values.
//
//SB_BOTTOM - Scrolls to the lower right.
//SB_ENDSCROLL - Ends scroll.
//SB_LINEDOWN - Scrolls one line down.
//SB_LINEUP - Scrolls one line up.
//SB_PAGEDOWN - Scrolls one page down.
//SB_PAGEUP - Scrolls one page up.
//SB_THUMBPOSITION - The user has dragged the scroll box (thumb)
// and released the mouse button.
// The high-order word indicates
// the position of the scroll box at the end of the drag operation.
//SB_THUMBTRACK - The user is dragging the scroll box.
// This message is sent repeatedly until
// the user releases the mouse button.
// The high-order word indicates the position
// that the scroll box has been dragged to.
// SB_TOP
// Scrolls to the upper left.
//lParam
//------
//If the message is sent by a scroll bar,
//this parameter is the handle to the scroll bar control.
//If the message is not sent by a scroll bar,
//this parameter is NULL.
//
ScrollEventArgs scrollArgs = ScrollArgsFromMessage(ref m);
//Scroll Event
if (this.Scrolling != null)
{
Scrolling(this.mdiClient,scrollArgs);
}
//This means the Scroll Bar has moved so Fired Completed Event
if (scrollArgs.Type == ScrollEventType.ThumbPosition)
{
//Indicated Scolled before resetting flags..
if (Scrolled != null)
{
Scrolled(mdiClient,new EventArgs() );
}
verticalScrollActive = false;
horizontalScrollActive = false;
}
}
/// <summary>
/// Method: Create a Scroll Argument from a Windows Message
/// </summary>
/// <param name="m"></param>
/// <returns></returns>
private ScrollEventArgs ScrollArgsFromMessage(ref Message m)
{
//Scroll Button Enmeration...
SB sb = (SB)Win32API.WParam_LoWord(m);

ScrollEventType scrollType = (ScrollEventType)sb;
//Position
int scrollPosition = Win32API.WParam_HiWord(m);

return new ScrollEventArgs(scrollType,scrollPosition);
}
private void InitializeMdiClient()
{
// If the mdiClient has previously been set, unwire events connected
// to the old MDI.
if(mdiClient != null)
mdiClient.HandleDestroyed -= new EventHandler(MdiClientHandleDestroyed);

if(parentForm == null)
{
return;
}

// Get the MdiClient from the parent form.
for(int i = 0; i < parentForm.Controls.Count; i++)
{
// If the form is an MDI container, it will contain an MdiClient control
// just as it would any other control.
mdiClient = parentForm.Controls as MdiClient;
if(mdiClient != null)
{
// Assign the MdiClient Handle to the NativeWindow.
ReleaseHandle();
AssignHandle(mdiClient.Handle);

// Raise the HandleAssigned event.
//OnHandleAssigned(EventArgs.Empty);

// Monitor the MdiClient for when its handle is destroyed.
mdiClient.HandleDestroyed += new EventHandler(MdiClientHandleDestroyed);
}
}
}
/// <summary>
/// Method: MdiClient handle has been released, so drop the reference and
/// release the handle.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MdiClientHandleDestroyed(object sender, EventArgs e)
{
if(mdiClient != null)
{
mdiClient.HandleDestroyed -= new EventHandler(MdiClientHandleDestroyed);
mdiClient = null;
}
ReleaseHandle();
}
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top