我有一个MDI From和一个子表格
儿童形式的高度超过MDI儿童..每当儿童形式在MDI形式打开时滚动条显示正确,但是当我尝试使用鼠标滚轮滚动时,它什么都没有?
如何使用鼠标滚轮向下和向上滚动?
答案 0 :(得分:3)
鼠标滚轮通知消息(WM_MOUSEWHEEL)是一个不寻常的消息,它“起泡”。只要没有窗口处理它,消息就会被发送到窗口的父级。反复进行,直到窗口处理它或者没有更多的父窗口。
MDI客户端窗口的Windows实现中存在一个不幸的缺陷,即您在MDI父窗口中看到的深灰色窗口。它是显示滚动条的那个,但它不够智能来处理鼠标滚轮通知。不知道为什么,但是MDI已经老了,并且在老鼠开车前很久就存在了。
Winforms很好,它可以修复这样的缺陷。它不能通过替换MDI客户端窗口来完成,它很难烘焙。需要的是对窗口进行子类化并捕获WM_MOUSEWHEEL消息。并通过使用WM_VSCROLL消息滚动窗口来添加此缺少的功能。这确实需要一些pinvoke魔法,Winforms也不容易获得对MDI客户端窗口的引用。在项目中添加一个新类并粘贴此代码:
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class MdiScroller : NativeWindow {
public static void Install(Form mdiParent) {
if (!mdiParent.IsMdiContainer) throw new ArgumentException("Not an MDI application");
if (!mdiParent.IsHandleCreated) throw new InvalidOperationException("Create me in the Load event please");
foreach (Control ctl in mdiParent.Controls) {
if (ctl is MdiClient) {
var hooker = new MdiScroller();
hooker.AssignHandle(ctl.Handle);
break;
}
}
}
protected override void WndProc(ref Message m) {
if (m.Msg == WM_DESTROY) this.ReleaseHandle();
if (m.Msg == WM_MOUSEWHEEL) {
short delta = (short)((int)(long)m.WParam >> 16);
SendMessage(this.Handle, WM_VSCROLL, (IntPtr)(delta < 0 ? SB_LINEUP : SB_LINEDOWN), IntPtr.Zero);
m.Result = IntPtr.Zero;
}
base.WndProc(ref m);
}
// PInvoke:
private const int WM_DESTROY = 0x002;
private const int WM_MOUSEWHEEL = 0x20a;
private const int WM_VSCROLL = 0x115;
private const int SB_LINEDOWN = 0;
private const int SB_LINEUP = 1;
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}
在MDI Parent窗体中,实现Load事件处理程序或覆盖OnLoad()以激活此代码。像这样:
protected override void OnLoad(EventArgs e) {
MdiScroller.Install(this);
base.OnLoad(e);
}
或者:
private void Form1_Load(object sender, EventArgs e) {
MdiScroller.Install(this);
}
通过关注滚动量(delta)可以进一步改善代码。但是这个简单的实现在我的机器上运行得很好,ymmv。
答案 1 :(得分:2)
Hans Passant的答案很棒,但遗漏了一点:如果父表单没有垂直滚动条,滚动时子表单就会消失......
修复很简单 - 您只需检查是否存在垂直滚动条;我复制了Passant的MdiScroller
课程并添加了一些内容:
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class MdiScroller : NativeWindow
{
public static void Install(Form mdiParent)
{
if (!mdiParent.IsMdiContainer) throw new ArgumentException("Not an MDI application");
if (!mdiParent.IsHandleCreated) throw new InvalidOperationException("Create me in the Load event please");
foreach (Control ctl in mdiParent.Controls)
{
if (ctl is MdiClient)
{
var hooker = new MdiScroller();
hooker.AssignHandle(ctl.Handle);
break;
}
}
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_DESTROY) this.ReleaseHandle();
if (m.Msg == WM_MOUSEWHEEL)
{
short delta = (short)((int)(long)m.WParam >> 16);
var scrollbars = GetVisibleScrollbars();
// ** ADDED **
if (scrollbars == ScrollBars.Horizontal || scrollbars == ScrollBars.None) { return; }
SendMessage(this.Handle, WM_VSCROLL, (IntPtr)(delta < 0 ? SB_LINEUP : SB_LINEDOWN), IntPtr.Zero);
m.Result = IntPtr.Zero;
}
base.WndProc(ref m);
}
// ** ADDED **
private ScrollBars GetVisibleScrollbars()
{
int wndStyle = GetWindowLong(this.Handle, GWL_STYLE);
bool hsVisible = (wndStyle & WS_HSCROLL) != 0;
bool vsVisible = (wndStyle & WS_VSCROLL) != 0;
if (hsVisible)
{
return vsVisible ? ScrollBars.Both : ScrollBars.Horizontal;
}
else
{
return vsVisible ? ScrollBars.Vertical : ScrollBars.None;
}
}
// PInvoke:
private const int WM_DESTROY = 0x002;
private const int WM_MOUSEWHEEL = 0x20a;
private const int WM_VSCROLL = 0x115;
private const int SB_LINEDOWN = 0;
private const int SB_LINEUP = 1;
// ** ADDED **
public const int GWL_STYLE = -16;
public const int WS_VSCROLL = 0x00200000;
public const int WS_HSCROLL = 0x00100000;
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
// ** ADDED **
[DllImport("user32.dll", SetLastError = true)]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
}
答案 2 :(得分:0)
滚动鼠标滚轮在Windows中的工作方式是具有输入焦点的窗口是滚动的窗口。
这意味着如果您的MDI 子窗口具有焦点,则它将是接收滚动消息的窗口。如果它没有滚动条,那么它就会忽略这些消息,因为它没有任何可滚动的内容。
比较一下,如果您的MDI 父窗口具有焦点。然后父级将滚动,显示MDI子窗口的底部,就像移动了附加到父窗口的滚动条一样。
您可以通过单击MDI父窗口(如背景中未被子窗口覆盖的空白区域)来演示此内容。当它收到焦点时,你的滚轮应该使它像你期望的那样滚动。