По мотивам письма ----- 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; }
Из структуры я извлек название, получил указатель на активный элемент и поменял текст элемента. Если корень изменять нельзя, то нужно определить функцию, которая проверяет, что это именно корень.