📄 propertygrid.h
字号:
#ifndef __PROPERTYGRID__H
#define __PROPERTYGRID__H
#pragma once
/////////////////////////////////////////////////////////////////////////////
// CPropertyGrid - A simple grid control
//
// Written by Bjarke Viksoe (bjarke@viksoe.dk)
// Copyright (c) 2002-2003 Bjarke Viksoe.
// Thanks to Ludvig A. Norin for the PGS_EX_ADDITEMATEND feature.
//
// Add the following macro to the parent's message map:
// REFLECT_NOTIFICATIONS()
//
// This code may be used in compiled form in any way you desire. This
// 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 __ATLAPP_H__
#error PropertyGrid.h requires atlapp.h to be included first
#endif
#if (_WTL_VER < 0x0700)
#error This file requires WTL version 7.0 or higher
#endif
#ifndef __ATLCTRLS_H__
#error PropertyGrid.h requires atlctrls.h to be included first
#endif
#if (_WIN32_IE < 0x0400)
#error PropertyGrid.h requires IE4
#endif
#if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400))
#include <zmouse.h>
#endif //!((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400))
// Include property base class
#include "PropertyItem.h"
// Include property implementations
#include "PropertyItemEditors.h"
#include "PropertyItemImpl.h"
/////////////////////////////////////////////////////////////////////////////
// CPropertyGrid control
// Extended Grid styles
#define PGS_EX_SINGLECLICKEDIT 0x00000001
#define PGS_EX_NOGRID 0x00000002
#define PGS_EX_TABNAVIGATION 0x00000004
#define PGS_EX_NOSHEETNAVIGATION 0x00000008
#define PGS_EX_FULLROWSELECT 0x00000010
#define PGS_EX_INVERTSELECTION 0x00000020
#define PGS_EX_ADDITEMATEND 0x00000040
/////////////////////////////////////////////////////////////////////////////
// The "Last item" property (PGS_EX_ADDITEMATEND support)
class CPropertyLastItem : public CProperty
{
public:
CPropertyLastItem(LPCTSTR pstrName, LPARAM lParam) :
CProperty(pstrName, lParam)
{
}
BYTE GetKind() const
{
return PROPKIND_SIMPLE;
}
void DrawValue(PROPERTYDRAWINFO& di)
{
try{
#ifdef IDS_LASTVALUE
TCHAR szText[128] = { 0 };
::LoadString(_Module.GetResourceInstance(), IDS_LASTVALUE, szText, (sizeof(szText)/sizeof(TCHAR))-1);
LPCTSTR pstrText = szText;
#else
LPCTSTR pstrText = _T("<< 单击这里添加新的选项 >>");
#endif
CDCHandle dc(di.hDC);
dc.SetBkMode(TRANSPARENT);
dc.SetTextColor(di.state & ODS_DISABLED ? di.clrDisabled : di.clrText);
dc.SetBkColor(di.clrBack);
RECT rcText = di.rcItem;
rcText.left += PROP_TEXT_INDENT;
dc.DrawText(pstrText, -1,
&rcText,
DT_LEFT | DT_SINGLELINE | DT_EDITCONTROL | DT_NOPREFIX | DT_END_ELLIPSIS | DT_VCENTER);
}
catch (_com_error &e)
{
error("File:%s\nLine:%d\nCode = %08lx\nCode meaning = %s\nSource = %s\nError Description = %s\n",
__FILE__,
__LINE__,
e.Error(),
(TCHAR*) e.ErrorMessage(),
(TCHAR*) e.Source()
, (TCHAR*) e.Description());
}
catch(...)
{
TCHAR szErr[1024];
GetErrDesc(szErr);
error("File:%s \n Line:%d\n Error:%s",__FILE__,__LINE__,szErr);
}
}
BOOL Activate(UINT action, LPARAM /*lParam*/)
{
switch( action ) {
case PACT_SPACE:
case PACT_CLICK:
case PACT_DBLCLICK:
// Send AddItem notification to parent of control
NMPROPERTYITEM nmh = { m_hWndOwner, ::GetDlgCtrlID(m_hWndOwner), PIN_ADDITEM, NULL };
::SendMessage(::GetParent(m_hWndOwner), WM_NOTIFY, nmh.hdr.idFrom, (LPARAM) &nmh);
break;
}
return TRUE;
}
};
inline HPROPERTY PropCreateLastItem(LPCTSTR pstrName, LPARAM lParam = 0)
{
return new CPropertyLastItem(pstrName, lParam);
}
/////////////////////////////////////////////////////////////////////////////
// Block property - looks like a header (or button)
class CPropertyBlockItem : public CProperty
{
public:
CPropertyBlockItem(LPCTSTR pstrName, LPARAM lParam) :
CProperty(pstrName, lParam)
{
}
BYTE GetKind() const
{
return PROPKIND_SIMPLE;
}
void DrawValue(PROPERTYDRAWINFO& di)
{
RECT rc = di.rcItem;
rc.bottom--;
::DrawFrameControl(di.hDC, &rc, DFC_BUTTON, DFCS_BUTTONPUSH);
}
BOOL Activate(UINT action, LPARAM /*lParam*/)
{
switch( action ) {
case PACT_SPACE:
case PACT_CLICK:
case PACT_DBLCLICK:
// Send AddItem notification to parent of control
NMPROPERTYITEM nmh = { m_hWndOwner, ::GetDlgCtrlID(m_hWndOwner), PIN_BROWSE, NULL };
::SendMessage(::GetParent(m_hWndOwner), WM_NOTIFY, nmh.hdr.idFrom, (LPARAM) &nmh);
break;
}
return TRUE;
}
};
inline HPROPERTY PropCreateBlockItem(LPCTSTR pstrName, LPARAM lParam = 0)
{
return new CPropertyBlockItem(pstrName, lParam);
}
/////////////////////////////////////////////////////////////////////////////
// The Property Grid control
template< class T, class TBase = CListViewCtrl, class TWinTraits = CWinTraitsOR<LVS_SINGLESEL|LVS_SHOWSELALWAYS> >
class ATL_NO_VTABLE CPropertyGridImpl :
public CWindowImpl< T, TBase, TWinTraits >,
public CCustomDraw< CPropertyGridImpl >
{
public:
DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
CHeaderCtrl m_ctrlHeader;
PROPERTYDRAWINFO m_di;
CFont m_TextFont;
CFont m_CategoryFont;
CPen m_BorderPen;
HWND m_hwndInplace;
int m_iInplaceRow;
int m_iInplaceCol;
int m_nColumns;
int m_iSelectedRow;
int m_iSelectedCol;
CPropertyGridImpl() :
m_hwndInplace(NULL),
m_iInplaceRow(-1),
m_iInplaceCol(-1),
m_nColumns(0),
m_iSelectedRow(-1),
m_iSelectedCol(-1)
{
m_di.dwExtStyle = 0;
}
// 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;
}
void SetExtendedGridStyle(DWORD dwExtStyle)
{
// Handle change of PGS_EX_ADDITEMATEND flag
if( (m_di.dwExtStyle & PGS_EX_ADDITEMATEND) != 0
&& (dwExtStyle & PGS_EX_ADDITEMATEND) == 0 )
{
// Remove last item
DeleteItem(TBase::GetItemCount()-1);
}
else if( (dwExtStyle & PGS_EX_ADDITEMATEND) != 0 ) {
// Add last item
InsertItem(TBase::GetItemCount(), PropCreateLastItem(_T("")));
}
// Assign new style
m_di.dwExtStyle = dwExtStyle;
// Recalc colours and fonts
SendMessage(WM_SETTINGCHANGE);
}
DWORD GetExtendedGridStyle() const
{
return m_di.dwExtStyle;
}
BOOL SelectItem(int iRow, int iCol = 0)
{
// No editor and remove focus
_DestroyInplaceWindow();
_InvalidateItem(m_iSelectedRow, m_iSelectedCol);
// Select new item. If on same row then use internal update.
m_iSelectedCol = iCol;
if( GetSelectedIndex() == m_iSelectedRow && m_iSelectedRow == iRow ) {
NMLISTVIEW nmlv = { m_hWnd, 0, 0, m_iSelectedRow, m_iSelectedCol, LVIS_SELECTED };
BOOL bDummy;
OnSelChanged(0, reinterpret_cast<LPNMHDR>(&nmlv), bDummy);
return TRUE;
}
else {
return TBase::SelectItem(iRow);
}
}
int GetItemCount() const
{
if( (m_di.dwExtStyle & PGS_EX_ADDITEMATEND) != 0 ) return TBase::GetItemCount()-1;
return TBase::GetItemCount();
}
int InsertItem(int nItem, HPROPERTY hProp)
{
// NOTE: This is the only InsertItem() we support...
ATLASSERT(::IsWindow(m_hWnd));
ATLASSERT(hProp);
// You must have initialized columns before calling this!
// And you are not allowed to add columns once the list is populated!
if( m_nColumns == 0 ) m_nColumns = m_ctrlHeader.GetItemCount();
ATLASSERT(m_nColumns>0);
ATLASSERT(m_ctrlHeader.GetItemCount()==m_nColumns);
// Create a place-holder for all sub-items
IProperty** props;
ATLTRY( props = new IProperty*[m_nColumns] );
ATLASSERT(props);
if( props == NULL ) return -1;
::ZeroMemory(props, sizeof(IProperty*) * m_nColumns);
props[0] = hProp;
// Finally create the listview item itself...
if( nItem < 0 || nItem > GetItemCount() ) nItem = GetItemCount();
UINT mask = LVIF_TEXT | LVIF_PARAM;
int iItem = TBase::InsertItem(mask, nItem, hProp->GetName(), 0, 0, 0, (LPARAM) props);
if( iItem != -1 ) hProp->SetOwner(m_hWnd, NULL);
return iItem;
}
BOOL SetSubItem(int nItem, int nSubItem, HPROPERTY hProp)
{
ATLASSERT(::IsWindow(m_hWnd));
ATLASSERT(hProp);
ATLASSERT(nSubItem >= 0);
ATLASSERT(nSubItem < m_nColumns);
IProperty** props = reinterpret_cast<IProperty**>(TBase::GetItemData(nItem));
ATLASSERT(props);
ATLASSERT(props[nSubItem]==NULL); // Do not replace HPROPERTY nodes.
if( props == NULL ) return FALSE;
if( nSubItem < 0 || nSubItem >= m_nColumns ) return FALSE;
props[nSubItem] = hProp;
hProp->SetOwner(m_hWnd, NULL);
// Trick ListView into thinking there is a subitem...
return TBase::SetItemText(nItem, nSubItem, _T(""));
}
BOOL GetItemText(int iItem, int iSubItem, LPTSTR pstrText, UINT cchMax) const
{
return GetItemText(GetProperty(iItem, iSubItem), pstrText, cchMax);
}
BOOL GetItemText(HPROPERTY hProp, LPTSTR pstrText, UINT cchMax) const
{
ATLASSERT(::IsWindow(m_hWnd));
ATLASSERT(hProp);
ATLASSERT(!::IsBadWritePtr(pstrText,cchMax));
if( hProp == NULL || pstrText == NULL ) return FALSE;
return hProp->GetDisplayValue(pstrText, cchMax);
}
BOOL GetItemValue(HPROPERTY hProp, VARIANT* pValue) const
{
ATLASSERT(::IsWindow(m_hWnd));
ATLASSERT(hProp);
ATLASSERT(pValue);
if( hProp == NULL || pValue == NULL ) return FALSE;
return hProp->GetValue(pValue);
}
BOOL SetItemValue(HPROPERTY hProp, VARIANT* pValue)
{
ATLASSERT(::IsWindow(m_hWnd));
ATLASSERT(hProp);
ATLASSERT(pValue);
if( hProp == NULL || pValue == NULL ) return FALSE;
// Assign value and repaint
BOOL bRes = hProp->SetValue(*pValue);
// Find property position and repaint it...
int nItem;
int nSubItem;
if( !FindProperty(hProp, nItem, nSubItem) ) return FALSE;
_InvalidateItem(nItem, nSubItem);
// If changing selected item then recreate in-place editor
if( (m_hwndInplace != NULL) && (nItem == m_iInplaceRow) && (nSubItem == m_iInplaceCol) ) _SpawnInplaceWindow(hProp, m_iInplaceRow, m_iInplaceCol);
return bRes;
}
int GetSelectedColumn() const
{
ATLASSERT(::IsWindow(m_hWnd));
return m_iSelectedCol;
}
BOOL DeleteColumn(int nCol)
{
ATLASSERT(::IsWindow(m_hWnd));
if( TBase::GetItemCount() == 0 ) {
m_nColumns = 0;
return TBase::DeleteColumn(nCol);
}
ATLASSERT(false); // Remove items first
return FALSE;
}
BOOL GetColumnCount() const
{
ATLASSERT(::IsWindow(m_hWnd));
return GetHeader().GetItemCount();
}
BOOL FindProperty(IProperty* prop, int& iItem, int& iSubItem) const
{
// Looks up the item index based on the property class.
// The property class may be a subitem, so we need to scan ALL properties.
ATLASSERT(prop);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -