环境:VS2005 C# XP小弟以webbrowser控件为基础做一个多页面网页流览器,页面切换时会有相对明显的闪烁。经goole,网上找到这么一篇资料:Web Browser控件和CHtmlView有什么具体应用我也不废话了,咱们开门见山吧。通常使用上述两个东西的时候会发现切换的时候有很严重的闪烁。仔细观察你会发现,比方在ChtmlView中,窗口的绘画过程是:先画View的背景,然后在上层画控件(即网页)解决办法很简单,禁止View进行那无用的导致闪烁的绘制背景过程。具体做法 :在派生类里处理WM_ERASEBKGND消息BOOL CMyHtmlView::OnEraseBkgnd(CDC* pDC) 
{
 return TRUE; 
// return CHtmlView::OnEraseBkgnd(pDC);
}通过上面的改动以后,你会发现VIEW会跟腾讯的QQ浏览器一样无闪烁:)Web Browser控件的相应做法类似,不再详述
于是在Webbrowser控件里重写WndProc方法,截获该消息,但没有效果。重写OnPaintBackground,让它啥也不干,照样没效果。忽然想到Webbrowser不过是对底层axwebbrowser控件的封装,于是改用底层的axwebbrowser,还是重写WndProc和OnPaintBackground,结果令人大失所望,页面切换时闪烁依旧。请问有什么好的办法吗?

解决方案 »

  1.   

    该问题可以这样概括:双缓冲有效解决了自定义控件闪烁问题,但对于一些系统控件而且不能设置双缓冲的控件比如Webbrowser,如何防止其闪烁呢?难道我得回归VC了?
      

  2.   

    用webbrowser控件做多标签式的浏览器,当点击标签切换不同页面时会有闪烁---当页面以黑色为背景时尤其明显
      

  3.   

    解决方法如下:从WebBrowser派生一个新类
    构造函数中添加DocumentCompleted事件响应相关代码:
            [DllImport("user32.dll")]
            protected static extern int GetWindowLong(int hwindow, int unindex);
            [DllImport("user32.dll")]
            protected static extern int CallWindowProc(int lpPrevWndFunc, int hWnd, int Msg, int wParam, int lParam);
            [DllImport("user32.dll")]
            protected static extern int SetWindowLong(int hwindow, int unindex, CallWindowProcDelegate lnewvalue);
            public static int oldWindow = 0; 
            public const int GWL_WNDPROC = -4;
            public delegate int CallWindowProcDelegate(int Wnd, int Msg, int WParam, int LParam);
            public  CallWindowProcDelegate MyCallWindowProc;     bool add = false;        void ExtendWebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
            {
                if(add)
                {  return; }            add = true;
                FindWindow fw = new FindWindow(this.Handle, "Internet Explorer_Server");
                IntPtr hIE = fw.FoundHandle;            if (hIE.ToInt32() != 0)
                {
                    oldWindow = (int)GetWindowLong(hIE.ToInt32(), GWL_WNDPROC);
                    MyCallWindowProc = new CallWindowProcDelegate(WndProc);
                    SetWindowLong(hIE.ToInt32(), GWL_WNDPROC, MyCallWindowProc);
                }
            }        private int WndProc(int Wnd, int Msg, int WParam, int LParam)
            {
                const int WM_ERASEBKGND = 0x14;            if (Msg == WM_ERASEBKGND)
                {
                    return 1;
                }            return CallWindowProc(oldWindow, Wnd, Msg, WParam, LParam);
            }      /**/
        /// <summary>
        /// This class is to find the given window's child window accroding to the given child window's name.
        /// The useage: FindWindow fw = new FindWindow(wndHandle, "ChildwndClassName"); IntPtr ip = fw.FoundHandle;
        /// I adapt the code from Paul DiLascia,who is the MSDN Magazine's writer.
        /// The original class is named CFindWnd which is written in C++, and you could get it on Internet.
        /// www.pinvoke.net is a great website.It includes almost all the API fuctoin to be used in C#.
        /// </summary>
        class FindWindow
        {
            [DllImport("user32")]
            [return: MarshalAs(UnmanagedType.Bool)]
            //IMPORTANT : LPARAM  must be a pointer (InterPtr) in VS2005, otherwise an exception will be thrown
            private static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
            //the callback function for the EnumChildWindows
            private delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);        //if found  return the handle , otherwise return IntPtr.Zero
            [DllImport("user32.dll", EntryPoint = "FindWindowEx")]
            private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);        private string m_classname; // class name to look for        private IntPtr m_hWnd; // HWND if found
            public IntPtr FoundHandle
            {
                get { return m_hWnd; }
            }
            // ctor does the work--just instantiate and go
            public FindWindow(IntPtr hwndParent, string classname)
            {
                m_hWnd = IntPtr.Zero;
                m_classname = classname;
                FindChildClassHwnd(hwndParent, IntPtr.Zero);
            }        //EnumChildWindows是API函数,能够遍历主窗口下所有子窗口。不过它的遍历过程是通过
            //第二个参数即回调函数与程序员交互的。EnumChildWindows每找到一个窗口。就调用回调
            //函数。回调函数如果返回false。遍历就会结束。        /**/
            /// <summary>
            /// Find the child window, if found m_classname will be assigned 
            /// </summary>
            /// <param name="hwndParent">parent's handle</param>
            /// <param name="lParam">the application value, nonuse</param>
            /// <returns>found or not found</returns>
            //The C++ code is that  lParam is the instance of FindWindow class , if found assign the instance's m_hWnd
            private bool FindChildClassHwnd(IntPtr hwndParent, IntPtr lParam)
            {
                EnumWindowProc childProc = new EnumWindowProc(FindChildClassHwnd);
                IntPtr hwnd = FindWindowEx(hwndParent, IntPtr.Zero, this.m_classname, string.Empty);
                if (hwnd != IntPtr.Zero)
                {
                    this.m_hWnd = hwnd; // found: save it
                    return false; // stop enumerating
                }
                EnumChildWindows(hwndParent, childProc, IntPtr.Zero); // recurse  redo FindChildClassHwnd
                return true;// keep looking
            }
        }