欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程语言 > c/c++ >内容正文

c/c++

C/C++:Windows编程—MFC基于CWnd自绘CListCtrl控件且带滚动条

发布时间:2025/3/15 c/c++ 48 豆豆
生活随笔 收集整理的这篇文章主要介绍了 C/C++:Windows编程—MFC基于CWnd自绘CListCtrl控件且带滚动条 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

文章目录

    • 前言
    • 效果图
    • 代码思路
    • 滚动条思路
    • 完整代码

前言

由于工作需要,需要自绘类似CListCtrl那种控件 而且每项能带类似按钮的点击事件。所以笔者去进行研究,费了点时间写了个简单的自绘窗口 顺带也研究了下滚动条。博主在网络搜索的时候经常遇到只有代码没有效果图,这样有意思吗,既然都写了 弄全一点不行吗。在这里也呼吁一下广大博友,写博客的时候都把效果图带上。我们也先看效果图。

效果图





代码思路

和题目想呼应,这里我们绘制的CMyListWnd窗口,是基于CWnd类。这样可以最大发挥我们的自由度,可以完全自由发挥。直接在MFC项目添加类->MFC类,这样添加的类就直接继承CWnd类了,免的自己写错。博主绘制窗口是在BOOL CMyListWnd::OnEraseBkgnd(CDC* pDC) 回调函数中,因为该函数自带CDC参数直接用就行了。

博主绘制使用的gdi+相关API进行绘制的。gdi+简单例子demo可以参考这个 https://blog.csdn.net/shaoyubin999/article/details/84143222。绘制list内容实上际比较简单,一行一行绘制,按钮效果也比较好弄,就是鼠标悬停到按钮区域是 将鼠标样式改为鼠标手,然后按钮点击处理逻辑,博主是在 void CMyListWnd::OnLButtonUp(UINT nFlags, CPoint point) 回调函数中处理的,判断鼠标此时位置是否在 按钮区域然后做的处理。

废话太多,怕大家看不下去,下面直接是OnEraseBkgnd代码。其实最复杂的是滚动条绘制和滚动条功能的实现,思路看下一节。

