表单在客户端桌面区域内移动 [英] Form move inside client desktop area

查看:23
本文介绍了表单在客户端桌面区域内移动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的目标是防止用户通过鼠标移动表单时部分或全部隐藏我的表单.例如,如果我的桌面分辨率为 1024x768,则表单位置属性的坐标的x/y"最小值/最大值如下:0, 1024 - form.width, 0, 768 - form.height.

My goal is to prevent partial or total hidden of my form when user move it through mouse. For instance, if my desktop resolution is 1024x768 the following will be "x/y" minimum/maximum value of coordinates for form location properties: 0, 1024 - form.width, 0, 768 - form.height.

我使用一些原生 API 来完成所有这些并且一切正常,但我经常注意到我的表单刷新不良(闪烁)(我在 form_move 事件上管理所有内容).
似乎 SuspendLayout 方法不能正常工作,否则表单移动事件不会触发每个像素更改,但可能不止一个(请参阅下面的代码以了解我的意思).

I use some native APi to accomplish all this and all works fine, but often I notice a bad refresh (flickering) of my form (I'm managing all on form_move event).
It seems like SuspendLayout method doesn't works fine, or else the form move event doesn't fire every pixel changed, but maybe more than one (see the below code to realize what I mean).

我的代码如下所示:

private void Form1_Move(object sender, EventArgs e)
{
    this.SuspendLayout();

    // Desktop Resolution (API utility)
    int x = Desktop.GetXResolution();
    int y = Desktop.GetYResolution();

    // Taskbar Info (API utility)
    Taskbar tb = new Taskbar();
    int minX = 0;
    int maxX = x - this.Width;
    int minY = 0;
    int maxY = y - this.Height;
    if (!tb.AutoHide)
    {
        if (tb.Position != TaskbarPosition.Unknown && !tb.Size.IsEmpty)
        {
            if (tb.Position == TaskbarPosition.Top) minY += tb.Size.Height;
            switch (tb.Position)
            {
                case TaskbarPosition.Top: minY = tb.Size.Height; break;
                case TaskbarPosition.Bottom: maxY -= tb.Size.Height; break;
                case TaskbarPosition.Left: minX = tb.Size.Width; break;
                case TaskbarPosition.Right: maxX -= tb.Size.Width; break;
            }
        }
    }

    // Restore X Position
    if (this.Location.X < minX) this.Location = new Point(minX, this.Location.Y);
    if (this.Location.X > maxX) this.Location = new Point(maxX, this.Location.Y);


    // Restore Y Poistion
    if (this.Location.Y < minY) this.Location = new Point(this.Location.X, minY);
    if (this.Location.Y > maxY) this.Location = new Point(this.Location.X, maxY);

    this.ResumeLayout(false);
}

正如我已经说过的,通常当我恢复 Location 属性时,我的 winForm 会闪烁.

As I already said, often when I restore Location property my winForm take a flickering.

任何建议将不胜感激.

推荐答案

Move 在移动完成后发生.表格已移动,然后您将其移回.无论如何它都会闪烁.

Move occurs after a move has completed. The form has moved and then you move it back. It's going to flicker anyway.

相反,首先要防止它移动:

Instead, prevent it from moving in the first place:

private const int WM_MOVING = 0x216;

private void WriteTheRect(IntPtr dest, Rectangle rect) {
    System.Runtime.InteropServices.Marshal.WriteInt32(dest, 0, rect.Left);
    System.Runtime.InteropServices.Marshal.WriteInt32(dest, 4, rect.Top);
    System.Runtime.InteropServices.Marshal.WriteInt32(dest, 8, rect.Right);
    System.Runtime.InteropServices.Marshal.WriteInt32(dest, 12, rect.Bottom);
}

protected override void WndProc(ref Message m) {
    if (m.Msg == WM_MOVING)
    {
        // RECT structure pointed to by lParam: left, top, right, bottom

        Rectangle r = Rectangle.FromLTRB(System.Runtime.InteropServices.Marshal.ReadInt32(m.LParam, 0),
                                         System.Runtime.InteropServices.Marshal.ReadInt32(m.LParam, 4),
                                         System.Runtime.InteropServices.Marshal.ReadInt32(m.LParam, 8),
                                         System.Runtime.InteropServices.Marshal.ReadInt32(m.LParam, 12)
                                         );

        Rectangle allowed = Rectangle.FromLTRB(0, 0, 1600, 900);

        if (r.Left <= allowed.Left || r.Top <= allowed.Top || r.Right >= allowed.Right || r.Bottom >= allowed.Bottom)
        {
            int offset_x = r.Left < allowed.Left ? (allowed.Left - r.Left) : (r.Right > allowed.Right ? (allowed.Right - r.Right) : (0));
            int offset_y = r.Top < allowed.Top ? (allowed.Top - r.Top) : (r.Bottom > allowed.Bottom ? (allowed.Bottom - r.Bottom) : (0));

            r.Offset(offset_x, offset_y);

            WriteTheRect(m.LParam, r);
        }
    }
    base.WndProc(ref m);
}

显然,将常量替换为您实际所需的边界.

Obviously, replace the constants with your actual desired bounds.

这篇关于表单在客户端桌面区域内移动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