MFC中利用CDockablePane实现悬浮窗

本文介绍如何使用VS2008中的CDockablePane实现可自由拖动的小窗口,并解决了一些常见问题,包括刷新显示区域和去除关闭按钮。

VS2008提供CDockablePane实现悬浮窗效果,各小窗口可以根据需要调整布局。本文介绍CDockablePane的使用方法以及使用中遇到的一些问题及解决方案。

 

首先展示一下最终效果

 

按住鼠标拖动小窗口,效果如下,可以根据需要选择放置位置:

 

 

实现步骤:

 

1.       新建一个MFC应用程序,其他选择默认。

2.       新建MFCCLeftDlg,选择基类为CDialog。点击完成系统会自动生成对话框资源IDD_LEFTDLG,在资源视图中打开该对话框,放入自己想要的内容,如文本框,按钮等。

3.       新建MFCCleftPane,选择基类为CDockablePane。添加此类的WN_CREATEWM_SIZE相应,和成员变量

          CLeftDlg m_LeftDlg;

编辑OnCreate OnSize函数:

 

int CLeftPane::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

    if (CDockablePane::OnCreate(lpCreateStruct) == -1)

               return -1;

 

    // TODO:  在此添加您专用的创建代码

    CRect rectDummy;

    rectDummy.SetRectEmpty();

    // 创建选项卡窗口:

    if (!m_LeftDlg.Create(IDD_LEFTDLG,this))

    {

           TRACE0("未能创建输出选项卡窗口/n");

           return -1;      //未能创建

    }

    m_LeftDlg.ShowWindow(SW_SHOW);

 

    return 0;

}

 

void CLeftPane::OnSize(UINT nType, int cx, int cy)

{

    CDockablePane::OnSize(nType, cx, cy);

    m_LeftDlg.SetWindowPos (this, -1, -1, cx, cy, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);

    m_LeftDlg.ShowWindow(SW_SHOW);

}

4.       Mainframe中添加变量CleftPane m_LeftPane; OnCreate中添加代码

  

         if (!m_LeftPane.Create(_T("LeftPane"), this, CRect(0, 0, 100, 100), TRUE, IDD_TESTDLG,

                   WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_LEFT | CBRS_FLOAT_MULTI))

         {

                   TRACE0("未能创建输出窗口/n");

                   return FALSE;

         }

         m_LeftPane.SetControlBarStyle(m_LeftPane.GetControlBarStyle() | CBRS_LEFT | CBRS_SIZE_DYNAMIC);

        m_LeftPane.EnableDocking(CBRS_ALIGN_ANY);

        DockPane(&m_LeftPane);

       

5.       重复步骤2-4,为CBottomPane添加对话框CBottomDlg,并在MainFrame中添加m_BottomFrame变量和OnCreate中的代码。

6.       此时,初步的页面布局已经没有问题,但是运行的结果会出现如下小窗口不能刷新现实区域,拖动时出现叠影。此时需要为CLeftPaneCBottomPane添加OnPaint函数,将背景填充为白色:

void CBottomPane::OnPaint()

{

        CPaintDC dc(this); // device context for painting

       CRect rc;

       GetClientRect(rc);

       CBrush   brush;

       brush.CreateSolidBrush(COLORREF(RGB(255, 255, 255)));

       dc.FillRect(&rc,&brush);

       brush.DeleteObject();

}

7.       如果需要去掉CDockablePane右上角的close按钮,可以在创建pane的时候加入如下参数:

  m_LeftPane.Create(_T("LeftPane"), this, CRect(0, 0, 100, 100), TRUE, IDD_TESTDLG,

                   WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_LEFT | CBRS_FLOAT_MULTI, AFX_CBRS_REGULAR_TABS, AFX_CBRS_RESIZE))

 

效果如下:

 

8.       CDockablePane的内部按鼠标右键或出现系统菜单,要屏蔽此菜单,可以为CLeftPaneCBottomPane添加相应,运行,菜单不见鸟。

void CLeftPane::OnContextMenu(CWnd* /*pWnd*/, CPoint /*point*/)

 

9.       编译运行,最终效果如第一张图所示。

 

 

