Шаг 182 - Редактирование по месту CTreeView

	По мотивам письма
	----- Original Message ----- 
	From: Yegor A. Blackheel 
	To: 
	Sent: Friday, May 05, 2000 1:22 AM
	Subject: MFCByStep
> Хочу рассказать, как делается "in-place" редактирование, т.е. редактирование названия
> элемента без использования диалога. Так редактируются, например, именa файлов в Проводнике.
> Смысл редактирования "на-месте" в том, что Windows создает некий временный
> CEdit контрол,  в котором производится изменение текста элемента.
> Проблема в том, что сама система автоматически НЕ обновляет текст элемента
> дерева,  хотя  на экране пользователь его изменяет. Таким образом, порядок работы д.б.
> следующий: 
> 1. перехватить сообщение о начале редактирования
> 2. выполнить необходимые проверки (например, если нельзя редактировать  корень)
> и разрешить или не разрешить собственно редактирование
> 3. Windows сделает всю работу по созданию элемента редактирования
> 4. Пользователь введет новый текст
> 5. перехватить сообщение об окончании ввода
> 6. самое главное - изменить элемент дерева вручную
> Пример приводится для использования CTreeView (sorry, выдирал из
> собственного проекта)
> I. Прежде всего, дерево должно иметь стиль TVS_EDITLABELS. Его можно
> установить с помощью Resource Editora (если используется CTreeCtrl), при создании вида
> функцией  Create, или вот так:
> void CLeftView::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. Далее с помощью ClassWizarda (или вручную) нам необходимо перегрузить
> функции,
> отвечающие на нотификационные сообщения от CTreeCtrl`a, посылаемые CTreeView
> Это сообщения TVN_BEGINLABELEDIT и TVN_ENDLABELEDIT
> Коротко скажу, что эти сообщения посылаются сразу перед началом и перед
> окончанием редактирования
> Программист может модифицировать результат, возвращаемый ими, тем самым
> влияя  на поведение программы
> file://это кусок хэдера
> // Generated message map functions
> protected:
> file://{{AFX_MSG(CLeftView)
>  ................................
>  afx_msg void OnBeginlabeledit(NMHDR* pNMHDR, LRESULT* pResult);
>  afx_msg void OnEndlabeledit(NMHDR* pNMHDR, LRESULT* pResult);
> ................................
> file://}}AFX_MSG
>  DECLARE_MESSAGE_MAP()
> ---------------------------------------------------
> file://это кусок срр файла
> 
> BEGIN_MESSAGE_MAP(CLeftView, CTreeView)
> file://{{AFX_MSG_MAP(CLeftView)
> ................................
>  ON_NOTIFY_REFLECT(TVN_BEGINLABELEDIT, OnBeginlabeledit)
>  ON_NOTIFY_REFLECT(TVN_ENDLABELEDIT, OnEndlabeledit)
> ................................
> file://}}AFX_MSG_MAP
> END_MESSAGE_MAP()
> 
> --------------------------------------------
> III. Теперь собственно реализация функций
> 
> -------------------------------------------------------------------
> /*эта функция вызывается системой непосредственно перед
> передачей управления элементу редакирования (CEdit).
> программист имеет возможность запретить или разрешить
> редактирование путем модификации возвращаемого
> значения pResult. 0 - разрешить редактирование, не 0- запретить
> В данном примере запрещается редакитировать название корневого элемента
> */
> void CLeftView::OnBeginlabeledit(NMHDR* pNMHDR, LRESULT* pResult)
> {
>  TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
> 
>  CString name;
>  name.LoadString(IDS_TREE_ROOT); /*загрузить строку с названием корня*/
>  /*если название редактируемого элемента совпадает с корнем -
> запретить редактирование*/
>  if (name==pTVDispInfo->item.pszText)
> {
>   *pResult = 1;
>    return;
> }
>  else
>   *pResult = 0;
> }
> 
> 
> /*эта функция вызывается системой непосредвтенно перед
> потерей фокуса ввода элементом редакирования (CEdit).
> программист имеет возможность запретить или разрешить
> сохранение изменений, сделанный при редактировании
> путем модификации возвращаемого
> значения pResult. 0 - принять изменения, не 0- отвергнуть изменения
> */
> 
> 
> void CLeftView::OnEndlabeledit(NMHDR* pNMHDR, LRESULT* pResult)
> {
>  TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
>  TVITEM newitem;
>  newitem=pTVDispInfo->item;
>  CTreeCtrl &tree = GetTreeCtrl();
> /* похоже, пользователь отменил ввод....*/
>  if (newitem.pszText==0)
>  {
>   *pResult = 0;
>   return;
>  }
>  *pResult = 0;
>  /* вот тут и происходит самое интересное. на первый взгляд, абсолютно
> бессмысленные  действия. На самом деле происходит следующее.  наша функция получает
> указатель на  элемент, который является копий редактируемого, но содержащий новый текст.
> А программист уже решает, заменить ли старый элемент на новый, или нет.*/
>  newitem.mask=TVIF_TEXT; /*говорим, что меняем только текст*/
>  tree.SetItem(&newitem);       /* новый элемент */
> }
> С уважением, Yegor A. Blackheel (blackheel@mail.ru)

Уважаемые, Егор совершенно прав. Для того, чтобы можно было редактировать по месту прямо в дереве без дополнительных окон, во-первых, надо присвоить дереву стиль в функции Create этот стиль называется TVS_EDITLABELS.

dwStyle=dwStyle |  TVS_EDITLABELS;

Установив этот стиль теперь можно вызывать функцию редактирования по месту на основе указателя.

	GetTreeCtrl().EditLabel(hi); 

Полное описание этой функции:

CEdit* EditLabel( HTREEITEM hItem );

Действительно создается временный элемент управления, который при желании можно и перехватить. Но вот только отредактировав этот элемент управления вы не сможете сохранить текст. Все нормально. После окончания редактирования дереву посылается сообщение TVN_ENDLABELEDIT, которое говорит о том, что редактирование дерева закончено. Это сообщение нужно связать с функцией через Add Windows Message Handle. И после этого написать код в функцию для изменения текста дерева. Вот такой:

void CLeftView::OnEndlabeledit(NMHDR* pNMHDR, LRESULT* pResult) 
{
	TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
	HTREEITEM hi=TestSelectItemTree();
	GetTreeCtrl().SetItemText(hi,pTVDispInfo->item.pszText); 	
	*pResult = 0;
}

Из структуры я извлек название, получил указатель на активный элемент и поменял текст элемента. Если корень изменять нельзя, то нужно определить функцию, которая проверяет, что это именно корень.


Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Каев Артем - 09.05.2000