📄 propertygrid.h
字号:
// Hmm, this would be an awfully slooow method!
// So most of the time we're really searching for the in-place editor...
if( m_iInplaceRow != -1 ) {
IProperty** props = reinterpret_cast<IProperty**>(TBase::GetItemData(m_iInplaceRow));
if( props ) {
for( int i = 0; i < m_nColumns; i++ ) {
if( props[i] == prop ) {
iItem = m_iInplaceRow;
iSubItem = i;
return TRUE;
}
}
}
}
// Ok, scan all items...
iItem = GetNextItem(-1, LVNI_ALL);
while( iItem != -1 ) {
IProperty** props = reinterpret_cast<IProperty**>(TBase::GetItemData(iItem));
for( int i = 0; i < m_nColumns; i++ ) {
if( props[i] == prop ) {
iSubItem = i;
return TRUE;
}
}
iItem = GetNextItem(iItem, LVNI_ALL);
}
return FALSE;
}
IProperty* GetProperty(int iRow, int iCol) const
{
ATLASSERT(iCol >= 0 && iCol < m_nColumns);
ATLASSERT(iRow >= 0 && iRow < TBase::GetItemCount());
if( iCol < 0 || iCol >= m_nColumns ) return NULL;
if( iRow < 0 || iRow >= TBase::GetItemCount() ) return NULL;
IProperty** props = reinterpret_cast<IProperty**>(TBase::GetItemData(iRow));
ATLASSERT(props);
if( props == NULL ) return NULL;
IProperty* prop = props[iCol];
ATLASSERT(prop); // If you hit this assert, most likely you forgot to add properties to all subitems.
// Use read-only properties to fill out with dummies.
return prop;
}
LPARAM GetItemData(HPROPERTY hProp) const
{
ATLASSERT(::IsWindow(m_hWnd));
ATLASSERT(hProp);
if( hProp == NULL ) return 0;
return hProp->GetItemData();
}
void SetItemData(HPROPERTY hProp, LPARAM lParam)
{
ATLASSERT(::IsWindow(m_hWnd));
ATLASSERT(hProp);
if( hProp == NULL ) return;
hProp->SetItemData(lParam);
}
BOOL GetItemEnabled(HPROPERTY hProp) const
{
ATLASSERT(hProp);
if( hProp == NULL ) return FALSE;
return hProp->IsEnabled();
}
void SetItemEnabled(HPROPERTY hProp, BOOL bEnable)
{
ATLASSERT(::IsWindow(m_hWnd));
ATLASSERT(hProp);
if( hProp == NULL ) return;
hProp->SetEnabled(bEnable);
// Repaint item...
int nItem;
int nSubItem;
if( !FindProperty(hProp, nItem, nSubItem) ) return;
_InvalidateItem(nItem, nSubItem);
}
void Navigate(UINT wCode)
{
SendMessage(WM_USER_PROP_NAVIGATE, wCode);
}
// Unsupported methods
BOOL SetItem(int /*nItem*/, int /*nSubItem*/, UINT /*nMask*/, LPCTSTR /*lpszItem*/,
int /*nImage*/, UINT /*nState*/, UINT /*nStateMask*/, LPARAM /*lParam*/)
{
ATLASSERT(false);
return FALSE;
}
BOOL SetItemText(int /*nItem*/, int /*nSubItem*/, LPCTSTR /*lpszText*/)
{
ATLASSERT(false);
return FALSE;
}
DWORD SetViewType(DWORD /*dwType*/)
{
ATLASSERT(false);
return FALSE;
}
CEdit EditLabel(int /*nItem*/)
{
ATLASSERT(false);
return NULL;
}
// Implementation
void _Init()
{
ATLASSERT(::IsWindow(m_hWnd));
// We need the LVS_SINGLESEL style
ATLASSERT((GetStyle() & (LVS_SINGLESEL))==(LVS_SINGLESEL));
ATLASSERT((GetStyle() & (LVS_EDITLABELS|LVS_OWNERDRAWFIXED|LVS_OWNERDATA))==0);
// Prepare ListView control
TBase::SetViewType(LVS_REPORT);
SetExtendedListViewStyle(LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_EX_TRACKSELECT|LVS_EX_HEADERDRAGDROP|LVS_EX_FLATSB);
m_ctrlHeader = GetHeader();
// Update colours and text
SendMessage(WM_SETTINGCHANGE);
}
BOOL _SpawnInplaceWindow(IProperty* prop, int iItem, int iSubItem)
{
ATLASSERT(prop);
ATLASSERT(iItem!=-1);
ATLASSERT(iSubItem!=-1);
// Destroy old in-place editor
_DestroyInplaceWindow();
// Do we need an editor here?
if( iItem == -1 || iSubItem == -1 || iItem != GetSelectedIndex() ) return FALSE;
if( !prop->IsEnabled() ) return FALSE;
// Create a new editor window
RECT rcValue;
_GetSubItemRect(iItem, iSubItem, &rcValue);
rcValue.right--; // Let the borders
rcValue.bottom--; // display (right/bottom)
m_hwndInplace = prop->CreateInplaceControl(m_hWnd, rcValue);
if( m_hwndInplace ) {
// Activate the new editor window
ATLASSERT(::IsWindow(m_hwndInplace));
::SetWindowPos(m_hwndInplace, HWND_TOP, 0,0,0,0, SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOSIZE);
::SetFocus(m_hwndInplace);
m_iInplaceRow = iItem;
m_iInplaceCol = iSubItem;
return TRUE;
}
return m_hwndInplace != NULL;
}
void _DestroyInplaceWindow()
{
if( ::IsWindow(m_hwndInplace) ) {
IProperty* prop = GetProperty(m_iInplaceRow, m_iInplaceCol);
ATLASSERT(prop);
BYTE bKind = prop->GetKind();
// Set focus back to our control
if( ::GetFocus() != m_hWnd && IsChild(::GetFocus()) ) SetFocus();
// Destroy control
switch( bKind ) {
case PROPKIND_CONTROL:
::DestroyWindow(m_hwndInplace);
break;
default:
::PostMessage(m_hwndInplace, WM_CLOSE, 0,0);
}
}
m_hwndInplace = NULL;
m_iInplaceRow = -1;
m_iInplaceCol = -1;
}
void _GetSubItemRect(int iItem, int iSubItem, RECT* pRect) const
{
if( iSubItem == 0 && _IsLastAddItem(iItem) ) {
GetItemRect(iItem, pRect, LVIR_BOUNDS);
}
else if( iSubItem == 0 ) {
GetItemRect(iItem, pRect, LVIR_BOUNDS);
if( m_nColumns > 1 ) {
RECT rcSecondColumn;
GetSubItemRect(iItem, 1, LVIR_BOUNDS, &rcSecondColumn);
pRect->right = rcSecondColumn.left - 1;
}
}
else {
GetSubItemRect(iItem, iSubItem, LVIR_BOUNDS, pRect);
}
}
void _InvalidateItem(int iItem, int iSubItem)
{
if( iItem == -1 || iSubItem == -1 ) return;
RECT rc;
_GetSubItemRect(iItem, iSubItem, &rc);
InvalidateRect(&rc);
}
bool _IsValidSelection() const
{
ATLASSERT(m_iSelectedRow==GetSelectedIndex()); // Should be in sync!
return (m_iSelectedRow != -1) && (m_iSelectedCol != -1);
}
bool _IsLastAddItem(int iItem) const
{
return (m_di.dwExtStyle & PGS_EX_ADDITEMATEND) != 0 &&
(iItem == TBase::GetItemCount()-1);
}
// Message map and handlers
BEGIN_MSG_MAP(CPropertyGridImpl)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus)
MESSAGE_HANDLER(WM_SIZE, OnScroll)
MESSAGE_HANDLER(WM_VSCROLL, OnScroll)
MESSAGE_HANDLER(WM_HSCROLL, OnScroll)
MESSAGE_HANDLER(WM_MOUSEWHEEL, OnScroll)
MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
MESSAGE_HANDLER(WM_CHAR, OnChar)
MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode)
MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDown)
MESSAGE_HANDLER(WM_USER_PROP_NAVIGATE, OnNavigate);
MESSAGE_HANDLER(WM_USER_PROP_UPDATEPROPERTY, OnUpdateProperty);
MESSAGE_HANDLER(WM_USER_PROP_CANCELPROPERTY, OnCancelProperty);
MESSAGE_HANDLER(WM_USER_PROP_CHANGEDPROPERTY, OnChangedProperty);
NOTIFY_CODE_HANDLER(HDN_ITEMCHANGEDA, OnHeaderChanging)
NOTIFY_CODE_HANDLER(HDN_ITEMCHANGEDW, OnHeaderChanging)
NOTIFY_CODE_HANDLER(HDN_ITEMCHANGINGA, OnHeaderChanging) // See Q183258 why we all need these
NOTIFY_CODE_HANDLER(HDN_ITEMCHANGINGW, OnHeaderChanging)
NOTIFY_CODE_HANDLER(HDN_TRACKA, OnHeaderChanging)
NOTIFY_CODE_HANDLER(HDN_TRACKW, OnHeaderChanging)
NOTIFY_CODE_HANDLER(HDN_DIVIDERDBLCLICKA, OnHeaderDblClick)
NOTIFY_CODE_HANDLER(HDN_DIVIDERDBLCLICKW, OnHeaderDblClick)
REFLECTED_NOTIFY_CODE_HANDLER(LVN_ITEMCHANGED, OnSelChanged)
REFLECTED_NOTIFY_CODE_HANDLER(LVN_DELETEITEM, OnDeleteItem)
CHAIN_MSG_MAP_ALT(CCustomDraw<CPropertyGridImpl>, 1)
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
LRESULT lRes = DefWindowProc();
_Init();
return lRes;
}
LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
DeleteAllItems(); // Make sure we clean up all items...
return 0;
}
LRESULT OnFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
// Avoid focus-rectangle problem in ownerdrawn ListView
_InvalidateItem(m_iSelectedRow, m_iSelectedCol);
bHandled = FALSE;
return 0;
}
LRESULT OnScroll(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
// HACK: When scrolling we need to destroy the in-place editor
// because the cursor would become out-of-sync...
_DestroyInplaceWindow();
bHandled = FALSE;
return 0;
}
LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
// Repaint previous item in any case
_InvalidateItem(m_iSelectedRow, m_iSelectedCol);
// Gather information about clicked item.
// We need this information before the control processes the
// actual click event...
LVHITTESTINFO hti = { 0 };
DWORD dwPos = ::GetMessagePos();
POINTSTOPOINT(hti.pt, dwPos);
ScreenToClient(&hti.pt);
int iItem = SubItemHitTest(&hti);
int iOldRow = m_iSelectedRow;
int iOldColumn = m_iSelectedCol;
// FIX: To prevent selection from jumping to colunm 0 since
// the DefWindowProc() below invokes LVN_ITEMCHANGED, which
// in turn sets the selection to the correct row (but not column)!
m_iSelectedCol = hti.iSubItem;
_DestroyInplaceWindow();
// Let control process WM_LBUTTONDOWN event!
// This may cause LVN_ITEMCHANGED to fire...
LRESULT lRes = DefWindowProc();
// Check if we've changed selection.
// We delayed this to now, because we need to check
// if we clicked on the same item...
if( iItem != -1 ) {
// Do click thing
bool bIsLastAddItem = _IsLastAddItem(hti.iItem);
if( bIsLastAddItem ) hti.iSubItem = 0;
IProperty* prop = GetProperty(hti.iItem, hti.iSubItem);
if( prop == NULL ) return 0;
// Ask owner first
NMPROPERTYITEM nmh = { m_hWnd, GetDlgCtrlID(), PIN_CLICK, prop };
if( ::SendMessage(GetParent(), WM_NOTIFY, nmh.hdr.idFrom, (LPARAM) &nmh) == 0 ) {
// Property is allowed to react on click event
LPARAM lParam = ::GetMessagePos();
prop->Activate(PACT_CLICK, lParam);
// Set new selection...
m_iSelectedRow = hti.iItem;
m_iSelectedCol = hti.iSubItem;
if( !bIsLastAddItem ) {
// Generate selection change notification on pure horizontal moves
if( (iOldRow == m_iSelectedRow) && (iOldColumn != m_iSelectedCol) ) {
// Let owner know
NMPROPERTYITEM nmh = { m_hWnd, GetDlgCtrlID(), PIN_SELCHANGED, prop };
::SendMessage(GetParent(), WM_NOTIFY, nmh.hdr.idFrom, (LPARAM) &nmh);
}
// Recycle in-place editor...
if( prop->IsEnabled() ) {
bool fActivate = false;
if( iOldRow == m_iSelectedRow && iOldColumn == m_iSelectedCol ) fActivate = true;
if( (m_di.dwExtStyle & PGS_EX_SINGLECLICKEDIT) != 0 ) fActivate = true;
if( fActivate ) {
if( _SpawnInplaceWindow(prop, m_iSelectedRow, m_iSelectedCol) ) {
prop->Activate(PACT_SPACE, 0);
}
else {
prop->Activate(PACT_ACTIVATE, 0);
}
}
}
}
// Repaint item
_InvalidateItem(m_iSelectedRow, m_iSelectedCol);
}
}
else {
// Clicked outside list elements; remove selection...
_DestroyInplaceWindow();
_InvalidateItem(m_iSelectedRow, m_iSelectedCol);
m_iSelectedRow = m_iSelectedCol = -1;
// Let owner know
NMPROPERTYITEM nmh = { m_hWnd, GetDlgCtrlID(), PIN_SELCHANGED, NULL };
::SendMessage(GetParent(), WM_NOTIFY, nmh.hdr.idFrom, (LPARAM) &nmh);
}
return lRes;
}
LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
{
switch( wParam ) {
case VK_F2:
case VK_SPACE:
case VK_RETURN:
if( _IsValidSelection() ) {
IProperty* prop = GetProperty(m_iSelectedRow, m_iSelectedCol);
ATLASSERT(prop);
if( prop->IsEnabled() ) {
_SpawnInplaceWindow(prop, m_iSelectedRow, m_iSelectedCol);
prop->Activate(PACT_SPACE, 0);
_InvalidateItem(m_iSelectedRow, m_iSelectedCol);
}
}
return 0;
case VK_TAB:
if( (m_di.dwExtStyle & PGS_EX_TABNAVIGATION) != 0 ) {
SendMessage(WM_USER_PROP_NAVIGATE, VK_TAB);
}
else {
::PostMessage(GetParent(), WM_NEXTDLGCTL, ::GetKeyState(VK_SHIFT) < 0 ? 1 : 0, (LPARAM) FALSE);
}
return 0;
break;
case VK_LEFT:
case VK_RIGHT:
SendMessage(WM_USER_PROP_NAVIGATE, wParam);
return 0;
case VK_DELETE:
if( _IsValidSelection() ) {
IProperty* prop = GetProperty(m_iSelectedRow, m_iSelectedCol);
ATLASSERT(prop);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -