MFC如何在樹形圖邊上新增動態小地圖
MFC如何在樹形圖邊上新增動態小地圖
https://www.jianshu.com/p/7b1d828bf5db (簡書無法識別縮排的。。。早知道先在部落格園發了)
(轉載請註明出處)
作者:夢鏡穀雨
萌新第一次寫文章,請多多包涵。末尾附上相應程式碼(PS公司繁體系統所以部分註釋繁體請別介意)。
第一次接觸MFC時做的一個小專案上有做個樹形圖邊上帶小地圖的需求。(IDE:VS2010)
大四剛實習時寫的,當時網上沒找到現成的,打算記錄下來也算篇技術談不上的思路吧。
快2019了打算開始試著以後多記錄點東西,因為本人大學微電子專業不是軟體方向想往這邊發展的,程式碼裡東西這時文章寫到一半自己看著都感覺很糟糕(╯﹏╰),也算是記錄個黑歷史吧能實現功但糟糕就是糟糕。當時第一次學自繪沒想著要寫文章記錄下來,參考了些網上教樹形圖自繪好像csdn看的但連結沒記現在也忘了當時是參考哪一個了。在此感謝加抱歉。
一.思路:
Step1.自繪樹形圖控制元件(在樹形圖文字左邊顯示CImagelist裡的圖片)
Step2.建立個CBitmap動態儲存需要的圖片資訊存入樹形圖關聯的CImagelist中
二.最終效果(使用60*60符文3):
在畫板上的改變(60*60符文3)能動態反映到左邊樹形圖控制元件的對應專案(60*60符文3)上
三.重繪(我就多添加註釋吧在註釋裡講解。思路很簡單):
1.建立個類CViewTree繼承自CTreeCtrl
2.過載OnPaint使用雙緩衝(防閃爍,雙緩衝原理網上很多,後面建立小地圖也是同個思路)
/*雙緩衝重繪樹形圖*/
void CViewTree::OnPaint()
{
CLED_NEWDoc* pDoc =(CLED_NEWDoc*)((CMainFrame*)AfxGetApp()->GetMainWnd())->GetActiveDocument();//以為單文件工程有些東西存在doc裡
CPaintDC dc(this); // device context for painting
// TODO: 重繪樹形圖控制元件
// Do not call CTreeCtrl::OnPaint() for painting messages
GetClientRect(&m_ClientRect);
CBitmap bitmap;
CDC MemeDc;
MemeDc.CreateCompatibleDC(&dc);
bitmap.CreateCompatibleBitmap(&dc, m_ClientRect.Width(), m_ClientRect.Height());
CBitmap *pOldBitmap = MemeDc.SelectObject(&bitmap);
//繪圖部分
MemeDc.FillSolidRect(0, 0,m_ClientRect.Width(),m_ClientRect.Height(),RGB(255, 255, 255)); //填充背景
if (pDoc->m_CsFileName != _T(""))//如果有開啟檔案則可以畫樹形圖
DrawItem(&MemeDc);
//繪圖部分
dc.BitBlt( m_ClientRect.left, m_ClientRect.top, m_ClientRect.Width(), m_ClientRect.Height(), &MemeDc, 0, 0,SRCCOPY);
MemeDc.SelectObject(pOldBitmap);
MemeDc.DeleteDC();
}
3.畫顯示的專案
void CViewTree::DrawItem(CDC* pDc)
{
CLED_NEWDoc* pDoc =(CLED_NEWDoc*)((CMainFrame*)AfxGetApp()->GetMainWnd())->GetActiveDocument();
HTREEITEM currentItem;//當前的控制代碼
DWORD treeStyle;// 數的型別
CRect itemRect;//每一項的區域
int itemState;//某項的狀態
int itemImage;//圖片
int Open_num=0;
int HScroll = GetScrollPos(SB_HORZ);
CImageList* imagelist = GetImageList(TVSIL_NORMAL);
treeStyle =:: GetWindowLong( m_hWnd, GWL_STYLE );
currentItem = GetFirstVisibleItem();//獲取第一個課可見的項
//設定顯示字型
static CFont font;
font.DeleteObject();
font.CreatePointFont(100, _T("新宋體"));
pDc->SelectObject(&font);
do //beginwhile ((currentItem=GetNextVisibleItem(currentItem)) != NULL);
{
Open_num++;
//判斷是否為選擇狀態
if (Open_num + GetScrollPos(SB_VERT) == ((CMainFrame*)AfxGetApp()->GetMainWnd())->m_wndMaskView.Select)//選中是第幾個數存在MaskView的Select裡
pDc->SetBkColor(RGB(233,233,233));
else
pDc->SetBkColor(RGB(255,255,255));
CRect fillRect(0,itemRect.top,m_ClientRect.right,itemRect.bottom);//填背景用的後來沒刪
itemState = GetItemState(currentItem,TVIF_STATE);
//每一項的位置和圖片
GetItemImage(currentItem,itemImage,itemImage);
GetItemRect(currentItem,itemRect,FALSE);
CPoint point;
point.y = itemRect.top;
point.x = itemRect.left-HScroll+(GetLevel(currentItem)-1)*42;//(以前寫的時候多次用上的數字都沒#define成英文。很糟糕,建議養成習慣)
if (itemRect.top>m_ClientRect.bottom) //說明這一項已超出視窗的邊界
{
break;
}
if ( GetChildItem(currentItem) != NULL )
{
if (ItemHasChildren(currentItem))//有子項則畫上對應三角形圖示
{
if (itemState & TVIS_EXPANDED )
{
imagelist->Draw(pDc,1,point,ILD_TRANSPARENT);
}
else
{
imagelist->Draw(pDc,0,point,ILD_TRANSPARENT);
}
}
}else{//無子項則畫上對應imagelist圖片
//imagelist->Draw(pDc,3,point,ILD_TRANSPARENT);
point.x = point.x + 50;
imagelist->Draw(pDc,itemImage,point,ILD_TRANSPARENT);
point.x = point.x - 50;
}
if ( GetLevel(currentItem) != 3 )//專案不為第三層就加藍色資料夾圖示
{
point.x = point.x + 42;
point.y = point.y + 5;
pDc->DrawIcon(point,AfxGetApp()->LoadIcon(IDI_ICON_PROJECT));//因為存imagelist無法有透明效果(現在這個能自己能花式解決吧。以前寫的真糟糕,但是因為提供的是ICON資源載入到imagelist解析度降了很多,看他只有一張於是就直接畫了)
point.x = point.x - 42;
point.y = point.y - 5;
}
GetItemRect(currentItem,itemRect,TRUE);
if (Open_num == ((CMainFrame*)AfxGetApp()->GetMainWnd())->m_wndMaskView.Rename && pDoc->m_bFlagRenameShow == TRUE);
else
pDc->TextOut(itemRect.left ,itemRect.top+itemRect.Height()/2-6,GetItemText(currentItem));//輸出樹形圖的文字
}while ((currentItem=GetNextVisibleItem(currentItem)) != NULL);
}//好了到此為止,以下不重要可以直接看四.
//現在忘了為什麼加這個
BOOL CViewTree::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
// TODO: Add your specialized code here and/or call the base class
return CTreeCtrl::OnWndMsg(message, wParam, lParam, pResult);
}
//這個現在也忘記了為什麼加了
BOOL CViewTree::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
BOOL bRes = CTreeCtrl::OnNotify(wParam, lParam, pResult);
NMHDR* pNMHDR = (NMHDR*)lParam;
ASSERT(pNMHDR != NULL);
if (pNMHDR && pNMHDR->code == TTN_SHOW && GetToolTips() != NULL)
{
GetToolTips()->SetWindowPos(&wndTop, -1, -1, -1, -1, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOSIZE);
}
return bRes;
}
//不要擦除背景,會閃爍
BOOL CViewTree::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
return TRUE;
}
//滾動時當然要重新整理
void CViewTree::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: Add your message handler code here and/or call default
Invalidate();
CTreeCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
}
//搬的磚,放在裡面方便用
int CViewTree::GetLevel(HTREEITEM inputTree)
{
//獲得Item所在樹形圖層數
HTREEITEM temp;
temp = inputTree;
int level = 0;
while (temp != NULL)
{
temp = GetParentItem(temp);
++level;
}
return level;
}
四.建立CBitmap存入CImagelist(那時存class CMaskView : public CDockablePane):
建立自繪的樹形圖:CViewTree m_wndFileView;
建立圖片列表:CImageList m_FileViewImages;
1.建立樹形圖
//vs建立單文件工程造著裡面來就好了,樹形圖類相關操作msdn裡面自查CTreeCtrl,新增刪除重新命名什麼的網上很多就不寫了
if (!m_wndFileView.Create(dwViewStyle, rectDummy, this, 4))
{
TRACE0("Failed to create file view\n");
return -1; // fail to create
}//調整位置大小也是vs新建單文件工程時有懸浮窗就有看vs吧
2.建立圖片並填充(在畫樹形圖時每個項對應上Imagelist裡的相應項就OK了)
void CMaskView::FillTreeImage()
{
/***********************///(和雙緩衝同個思路,誒,現在看起來能寫的簡單整潔多的,怪當時理解不深,真糟糕)
CLED_NEWDoc* pDoc =(CLED_NEWDoc*)((CMainFrame*)AfxGetApp()->GetMainWnd())->GetActiveDocument();
CDC MemDC,MemDC2;
CDC * pDC;
CDC * pDC2;
pDC =GetDC();
pDC2=GetDC();
HTREEITEM hItem;
CBitmap bmp,bmp2;
UINT nFlags = ILC_MASK;
nFlags |= (theApp.m_bHiColorIcons) ? ILC_COLOR24 : ILC_COLOR4;
m_imagelist.DeleteImageList();//清除圖片列表
m_imagelist.Create(m_iImageSize,m_iImageSize,nFlags,0,300);//建立圖片列表
//m_wndFileView.SetImageList(&pDoc->m_imagelist, TVSIL_NORMAL);
MemDC.CreateCompatibleDC(pDC);
bmp.CreateCompatibleBitmap(pDC,60,60);
MemDC.SelectObject(&bmp);
MemDC2.CreateCompatibleDC(pDC2);
bmp2.CreateCompatibleBitmap(pDC2,m_iImageSize,m_iImageSize);
MemDC2.SelectObject(&bmp2);
MemDC.FillSolidRect(0,0,pDoc->SymImage.m_Column,pDoc->SymImage.m_Row,RGB(255, 255, 255));
/*/畫十字(未展開狀態圖示)
MemDC2.FillSolidRect(0,0,m_iImageSize,m_iImageSize,RGB(255, 255, 255));
MemDC2.FillSolidRect(m_iImageSize/2-10,m_iImageSize/2-1,20,2,RGB(0, 0, 0));
MemDC2.FillSolidRect(m_iImageSize/2-1,m_iImageSize/2-10,2,20,RGB(0, 0, 0));
m_imagelist.Add(&bmp2,RGB(255, 255, 255));
*/
//90度直角三角形
int start = m_iImageSize/3,end = m_iImageSize*2/3;
int H = m_iImageSize/2,mid = m_iImageSize/2;
int x,y;
MemDC2.FillSolidRect(0,0,m_iImageSize,m_iImageSize,RGB(255, 255, 255));
for (x = 0;x<m_iImageSize;x++)
for (y = 0;y<m_iImageSize;y++)
if(x>start && (x-start) < (y - mid + H/2) && -(x-start) > (y - mid - H/2))
MemDC2.SetPixel(x,y,RGB(0, 0, 0));
m_imagelist.Add(&bmp2,RGB(255, 255, 255));
/*/畫減號(展開狀態圖示)
MemDC2.FillSolidRect(0,0,m_iImageSize,m_iImageSize,RGB(255, 255, 255));
MemDC2.FillSolidRect(m_iImageSize/2-10,m_iImageSize/2-1,20,2,RGB(0, 0, 0));
m_imagelist.Add(&bmp2,RGB(255, 255, 255));
*/
//135直角三角形
MemDC2.FillSolidRect(0,0,m_iImageSize,m_iImageSize,RGB(255, 255, 255));
for (x = 0;x<m_iImageSize;x++)
for (y = 0;y<m_iImageSize;y++)
if(x < mid + 7*H/20 && y < mid + 7*H/20 && -(x - mid - 7*H/20) < y - mid + 7*H/20)
MemDC2.SetPixel(x,y,RGB(0, 0, 0));
m_imagelist.Add(&bmp2,RGB(255, 255, 255));
//塗白(未含有子顯圖示)
MemDC2.FillSolidRect(0,0,m_iImageSize,m_iImageSize,RGB(255, 255, 255));
m_imagelist.Add(&bmp2,RGB(255, 255, 255));
HICON hIcon[2];
hIcon[0] = AfxGetApp()->LoadIcon(IDI_ICON_FILE_SE);
m_imagelist.Add(hIcon[0]);
//bmp.LoadBitmapW(IDB_BITMAP_F1);
hIcon[1] = AfxGetApp()->LoadIcon(IDI_ICON_PROJECT);
m_imagelist.Add(hIcon[1]);
//給樹形圖對應imagelist插入自畫的圖片並設定每個項的對應
int n = 4;
hItem = m_wndFileView.GetRootItem();
hItem = m_wndFileView.GetChildItem(hItem);
while (hItem != NULL)//幾個判斷判斷是否是第3層,因為當時只有第三層需要小地圖,掃描他們
{
if(m_wndFileView.GetChildItem(hItem) != NULL)
{
hItem = m_wndFileView.GetChildItem(hItem);
while (hItem != NULL)
{
n++;
CString name;
name = m_wndFileView.GetItemText(hItem);
CString ParentItemName = m_wndFileView.GetItemText(m_wndFileView.GetParentItem(hItem));
if (ParentItemName == _T("5*8"))
pDoc->SymImage = pDoc->m_Array_List_Sym[pDoc->Finding_Sym_limit(name,5,8)];
else if (ParentItemName == _T("16*16"))
pDoc->SymImage = pDoc->m_Array_List_Sym[pDoc->Finding_Sym_limit(name,16,16)];
else if (ParentItemName == _T("32*32"))
pDoc->SymImage = pDoc->m_Array_List_Sym[pDoc->Finding_Sym_limit(name,32,32)];
else if
(ParentItemName == _T("其他") || ParentItemName == _T("Other"))
pDoc->SymImage = pDoc->m_Array_List_Sym[pDoc->Finding_Sym_for_another(name)];
//給圖片重新整理背景
MemDC.FillSolidRect(0,0,pDoc->SymImage.m_Column,pDoc->SymImage.m_Row,pDoc->m_clrLedBK);
//按點填充圖案
for (x = 0;x<pDoc->SymImage.m_Column;x++)
for (y = 0;y<pDoc->SymImage.m_Row;y++)
if (pDoc->SymImage.led_data[x][y] == TRUE)
MemDC.SetPixel(x,y,pDoc->m_clrLed);//(根據已知資訊繪製圖,MemDC可以存其他圖片,當時對CDC這些也理解不深)
MemDC2.StretchBlt(1,1,m_iImageSize-2,m_iImageSize-2,&MemDC,0,0,pDoc->SymImage.m_Column,pDoc->SymImage.m_Row,SRCCOPY);//縮放儲存到MemDC2裡,StretchBlt可能有失真
m_imagelist.Add(&bmp2,RGB(255,255,255));//新增到圖片列表
m_wndFileView.SetItemImage(hItem,n,n);//設定對應圖片
if (m_wndFileView.GetNextSiblingItem(hItem) == NULL)
break;
else
hItem = m_wndFileView.GetNextSiblingItem(hItem);
}//endwhile (hItem != NULL)
hItem = m_wndFileView.GetParentItem(hItem);
}
hItem = m_wndFileView.GetNextSiblingItem(hItem);
}//endwhile (hItem != NULL)
MemDC2.FillSolidRect(0,0,m_iImageSize,m_iImageSize,RGB(255, 255, 255));
m_imagelist.Add(&bmp2,RGB(0, 0, 0));
m_wndFileView.SetImageList(&m_imagelist, TVSIL_NORMAL);//關聯圖片列表
::ReleaseDC(this->m_hWnd, MemDC);
::ReleaseDC(this->m_hWnd, MemDC2);
/***********************************/
}
//就這樣,樹形圖子項要對應的圖片存到關聯的CImagelist裡,小地圖需要變化時替換CImagelist裡對應的項然後再重新整理樹形圖就可以實現樹形圖旁邊帶著的小地圖動態變換了。
五.結語:
這時寫文章回顧這個程式碼寫的真的很糟糕(負能量程式碼,抱歉),只是實現了功能。在部落格園還是哪個地方看了篇技術觀念的文章,嗯,深耕技術(這個詞很喜歡)。希望未來我們都能做喜歡乾的事情吧(希望終有一天能成為穀雨大神或雨神吧,嗯,希望),這算是記錄下當時的思路(黑歷史)的處女作吧,見諒。