Чернопятов Е.А. Inplace-редактирование (редактирование "по-месту") в элементе
управления CTreeCtrl
В этой короткой статье речь пойдет об "in-place" редактировании, т.е.
редактирование текста элемента дерева без использования специального диалога.
Так редактируются, например, именa файлов в Проводнике.
Смысл редактирования "на-месте" в том, что Windows создает некий временный
контрол типа EDIT, в котором производится изменение текста элемента. Затем
введенный текст может быть передан в элемент. Порядок работы следующий:
-
перехватить сообщение о начале редактирования
-
выполнить необходимые проверки (например, если нельзя редактировать корень) и
разрешить или не разрешить собственно редактирование
-
Windows сделает всю работу по созданию элемента редактирования
-
пользователь введет новый текст
-
перехватить сообщение об окончании ввода
-
разрешить или запретить внесение изменений в текст элемента
Пример приводится для использования CTreeView. Если вы используете CTreeCtrl
непосредственно, то часть функций будет выглядеть проще.
I. Прежде всего, дерево должно иметь стиль TVS_EDITLABELS. Его можно
установить с помощью Resource Editora (если используется CTreeCtrl),
при создании вида функцией Create или вот так:
void CTreeViewInPlaceEditView::OnInitialUpdate()
{
CTreeView::OnInitialUpdate();
//.................................
LONG TreeStyle = GetWindowLong(GetTreeCtrl().m_hWnd, // handle of window
GWL_STYLE); // offset of value to retrieve
TreeStyle|=TVS_EDITLABELS;
SetWindowLong(GetTreeCtrl().m_hWnd,GWL_STYLE,TreeStyle);
}
II. Далее с помощью ClassWizard-a (или вручную) нам необходимо
перегрузить функции, отвечающие на нотификационные сообщения от CTreeCtrl-a,
посылаемые CTreeView. Это сообщения TVN_BEGINLABELEDIT и TVN_ENDLABELEDIT.
Коротко скажу, что эти сообщения посылаются перед началом и перед окончанием
редактирования. Программист может модифицировать результат, возвращаемый ими,
тем самым влияя на поведение CTreeCtrl
-a.
В хэдер (.h) добавляем:
// Generated message map functions
protected:
//{{AFX_MSG(CLeftView)
// ................................
afx_msg void OnTvnBeginlabeledit(NMHDR *pNMHDR, LRESULT *pResult);
afx_msg void OnTvnEndlabeledit(NMHDR *pNMHDR, LRESULT *pResult);
// ................................
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
В .cpp добавляем:
BEGIN_MESSAGE_MAP(CLeftView, CTreeView)
//{{AFX_MSG_MAP(CLeftView)
//................................
ON_NOTIFY_REFLECT(TVN_BEGINLABELEDIT, OnTvnBeginlabeledit)
ON_NOTIFY_REFLECT(TVN_ENDLABELEDIT, OnTvnEndlabeledit)
//................................
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
III. Теперь собственно реализация функций
-------------------------------------------------------------------
/* Эта функция вызывается системой непосредственно перед
передачей управления элементу редакирования.
Программист имеет возможность запретить или разрешить
редактирование путем модификации возвращаемого значения pResult.
0 - разрешить редактирование, не 0 - запретить
В данном примере запрещается редактировать название корневого элемента. */
void CTreeViewInPlaceEditView::OnTvnBeginlabeledit(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);
CString sRootName;
// предопределенное имя корневого элемента
sRootName = _T("The root name");
// в этом примере мы явно запрещаеи редактировать корневой элемент
if (sRootName.CompareNoCase(pTVDispInfo->item.pszText)==0)
{
*pResult = 1;
return;
}
*pResult = 0;
}
/* Эта функция вызывается системой непосредcтвенно перед
потерей фокуса ввода элементом редакирования (CEdit).
Программист имеет возможность запретить или разрешить
сохранение изменений, сделанных при редактировании
путем модификации возвращаемого значения pResult.
0 - отвергнуть изменения, не 0 - принять изменения. */
void CTreeViewInPlaceEditView::OnTvnEndlabeledit(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);
//был ли ввод нового текста?
if(pTVDispInfo->item.pszText == NULL) // не было, пользователь отменил его
{
*pResult = 0;
return;
}
// пусть у нас для примера нельзя, чтобы новое имя элемента было "The item"
CString sForbiddenName;
sForbiddenName = _T("The item");
// если пользователь ввел запрещенное имя для элемента - отвергнуть его
if(sForbiddenName.CompareNoCase(pTVDispInfo->item.pszText)==0)
{
*pResult = 0;
return;
}
// иначе принять изменения
*pResult = 1;
}
Исходники
Здесь вы можете загрузить иллюстрирующий проект (VC++ 7.0)
Пример для CTreeCtrl, архив ZIP,~23 Кб
Пример для CTreeView, архив ZIP,~28 Кб
Оглавление
Последние изменения от 09.08.2013