none
c# 如何实现悬浮窗口的问题 RRS feed

  • 问题

  • 我看了 好多关于悬浮窗口的文章,好像跟我的要求不太符合。。

    我想要的一个效果是点击按钮,然后那个窗口就会在按钮下方固定位置出现。。

    比如有个主窗体 按钮在(0,100)的位置,然后点击按钮,悬浮窗口就在按钮的下方,给人感觉像是按了按钮,界面换了的感觉。。我拖动这个悬浮窗口的时候,主窗体也会跟着移动, 主窗体变大,悬浮的也跟着改变,就像是嵌套在主窗体的那种效果。。。

    请高手指点一二
    2012年9月24日 2:22

答案

  • 第一步:建立一个窗体,设置其属性:

                this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
                this.MaximizeBox = false;
                this.MinimizeBox = false;
                this.Opacity = 0.5;

    第二步:设置窗体的OnLoad事件:

            private void Form2_Load(object sender, EventArgs e)
            {
                this.Top = 20;
                this.Left = Screen.PrimaryScreen.Bounds.Width - 80;
                this.Width = 60;
                this.Height = 60;
            }

    第三步:修改窗体的Paint事件,美化界面,这里做一个渐变背景,需要引入System.Drawing.Drawing2D;

            private void Form2_Paint(object sender, PaintEventArgs e)
            {
                Graphics g = e.Graphics;
                Color FColor = Color.Red;
                Color TColor = Color.Yellow;
                Brush b = new LinearGradientBrush(this.ClientRectangle, FColor, TColor, LinearGradientMode.ForwardDiagonal);
                g.FillRectangle(b, this.ClientRectangle);
            }

    第四步:需要实现鼠标拖动悬浮窗体,

            const int WM_NCHITTEST = 0x0084;
            const int HTCLIENT = 0x0001;
            const int HTCAPTION = 0x0002;
            protected override void WndProc(ref System.Windows.Forms.Message m)
           {
                switch(m.Msg)
               {
                        case WM_NCHITTEST:
                                 base.WndProc(ref m);
                                 if (m.Result==(IntPtr)HTCLIENT)
                                          m.Result=(IntPtr)HTCAPTION;
                                 break;
                        default:
                                 base.WndProc(ref m);
                                 break;
               }
          }

    关于这一步,愚翁不是这样处理的,他是处理鼠标事件来实现的,效果差不多,也贴在这里学习一下吧:
    先定义几个类成员变量:
            private Point ptMouseCurrrnetPos, ptMouseNewPos,ptFormPos, ptFormNewPos;
            private bool blnMouseDown = false;

    再添加三个鼠标事件:
            private void Form2_MouseDown(object sender, MouseEventArgs e)
            {
                if (e.Button == MouseButtons.Left)
                {
                    blnMouseDown = true;
                    // Save window position and mouse position
                    ptMouseCurrrnetPos = Control.MousePosition;
                    ptFormPos = Location;
                }

            }

            private void Form2_MouseMove(object sender, MouseEventArgs e)
            {
                if (blnMouseDown)
                {
                    //Get the current position of the mouse in the screen
                    ptMouseNewPos = Control.MousePosition;
                    //Set window position
                    ptFormNewPos.X = ptMouseNewPos.X - ptMouseCurrrnetPos.X + ptFormPos.X;
                    ptFormNewPos.Y = ptMouseNewPos.Y - ptMouseCurrrnetPos.Y + ptFormPos.Y;
                    //Save window position
                    Location = ptFormNewPos;
                    ptFormPos = ptFormNewPos;
                    //Save mouse position
                    ptMouseCurrrnetPos = ptMouseNewPos;

                }
            }

            private void Form2_MouseUp(object sender, MouseEventArgs e)
            {
                if (e.Button == MouseButtons.Left)
                    //Return back signal
                    blnMouseDown = false;
            }


    第三种移动无标标题窗体的办法:
    通过API来处理,需要引入System.Runtime.InteropServices;

      [DllImport("user32.dll")]
      public static extern bool ReleaseCapture();
      [DllImport("user32.dll")]
      public static extern bool SendMessage(IntPtr hwnd,int wMsg,int wParam,int lParam);
                                         
      public const int WM_SYSCOMMAND=0x0112;
      public const int SC_MOVE=0xF010;
      public const int HTCAPTION=0x0002;
     
      private void Form2_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
      {
       ReleaseCapture();
       SendMessage(this.Handle,WM_SYSCOMMAND,SC_MOVE+HTCAPTION, 0);
      }

    呵,这个看起来好像比较“正统”。
    2012年9月25日 3:10
  • 感觉使用委托就可以了吧。主画面定义更新子画面的委托,影射到子画面的某个方法,子画面定义一个更新主画面的委托,影射到主画面的一个方法,各个画面location和size发生变化的时候实时调用对应的更新主/子画面的委托。
    2012年9月25日 6:06

