CSliderCtrl与工具栏@MFC

CSliderCtrl是种稍微变形的控件,祖宗是Wnd,即窗体。先废点话,关于之前提到使用gluscaleimage出现的异常,后来不知怎的,居然在gluBuild2DMipmaps使用的时候也出现了神似的错误,错误的说明有很大的重复:中的 OpenGLPlat.exe 中的 0x6ec470e5 处最可能的异常: 0xC0000005: 读取位置 0x06be6000 时发生访问冲突,就上面的画线处不一样,后来发现是因为少加了一句代码:

glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // 设置图像对齐模式

本以为,能把之前那个诡异的异常搞定,怀疑是因为没有设置图像的字节排列方式,从而导致的读取、绑定失败,可最后还是未果,先不管了,还是。

昨晚+今天一直在弄滑动条(CSliderCtrl),有两种方式,静态的和动态的,静态的就是直接往窗口上拖一个控件,然后怎么搞啊搞(现在还不会),动态的是,也是我这次需要的,就是想往工具栏上放一个滑动条,这里给出了两种基本思路:

  • 方法1:用Rebar,然后把Slider创建在DIalogBAr上,最后ReBar.AddBar(.......)
  • 方法2:在TOOLBAR上建一个按狃,然后找到这个ID,创建CSlidertrl,最后Show

我采用了第二种方法,因为在这里看到了现成的代码和方法描述,但也不详尽,消息的响应没怎么提(对于我这种初学者就惨了),在工具栏上插入一个滑动条的方法是,在对应的ToolBar处新建一个按钮,并带有了一个ID,设成IDC_SLIDERXX,然后在相应的ToolBar上找到这个控件,把它设置成滑动条的形状就可以。可是怎么响应相应的读取和写入的动作呢,网上提到的比较流行的一种方法是去找到WM_HSCROLL这个消息,重写OnHScroll函数,但一个控件的动作是它的父容器去捕获的,这时,在这里看到了一个很强大的方法,学习下了。方法是重新封装下ToolBar,我这里从CToolBar继承了一个类:CMToolBar,这个工具栏指的就是放有CSliderCtrl的工具栏,然后OnHScroll函数从CMToolBar处重写就可以了,赞。

本来以为问题解决了,谁知道又衍生出一个这样的问题,我弄的是多文档的,多个Doc,每个Doc对应多个View,发现他们共享一个Slider,也难怪,这个Slider是在MainFrm下的ToolBar里的,我想了个这样的方法,解决了这个问题,感觉比较山寨,凑合着用了,在Doc里添加一个Pos变量,用来记录当前文档下的滑动条的值,CMToolBar的成员函数OnHScroll响应滑动的操作,这里,当值发生改变的时候,去获取MainFrm指针,获取View指针,获取Doc指针,然后修改Doc的值,由于修改后,需要对View选择性的进行重绘操作(我的程序有两个View),又遍历了所有的View,找到了想要的View之后,重绘后就可以了,遍历View的代码,这里可以看到。至于在View中如何使用这个滑动条,就直接根据Pos进行操作就可以了,至此,问题解决大半。

还有个比较棘手的问题,怀疑是我那山寨的方法的后遗症,由于这个Slider控件是个动态出现,和其他的按钮不同步,比如,没有Doc出现的时候,这个Slider应该不可用才更合理,在这里,还有这里知道了设置Slider不可用就是要设置它的Style,通过GetWindowLong函数和SetWindowLong函数实现,获取到Style后,并进行修改,然后加载进去就可以了。我在Doc的构造函数中实现了这个方法,即获取了MainFrm指针,根据控件的ID,找到该控件,然后修改风格,可是会报错,怎么也找不出原因,又是个诡异的错误,对那之间的指针不是很懂得,调起来也就很麻烦了。之后这样实现就可以了,把设置不可用,设置可用写成MToolBar的一个方法,这样就在Doc构造函数中,就可以直接通过MainFrm指针调用,在析构函数中同理加上设置不可用。

除上述之外,还要响应一个消息,WM_NCACTIVATE,它指的是ChildFrm激活,重写OnNcActivate函数,每当激活某个ChildFrm的时候,就用Doc中的值来更新ToolBar上的slider,也就实现了多文档下的工具栏上的插入slider控件。至此,目前的问题全部解决了。

获取指针,这里写的不错,摘抄些代码如下,至于重绘的话,有InvalidateRect,还有Invalidate什么的,看了不是很懂得,就知道,优先级比较低,等到该Paint的时候,才Paint出来,相关内容可以参考这里,感觉写的比较好。

