📄 propertygrid.h
字号:
if( prop->GetKind() == PROPKIND_EDIT &&
prop->IsEnabled() )
{
if( _SpawnInplaceWindow(prop, m_iSelectedRow, m_iSelectedCol) ) {
prop->Activate(PACT_SPACE, 0);
// Simulate typing in the inplace editor...
::SendMessage(m_hwndInplace, WM_KEYDOWN, wParam, 0L);
_InvalidateItem(m_iSelectedRow, m_iSelectedCol);
}
}
}
return 0;
}
bHandled = FALSE;
return 0;
}
LRESULT OnChar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
{
// If the user is typing stuff, we should spawn an editor right away
// and simulate the keypress in the editor-window...
if( wParam > _T(' ') ) {
if( _IsValidSelection() ) {
IProperty* prop = GetProperty(m_iSelectedRow, m_iSelectedCol);
ATLASSERT(prop);
if( prop->IsEnabled() ) {
if( _SpawnInplaceWindow(prop, m_iSelectedRow, m_iSelectedCol) ) {
prop->Activate(PACT_SPACE, 0);
// Simulate typing in the inplace editor...
::SendMessage(m_hwndInplace, WM_CHAR, wParam, 1L);
_InvalidateItem(m_iSelectedRow, m_iSelectedCol);
}
}
return 0;
}
}
if( wParam == _T(' ') ) return 0;
bHandled = FALSE;
return 0;
}
LRESULT OnGetDlgCode(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
return DefWindowProc() | DLGC_WANTALLKEYS;
}
LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
// Standard colors
m_di.clrText = ::GetSysColor(COLOR_WINDOWTEXT);
m_di.clrBack = ::GetSysColor(COLOR_WINDOW);
m_di.clrSelText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
m_di.clrSelBack = ::GetSysColor(COLOR_HIGHLIGHT);
m_di.clrDisabled = ::GetSysColor(COLOR_GRAYTEXT);
m_di.clrDisabledBack = ::GetSysColor(COLOR_BTNFACE);
// Border
m_di.clrBorder = ::GetSysColor(COLOR_BTNFACE);
if( !m_BorderPen.IsNull() ) m_BorderPen.DeleteObject();
m_di.Border = m_BorderPen.CreatePen(PS_SOLID, 1, m_di.clrBorder);
// Fonts
if( !m_TextFont.IsNull() ) m_TextFont.DeleteObject();
if( !m_CategoryFont.IsNull() ) m_CategoryFont.DeleteObject();
LOGFONT lf;
HFONT hFont = (HFONT) ::SendMessage(GetParent(), WM_GETFONT, 0, 0);
if( hFont == NULL ) hFont = AtlGetDefaultGuiFont();
::GetObject(hFont, sizeof(lf), &lf);
m_di.TextFont = m_TextFont.CreateFontIndirect(&lf);
SetFont(m_di.TextFont);
lf.lfWeight += FW_BOLD;
m_di.CategoryFont = m_CategoryFont.CreateFontIndirect(&lf);
// Text metrics
CClientDC dc(m_hWnd);
HFONT hOldFont = dc.SelectFont(m_di.TextFont);
dc.GetTextMetrics(&m_di.tmText);
dc.SelectFont(hOldFont);
// Repaint
Invalidate();
return 0;
}
LRESULT OnSelChanged(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
{
LPNMLISTVIEW pnmlv = (LPNMLISTVIEW) pnmh;
// Only items with selection changes...
if( (pnmlv->uNewState & LVIS_SELECTED) == 0 ) return 0;
// Process changed item
int iItem = pnmlv->iItem;
if( iItem != -1 ) {
m_iSelectedRow = iItem;
if( m_iSelectedCol == -1 ) m_iSelectedCol = 0;
if( _IsLastAddItem(iItem) ) m_iSelectedCol = 0;
_InvalidateItem(m_iSelectedRow, m_iSelectedCol);
// Let owner know
IProperty* prop = GetProperty(m_iSelectedRow, m_iSelectedCol);
ATLASSERT(prop);
NMPROPERTYITEM nmh = { m_hWnd, GetDlgCtrlID(), PIN_SELCHANGED, prop };
::SendMessage(GetParent(), WM_NOTIFY, nmh.hdr.idFrom, (LPARAM) &nmh);
}
bHandled = FALSE;
return 0;
}
LRESULT OnDeleteItem(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/)
{
ATLASSERT(m_nColumns>0);
LPNMLISTVIEW pnmlv = (LPNMLISTVIEW) pnmh;
// Destroy in-place editor if active
_DestroyInplaceWindow();
if( pnmlv->iItem == m_iSelectedRow && m_iSelectedRow != -1 ) SelectItem(-1, -1);
// Free sub-item place-holder array
ATLASSERT(pnmlv->lParam);
IProperty** props = reinterpret_cast<IProperty**>(pnmlv->lParam);
for( int i = 0; i < m_nColumns; i++ ) if( props[i] ) delete props[i];
delete [] props;
return 0;
}
LRESULT OnNavigate(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
if( (m_di.dwExtStyle & PGS_EX_NOSHEETNAVIGATION) != 0 ) return 0;
if( !_IsValidSelection() ) return 0;
// No editor, please
_DestroyInplaceWindow();
// Navigate in the grid control
switch( wParam) {
case VK_TAB:
SendMessage(WM_USER_PROP_NAVIGATE, VK_RIGHT);
break;
case VK_LEFT:
if( m_iSelectedRow >= 0 && m_iSelectedCol > 0 ) {
// Can we navigate?
if( _IsLastAddItem(m_iSelectedRow ) ) return 0;
// Navigate
m_iSelectedCol--;
// Let owner know
IProperty* prop = GetProperty(m_iSelectedRow, m_iSelectedCol);
ATLASSERT(prop);
NMPROPERTYITEM nmh = { m_hWnd, GetDlgCtrlID(), PIN_SELCHANGED, prop };
::SendMessage(GetParent(), WM_NOTIFY, nmh.hdr.idFrom, (LPARAM) &nmh);
// Repaint new and old item
_InvalidateItem(m_iSelectedRow, m_iSelectedCol + 1);
_InvalidateItem(m_iSelectedRow, m_iSelectedCol);
}
break;
case VK_RIGHT:
if( m_iSelectedRow >= 0 && m_iSelectedCol < m_nColumns - 1 ) {
// Can we navigate?
if( _IsLastAddItem(m_iSelectedRow ) ) return 0;
// Navigate
m_iSelectedCol++;
// Let owner know
IProperty* prop = GetProperty(m_iSelectedRow, m_iSelectedCol);
ATLASSERT(prop);
NMPROPERTYITEM nmh = { m_hWnd, GetDlgCtrlID(), PIN_SELCHANGED, prop };
::SendMessage(GetParent(), WM_NOTIFY, nmh.hdr.idFrom, (LPARAM) &nmh);
// Repaint new and old item
_InvalidateItem(m_iSelectedRow, m_iSelectedCol - 1);
_InvalidateItem(m_iSelectedRow, m_iSelectedCol);
}
break;
case VK_UP:
if( m_iSelectedRow > 0 ) SelectItem(m_iSelectedRow - 1, m_iSelectedCol);
break;
case VK_DOWN:
if( m_iSelectedRow < GetItemCount() - 1 ) SelectItem(m_iSelectedRow + 1, m_iSelectedCol);
break;
}
return 0;
}
LRESULT OnUpdateProperty(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
{
// Updates a property value using an active editor window.
// The editor window uses this message to update the attached property class.
// Parameters:
// LPARAM = Window (HWND)
HWND hWnd = reinterpret_cast<HWND>(lParam);
ATLASSERT(hWnd);
if( !::IsWindow(hWnd) || (m_iInplaceRow == -1) || (m_iInplaceCol == -1) ) return 0;
ATLASSERT(hWnd==m_hwndInplace);
IProperty* prop = GetProperty(m_iInplaceRow, m_iInplaceCol);
if( prop == NULL ) return 0;
// Ask owner about change
NMPROPERTYITEM nmh = { m_hWnd, GetDlgCtrlID(), PIN_ITEMCHANGING, prop };
if( ::SendMessage(GetParent(), WM_NOTIFY, nmh.hdr.idFrom, (LPARAM) &nmh) == 0 ) {
// Set new value
if( !prop->SetValue(hWnd) ) ::MessageBeep((UINT)-1);
// Let owner know
nmh.hdr.code = PIN_ITEMCHANGED;
::SendMessage(GetParent(), WM_NOTIFY, nmh.hdr.idFrom, (LPARAM) &nmh);
// Repaint item
_InvalidateItem(m_iInplaceRow, m_iInplaceCol);
}
// Replace in-place control...
_DestroyInplaceWindow();
return 0;
}
LRESULT OnCancelProperty(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
{
// Restores a property value using an active editor window.
// The editor window uses this message to restore the attached property class
// with it original value.
// Parameters:
// LPARAM = Window (HWND)
HWND hWnd = reinterpret_cast<HWND>(lParam);
ATLASSERT(hWnd);
if( !::IsWindow(hWnd) || (m_iInplaceRow == -1) || (m_iInplaceCol == -1) ) return 0;
ATLASSERT(hWnd==m_hwndInplace);
// NOTE: The list-item already has the original value; it has only been
// obscured by the editor window which it was shown. All we need
// to do, is to repaint the list control.
// Repaint item
_InvalidateItem(m_iInplaceRow, m_iInplaceCol);
// Replace in-place control...
_DestroyInplaceWindow();
return 0;
}
LRESULT OnChangedProperty(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
// Updates a property value.
// A property class uses this message to make sure the corresponding editor window
// is updated as well.
// Parameters:
// WPARAM = New value (VARIANT*)
// LPARAM = Property (IProperty*)
IProperty* prop = reinterpret_cast<IProperty*>(lParam);
VARIANT* pVariant = reinterpret_cast<VARIANT*>(wParam);
ATLASSERT(prop && pVariant);
if( prop == NULL || pVariant == NULL ) return 0;
// Ask owner about change
NMPROPERTYITEM nmh = { m_hWnd, GetDlgCtrlID(), PIN_ITEMCHANGING, prop };
if( ::SendMessage(GetParent(), WM_NOTIFY, nmh.hdr.idFrom, (LPARAM) &nmh) == 0 ) {
// Set new value
// NOTE: Do not call this from IProperty::SetValue(VARIANT*) = endless loop
if( !prop->SetValue(*pVariant) ) ::MessageBeep((UINT)-1);
// Let owner know
nmh.hdr.code = PIN_ITEMCHANGED;
::SendMessage(GetParent(), WM_NOTIFY, nmh.hdr.idFrom, (LPARAM) &nmh);
}
// Locate the updated list item
int iItem;
int iSubItem;
if( !FindProperty(prop, iItem, iSubItem) ) return 0;
// Repaint item
_InvalidateItem(iItem, iSubItem);
// Recycle in-place control so it displays the new value
if( (iItem != -1) && (iItem == m_iInplaceRow) ) _SpawnInplaceWindow(prop, m_iInplaceRow, m_iInplaceCol);
return 0;
}
// Header messages
LRESULT OnHeaderChanging(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& bHandled)
{
// HACK: With the PGS_EX_ADDITEMATEND style we need to
// repaint the last item to avoid a paint bug.
// Code supplied by Ludvig A. Norin.
if( (m_di.dwExtStyle & PGS_EX_ADDITEMATEND) != 0 ) {
RECT rcItem = { 0 };
GetItemRect(GetItemCount(), &rcItem, LVIR_BOUNDS);
InvalidateRect(&rcItem, FALSE);
}
// Destroy in-place window before dragging header control
_DestroyInplaceWindow();
// Done
bHandled = FALSE;
return 0;
}
LRESULT OnHeaderDblClick(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
{
// TODO
return 0;
}
// Custom painting
DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)
{
return CDRF_NOTIFYITEMDRAW; // We need per-item notifications
}
DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)
{
return CDRF_NOTIFYSUBITEMDRAW; // We need per-subitem notifications
}
DWORD OnSubItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
{
T* pT = static_cast<T*>(this);
LPNMLVCUSTOMDRAW lpNMLVCustomDraw = (LPNMLVCUSTOMDRAW) lpNMCustomDraw;
pT->DrawItem(lpNMLVCustomDraw->nmcd.hdc,
(int) lpNMLVCustomDraw->nmcd.dwItemSpec,
lpNMLVCustomDraw->iSubItem,
lpNMLVCustomDraw->nmcd.uItemState);
return CDRF_SKIPDEFAULT;
}
// Overridables
void DrawItem(CDCHandle dc, int iItem, int iSubItem, UINT iState)
{
bool bIsLastItem = _IsLastAddItem(iItem);
if( bIsLastItem && iSubItem != 0 ) return;
IProperty* prop = GetProperty(iItem, iSubItem);
if( prop == NULL ) return;
// Figure out rectangle
RECT rc;
_GetSubItemRect(iItem, iSubItem, &rc);
// Draw properties
PROPERTYDRAWINFO di = m_di;
di.hDC = dc;
di.rcItem = rc;
di.state = 0;
if( iItem == m_iSelectedRow && iSubItem == m_iSelectedCol ) di.state |= ODS_SELECTED;
if( iItem == m_iInplaceRow && iSubItem == m_iInplaceCol ) di.state |= ODS_COMBOBOXEDIT;
if( !prop->IsEnabled() ) di.state |= ODS_DISABLED;
if( iItem == m_iSelectedRow ) {
// Full row-select feature
if( (m_di.dwExtStyle & PGS_EX_FULLROWSELECT) != 0 ) {
di.clrBack = di.clrDisabledBack;
}
// Is this the selected sub-item
if( iSubItem == m_iSelectedCol ) {
di.state |= ODS_SELECTED;
// Selection inverted feature
if( (m_di.dwExtStyle & PGS_EX_INVERTSELECTION) != 0 ) {
di.clrBack = di.clrSelBack;
di.clrText = di.clrSelText;
}
}
}
// A disabled ListView control is painted grey on Win2000
// but the text remains black...
DWORD dwStyle = GetStyle();
if( dwStyle & WS_DISABLED ) di.clrBack = di.clrDisabledBack;
// Erase background
dc.FillSolidRect(&rc, di.clrBack);
// Draw item
HFONT hOldFont = dc.SelectFont(di.TextFont);
prop->DrawValue(di);
dc.SelectFont(hOldFont);
// Draw borders
if( (di.dwExtStyle & PGS_EX_NOGRID) == 0 ) {
CPen pen;
pen.CreatePen(PS_SOLID, 1, di.clrBorder);
HPEN hOldPen = dc.SelectPen(pen);
dc.MoveTo(rc.left, rc.bottom-1);
dc.LineTo(rc.right - 1, rc.bottom - 1);
dc.LineTo(rc.right - 1, rc.top - 1);
dc.SelectPen(hOldPen);
}
// Paint focus rectangle
if( (::GetFocus() == m_hWnd && (iState & CDIS_FOCUS) != 0) || (dwStyle & LVS_SHOWSELALWAYS) != 0 ) {
if( (iItem == m_iSelectedRow) && (iSubItem == m_iSelectedCol || bIsLastItem ) ) {
if( iItem != m_iInplaceRow && iSubItem != m_iInplaceCol ) {
if( (GetExtendedListViewStyle() & LVS_EX_GRIDLINES) != 0 ) {
rc.left++; rc.right--; rc.bottom--;
}
CBrush brush;
brush.CreateSolidBrush(di.clrSelBack);
dc.FrameRect(&rc, brush);
}
}
}
}
};
class CPropertyGridCtrl : public CPropertyGridImpl<CPropertyGridCtrl>
{
public:
DECLARE_WND_SUPERCLASS(_T("WTL_PropertyGrid"), GetWndClassName())
};
#endif __PROPERTYGRID__H
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -