将控件放在滚动窗口上时如何解决闪烁问题?(WM/WINCE,不是WIN32) [英] How to solve the flicker problem when the control is put on a scroll window ?(WM/WINCE, not WIN32)

查看:120
本文介绍了将控件放在滚动窗口上时如何解决闪烁问题?(WM/WINCE,不是WIN32)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

平台; Windows Mobile 6.0(专业版)

硬件:O2移动设备,CPU 600 mHz,128M mem

IDE:VS2005

问题描述:

我已经使用MFC在当前项目中构建UI应用程序,该应用程序的UI界面非常简单,它只有一个滚动窗口,上面带有多个控件.该应用程序要求窗口滚动应该非常平滑且没有闪烁.但是,似乎很难达到这一要求.以下是我的难题:

我使用了CScrollview,它具有滚动功能,而无需添加额外的代码.当我上下拖动滚动条时,我发现窗口在闪烁.我的第一个念头可能是我在窗口上放置了太多控件,因此我减少了窗口上的控件以作证,但不幸的是,即使我只在窗口上放置了一个按钮,闪烁仍然存在.

我花了几天的时间研究了这个问题,并在互联网上进行了搜索,终于找到了闪烁的原因.在给出原因之前,我想对滚动过程做一些解释.

当滚动条从上到下拖动时:

1.滚动条向其父窗口发送滚动消息
2.滚动窗口收到消息,然后准备滚动.假设滚动导致移动100像素,并且整个窗口工作区的高度为320像素
3.滚动窗口将底部矩形(高度为220(= 320-100))中的所有像素复制到顶部,现在已绘制了窗口的顶部220行.
4.调用OnEraseBkgnd绘制窗口的背景
5.调用OnDraw绘制底部高度为100的矩形,现在整个窗口都已重新绘制.
6.将所有子窗口从下到上移动100像素偏移
7.窗口将WM_PAINT发送给它的所有子窗口,以重新绘制子窗口或控件

闪烁问题是由削波机制引起的.众所周知,剪切机制是一种有效绘制窗口的方式,因此当窗口绘制客户区时,子窗口所占的区域将不会被绘制,然后子窗口将自己绘制该区域.但是,这对滚动不利,因为它会引起闪烁.如果在坐标(0,50)上有一个宽度为100像素,高度为30像素的按钮,则此按钮所占据的整个矩形区域不会被复制并在步骤3中绘制,因为它已被裁剪.由于此区域无效,该区域将在步骤4中涂上背景色,然后在步骤5中再次涂上正常内容.最后,绘制按钮将移动到的区域(坐标为(0,-50)).

屏幕上有四个变化

1.从下到上复制屏幕
2.使用背景颜色填充已修剪的按钮区域
3.在剪切区域上打印内容
4.在新位置打印子窗口

尽管改变的时间很短,但所有的改变都可以用我们的眼睛看到.所以我们看不到整个变化过程,而是忽隐忽现.

解决该问题的解决方案是删除样式WS_CLIPCHILDREN.没有这种样式,剪裁将不起作用,因此将复制整个区域,更改2、3将被取消.更改4仍会完成,但对眼睛没有影响.

我已经在WIN32 MFC上对其进行了测试,并且可以正常工作.

但是,在Windows Mobile上禁止删除Windows样式WS_CLIPCHILDREN,因为MSDN表示所有窗口都隐式具有WS_CLIPSIBLINGS和WS_CLIPCHILDREN样式".因此,如果没有WS_CLIPCHILDREN,我们就无法创建CScrollView.



我该如何解决此闪烁问题?有人知道此问题的任何解决方案或解决方法吗?这真的让我发疯,因为我花了整整一周的时间寻找解决方案.

如果对此说明有任何错误,请告诉我.

Platform; Windows Mobile 6.0 (Professional editon)

hardware: O2 mobile with 600 mHz cpu, 128M mem

IDE: VS2005

Problem Description:

I''ve used MFC to build UI application in my current project, The UI interface of the app is very simple, it only has a scroll window with several controls on it. The application requires the scrolling of the window should be very smooth with no flicker. However, it seems that this requirement is difficult to achieve. Following is my puzzle:

I used CScrollview which has scrolling functionality without adding extra code. I found that the window was flickering when I dragged the scrollbar up and down. My first thought was maybe I put too much controls on the window, so I reduced the controls on the window to testify, but unfortunately the flicker was still there even I put only one button on the window.

I studied this problem for days and also searched on the internet, and finally I found the reason why the flicker happens. I''d like to do a little explanation on the scrolling procedure before I give the reason.

When the scroll bar is being drag from top to bottom:

1. The scroll bar sends a scroll message to it''s parent window
2. The scroll window receives the message and then prepare to scroll. Suppose the scrolling results in 100 pixels movement and the whole window client area''s height is 320 pixel
3. scroll window copies all pixels in bottom rectangle, whose height is 220 ( = 320 - 100 ) to the top, now the top 220 lines of the window has been painted.
4. Invokes the OnEraseBkgnd to paint the background of the window
5. Invokes OnDraw to paint the bottom rectangle, whose height is 100, now the whole window has been re-painted.
6. Move all child windows from bottom to top with 100 pixels offset
7. The window sends WM_PAINT to all it''s children to re-painted the child windows or controls