全部回复

  • 第一步:建立一个窗体,设置其属性:

                this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
                this.MaximizeBox = false;
                this.MinimizeBox = false;
                this.Opacity = 0.5;

    第二步:设置窗体的OnLoad事件:

            private void Form2_Load(object sender, EventArgs e)
            {
                this.Top = 20;
                this.Left = Screen.PrimaryScreen.Bounds.Width - 80;
                this.Width = 60;
                this.Height = 60;
            }

    第三步:修改窗体的Paint事件,美化界面,这里做一个渐变背景,需要引入System.Drawing.Drawing2D;

            private void Form2_Paint(object sender, PaintEventArgs e)
            {
                Graphics g = e.Graphics;
                Color FColor = Color.Red;
                Color TColor = Color.Yellow;
                Brush b = new LinearGradientBrush(this.ClientRectangle, FColor, TColor, LinearGradientMode.ForwardDiagonal);
                g.FillRectangle(b, this.ClientRectangle);
            }

    第四步:需要实现鼠标拖动悬浮窗体,

            const int WM_NCHITTEST = 0x0084;
            const int HTCLIENT = 0x0001;
            const int HTCAPTION = 0x0002;
            protected override void WndProc(ref System.Windows.Forms.Message m)
           {
                switch(m.Msg)
               {
                        case WM_NCHITTEST:
                                 base.WndProc(ref m);
                                 if (m.Result==(IntPtr)HTCLIENT)
                                          m.Result=(IntPtr)HTCAPTION;
                                 break;
                        default:
                                 base.WndProc(ref m);
                                 break;
               }
          }

    关于这一步,愚翁不是这样处理的,他是处理鼠标事件来实现的,效果差不多,也贴在这里学习一下吧:
    先定义几个类成员变量:
            private Point ptMouseCurrrnetPos, ptMouseNewPos,ptFormPos, ptFormNewPos;
            private bool blnMouseDown = false;

    再添加三个鼠标事件:
            private void Form2_MouseDown(object sender, MouseEventArgs e)
            {
                if (e.Button == MouseButtons.Left)
                {
                    blnMouseDown = true;
                    // Save window position and mouse position
                    ptMouseCurrrnetPos = Control.MousePosition;
                    ptFormPos = Location;
                }

            }

            private void Form2_MouseMove(object sender, MouseEventArgs e)
            {
                if (blnMouseDown)
                {
                    //Get the current position of the mouse in the screen
                    ptMouseNewPos = Control.MousePosition;
                    //Set window position
                    ptFormNewPos.X = ptMouseNewPos.X - ptMouseCurrrnetPos.X + ptFormPos.X;
                    ptFormNewPos.Y = ptMouseNewPos.Y - ptMouseCurrrnetPos.Y + ptFormPos.Y;
                    //Save window position
                    Location = ptFormNewPos;
                    ptFormPos = ptFormNewPos;
                    //Save mouse position
                    ptMouseCurrrnetPos = ptMouseNewPos;

                }
            }

            private void Form2_MouseUp(object sender, MouseEventArgs e)
            {
                if (e.Button == MouseButtons.Left)
                    //Return back signal
                    blnMouseDown = false;
            }


    第三种移动无标标题窗体的办法:
    通过API来处理,需要引入System.Runtime.InteropServices;

      [DllImport("user32.dll")]
      public static extern bool ReleaseCapture();
      [DllImport("user32.dll")]
      public static extern bool SendMessage(IntPtr hwnd,int wMsg,int wParam,int lParam);
                                         
      public const int WM_SYSCOMMAND=0x0112;
      public const int SC_MOVE=0xF010;
      public const int HTCAPTION=0x0002;
     
      private void Form2_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
      {
       ReleaseCapture();
       SendMessage(this.Handle,WM_SYSCOMMAND,SC_MOVE+HTCAPTION, 0);
      }

    呵,这个看起来好像比较“正统”。
    2012年9月25日 3:10
  • 感觉使用委托就可以了吧。主画面定义更新子画面的委托,影射到子画面的某个方法,子画面定义一个更新主画面的委托,影射到主画面的一个方法,各个画面location和size发生变化的时候实时调用对应的更新主/子画面的委托。
    2012年9月25日 6:06