Search code examples
c++treemfc

C++ CTreeCtrl, how to edit only one item of a tree


Is there any way to make editable CTreeCtrl items that are fit in exact condition, not all of tree items.

If I set TVS_EDITLABELS, all tree items can be changed by right button click

CViewTree m_ModelTree;   //  CViewTree based on CTreeCtrl
const DWORD dwViewStyle = WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TVS_EDITLABELS;
    
    if (!m_ModelTree.Create(dwViewStyle, rectDummy, this, IDC_TREEMODEL))
    {
        TRACE0(_T("Failed to create Class View\n"));
        return -1;      // fail to create
    }

And I need to edit only exact type of nodes. Something like this:

HTREEITEM item = m_ModelTree.GetSelectedItem();
auto it = theApp.TreeMap.find(item);
if (it == theApp.TreeMap.end()) return;
if(it->type == tree_node_type::indexes)
    m_ModelTree.EditLabel(hItem);
else
   //FORBID EDIT 

Or maybe is there a way to forbid edit lable with TV_INSERTSTRUCT on the InsertItem step...?

TV_INSERTSTRUCT tvinsert;
m_ModelTree.InsertItem(&tvinsert);

Solution

  • The standard Tree View control with the TVS_EDITLABELS style allows clients to control, at run time, whether any given item can be edited. This is offered in the form of a TVN_BEGINLABELEDIT notification sent to the control's parent, where the return value determines whether editing should be canceled.

    The MFC entry Tree Control Label Editing in the documentation provides details:

    When label editing begins, a tree control sends a TVN_BEGINLABELEDIT notification message. By processing this notification, you can allow editing of some labels and prevent editing of others. Returning 0 allows editing, and returning nonzero prevents it.

    To receive TVN_BEGINLABELEDIT notifications, the control parent (presumably a dialog or regular window) must have an entry in its message map that wires the notification to a callback function. The ON_NOTIFY macro is the most convenient way to go about this:

    BEGIN_MESSAGE_MAP(CMyDialog, CDialogEx)
        // ...
        ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_TREEMODEL, OnTCBeginLabelEdit)
    END_MESSAGE_MAP()
    

    The OnTCBeginLabelEdit callback must have the following signature:

    afx_msg void OnTCBeginLabelEdit(NMHDR* pNotifyStruct, LRESULT* result);
    

    Its implementation is where the logic to decide whether editing should be canceled goes:

    void CMyDialog::OnTCBeginLabelEdit(NMHDR* pNotifyStruct, LRESULT* result) {
        // The TVN_BEGINLABELEDIT notification sends an NMTVDISPINFO structure
        auto const& disp_info = *reinterpret_cast<NMTVDISPINFO*>(pNotifyStruct);
        // Get the target item
        auto const current_item = disp_info.item.hItem;
    
        // Implement the logic here
        bool do_cancel = ...;
    
        // Pass the decision through result
        *result = do_cancel;
    }
    

    The above assumes that the tree view control is a child of a dialog called CMyDialg that derives from CDialogEx. Those are the only parts that need to be adjusted accordingly.