List Control如何实现下面的效果?
VIEW为Report,如何是其他view能够实现也可以
1 单元格显示多行字符。
2 单元格高度随字符行数自动变换。
3 可以编辑单元格,接受回车,鼠标移开自动保存的界面谢谢

解决方案 »

  1.   

    http://www.vckbase.com/code/viewcode.asp?id=2914
      

  2.   


    //   DropDownList.h   
        
      #pragma   once   
        
      #include   <vector>   
      #include   <string>   
        
      using   namespace   std;   
        
      //   CInplaceComboBox   
        
      class   CInplaceComboBox   :   public   CComboBox   
      {   
      DECLARE_DYNAMIC(CInplaceComboBox)   
        
      public:   
      CInplaceComboBox(int   item,   int   col,   CString&   text);   
      virtual   ~CInplaceComboBox();   
        
      protected:   
      CString   _text;   
      int   _item;   
      int   _subItem;   
        
      DECLARE_MESSAGE_MAP()   
      public:   
      afx_msg   void   OnNcDestroy();   
      afx_msg   void   OnKillFocus(CWnd*   pNewWnd);   
      void   SelfDestroy(void);   
      };   
    //   CDropDownListCtrl   
        
      class   CDropDownListCtrl   :   public   CListCtrl   
      {   
      DECLARE_DYNAMIC(CDropDownListCtrl)   
        
      public:   
      CDropDownListCtrl();   
      virtual   ~CDropDownListCtrl();   
        
      void   setComboItems(vector<string>&   domains)   {   _domains=domains;   }   
        
      protected:   
      vector<string>   _domains;   
        
      DECLARE_MESSAGE_MAP()   
      public:   
      CComboBox*   EditSubLabel(int   nItem,   int   nCol);   
      int   HitTestEx(CPoint&   point,   int*   col);   
      afx_msg   void   OnLvnEndlabeledit(NMHDR   *pNMHDR,   LRESULT   *pResult);   
      afx_msg   void   OnLButtonDown(UINT   nFlags,   CPoint   point);   
      afx_msg   void   OnHScroll(UINT   nSBCode,   UINT   nPos,   CScrollBar*   pScrollBar);   
      afx_msg   void   OnVScroll(UINT   nSBCode,   UINT   nPos,   CScrollBar*   pScrollBar);   
      };   
      

  3.   


    //////////////////////////////////////////////////////////////////   
      //   
      //   DropDownListCtrl.cpp   :   implementation   file   
      //   
        
      #include   "stdafx.h"   
      #include   "DropDownListCtrl.h"   
      #include   ".\dropdownlistctrl.h"   
        
      #define   IDC_INPLACECOMBO   75001   
      //   CInplaceComboBox   
        
      IMPLEMENT_DYNAMIC(CInplaceComboBox,   CComboBox)   
        
      BEGIN_MESSAGE_MAP(CInplaceComboBox,   CComboBox)   
      ON_WM_NCDESTROY()   
      ON_WM_KILLFOCUS()   
      // ON_CONTROL_REFLECT(CBN_KILLFOCUS,   OnCbnKillfocus)   
      END_MESSAGE_MAP()   
        
        
      //   CInplaceComboBox   message   handlers   
      CInplaceComboBox::CInplaceComboBox(int   item,   int   col,   CString&   text)   
      :_text(text),_item(item),_subItem(col)   
      {   
      }   
        
      CInplaceComboBox::~CInplaceComboBox()   
      {   
      }   
        
      //   CDropDownListCtrl   message   handlers   
      void   CInplaceComboBox::OnNcDestroy()   
      {   
      CComboBox::OnNcDestroy();   
      delete   this;   
      }   
        
      void   CInplaceComboBox::OnKillFocus(CWnd*   pNewWnd)   
      {   
      CComboBox::OnKillFocus(pNewWnd);   
      SelfDestroy();   
      }   
        
      void   CInplaceComboBox::SelfDestroy(void)   
      {   
      CString   str;   
      GetWindowText(str);   
        
      if(   !str.IsEmpty())   {   
        
      //   Send   Notification   to   parent   of   ListView   ctrl   
      LV_DISPINFO   dispinfo;   
      dispinfo.hdr.hwndFrom   =   GetParent()->m_hWnd;   
      dispinfo.hdr.idFrom   =   GetDlgCtrlID();   
      dispinfo.hdr.code   =   LVN_ENDLABELEDIT;   
        
      dispinfo.item.mask   =   LVIF_TEXT;   
      dispinfo.item.iItem   =   _item;   
      dispinfo.item.iSubItem   =   _subItem;   
      dispinfo.item.pszText   =   LPTSTR((LPCTSTR)str);   
      dispinfo.item.cchTextMax   =   str.GetLength();   
        
      GetParent()->GetParent()->SendMessage(   WM_NOTIFY,   GetParent()->GetDlgCtrlID(),   
      (LPARAM)&dispinfo   );   
      }   
        
      DestroyWindow();   
      }   
        
      //////////////////////////////////////////////////////////////////////////////////////////////   
      //   DropDownList.cpp   
      //   CDropDownListCtrl   
        
      IMPLEMENT_DYNAMIC(CDropDownListCtrl,   CListCtrl)   
        
      BEGIN_MESSAGE_MAP(CDropDownListCtrl,   CListCtrl)   
      // ON_WM_CREATE()   
      ON_NOTIFY_REFLECT(LVN_ENDLABELEDIT,   OnLvnEndlabeledit)   
      ON_WM_LBUTTONDOWN()   
      ON_WM_HSCROLL()   
      ON_WM_VSCROLL()   
      END_MESSAGE_MAP()   
        
      CDropDownListCtrl::CDropDownListCtrl()   
      {   
      }   
        
      CDropDownListCtrl::~CDropDownListCtrl()   
      {   
      }   
        
        
        
      CComboBox*   CDropDownListCtrl::EditSubLabel(int   nItem,   int   nCol)   
      {   
      //   Make   sure   that   the   item   is   visible   
      CString   text   =   GetItemText(   nItem,   nCol   );   
      if(   !EnsureVisible(   nItem,   TRUE   )   ||   text.IsEmpty()   )   return   NULL;   
        
      //   Make   sure   that   nCol   is   valid   
      CHeaderCtrl*   pHeader   =   (CHeaderCtrl*)GetDlgItem(0);   
      int   nColumnCount   =   pHeader->GetItemCount();   
      if(   nCol   >=   nColumnCount   ||   GetColumnWidth(nCol)   <   5   )   
      return   NULL;   
        
      //   Get   the   column   offset   
      int   offset   =   0;   
      for(   int   i   =   0;   i   <   nCol;   i++   )   
      offset   +=   GetColumnWidth(   i   );   
        
      CRect   rect;   
      GetItemRect(   nItem,   &rect,   LVIR_BOUNDS   );   
        
      //   Now   scroll   if   we   need   to   expose   the   column   
      CRect   rcClient;   
      GetClientRect(   &rcClient   );   
      if(   offset   +   rect.left   <   0   ||   offset   +   rect.left   >   rcClient.right   )   
      {   
      CSize   size;   
      size.cx   =   offset   +   rect.left;   
      size.cy   =   0;   
      Scroll(   size   );   
      rect.left   -=   size.cx;   
      }   
        
      //   Get   Column   alignment   
      LV_COLUMN   lvcol;   
      lvcol.mask   =   LVCF_FMT;   
      GetColumn(   nCol,   &lvcol   );   
      DWORD   dwStyle   ;   
      if((lvcol.fmt&LVCFMT_JUSTIFYMASK)   ==   LVCFMT_LEFT)   
      dwStyle   =   ES_LEFT;   
      else   if((lvcol.fmt&LVCFMT_JUSTIFYMASK)   ==   LVCFMT_RIGHT)   
      dwStyle   =   ES_RIGHT;   
      else   dwStyle   =   ES_CENTER;   
        
      rect.left   +=   offset+4;   
      rect.right   =   rect.left   +   GetColumnWidth(   nCol   )   -   3   ;   
      if(   rect.right   >   rcClient.right)   rect.right   =   rcClient.right;   
      rect.bottom   =   rect.top+200;   
        
      dwStyle   |=   WS_BORDER|WS_CHILD|WS_VISIBLE|CBS_DROPDOWNLIST;   
        
      CComboBox   *cb   =   new   CInplaceComboBox(nItem,   nCol,   text);   
      cb->Create(   dwStyle,   rect,   this,   IDC_INPLACECOMBO   );   
      cb->SetFocus();   
        
      for(   size_t   i   =   0;   i<_domains.size();   ++i) cb->AddString(_domains[i].c_str());   
      cb->SelectString(-1,text);   
      return   cb;   
      }   
        
      int   CDropDownListCtrl::HitTestEx(CPoint&   point,   int*   col)   
      {   
      int   colnum   =   0;   
      int   row   =   HitTest(   point,   NULL   );   
        
      if(   col   )   *col   =   0;   
        
      //   Make   sure   that   the   ListView   is   in   LVS_REPORT   
      if(   (GetWindowLong(m_hWnd,   GWL_STYLE)   &   LVS_TYPEMASK)   !=   LVS_REPORT   )   
      return   row;   
        
      //   Get   the   top   and   bottom   row   visible   
      row   =   GetTopIndex();   
      int   bottom   =   row   +   GetCountPerPage();   
      if(   bottom   >   GetItemCount()   )   
      bottom   =   GetItemCount();   
        
      //   Get   the   number   of   columns   
      CHeaderCtrl*   pHeader   =   (CHeaderCtrl*)GetDlgItem(0);   
      int   nColumnCount   =   pHeader->GetItemCount();   
        
      //   Loop   through   the   visible   rows   
      for(   ;row   <=bottom;row++)   
      {   
      //   Get   bounding   rect   of   item   and   check   whether   point   falls   in   it.   
      CRect   rect;   
      GetItemRect(   row,   &rect,   LVIR_BOUNDS   );   
      if(   rect.PtInRect(point)   )   
      {   
      //   Now   find   the   column   
      for(   colnum   =   0;   colnum   <   nColumnCount;   colnum++   )   
      {   
      int   colwidth   =   GetColumnWidth(colnum);   
      if(   point.x   >=   rect.left   
      &&   point.x   <=   (rect.left   +   colwidth   )   )   
      {   
      if(   col   )   *col   =   colnum;   
      return   row;   
      }   
      rect.left   +=   colwidth;   
      }   
      }   
      }   
      return   -1;   
      }   
        
      void   CDropDownListCtrl::OnLvnEndlabeledit(NMHDR   *pNMHDR,   LRESULT   *pResult)   
      {   
      //NMLVDISPINFO   *pDispInfo   =   reinterpret_cast<NMLVDISPINFO*>(pNMHDR);   
      //   TODO:   Add   your   control   notification   handler   code   here   
      //*pResult   =   0;   
        
      LV_DISPINFO   *plvDispInfo   =   (LV_DISPINFO   *)pNMHDR;   
      LV_ITEM *plvItem   =   &plvDispInfo->item;   
        
      if   (plvItem->pszText   !=   NULL)   
      {   
      SetItemText(plvItem->iItem,   plvItem->iSubItem,   plvItem->pszText);   
      }   
      *pResult   =   FALSE;   
        
      }   
        
      void   CDropDownListCtrl::OnLButtonDown(UINT   nFlags,   CPoint   point)   
      {   
      int   index;   
      CListCtrl::OnLButtonDown(nFlags,   point);   
        
      int   colnum;   
      if(   (   index   =   HitTestEx(   point,   &colnum   ))   !=   -1   )   
      {   
      UINT   flag   =   LVIS_FOCUSED;   
      if(   (GetItemState(   index,   flag   )   &   flag)   ==   flag   &&   colnum   ==   1)   
      {   
      //   Add   check   for   LVS_EDITLABELS   
      //   if(   GetWindowLong(m_hWnd,   GWL_STYLE)   &   LVS_EDITLABELS   )   
      EditSubLabel(   index,   colnum   );   
      }   
      else   
      SetItemState(   index,   LVIS_SELECTED   |   LVIS_FOCUSED   ,   
      LVIS_SELECTED   |   LVIS_FOCUSED);     
      }   
      }   
        
      void   CDropDownListCtrl::OnHScroll(UINT   nSBCode,   UINT   nPos,   CScrollBar*   pScrollBar)   
      {   
      if(   GetFocus()   !=   this   )   SetFocus();   
      CListCtrl::OnHScroll(nSBCode,   nPos,   pScrollBar);   
      }   
        
      void   CDropDownListCtrl::OnVScroll(UINT   nSBCode,   UINT   nPos,   CScrollBar*   pScrollBar)   
      {   
      if(   GetFocus()   !=   this   )   SetFocus();   
      CListCtrl::OnVScroll(nSBCode,   nPos,   pScrollBar);   
      }   
     
      

  4.   

    LZ, 在codeproject里有个cgridctrl, 呵呵, 已经实现你要的要求了, 超强。 扩展性也好, 你参考下,呵呵
    http://www.codeproject.com/KB/miscctrl/gridctrl.aspx
    http://www.codeproject.com/KB/miscctrl/adding___quot_merge_cells.aspx
      

  5.   


    1 http://www.vckbase.com/code/viewcode.asp?id=2914
    此类只能编辑,不能改变行高2 Tr0j4n 
    代码是与组合框的结合。3 muzizongheng http://www.codeproject.com/KB/miscctrl/gridctrl.aspx 
    不能自适应行高
    http://www.codeproject.com/KB/miscctrl/adding___quot_merge_cells.aspx
    此类功能确实强大,但是不能多行显示字符,不能输入回车符谢谢回答
      

  6.   

    lz,自适应行高的啊, 你调用 AutoSize就可以了。 行高列宽都自适应的。 
      

  7.   

    这个你把col固定, 只autosize(row)的, 然后在inplaceEdit的构造里 修改创建时的风格, wantreturn 和multiLine。
      

  8.   

    构造风格  |ES_WANTRETURN   |ES_MULTILINE
      

  9.   

    我在inplaceEdit的构造里 已经修改了创建时的风格。
    编辑框已经接受回车,可以输入多行的问题已经解决,但是输入之后单元格显示的是一行。现在的问题是:
    如何让单元格显示多行并且行的高度自动跟随输入文字的行数变化?
      

  10.   

    第一, 你在CGridDefaultCell::CGridDefaultCell() 里把默认的|DT_SINGLELINE注释掉:
     m_nFormat = DT_LEFT|DT_VCENTER/*|DT_SINGLELINE*/|DT_NOPREFIX | DT_END_ELLIPSIS;  //change by jg for multi line.第二, void CGridCtrl::OnEndEditCell(int nRow, int nCol, CString str)里添加一句:
    CString strCurrentText = GetItemText(nRow, nCol);
        if (strCurrentText != str)
        {
            SetItemText(nRow, nCol, str);
            if (ValidateEdit(nRow, nCol, str) && 
                SendMessageToParent(nRow, nCol, GVN_ENDLABELEDIT) >= 0)
            {
                SetModified(TRUE, nRow, nCol);
                RedrawCell(nRow, nCol); RedrawWindow();//add by jg for multi line text.
            }
            else
            {
                SetItemText(nRow, nCol, strCurrentText);
            }
        }    CGridCellBase* pCell = GetCell(nRow, nCol);
        if (pCell)
            pCell->OnEndEdit();第三, 就是那个void CInPlaceEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)里,//add by jg for multi line.
    CRect rect;
    UINT nFormat = DT_WORDBREAK | DT_CALCRECT;
    int nDescrHeight = dc.DrawText(str, rect, nFormat);
    //end
    。。
    if (nDescrHeight > m_Rect.Height())
    {
    ((CGridCtrl*)GetParent())->SetRowHeight(m_nRow, nDescrHeight);
    }
      

  11.   

    我给的代码假定你 已经修改了edit方面的style和enter处理。 呵呵。如果你没实现
    代码如下:
    CInPlaceEdit::CInPlaceEdit(CWnd* pParent, CRect& rect, DWORD dwStyle, UINT nID,
                               int nRow, int nColumn, CString sInitText, 
                               UINT nFirstChar)
    {
        m_sInitText     = sInitText;
        m_nRow          = nRow;
        m_nColumn       = nColumn;
        m_nLastChar     = 0; 
        m_bExitOnArrows = (nFirstChar != VK_LBUTTON);    // If mouse click brought us here,
                                                         // then no exit on arrows    m_Rect = rect;  // For bizarre CE bug.
        
        DWORD dwEditStyle = WS_BORDER|WS_CHILD|WS_VISIBLE/*| ES_AUTOHSCROLL*/ | ES_MULTILINE | ES_WANTRETURN | ES_AUTOVSCROLL //add by jg for multiline
            | dwStyle;void CInPlaceEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
    {
        if (nChar == VK_TAB/* || nChar == VK_RETURN*/) //delete by jg for multiline
        {
            m_nLastChar = nChar;
            GetParent()->SetFocus();    // This will destroy this window
            return;
        }
      

  12.   

    第三, 就是那个void CInPlaceEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)里, 
    C/C++ code
    //add by jg for multi line.
        CRect rect;
        UINT nFormat = DT_WORDBREAK | DT_CALCRECT;
        int nDescrHeight = dc.DrawText(str, rect, nFormat);
        //end
    。。
    if (nDescrHeight > m_Rect.Height())
        {
            ((CGridCtrl*)GetParent())->SetRowHeight(m_nRow, nDescrHeight);
        }这段代码是不是添加在void CInPlaceEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
    函数的最下面。现在只剩下单元格内回车自动改变行高。
      

  13.   

    按照你们提示的方法,我验证过,在单元格里面输入文本可以这么换行,非常谢谢。但想再请问一下,我现在也碰到这个问题,我想让单元格开始显示文本的时候,就按照文本的内容进行自动的换行和调整行高,而不是在单元格中输入文本(OnChar事件),我应该怎么办,或者修改那个类,谢谢!
      

  14.   

    第三个,需要修改下,是在void CInPlaceEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)的最后面添加,如下。////add by jg for multi line.
    CRect rect = ParentRect;
    UINT nFormat = DT_WORDBREAK | DT_CALCRECT;
    int nDescrHeight = dc.DrawText(str, rect, nFormat);
    //end
    if (nDescrHeight > m_Rect.Height())
    {
    ((CGridCtrl*)GetParent())->SetRowHeight(m_nRow, nDescrHeight);
    }