/* 当CWnd对象的背景需要被擦除时(例如,当窗口大小被改变时),框架就调用这个函数。它被调用以便为绘图准备无效区域。 如果重载的OnEraseBkgnd在响应WM_ERASEBKGND时处理了这个消息并擦除了背景,则应当返回非零值,表明不需要进一步擦除。(实际上就是在这里可以绘图,并且返回TRUE) */ BOOL CMyListWnd::OnEraseBkgnd(CDC* pDC) {HDC hMemDC = CreateCompatibleDC( pDC->m_hDC ); if(m_iTotalDrawHeight<m_iWndHeight) m_iTotalDrawHeight=m_iWndHeight;HBITMAP hMemBitmap = CreateCompatibleBitmap( pDC->m_hDC, m_iWndWidth, m_iTotalDrawHeight); SelectObject(hMemDC, hMemBitmap); Graphics graph( hMemDC );//graph.SetSmoothingMode(SmoothingModeHighQuality);//graph.SetTextRenderingHint( TextRenderingHintAntiAliasGridFit );graph.SetSmoothingMode(SmoothingModeAntiAlias);graph.SetTextRenderingHint( TextRenderingHintClearTypeGridFit );SolidBrush solidBrushBlack( Color(250,0,0,0) );SolidBrush solidBrushBlack2( Color( 200,240,240,240) );SolidBrush solidBrushGray( Color( 255,225,225,225) );SolidBrush solidBrushWhite( Color(255,255,255,255) );SolidBrush solidBrushGrayText( Color( 250, 167, 167, 167 ) );//设置字体相关FontFamily fontFamily( m_strfontFamily );Gdiplus::Font fontRegular( &fontFamily, 13, FontStyleRegular,UnitPixel );Gdiplus::Font fontRegular2( &fontFamily, 12, FontStyleRegular,UnitPixel );StringFormat sf,sf2;sf.SetAlignment(StringAlignmentNear);sf.SetLineAlignment( StringAlignmentCenter );// 居中sf2.SetAlignment(StringAlignmentCenter);sf2.SetLineAlignment( StringAlignmentCenter );graph.FillRectangle( &solidBrushWhite, -1, -1, m_iWndWidth+1, m_iTotalDrawHeight+1 );// 每行高30UINT nCount = m_iTotalDrawHeight/m_iItemHeight;for( UINT i = 0; i < nCount ; i++ ){UINT y = m_iItemHeight*(i);if ( i%2 != 0 ){graph.FillRectangle( &solidBrushBlack2, 0, y, m_iWndWidth, m_iItemHeight );}}m_iDrawHeight = m_Items.size() * m_iItemHeight;std::list<CMyListItem>::iterator it = m_Items.begin();UINT i = 0;int btnRight = 20; // 最右边按钮距离容器右边框距离int btnTop = 3; // 按钮距离每行上边的距离int btnJianGe = 5;int btnWidth = 30,btnHeight = 24; // item 高为30for(;it != m_Items.end(); it ++,i++){UINT y = m_iItemHeight*(i);CString strtemp = it->m_strText;graph.DrawString( strtemp, strtemp.GetLength(), &fontRegular, it->m_rcRect, &sf, &solidBrushBlack );// 自己画 可以点击的按钮, // 复制graph.FillRectangle( &solidBrushGray,it->m_rcBtn1);graph.DrawString( it->m_strBtn1, it->m_strBtn1.GetLength(), &fontRegular2, it->m_rcBtn1,&sf2, &solidBrushBlack);// 删除graph.FillRectangle( &solidBrushGray, it->m_rcBtn2 );graph.DrawString( it->m_strBtn2, it->m_strBtn2.GetLength(), &fontRegular2, it->m_rcBtn2,&sf2, &solidBrushBlack);}int scrollWidth = 15;// 滚动条背景宽度// 滑动条if ( m_iDrawHeight > m_iWndHeight ){m_bDrawScroll = TRUE;SolidBrush solidbrushGray( Color( 150,150,150) );Pen penBroder( Color(180,180,180), 1.0 );// 画滚动条背景graph.FillRectangle( &solidBrushWhite, m_iWndWidth-scrollWidth, m_iDrawOffset, scrollWidth, m_iWndHeight ); // 填滑动条背景色graph.DrawRectangle( &penBroder, m_iWndWidth-scrollWidth, m_iDrawOffset+1, scrollWidth-1, m_iWndHeight-2 ); // 画滑动条边框// 计算滚动块大小位置double dDate = (double)m_iWndHeight/(double)m_iDrawHeight;int nScroll = int(m_iWndHeight*dDate); // 滑块高度SolidBrush solidbrushScroll( Color( 200,200,200) );Pen pen( Color( 0,0,0 ) );// 画滑块graph.FillRectangle( &solidbrushScroll, m_iWndWidth-scrollWidth, m_iDrawOffset+(INT)(m_iDrawOffset*dDate), scrollWidth, nScroll ); // 填充滑块颜色int mid = m_iDrawOffset+(INT)(m_iDrawOffset*dDate)+nScroll/2; //graph.DrawLine( &pen, m_iWndWidth-7, mid-4, m_iWndWidth-3, mid-4 );//graph.DrawLine( &pen, m_iWndWidth-8, mid, m_iWndWidth-2, mid );//graph.DrawLine( &pen, m_iWndWidth-7, mid+4, m_iWndWidth-3, mid+4 );graph.DrawRectangle( &penBroder, m_iWndWidth-scrollWidth, m_iDrawOffset+1+(INT)(m_iDrawOffset*dDate), scrollWidth-1, nScroll-2 ); // 画滑块边框// 滑块矩形设置m_rectScroll.SetRect( m_iWndWidth-scrollWidth, (int)(m_iDrawOffset*dDate), m_iWndWidth, (int)(m_iDrawOffset*dDate)+nScroll );}else {m_bDrawScroll = FALSE;m_iDrawOffset = 0;}Pen pen( Color(255,0,0 ),1.0);graph.DrawRectangle( &pen, 1, 1, m_iWndWidth-2, m_iTotalDrawHeight-2 );DeleteObject(hMemBitmap); //BitBlt( pDC->m_hDC, 0, 0, m_iWndWidth, m_iWndHeight, hMemDC, 0, 0, SRCCOPY); BitBlt( pDC->m_hDC, -1, -1, m_iWndWidth+1, m_iWndHeight, hMemDC, 0, m_iDrawOffset, SRCCOPY ); DeleteDC( hMemDC);return TRUE; }

滚动条思路

自己结合网络上所学知识 所融会的思路,自己打的草稿,自己可以研究完整代码,已经是比较简单的demo了。

绘制滚动条 原理m_iWndHeight 给的高度 m_iDrawHeight 内容(需要绘制)的高度 可超过实际高度 这时就需要滚动条了。 m_iTotalDrawHeight 绘制的总高度,m_iTotalDrawHeight = m_iWndHeight if(m_iDrawHeight>m_iWndHeight) m_iTotalDrawHeight = m_iDrawHeight m_bDrawScroll 是否绘制滚动条 m_iDrawOffset 偏移位置(以初始位置为准,可正可负 不过为负的时候强制为0 不偏移,为正 超过最大偏移 强制为最大偏移) {if ( m_iDrawOffset <=0 ){m_iDrawOffset = 0;}else if ( m_iDrawOffset > m_iDrawHeight - m_iWndHeight ){m_iDrawOffset = m_iDrawHeight - m_iWndHeight;} } nScroll 滑块高度 当m_iWndHeight == m_iDrawHeight 此时(如果规则让其显示的话)滑块长度为 m_iWndHeight 当m_iDrawHeight越长nScroll越小 算法如下 {double dData = m_iWndHeight/m_iDrawHeight; // 比率,用计算滑块长度、滑块偏移y位置int nScroll = m_iWndHeight * dData; } m_rectScroll 滑块位置,用于鼠标移动使用绘制滚动条总的逻辑 if(m_iDrawHeight > m_iWndHeight) {m_bDrawScroll = true// 绘制滚动条背景 // 宽度自定,高度为自定 <= m_iWndHeight// 位置(x自定) y是讲究,因为人眼看到滚动条背景是始终不动的,但是它同样也是位于总的内容窗口上// 那就是说它的y是根据 m_iDrawOffset 来的!和内容偏移同样graph.FillRectangle( &solidBrushWhite, m_iWndWidth-10, m_iDrawOffset, 10, m_iWndHeight ); // 填滑动条背景色graph.DrawRectangle( &penBroder, m_iWndWidth-10, m_iDrawOffset+1, 9, m_iWndHeight-2 ); // 画滑动条边框// 绘制滑块,这个也是有讲究的// 首先计算滑块的高度 在上面,宽度自定// 滑块位置x 自定,y 是根据滚动条背景的y确定的 还加上 偏移位置*dData滑块的y = m_iDrawOffset + m_iDrawOffset*dDatadouble dDate = (double)m_iWndHeight/(double)m_iDrawHeight;int nScroll = int(m_iWndHeight*dDate); // 滑块高度graph.FillRectangle( &solidbrushScroll, m_iWndWidth-10, m_iDrawOffset+(INT)(m_iDrawOffset*dDate), 10, nScroll ); // 填充滑块颜色int mid = m_iDrawOffset+(INT)(m_iDrawOffset*dDate)+nScroll/2; graph.DrawLine( &pen, m_iWndWidth-7, mid-4, m_iWndWidth-3, mid-4 );graph.DrawLine( &pen, m_iWndWidth-8, mid, m_iWndWidth-2, mid );graph.DrawLine( &pen, m_iWndWidth-7, mid+4, m_iWndWidth-3, mid+4 );graph.DrawRectangle( &penBroder, m_iWndWidth-10, m_iDrawOffset+1+(INT)(m_iDrawOffset*dDate), 9, nScroll-2 ); // 画滑块边框// 设置滑块的绝对位置,用于鼠标移动使用m_rectScroll.SetRect( m_iWndWidth-10, (int)(m_iDrawOffset*dDate), m_iWndWidth, (int)(m_iDrawOffset*dDate)+nScroll ); } else {m_bDrawScroll = FALSE;m_iDrawOffset = 0; }

完整代码

需要完整代码这里下载。

新人创作打卡挑战赛发博客就能抽奖!定制产品红包拿不停!

总结

以上是生活随笔为你收集整理的C/C++:Windows编程—MFC基于CWnd自绘CListCtrl控件且带滚动条的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。