2

I need to update the position of a child window inside my System.Windows.Forms.MDIClient container when the user scrolls it by dragging the MDIClient's scrollbar thumb.

However I can't find an event that triggers when this happens.

Am I simply missing it, or do I need a workaround, possibly by talking direct to the scrollbar?

I've already tried handling MDIClient.Layout events, but they aren't being triggered by scrolling.

EDIT: I actually only need to know when the scrolling has stopped, in order to change my child window's position.

EDIT2: As a temporary workaround, I'm resetting the child window position on a timer every second, obviously not ideal, but better than nothing. Looks terrible though!

Surfbutler
  • 1,529
  • 2
  • 17
  • 38
  • What exactly do you want about detecting scrolling here? I mean you can scroll by dragging the thumb and you can also scroll by wheeling the mouse. – King King Dec 02 '13 at 11:50
  • Any scrolling really, regardless of how it's done - the issue is that my child control needs to always be in the lower-right hand corner of the MDIClient container, and scrolling the container moves the control away from that corner without me knowing about it. If I change the size of the MDIClient's parent window (the top-level window) that's fine, as I detect that (with the parent's Layout event), but I get nothing from the MDIClient Layout event. – Surfbutler Dec 02 '13 at 11:58

1 Answers1

2

That's possible although a bit awkward. Winforms doesn't make it very easy to find the MdiClient window back and the class itself doesn't expose the Scroll event. That can be worked around, as always in Winforms, you have to sub-class the native MDI client window of your parent window so you can capture the WM_VSCROLL message. This code worked well, paste it into your parent form class:

void MdiClient_Scroll(object sender, ScrollEventArgs e) {
    if (e.Type == ScrollEventType.EndScroll) {
        // Do your stuff
        //...
    }
}

private MdiClientWrapper wrapper;

protected override void OnHandleCreated(EventArgs e) {
    // Find the MdiClient and sub-class it so we can get the Scroll event
    base.OnHandleCreated(e);
    if (wrapper != null) wrapper.Scroll -= MdiClient_Scroll;
    var client = this.Controls.OfType<MdiClient>().First();
    wrapper = new MdiClientWrapper();
    wrapper.AssignHandle(client.Handle);
    wrapper.Scroll += MdiClient_Scroll;
}

private class MdiClientWrapper : NativeWindow {
    public event ScrollEventHandler Scroll;
    private int oldPos;
    protected override void WndProc(ref Message m) {
        if (m.Msg == 0x115) {   // Trap WM_VSCROLL
            var type = (ScrollEventType)(m.WParam.ToInt32() & 0xffff);
            var pos = m.WParam.ToInt32() >> 16;
            Scroll(this, new ScrollEventArgs(type, oldPos, pos));
            oldPos = pos;
        }
        base.WndProc(ref m);
    }
}
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • That worked perfectly, right out of the box as they say (I added a test for horizontal scrolling too). Thanks again. – Surfbutler Dec 02 '13 at 14:13