想搞定之前提到的第9点需求,在TabCtrl上放View,实现多张View的显示。TabCtrl不是个View或其派生出来的,所以不能作为ChildFrame的一张分割视图出现,这是我猜测的,也是后面的依据。所以在之前一个很好的拆分窗口的例子VIEWEX中发现可以用FormView来作为视图,而FormView是支持窗口操作的,就是会跟着生成一个窗体,然后就可以在上面放TabCtrl,那怎么在TabCtrl上放View呢,网上有方法提到现在TabCtrl上放Dialog,然后在Dialog上放View,以上是摘要。
下面提提基本思路和参考链接,1,2,3提到怎么在Dialog上放View,1比较乱,用的是2中的代码,思路是在Dialog上放一个Static Text,然后在借助它来创建View,3中的方法是不用控件。借助的是CCreateContext,设置一些信息之后,便用它来创建View,见下面代码:
context.m_pCurrentDoc = pDoc;
context.m_pCurrentFrame = (CFrameWnd *)this;//设置父窗体指针,将对话框指针强制转换
context.m_pLastView = NULL;//前一个视图为空
context.m_pNewDocTemplate = NULL;//文档模板为空
context.m_pNewViewClass = RUNTIME_CLASS(MView);
//动态调用CreateObject创建一个对象并获得指针
m_pView = (MView*)context.m_pNewViewClass->CreateObject();//通过指针创建视图对象
关于初始化,是一个比较掣肘的问题,当你不知道它们初始化的顺序时。我从CFormView处派生出CRight,并在CRight处添加Dialog和TabCtrl成员,FormView有窗体支持的,在上面放了一个TabCtrl控件,并为它绑定了一个变量,也就是初始化交给MFC去操作了,而该更新是在DoDataExchange函数下的DDX_Control处初始化的。我想在初始化后,对TabCtrl进行大小自适应窗体。在FormView的OnInitialUpdate中进行,但之前写在了父类初始化之前了,导致那个对象为空,不能进行SetWindowPos来设置位置的。因为父类的初始化之后会调用DoDataExchange来初始化TabCtrl的。
因此,在添加一张图片后,就让CRight为TabCtrl添加一个选项卡上去,在这,这可以参考,方法就是New一个Dialog,添加一个选项卡Item,然后移动窗体位置什么的,这可以参考。然后View的创建在Dialog中的OnInitDialog中进行,注意对于窗体是在Init中初始化,对于视图则是在InitUpdate中进行了。||
有一点需要注意的是,CTabCtrl的一个事件OnTcnSelchangeTab1可以在其父容器的事件中找到相应的子控件里面的函数进行重写。
以上的方法便可以实现,但会遇到一个我不知道怎么解决的问题,就是拖动FormView后,上面的控件会跟着闪烁,用了网上说的两种方法没有得到改善,并在CSDN上发帖提问,得到相应的提示后有了下面的进展与解决方法。
(((((((((((((((((((((((((((我是括号)))))))))))))))))))))))))))
后来又到网上寻找解决方案,在这看到个对于“闪烁”这个问题很好的说法,引用如下:
1. 如果你自己重载过OnPaint之类的消息的话,画图的时候一定要加缓冲,最后一次性画到屏幕DC上。
2. 如果你界面布局比较复杂,层次很多,那就容易闪烁,层次越多闪的越厉害。
3. 是否使用了TabCtrl?如果把各个页面的父窗体设为TabCtrl,那么窗体在大小改变或者刷新的时候闪的就比较厉害。UltraEdit配置界面也用到了TabCtrl,但是大小变化的时候不会闪。他的做法是,各个页面的父窗体并不是TabCtrl,各个页面与TabCtrl是平级的,但是TabCtrl的Z-Order被放在最下面。
4. 适当的根据情况调整ClipSibling和ClipChildren属性,会明显的减少闪烁问题。
这也有个很好的帖子,谈论闪烁的原因,大致如下:
1. 没在一个周期内把前景和背景都绘制完毕,导致前后差别大。
2. 窗口层次过多。
3. 使用MoveWindow或是SetWindowPos来改变窗体大小,需要绘制完毕,才调用这类函数。
对于闪烁的本质原因,其实还是前后两次的页面的切换,反差过小,被人眼给识别出来了。WS_CLIPCHILDREN和WS_CLIPSIBLINGS,在这有讲解,其实一个是对兄弟窗口的绘制优化,一个是父子窗口的绘制优化。后来采用了上面的第3种方法,就是用一个View,然后把它和TabCtrl置于同级,需要注意的是要在FormView上,也就是View和TabCtrl的父级窗口上屏蔽掉背景重新绘制,就是那个OnEraseBkgnd函数,要直接返回TRUE。
到这里,关键问题基本上就解决了,接下来,要继续解决其他问题~
PS:以上内容由两天分别记录,以||作为分界线,因此前后可能会觉得比较跳跃。