// GradientProgressCtrl.cpp : implementation file // #include "stdafx.h" #include "VProgressCtrl.h" //#include "MemDC.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif class CMemDC : public CDC { public: CMemDC(CDC* pDC); CMemDC* operator->(){return this;} operator CMemDC*(){return this;} virtual ~CMemDC(); private: CBitmap m_bitmap; CBitmap* m_pOldBitmap; CDC* m_pDC; CRect m_rect; BOOL m_bMemDC; }; CMemDC::CMemDC(CDC* pDC):CDC() { ASSERT(pDC!=NULL); m_pDC=pDC; m_pOldBitmap=NULL; m_bMemDC=!pDC->IsPrinting(); // if(m_bMemDC) { pDC->GetClipBox(&m_rect); CreateCompatibleDC(pDC); m_bitmap.CreateCompatibleBitmap(pDC,m_rect.Width(),m_rect.Height()); m_pOldBitmap=SelectObject(&m_bitmap); SetWindowOrg(m_rect.left,m_rect.top); } else //为相关的现有设备准备打印 { m_bPrinting=pDC->m_bPrinting; m_hDC=pDC->m_hDC; m_hAttribDC=pDC->m_hAttribDC; } } CMemDC::~CMemDC() { if(m_bMemDC) { m_pDC->BitBlt(m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height(), this,m_rect.left,m_rect.top,SRCCOPY); SelectObject(m_pOldBitmap); } else { m_hDC=m_hAttribDC=NULL; } } ///////////////////////////////////////////////////////////////////////////// // CVProgressCtrl CVProgressCtrl::CVProgressCtrl() : m_nTextType(TextCustom) , m_strFormat(_T("%s")) , m_nLower(0) , m_nUpper(100) , m_nCurrentPosition(0) , m_nStep(1) , m_clrStart(RGB(255,0,0)) , m_clrEnd(RGB(0,0,255)) , m_clrBkGround(GetSysColor(COLOR_3DFACE)) , m_clrText(RGB(255,255,255)) { m_nLower=0; m_nUpper=100; m_nCurrentPosition=0; m_nStep=10; } CVProgressCtrl::~CVProgressCtrl() { } BEGIN_MESSAGE_MAP(CVProgressCtrl, CProgressCtrl) //{{AFX_MSG_MAP(CVProgressCtrl) ON_WM_ERASEBKGND() ON_WM_PAINT() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CVProgressCtrl message handlers void CVProgressCtrl::SetRange(int nLower,int nUpper) { //This Function is to Set Range of the progress m_nLower=nLower; m_nUpper=nUpper; m_nCurrentPosition=nLower; CProgressCtrl::SetRange(nLower,nUpper); } int CVProgressCtrl::SetStep(int nStep) { m_nStep=nStep; return (CProgressCtrl::SetStep(nStep)); } BOOL CVProgressCtrl::OnEraseBkgnd(CDC* pDC) { // TODO: Add your message handler code here and/or call default return TRUE;//CProgressCtrl::OnEraseBkgnd(pDC); } void CVProgressCtrl::DrawGradient(CPaintDC *pDC, const RECT &rectClient, const int &nMaxWidth) { RECT rectFill; //显示区域 float fStep; //每一步的幅宽 CBrush brush; //显示的颜色画刷 CMemDC memDC(pDC); int r,g,b; float rStep,gStep,bStep; //得到不同颜色并相减,返回颜色之间的最大差值 r=(GetRValue(m_clrEnd)-GetRValue(m_clrStart)); g=(GetGValue(m_clrEnd)-GetGValue(m_clrStart)); b=(GetBValue(m_clrEnd)-GetBValue(m_clrStart)); //使进程条显示的总数 等于最大的颜色差值 int nSteps=max(abs(r),max(abs(g),abs(b))); //确定每一颜色填充多大的矩形区域 fStep=(float)rectClient.right/(float)nSteps; //设置每一颜色填充的步数 rStep=r/(float)nSteps; gStep=g/(float)nSteps; bStep=b/(float)nSteps; r=GetRValue(m_clrStart); g=GetGValue(m_clrStart); b=GetBValue(m_clrStart); //绘制颜色渐变的进程条 for(int iOnBand=0;iOnBand<nSteps;iOnBand++) { ::SetRect(&rectFill, //以下为填充矩形区域的左上角x,y和右下角x,y (int)(iOnBand*fStep), 0, (int)((iOnBand+1)*fStep), rectClient.bottom+1); VERIFY(brush.CreateSolidBrush(RGB(r+rStep*iOnBand,g+gStep*iOnBand,b+bStep*iOnBand))); memDC.FillRect(&rectFill,&brush); VERIFY(brush.DeleteObject()); //在结束绘制之前,使用背景色填充乘下的的客户区域 if(rectFill.right>nMaxWidth) { ::SetRect(&rectFill,rectFill.right,0,rectClient.right,rectClient.bottom); VERIFY(brush.CreateSolidBrush(m_clrBkGround)); memDC.FillRect(&rectFill,&brush); VERIFY(brush.DeleteObject()); return; } } } void CVProgressCtrl::DrawProgressText(CPaintDC& dc,RECT& rectClient) { //根据文本类型,设置显示的文本 switch(m_nTextType) { case TextPercent: m_strText.Format(m_strFormat,(int)(100*(float)m_nCurrentPosition/m_nUpper)); break; case TextPos: m_strText.Format(m_strFormat,m_nCurrentPosition); break; default://TextCustom break; } //设置文本颜色 dc.SetTextColor(m_clrText); dc.SetBkMode(TRANSPARENT); //绘制文本 dc.DrawText(m_strText,&rectClient,DT_VCENTER|DT_CENTER|DT_SINGLELINE); } void CVProgressCtrl::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: Add your message handler code here if(m_nCurrentPosition<=m_nLower||m_nCurrentPosition>=m_nUpper) { CRect rect; GetClientRect(rect); CBrush brush; brush.CreateSolidBrush(::GetSysColor(COLOR_3DFACE)); dc.FillRect(&rect,&brush); VERIFY(brush.DeleteObject()); return; } CRect rectClient; GetClientRect(rectClient); float maxWidth((float)m_nCurrentPosition/(float)m_nUpper*(float)rectClient.right); //绘制进度条 DrawGradient(&dc,rectClient,(int)maxWidth); //绘制进度条文本 DrawProgressText(dc,rectClient); // Do not call CProgressCtrl::OnPaint() for painting messages } int CVProgressCtrl::SetPos(int nPos) { //Set the Position of the Progress m_nCurrentPosition = nPos; return (CProgressCtrl::SetPos(nPos)); } void CVProgressCtrl::SetText(LPCTSTR lpcszText) { m_strText.Format(m_strFormat,lpcszText); this->Invalidate(); } void CVProgressCtrl::SetTextType(int nTextType,LPCTSTR lpszFormat) { m_nTextType = nTextType;//文本类型 m_strFormat = lpszFormat;//文本格式化 int nIndex;//内容在格式化中的位置 if (m_strFormat.GetLength() == 0) { nIndex = 0; } else { int nTemp = m_strFormat.Find(_T("%s")); if (nTemp == -1) { nIndex = m_strFormat.GetLength(); } else { nIndex = nTemp; m_strFormat.Delete(nTemp,2); while ((nTemp = m_strFormat.Find(_T("%"),nTemp)) > -1) { m_strFormat.Insert(nTemp,_T("%")); nTemp += 2; } } } //根据文本类型不同,设置格式化字符串 switch(m_nTextType) { case TextPercent: m_strFormat.Insert(nIndex,_T("%d%%")); break; case TextPos: m_strFormat.Insert(nIndex,_T("%d")); break; default: m_strFormat.Insert(nIndex,_T("%s")); break; } } 这个代码中为什么进度条到达100%后会重置为0
最新发布
06-04
<think>好的,我现在需要解决用户的问题:为什么这段代码中的进度条到达100%后会重置为0。首先,我需要仔细阅读用户提供的代码,找出问题所在。用户提供的代码是C++ MFC的一个自定义进度控件CVProgressCtrl。首先,我会查看OnPaint函数实现,因为进度条的绘制逻辑在这里。在OnPaint函数中,有一个条件判断: if(m_nCurrentPosition<=m_nLower||m_nCurrentPosition>=m_nUpper){//用背景色填充并返回} 这里,当m_nCurrentPosition大于等于m_nUpper时,会执行填充背景色的操作,而不绘制进度条。这可能意味着当进度到达上限时,控件显示为背景色,看起来像是重置为0。但实际是否如此呢?接下来,检查SetPos函数。当调用SetPos时,m_nCurrentPosition被设置为nPos,然后调用基类的CProgressCtrl::SetPos(nPos)。基类的行为可能影响进度显示,但问题更可能出在绘制逻辑。在OnPaint函数中,当m_nCurrentPosition>=m_nUpper时,控件会用背景色填充整个区域,导致进度条消失,看起来像是重置了。例如,如果Upper是100,当CurrentPosition到达100时,条件成立,直接填充背景,不显示进度条,所以用户看到的是空进度条,即0%的效果。 此外,在DrawGradient函数中,maxWidth是根据当前进度计算的。当CurrentPosition等于Upper时,maxWidth等于rectClient.right,此时会绘制完整的渐变条。但在OnPaint的条件判断中,当CurrentPosition>=Upper时,不会执行DrawGradient,而是直接填充背景。因此,当进度到达100%时,控件显示背景色,而不是满进度条,导致看起来像重置。可能的原因是条件判断中的逻辑错误。正确的逻辑应该是当CurrentPosition小于等于Lower或者大于等于Upper时,停止绘制进度。但Upper是上限,当CurrentPosition等于Upper时,应该显示100%的进度,而不是隐藏。所以条件中的>=Upper应该改为>Upper,或者在设置范围时将Upper设为实际的最大值+1,但通常进度控件的Upper是包含在内的。例如,如果Upper是100,当CurrentPosition为100时,应该显示100%。但当前代码的条件是当CurrentPosition>=Upper时,不绘制进度条,这就导致在达到100%时,控件显示背景色,看起来像重置。因此,问题出在OnPaint函数中的条件判断。应将条件改为m_nCurrentPosition<m_nLower ||m_nCurrentPosition> m_nUpper,这样当CurrentPosition等于Upper时,仍然会绘制完整的进度条。或者,调整条件为m_nCurrentPosition<= m_nLower,这样只有低于下限时不显示,而当到达上限时正常显示。另一个可能是,当调用SetPos时,传入的值超过了Upper,导致触发该条件。例如,用户可能在代码中设置的位置超过了Upper,导致控件进入该分支。但根据代码中的SetPos函数,它只是将nPos赋给m_nCurrentPosition,然后调用基类的SetPos。需要确保传入的nPos不超过Upper。总结问题原因:当进度到达Upper时,OnPaint中的条件判断导致控件不绘制进度条,而是显示背景色,看起来像是重置为0。解决方法是修改条件,允许在CurrentPosition等于Upper时绘制进度条。</think>这个问题的根本原因在于`OnPaint()`函数中的条件判断逻辑错误。当进度达到上限值时,控件会直接绘制背景色而不再显示进度条。以下是具体分析: **关键问题代码段**: ```cpp void CVProgressCtrl::OnPaint() { CPaintDC dc(this); // 问题判断条件 if(m_nCurrentPosition<=m_nLower||m_nCurrentPosition>=m_nUpper) { CRect rect; GetClientRect(rect); CBrush brush; brush.CreateSolidBrush(::GetSysColor(COLOR_3DFACE)); dc.FillRect(&rect,&brush); return; // 直接返回不绘制进度条 } // ...后续绘制代码被跳过 } ``` **问题原因**: - 当`m_nCurrentPosition`等于`m_nUpper`时(即达到100%),条件`m_nCurrentPosition>=m_nUpper`成立 - 导致直接使用背景色填充控件区域,跳过渐变条绘制逻辑 - 视觉效果上进度条突然消失,看起来像是被重置 **解决方案**: 修改判断条件,允许在达到上限值时正常渲染进度条: ```cpp // 修改后的条件判断 if(m_nCurrentPosition < m_nLower || m_nCurrentPosition > m_nUpper) { // 绘制背景... return; } ``` **代码解释**: 1. 原条件`>= m_nUpper`改为`> m_nUpper`,允许等于上限值时继续绘制 2. 当进度值严格超过上限时才视为无效进度 3. 此时当进度正好达到100%时,会执行后续的`DrawGradient`和文本绘制 **完整修复代码**: ```cpp void CVProgressCtrl::OnPaint() { CPaintDC dc(this); // 修改后的判断条件 if(m_nCurrentPosition < m_nLower || m_nCurrentPosition > m_nUpper) { CRect rect; GetClientRect(rect); CBrush brush; brush.CreateSolidBrush(::GetSysColor(COLOR_3DFACE)); dc.FillRect(&rect,&brush); VERIFY(brush.DeleteObject()); return; } // 保留原有绘制逻辑... } ``` **验证方法**: 1. 设置进度范围`SetRange(0, 100)` 2. 调用`SetPos(100)`设置到最大值 3. 观察控件应显示完整的渐变条和"100%"文本 4. 只有设置超过100的值才会显示背景色
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值