CMainFrame* pFrame = (CMainFrame *)AfxGetMainWnd();   
CChildFrame* pChildFrame = (CChildFrame *)pFrame->GetActiveFrame();
CFDSFrameView* pView = (CFDSFrameView *)pChildFrame->GetActiveView();
CDC* pDC = pView->GetDC();

下面上一些我的代码,首先是MToolBar中的OnHScroll函数和CanUse、CanNotUse函数:

void CMToolBar::CanNotUse()
{
    long style = GetWindowLong(m_slider.m_hWnd, GWL_STYLE);    //获取样式
    style ^= WS_DISABLED;
    SetWindowLong(m_slider.m_hWnd, GWL_STYLE, style);    //设置样式
}
 
void CMToolBar::CanNotUse()
{
    long style = GetWindowLong(m_slider.m_hWnd, GWL_STYLE);
    style |= WS_DISABLED;
    SetWindowLong(m_slider.m_hWnd, GWL_STYLE, style);
    //m_slider.Invalidate();    如果不行,这里进行更新
}
 
void CMToolBar::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    CSliderCtrl* pSlidCtrl = (CSliderCtrl*)GetDlgItem(IDC_SLIDER99);    //在MToolBar上找空间slider
    CMainFrame* pFrame = (CMainFrame *)AfxGetMainWnd();   //获取MainFrm指针
    CChildFrame *pChildFrame = (CChildFrame *)pFrame->GetActiveFrame();    //获取ChildFrm指针
    CView* pView = (CView *)pChildFrame->GetActiveView();    //获取当前View指针
    COpenGLPlatDoc* pDoc = (COpenGLPlatDoc*)pView->GetDocument();    //获取Doc指针
    pDoc->pos = pSlidCtrl->GetPos();    //更新Doc中记录的pos值
    //遍历View,找到MView
    POSITION pp = pDoc->GetFirstViewPosition();
    while(pp != NULL)
    {
        pView = pDoc->GetNextView(pp);
        if(pView->IsKindOf(RUNTIME_CLASS(MView)))  break;
    }
    pView->InvalidateRect(NULL, FALSE);    //重绘View
    CToolBar::OnHScroll(nSBCode, nPos, pScrollBar);
}

下面是CMainFrm中的OnCreate函数中的关于新建slider的部分:

int index = m_wndToolBar1.CommandToIndex(IDC_SLIDER99);    //在ToolBar中获取索引
m_wndToolBar1.SetButtonInfo(index, IDC_SLIDER99, TBBS_SEPARATOR, 80);  //设置Buttong样式
CRect rect;  
m_wndToolBar1.GetItemRect(index, &rect);  //得到具体位置信息
if (!m_wndToolBar1.m_slider.Create(WS_CHILD|WS_VISIBLE | TBS_HORZ | TBS_AUTOTICKS |TBS_BOTTOM ,  
    rect, &m_wndToolBar1, IDC_SLIDER99))  
{  
    TRACE( "Failed to create slider ctrl\n ");  
    return FALSE;  
}  
m_wndToolBar1.CanNotUse();    //设置不可用
m_wndToolBar1.m_slider.SetRange(0,10);  
m_wndToolBar1.m_slider.SetPos(2);  
m_wndToolBar1.m_slider.ShowWindow(SW_SHOW);  

下面是CXXXDoc的构造函数中的初始化部分:

pos = 0;
//设置可用
CMainFrame* pFrame = (CMainFrame *)AfxGetMainWnd();   
pFrame->m_wndToolBar1.CanUse();

下面是CChildFrameOnNcActivate函数:

BOOL CChildFrame::OnNcActivate(BOOL bActive)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    COpenGLPlatDoc* pDoc = (COpenGLPlatDoc*)this->GetActiveView()->GetDocument();
    CMainFrame* pFrame = (CMainFrame *)AfxGetMainWnd();   
    pFrame->m_wndToolBar1.m_slider.SetPos(pDoc->pos);
    return CMDIChildWnd::OnNcActivate(bActive);
}

关键的代码(核心/实现的代码的80%部分)就上面这些,别看这些,折腾的可够呛的,上张图纪念下:

slider_finish_thumb23

这次的内容都是关于MFC的,没有涉及到OpenGL的知识,算是对MFC又掌握了些知识吧,要做的还蛮多的,继续~

posted @ 2011-02-22 20:49  litstrong  阅读(1213)  评论(0编辑  收藏  举报