开发者

MFC List Control

In MFC, I can edit the text of items in the list control but only for the first column by setting the Edit Labels to true. Now when I click the first column item to change its text, I'm able to change its text but when I hit Enter, its tex开发者_如何学JAVAt isn't updated, why and how do I edit text for other columns?


We create CEdit control on the List control's cell's position (when we double click on List Control) and when we press enter it updates the value. In this example we modify only 1 subitem (the 2nd), when we dbclick on List Control you can create many CEdit controls an do it with all the subitems. List Control SingleSelection = true

//.h
// Global variables in dialog
private:
    //..
    const static int        ID_TXTCTRL_TOMODIFY = 1001;
    bool                    m_IsEnterPressed;
    CEdit *                 m_pTxtCtrlToModify;

    //..
protected:
    //..
    BOOL PreTranslateMessage(MSG* pMsg);
    virtual BOOL OnInitDialog();
    //..

public:
    //..
    afx_msg void OnNMDblclkList(NMHDR *pNMHDR, LRESULT *pResult);
    afx_msg void OnLvnItemchangedList(NMHDR *pNMHDR, LRESULT *pResult);
    afx_msg void OnEnKillFocusCtrlToModify();
    //..

//------------------------------------------------------------------
// .cpp


BEGIN_MESSAGE_MAP(DlgMFC, CDialogEx)
    //..
    ON_NOTIFY(NM_DBLCLK, IDC_LIST, &DlgMFC::OnNMDblclkList)
    ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST, &DlgMFC::OnLvnItemchangedList)
    ON_EN_KILLFOCUS(ID_TXTCTRL_TOMODIFY, &DlgMFC::OnEnKillFocusCtrlToModify)
    //..
END_MESSAGE_MAP()

BOOL DlgMFC::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // Set the icon for this dialog.  The framework does this automatically
    //  when the application's main window is not a dialog
    SetIcon(m_hIcon, TRUE);         // Set big icon
    SetIcon(m_hIcon, FALSE);        // Set small icon

    // TODO: Add extra initialization here

    m_pTxtCtrlToModify = NULL;
    m_IsEnterPressed = false;

    //
    return TRUE;  // return TRUE  unless you set the focus to a control
}

// in this function we let to modify only 1 subitem (the 2nd),
// you can create many CEdit controls an do it with all the subitems



    // We create CEdit Control
void DlgMFC::OnNMDblclkList(NMHDR *pNMHDR, LRESULT *pResult)
{
    //LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
    //*pResult = 0;


    POSITION p = m_CtrlList.GetFirstSelectedItemPosition();
    int index, id;
    if(p)   
    {
            index   = m_CtrlList    .GetNextSelectedItem(p);       

            CRect rect, rect_ListControl; // rect wiil be cell position in m_CtrlList, rect_ListControl will be m_CtrlList's position in dialog
            if( m_CtrlList.GetSubItemRect(index, 2, LVIR_BOUNDS, rect)) // 2 is subitem number
            {
                if(m_pTxtCtrlToModify != NULL)
                    delete m_pTxtCtrlToModify;
                m_pTxtCtrlToModify = new CEdit(); // Do not forget to delete it at the end of your program (you can use OnClose())


                m_CtrlList.GetWindowRect(&rect_ListControl);
                this->ScreenToClient(&rect);

                rect.left += rect_ListControl.left + 2; // 2 is just a correction
                rect.right += rect_ListControl.left + 2;
                rect.top  += rect_ListControl.top + 2;
                rect.bottom += rect_ListControl.top + 2;

                m_pTxtCtrlToModify->Create(ES_CENTER | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL, rect, this, ID_TXTCTRL_TOMODIFY);
                m_pTxtCtrlToModify->SetFocus();
                m_pTxtCtrlToModify->SetWindowTextW(m_CtrlList.GetItemText(index, 2)); // 2 is subitem number

            }

    }
}


// If Selection changes, we delete that CEdit
void DlgMFC::OnLvnItemchangedList(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
    *pResult = 0;

    if(m_pTxtCtrlToModify != NULL)
    {
        delete m_pTxtCtrlToModify;
        m_pTxtCtrlToModify = NULL;
    }
}


// Do not let the dialog to be closed when we press enter
BOOL DlgMFC::PreTranslateMessage(MSG* pMsg)
{
    if (pMsg->message == WM_KEYDOWN)
    {
        if ((pMsg->wParam == VK_RETURN) || (pMsg->wParam == VK_ESCAPE))
        {
            pMsg->wParam = VK_TAB;
            m_IsEnterPressed = true;
        }
    }
    return CDialog::PreTranslateMessage(pMsg);
}


//If we pressed enter, we update it and delete CEdit control 
void DlgMFC::OnEnKillFocusCtrlToModify()
{
    // we will update only when we press enter
    if(m_IsEnterPressed == true)
    {       
        m_IsEnterPressed = false;

        // UPDATE here your Database or just ListControl or both ...
        // Example: We update the same m_CtrlList's cell
        POSITION p = m_CtrlList.GetFirstSelectedItemPosition();
        int index;
        if(p)   
        {
            CString str;
            m_pTxtCtrlToModify->GetWindowTextW(str);
            index   = m_CtrlList    .GetNextSelectedItem(p);
            m_CtrlList.SetItemText(index,2,str); // 2 is subitem number
        }


        // Delete CEdit control
        if(m_pTxtCtrlToModify != NULL)
        {
            delete m_pTxtCtrlToModify;
            m_pTxtCtrlToModify = NULL;
        }

        m_CtrlList.SetFocus();
    }
}

I hope it will help you. Thanks


Unfortunately it isn't possible to utilize LVS_EDITLABELS and LVN_ENDLABELEDIT for editing other columns than the first.

For a workaround see the XListCtrl article on CodeProject for further information, it dynamically creates an edit control when needed.


For the first column:

  • create the list with the LVS_EDITLABELS style
  • set a dialog control on your list if it does not have one, eg SetDlgCtrlID( ID_EDITLABEL );
  • you might need some code for tracking the current selection
  • create the edit in a messagehandler reacting on a click/doubleclick or other user input (seems you have that covered already), best to put this in the parent class.
  • add a handler for the edit end in the parent class

    ON_NOTIFY( LVN_ENDLABELEDIT, ID_EDITLABEL, OnEndEdit )
    
    void MyParentClass::OnEndEdit( NMHDR* pNMHDR, LRESULT* pResult )
    {
      NMLVDISPINFO* pLVDI = reinterpret_cast< NMLVDISPINFO* >( pNMHDR );
      if( pLVDI->item.pszText )
        m_List.SetItemText( m_iCurrentSelection, 0, pLVDI->item.pszText );
      *pResult = 0;
    }
    

For other columns: I haven't tried it yet, but it should not be too hard, as you can lookup in the MFC source code how they do it. Note that the code above is tested with a CMFCListCtrl from the latest feature pack, though I assume a plain CListCtrl behaves the same.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