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控件且带滚动条的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 24. PE结构-PE详解之基址重定位详
- 下一篇: python调用imblearn中SMO
