📄 collapsiblepanel.h
字号:
#if !defined(AFX_COLLAPSIBLEPANEL_H__20031129_797C_31F4_B029_0080AD509054__INCLUDED_)
#define AFX_COLLAPSIBLEPANEL_H__20031129_797C_31F4_B029_0080AD509054__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
/////////////////////////////////////////////////////////////////////////////
// Collapsible Panel - Windows XP Explorer widget
//
// Written by Bjarke Viksoe (bjarke@viksoe.dk)
// Copyright (c) 2003 Bjarke Viksoe.
// Scroll support kindly added by Anatoly Ivasyuk (aivasyuk@dtlink.com)
//
// This code may be used in compiled form in any way you desire. This
// source file may be redistributed by any means PROVIDING it is
// not sold for profit without the authors written consent, and
// providing that this notice and the authors name is included.
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability if it causes any damage to you or your
// computer whatsoever. It's free, so don't hassle me about it.
//
// Beware of bugs.
//
#ifndef __cplusplus
#error WTL requires C++ compilation (use a .cpp suffix)
#endif
#ifndef __ATLCTRLS_H__
#error CollapsiblePanel.h requires atlctrls.h to be included first
#endif
#ifndef __ATLGDIX_H__
#error This control requires my atlgdix.h to be included first
#endif
#if (_WIN32_IE < 0x0400)
#error This control requires _WIN32_IE >= 0x0400
#endif
// Control notifications
#define CPN_EXPANDING 1
#define CPN_EXPANDED 2
// Extended Panel style
#define CPS_EX_NOANIMATE 0x00000001
#define CPS_EX_FLATSTYLE 0x00000002
#define CPS_EX_DEEPDISABLE 0x00000004
#define CPS_EX_SHAREIMAGELIST 0x00000008
#define CPS_EX_OWNERDRAW 0x00000010
#define CPS_EX_NOEXPANDBUTTON 0x00000020
#define CPS_EX_EXPANDCLICK 0x00000040
#define CPS_EX_NOSCROLLBAR 0x00000080
#define CPS_EX_SELHIGHLIGHT 0x00000100
template< class T, class TBase = CWindow, class TWinTraits = CControlWinTraits >
class ATL_NO_VTABLE CCollapsiblePanelImpl :
public CWindowImpl< T, TBase, TWinTraits >,
public COffscreenDrawRect< T >
{
public:
enum { ANIMATETIMER_ID = 22 }; // Happens to be my lucky number!
enum { IMAGE_GAP = 5 }; // Gap between button-base and icon-base
typedef struct tagPANEL
{
HWND hWnd; // Handle to view
int iImage; // Image Index into ImageList
bool bEnabled; // Is panel enabled?
bool bExpanded; // Is panel expanded?
LPTSTR pstrTitle; // Display title
COLORREF clrText; // Text color. If CLR_INVALID then use default
COLORREF clrBar1; // Bar color. If CLR_INVALID then use default
COLORREF clrBar2; // Bar color. If CLR_INVALID then use default
HBITMAP hbmpButton; // Bitmap representation of button
SIZE szChild; // Preferred size of child pane
DWORD dwAnimateStopTime; // Time when animation should stop
int cy; // Current child height
} PANEL;
typedef struct tagPANELTHEME
{
COLORREF clrBack;
COLORREF clrTextActive;
COLORREF clrTextInactive;
COLORREF clrTextSel;
COLORREF clrBarActive1;
COLORREF clrBarActive2;
COLORREF clrBarInactive1;
COLORREF clrBarInactive2;
COLORREF clrBarSel1;
COLORREF clrBarSel2;
COLORREF clrBorder;
int iArc;
} PANELTHEME;
CSimpleArray<PANEL> m_aPanels; // Collection of panels
int m_iCurSel; // Last known selected panel
CFont m_fntBold; // Caption font
DWORD m_dwExtStyle; // Extended style flags
PANELTHEME m_Theme; // Color configuration
CIconHandle m_iconCollapse; // Icon for expander button
CIconHandle m_iconExpand; // Icon for expander button
bool m_bOwnCollapseIcons; // We own expand icons?
CImageList m_Images; // Images for icons
CImageList m_ImagesGrey; // Images for grey icons
SIZE m_szMargin; // Margins between window and panels
int m_cyPadding; // Padding between panels
int m_cyButton; // Height of button (including icon)
int m_cyBar; // Height of bar (colored text/caption)
WORD m_cxLastWidth; // Last known width of window
WORD m_cyLastHeight; // Last known height of window
DWORD m_dwAnimateTime; // How long time to allow animation (in ms)
// Operations
BOOL SubclassWindow(HWND hWnd)
{
ATLASSERT(m_hWnd==NULL);
ATLASSERT(::IsWindow(hWnd));
BOOL bRet = CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
if( bRet ) _Init();
return bRet;
}
int GetCurSel() const
{
return m_iCurSel;
}
void SetCurSel(int iSel)
{
ATLASSERT(::IsWindow(m_hWnd));
m_iCurSel = iSel;
_RecalcLayout();
SetItemExpanded(iSel, !GetItemExpanded(iSel), TRUE);
}
void RemoveAllItems()
{
ATLASSERT(::IsWindow(m_hWnd));
for( int i = 0; i < m_aPanels.GetSize(); i++ ) {
if( m_aPanels[i].hbmpButton != NULL ) ::DeleteObject(m_aPanels[i].hbmpButton);
delete [] m_aPanels[i].pstrTitle;
}
m_aPanels.RemoveAll();
Invalidate();
}
int GetItemCount() const
{
return m_aPanels.GetSize();
}
int InsertItem(HWND hWndView, LPCTSTR pstrTitle, int iImage = -1, COLORREF clrText = CLR_INVALID)
{
ATLASSERT(::IsWindow(hWndView));
ATLASSERT(!::IsBadStringPtr(pstrTitle,-1));
ATLASSERT(iImage==-1 || !m_Images.IsNull()); // Need to assign ImageList first
ATLASSERT(((DWORD)::GetWindowLong(hWndView, GWL_STYLE) & WS_CLIPSIBLINGS)!=0); // Must have CLIPSIBLINGS
// Assign new parent to panel
::SetParent(hWndView, m_hWnd);
// Make sure the panel is resized to the preferred size
// before it's added so we can know its size when expanding it!
RECT rc;
::GetWindowRect(hWndView, &rc);
ATLASSERT(rc.right-rc.left>0 && rc.bottom-rc.top>0);
// Add the item in the list
PANEL panel = { 0 };
panel.hWnd = hWndView;
ATLTRY( panel.pstrTitle = new TCHAR[::lstrlen(pstrTitle) + 1] );
if( panel.pstrTitle == NULL ) return -1;
::lstrcpy(panel.pstrTitle, pstrTitle);
panel.bExpanded = true;
panel.bEnabled = true;
panel.clrText = clrText;
panel.clrBar1 = CLR_INVALID;
panel.clrBar2 = CLR_INVALID;
panel.iImage = iImage;
panel.szChild.cx = rc.right - rc.left;
panel.cy = panel.szChild.cy = rc.bottom - rc.top;
m_aPanels.Add(panel);
if( IsWindow() ) Invalidate();
::ShowWindow(hWndView, SW_SHOWNOACTIVATE);
return m_aPanels.GetSize() - 1;
}
BOOL GetItemExpanded(int iIndex) const
{
ATLASSERT(::IsWindow(m_hWnd));
if( iIndex < 0 || iIndex >= m_aPanels.GetSize() ) return FALSE;
return m_aPanels[iIndex].bExpanded == true;
}
BOOL SetItemExpanded(int iIndex, BOOL bExpanded, BOOL bAnimate = FALSE)
{
ATLASSERT(::IsWindow(m_hWnd));
if( iIndex < 0 || iIndex >= m_aPanels.GetSize() ) return FALSE;
if( m_aPanels[iIndex].bExpanded == (bExpanded == TRUE) ) return TRUE;
// Send notification
::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), CPN_EXPANDING), (LPARAM) m_hWnd);
// Set panel attributes
PANEL& panel = m_aPanels[iIndex];
panel.bExpanded = bExpanded == TRUE;
DWORD dwTick = ::GetTickCount();
if( bAnimate && (m_dwExtStyle & CPS_EX_NOANIMATE) == 0 ) dwTick += m_dwAnimateTime;
panel.dwAnimateStopTime = dwTick;
// Display panel now so animation can be shown
::ShowWindow(panel.hWnd, SW_SHOWNOACTIVATE);
// Start the show
SetTimer(ANIMATETIMER_ID, 50L); // 50 ms timer
SendMessage(WM_TIMER, ANIMATETIMER_ID);
return TRUE;
}
BOOL GetItemEnabled(int iIndex) const
{
ATLASSERT(::IsWindow(m_hWnd));
if( iIndex < 0 || iIndex >= m_aPanels.GetSize() ) return FALSE;
return m_aPanels[iIndex].bEnabled == true;
}
BOOL SetItemEnabled(int iIndex, BOOL bEnabled)
{
ATLASSERT(::IsWindow(m_hWnd));
if( iIndex < 0 || iIndex >= m_aPanels.GetSize() ) return FALSE;
m_aPanels[iIndex].bEnabled = bEnabled == TRUE;
if( (m_dwExtStyle & CPS_EX_DEEPDISABLE) != 0 ) ::EnableWindow(m_aPanels[iIndex].hWnd, bEnabled);
_RecalcLayout();
return TRUE;
}
BOOL SetItemColors(int iIndex, COLORREF clrText, COLORREF clrBar1 = CLR_INVALID, COLORREF clrBar2 = CLR_INVALID)
{
ATLASSERT(::IsWindow(m_hWnd));
if( iIndex < 0 || iIndex >= m_aPanels.GetSize() ) return FALSE;
m_aPanels[iIndex].clrText = clrText;
m_aPanels[iIndex].clrBar1 = clrBar1;
m_aPanels[iIndex].clrBar2 = clrBar2;
_RecalcLayout();
return TRUE;
}
DWORD SetExtendedPanelStyle(DWORD dwStyle)
{
ATLASSERT(::IsWindow(m_hWnd));
DWORD dwOldStyle = m_dwExtStyle;
m_dwExtStyle = dwStyle;
Invalidate();
return dwOldStyle;
}
void SetAnimateTime(DWORD dwAnimateTime)
{
m_dwAnimateTime = dwAnimateTime;
}
void SetExpandBitmaps(UINT nExpand, UINT nCollapse)
{
ATLASSERT(m_iconCollapse.IsNull());
m_iconExpand.LoadIcon(nExpand);
m_iconCollapse.LoadIcon(nCollapse);
m_bOwnCollapseIcons = true;
ATLASSERT(!m_iconExpand.IsNull());
ATLASSERT(!m_iconCollapse.IsNull());
}
void SetExpandBitmaps(HICON hicoExpand, HICON hicoCollapse)
{
ATLASSERT(m_iconCollapse.IsNull());
ATLASSERT(hicoExpand!=NULL);
ATLASSERT(hicoCollapse!=NULL);
m_iconExpand = hicoExpand;
m_iconCollapse = hicoCollapse;
m_bOwnCollapseIcons = false;
}
CImageList SetImageList(HIMAGELIST hImages, int nImageList)
{
CImageList hOldImages;
switch( nImageList ) {
case LVSIL_NORMAL:
hOldImages = m_Images;
m_Images = hImages;
break;
case LVSIL_STATE:
hOldImages = m_ImagesGrey;
m_ImagesGrey = hImages;
break;
default:
ATLASSERT(false);
}
_RecalcLayout();
return hOldImages;
}
void SetMargins(int cx, int cy)
{
ATLASSERT(::IsWindow(m_hWnd));
m_szMargin.cx = cx;
m_szMargin.cy = cy;
_RecalcLayout();
}
int GetPadding() const
{
return m_cyPadding;
}
void SetPadding(int iPadding)
{
ATLASSERT(::IsWindow(m_hWnd));
m_cyPadding = iPadding;
_RearrangePanels();
_RecalcScrollBar();
}
PANELTHEME GetPanelTheme() const
{
return m_Theme;
}
void SetPanelTheme(const PANELTHEME Theme)
{
ATLASSERT(::IsWindow(m_hWnd));
m_Theme = Theme;
_RecalcLayout();
}
RECT GetPanelRect(int iIndex, BOOL bIncludeButton) const
{
ATLASSERT(::IsWindow(m_hWnd));
RECT rc = { 0 };
if( iIndex < 0 || iIndex >= m_aPanels.GetSize() ) return rc;
GetClientRect(&rc);
::OffsetRect(&rc, 0, -GetScrollPos(SB_VERT));
::InflateRect(&rc, -m_szMargin.cx, -m_szMargin.cy);
for( int i = 0; i <= iIndex; i++ ) {
if( m_aPanels[i].iImage >= 0 ) rc.top += m_cyButton - m_cyBar;
rc.top += m_cyBar;
if( i == iIndex ) {
rc.bottom = rc.top + m_aPanels[i].cy;
if( bIncludeButton ) rc.top -= m_cyButton;
if( !bIncludeButton && (GetStyle() & WS_BORDER) != 0 ) ::InflateRect(&rc, -1, -1);
return rc;
}
rc.top += m_aPanels[i].cy + m_cyPadding;
}
ATLASSERT(false);
return rc;
}
// Implementation
void _Init()
{
ATLASSERT(::IsWindow(m_hWnd));
ATLASSERT(GetStyle() & WS_CLIPCHILDREN);
m_iCurSel = -1;
m_dwExtStyle = 0;
m_cxLastWidth = 0;
m_cyLastHeight = 0;
CFontHandle font = GetFont();
if( font.IsNull() ) font = AtlGetDefaultGuiFont();
LOGFONT lf;
font.GetLogFont(&lf);
lf.lfWeight += FW_BOLD;
if( !m_fntBold.IsNull() ) m_fntBold.DeleteObject();
m_fntBold.CreateFontIndirect(&lf);
SetFont(m_fntBold);
m_szMargin.cx = 10;
m_szMargin.cy = 12;
m_cyPadding = 8;
m_cyButton = 12;
m_cyBar = 12;
m_dwAnimateTime = 200;
m_Theme.clrBack = RGB(195,199,211);
m_Theme.clrTextActive = RGB(0,0,128);
m_Theme.clrTextInactive = RGB(120,120,120);
m_Theme.clrTextSel = RGB(255,255,255);
m_Theme.clrBarActive1 = RGB(255,255,255);
m_Theme.clrBarActive2 = RGB(214,215,224);
m_Theme.clrBarInactive1 = RGB(245,245,245);
m_Theme.clrBarInactive2 = RGB(184,184,184);
m_Theme.clrBarSel1 = RGB(45,120,245);
m_Theme.clrBarSel2 = RGB(128,128,128);
m_Theme.clrBorder = RGB(0,0,0);
m_Theme.iArc = 10;
}
void _RecalcLayout()
{
// No need to do this before first WM_SIZE is seen
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -