近日想重绘一下C# Winform窗体标题栏,以前用去掉边框的办法重绘过标题栏,那种方法虽然实现的效果较好,但终究不是一个完美的办法,我想用WndProc截取系统消息来重绘一下,但运行时发现窗体原来的按钮总会时不时的出现并遮掩了我重绘的按钮,我写的代码如下:        [DllImport("User32.dll")]
        private static extern IntPtr GetWindowDC(IntPtr hwnd);
        [DllImport("User32.dll")]
        private static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);
        [DllImport("Kernel32.dll")]
        private static extern int GetLastError();
        protected override void WndProc(ref System.Windows.Forms.Message m)
        {
            const int WM_NCCREATE = 0x0081;
            const int WM_PAINT = 0x000F;
            const int WM_SIZE = 0x0005;
            const int WM_CHILDACTIVATE = 0x0022;
            if (m.Msg == WM_NCCREATE || m.Msg == WM_PAINT || m.Msg == WM_SIZE || m.Msg == WM_CHILDACTIVATE)
            {
                this.Invalidate();
                IntPtr hDC = GetWindowDC(m.HWnd);
                Graphics gs = Graphics.FromHdc(hDC);
                TextureBrush brush = new TextureBrush(topbg,new Rectangle(0,0,1,30));
                gs.FillRectangle(brush, 0, 0, this.Width, 30);
                TextureBrush brushs = new TextureBrush(topCloseImg, new Rectangle(0, 0, 10, 11));
                gs.FillRectangle(brushs, this.Width-20, 0, 10, 11);
                gs.Dispose();                
                ReleaseDC(m.HWnd, hDC);//释放GDI资源            }
            base.WndProc(ref m);
        }
程序运行时的效果:请求高手来给出个解决方案,并讲一个窗体重绘的合理方法

解决方案 »

  1.   

    主要还是处理WM_NCPAINT吧:The WM_NCPAINT message is sent to a window when its frame must be painted. A window receives this message through its WindowProc function. Parameters
    wParam 
    Handle to the update region of the window. The update region is clipped to the window frame. When wParam is 1, the entire window frame needs to be updated. 
    lParam 
    This parameter is not used. 
    Return Values
    An application returns zero if it processes this message. Res
    The DefWindowProc function paints the window frame. An application can intercept the WM_NCPAINT message and paint its own custom window frame. The clipping region for a window is always rectangular, even if the shape of the frame is altered. The wParam value can be passed to GetDCEx as in the following example.case WM_NCPAINT:
    {
        HDC hdc;
        hdc = GetDCEx(hwnd, (HRGN)wParam, DCX_WINDOW|DCX_INTERSECTRGN);
        // Paint into this DC
        ReleaseDC(hwnd, hdc);
    }
      

  2.   

    WM_NCPAINT 程序发送此消息给某个窗口当它(窗口)的框架必须被绘制时;这前天试过好像不行,今天再试一下
      

  3.   

    你应该处理 WM_NCCALCSIZE WM_NCPAINT WM_NCACTIVATE WM_NCHITTEST 消息
      

  4.   

    要把SYSMENU窗口风格去掉,在.NET中你也可以直接把那三个按钮设置为不显示,
    剩下的就是重绘制标题栏
      

  5.   

    WS_SYSMENU不一定要去掉,你去掉的目的无非是为了防止系统的对那三个按钮的重绘,这只能说你的处理不到位,因为你用spy++看看QQ、TM或其他第三方皮肤库做出来的程序就知道了,人家是没有去掉WS_SYSMENU的。
      

  6.   

    补充一点,刚才说的简单了,QQ和TM是用spy++看聊天窗口,不是主界面。
      

  7.   

    SYSMENU窗口风格去掉?不会是将FormBorderStyle 设置为None 吧?如果是这样方法不行的,早期我就是用将FormBorderStyle 设置为None 来开发的,效果虽好,但不可作为实际用途,其效率很低
      

  8.   

    是的,我看了QQ的,人家并没有去掉WS_SYSMENU,在QQ上用alt+空格就可以看到它并没有去掉WS_SYSMENU
      

  9.   

    只处理 WM_NCCALCSIZE WM_NCPAINT WM_NCACTIVATE WM_NCHITTEST 消息好像不行,在移到关闭按钮并点下去时它还会显示出来
      

  10.   

    晕  我并没有说让你 "只处理" 啊移动的时候由于你还调用了Base.WndProc,所以这表明在移动到sysmenu button上时是要重绘标题栏的,而现在是系统重绘了,你没有重绘。
    类似的地方还有很多,你的重绘应该放到默认的绘制之后,以覆盖掉原来的绘制。WM_NCCALCSIZE是让你调整非客户区尺寸的,里面的参数虽然说是表明的客户区的大小,但是你修改这个大小也间接修改了非客户区的大小,如果你想标题栏变粗一些,就要处理这个消息。最后补充一句,自绘标题栏一定要在非xp风格,即xp及以上的系统要在windows经典风格下测试,这样会看出许多问题。
      

  11.   

    你的重绘应该放到默认的绘制之后,以覆盖掉原来的绘制?这句不知道怎样个写法了,另外如果知道自已的重绘是在系统重绘之后发生的,那么阻止系统自动重绘不就可以了吗?关键是怎样去实现的问题,C++里用钩子实现,但在C#里这只找到用WndProc重绘这一种方法
      

  12.   

    不去掉WS_SYSMENU,那你就得用VC++来写,
    像.NET这种应用程序太慢就会把系统绘制的图放出来,
    因为WS_SYSMENU这个样子导致系统对标题按钮这些的绘制,你找台慢一点的电脑来试,
    我想QQ窗体也会在绘制的瞬间先把系统绘制的效果呈现出来
      

  13.   


    for example :
    protected override void WndProc(ref Message m)
    {
         switch (m.Msg)
         {
            case WM_NCACTIVATE:
                 base.WndProc(ref m);    // here is the default process
                 //your nc paint code goes here 
                 break;
         .......
            default :
                 base.WndProc(ref m);
                 break;
         }
    }