The flicker problem is caused by clipping mechanism. As we known, the clipping mechanism is a way to paint window effectively, so the area occupied by child window will not be painting when the window paints the client area, and then the child window would paint this area by itself. However, this is not good for scrolling because it cause flicker. If there''s a button on coordinate (0, 50) with 100 pixels width, 30 pixels height, the whole rectangle area occupied by this button is not copied and painted in step 3 because it is clipped. And this area will be painted with background color in step 4 because it is invalidated, then will be painted again in step 5 for normal content. Finally the area, whose coordinate is (0, -50), that the button would be moved to would be painted.

So there are four changes on the screen

1. Copy the screen from bottom to top
2. fill the button area, which is clipped, with background color
3. print the content on the clipped area
4. print the child window on its new position

All the changes will be seen by our eyes although the time of changing is very short. So we can''t see the whole changing process clearly but flicker.

The solution to solve it is removing the style WS_CLIPCHILDREN. The clipping does not work without this style so the whole area will be copied, the change 2, 3 will be canceled. The change 4 will still be done but have no effect on eyes.

I have tested it on WIN32 MFC, and it works.

However, the removing of windows style WS_CLIPCHILDREN is forbidden on windows mobile , because the MSDN says "All windows implicitly have the WS_CLIPSIBLINGS and WS_CLIPCHILDREN styles." So we can''t create the CScrollView without WS_CLIPCHILDREN.



How do i solve this flicker problem? Does someone know any solution or workaround for this problem? It really drive me crazy because i''ve spend the whole week on finding the solution.

Please let me know if i make any mistake on this description.

推荐答案

仍然没有答案吗?真是可悲. :((
我终于找到了自己的解决方案,虽然有点丑陋,但是可以用.
我的解决方案是使用我自己的滚动功能而不是系统的滚动功能,以下是详细信息
1.重写CScrollview :: OnScrollBy,滚动的核心部分由此方法实现.
2.将MFC CScrollview :: OnScrollBy源代码复制到重写的函数
3.使用以下代码代替具有:: ScrollWindowEx
的行
Still no answer? That''s really sad. :((
I finally found a solution by myself, it''s a little ugly but it work.
My solution is using my own scrolling function instead of the system''s, following is detail
1. Overrides CScrollview::OnScrollBy, the core part of scrolling is implemented by this method.
2. Copies the MFC CScrollview::OnScrollBy source code to the overridden function
3. Uses following code instead of the line which has ::ScrollWindowEx
            CRect srcRect;
            GetClientRect(clientRect);
            // When visible, let Windows do the scrolling
            if( yAmount > 0 ){
                srcRect.DeflateRect( 0, 0, 0, yAmount );
            } else {
                srcRect.DeflateRect( 0, -yAmount, 0, 0 );
            }

            CDC* dc = GetDCEx( NULL, DCX_CACHE | DCX_WINDOW| DCX_CLIPSIBLINGS );
            CRect destRect = srcRect;
            destRect.OffsetRect( 0, yAmount );
            dc->BitBlt( destRect.left, destRect.top, destRect.Width(), destRect.Height(),
                dc, srcRect.left, srcRect.top,
                SRCCOPY);
            CRect invalidRect;
            GetClientRect( &invalidRect );
            invalidRect.SubtractRect( invalidRect, dest );
            InvalidateRect( &invalidRect, TRUE );
//             The original code
//             ::ScrollWindowEx(m_hWnd, xAmount, yAmount, NULL, NULL,
//                 NULL, NULL, SW_INVALIDATE | SW_ERASE);
        }

        {
            HWND hWndChild = ::GetWindow(m_hWnd, GW_CHILD);
            if (hWndChild != NULL)
            {
                for (; hWndChild != NULL;
                    hWndChild = ::GetNextWindow(hWndChild, GW_HWNDNEXT))
                {
                    CRect rect;
                    ::GetWindowRect(hWndChild, &rect);
                    ScreenToClient(&rect);
                    ::SetWindowPos(hWndChild, NULL,
                        rect.left+xAmount, rect.top+yAmount, 0, 0,
                        SWP_NOSIZE|SWP_NOACTIVATE|SWP_NOZORDER);
                }
            }
        }



注意:上面的代码仅实现垂直滚动,但是您可以轻松添加水平实现.

代码的原理是使用非剪切DC而不是剪切DC进行滚动,GetDCEx()用于获取此类非剪切DC.然后计算bitblt()的源和目标,并根据计算结果将内容从源复制到目标.接下来计算窗口的无效矩形并将其无效,系统稍后将自动重新绘制该区域.最后滚动所有子窗口.

我已经在模拟器和O2手机上对其进行了测试,到目前为止,它仍然可以正常工作.我不得不说这不是一个完美的解决方案,但这是我到目前为止所能找到的唯一方法.

如果您有任何疑问或建议,请不要犹豫.
goldencz(AT)263.net



Note: The above code implements the vertical scrolling only, but it''s easy to add horizontal implementation by yourself.

The principal of the code is using a non-clipped DC instead of clipped DC to do scrolling, GetDCEx() is used to get such non-clipped DC. Then the source and destination of bitblt() are calculated, and copies the content from source to destination according to the calculation result. Next calculates the invalid rect of the window and invalidate it, the system will automatically repaint this area later. Finally scrolls all child windows.

I''ve test it on the emulator and my O2 mobile phone, it works fine so far. I have to say it''s not a perfect solution but it''s the only way I can find till now.

Please let me know if you have any problem or suggestion with no hesitate.
goldencz(AT)263.net


这篇关于将控件放在滚动窗口上时如何解决闪烁问题?(WM/WINCE,不是WIN32)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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