Insert and Replace in a List Control

Environment: VC6 SP5, NT4 SP6

The normal way to replace items in a list control is to remove an item and then insert a new item. This can get quite tedious, so I needed to allow users to insert or replace items in a list control. To do this, I added functionality to show the user when a replace would be done by highlighting a line in a different colour so that the user could easily tell whether data would be inserted or replaced.

  1. The first step is to overide and create a new class CListCtrlEx that inherits from CListCtrl.
  2. It is then necessary to add code that supports dragging and dropping between lists. I used the Microsoft Exsample found here as the basis of my drag and drop code. I copied the onLButtonUp, OnMouseMove, OnBeginDrag, and DropItemOnList functions changing them from CView to CListCtrl.
  3. I then added code to allow a row to be highlighted in another colour. This is described in the Codeguru article "Selection Highlighting of Entire Row" with accompanying source code here. I used this code as the basis of my row highlighting.
  4. I created an enumerated type to make it easier to understand whether a row will be inserted or replaced. There is also a member variable and a Set function.
  5. Enum EType
    {
      EInsert,
      EReplace
    };
    
    EDropType m_eType;
    void SetDropType(EDropType eType) {m_eType = eType;}
    
  6. Add to OnBeginDrag and the Constructor the default state.
  7.   m_eType = EInsert;
  8. I changed the drawing code by replacing LVIS_SELECTED with LVIS_DROPHILITED in functions IsRowSelected and UseOurHighlighting. This means that the change in colour for row highlighting will occur for dropping rather than selection. Then, I added new code to UseOurHighlighting so that replacing will be shown in a different colour from inserting.
  9. if (m_eType == EReplace)
    {re
      pcd->clrTextBk = m_clrHighlightBack;
    }
    else if (m_eType == EInsert)
    {
      pcd->clrTextBk = GetSysColor(COLOR_HIGHLIGHT);
    }
    
    It should be noted that this obviously requires co-operation between the screen layer and the data layer to decide whether or not to replace something. Although I have provided a sample, there would normally be a greater separation between data and screen.
  10. Add a new function.
  11. void SetDropType(const CPoint& WndPoint,
                           CListCtrlEx* pDropList)
    
    to the class header. It was decided that the replace highlight would be shown when the cursor was above the main body of text with Insert being when the cursor was at the top or bottom of the text. This meant it was necesssary to detect where the mouse was in relation to the highlighting of a row. If it was in the top or bottom two units of the row, it would display a normal insert colour; otherwise, the replacement colour would be used. A new member variable, m_nModifiedDropIndex, is used to hold the drop position as it is now possible to insert above or below a row.
    void CListCtrlEx::SetDropType(const CPoint& WndyPoint,
                                        CListCtrlEx* pDropList)
    {
      //don't allow dragging and dropping on the same list
      if (pDropList == this)
      {
        return;
      }
    
      CPoint ScreenPoint(WndyPoint);
      ClientToScreen(&ScreenPoint);
      CWnd* pWnd = CWnd::WindowFromPoint(ScreenPoint);
      CPoint WndPoint(ScreenPoint);
      pWnd->ScreenToClient(&WndPoint);
    
      CRect rect;
    
      //If a valid row has been selected
      if (m_nDropIndex != -1)
      {
        //Get the rectangle for the highlighted row
        pDropList->GetItemRect(m_nDropIndex, &rect, LVIR_BOUNDS);
      }
      else
      {
        //Trying to insert at the end of the list? So set the
        //Default State
        pDropList->SetDropType(EInsert);
      }
    
      if(rect.PtInRect(WndPoint))
      {
        if (abs(rect.top - WndPoint.y) <= 2)
        {
          pDropList->SetDropType(EInsert);
          m_nModifiedDropIndex = m_nDropIndex;
        }
        else if (abs(rect.bottom - WndPoint.y) <= 2)
        {
          pDropList->SetDropType(EInsert);
          m_nModifiedDropIndex = m_nDropIndex + 1;
        }
        else
        {
          pDropList->SetDropType(EReplace);
          m_nModifiedDropIndex = m_nDropIndex;
        }
      }
    }
    
    At this stage, it is now possible to insert above or below a row rather than the standard above functionality. The replace code is not yet fully in place.
  12. It is now necessary to modify the DropItemOnList function. I need to change this line:
  13.   lvi.iItem = (m_nModifiedDropIndex == -1) ?
                  pDropList->GetItemCount () :
                  m_nModifiedDropIndex;
    
    to use the Modified drop Index and the code beneath that to support Replace and Insert. Normally, there would be a separation between the data layer and the screen layer, but for the purposes of this example they are closely tied together.
      if (pDropList->m_eType == EReplace)
      {
        VERIFY (0 != pDropList->DeleteItem (m_nModifiedDropIndex));
    
        VERIFY (-1 != pDropList->InsertItem (&lvi));
    
        // select the new item we just inserted
        VERIFY (pDropList->SetItemState (lvi.iItem,
                                         LVIS_SELECTED,
                                         LVIS_SELECTED));
    
        // delete the original item (move operation)
        //VERIFY (DeleteItem (m_nDragIndex));
      }
    
      if (pDropList->m_eType == EInsert)
      {
        VERIFY (-1 != pDropList->InsertItem (&lvi));
    
        // select the new item we just inserted
        VERIFY (pDropList->SetItemState (lvi.iItem,
                                         LVIS_SELECTED,
                                         LVIS_SELECTED));
    
        // delete the original item (move operation)
        //VERIFY (DeleteItem (m_nDragIndex));
      }
    
  14. All that is now left to do is to call the function SetDropType that decides whether a Replace or Insert is possible. This is done in OnMouseMove by adding the call before the highlight line.
  15. SetDropType(point, (CListCtrlEx*)m_pDropWnd);
    
    pList->SetItemState (m_nDropIndex, LVIS_DROPHILITED,
                                       LVIS_DROPHILITED);
    

Downloads

Download demo project - 21 Kb


Comments

  • Urgent help needed

    Posted by Legacy on 07/09/2003 12:00am

    Originally posted by: Gary

    Hi,
    The list control is wonderful. But, I have one problem. Just as we have CBN_SELCHANGE message in combobox to trap the change of selection event, what event shall I use in this list control? Basically, I want to display related records in a different grid when the selection in list control changes. I could not find the event to do this.

    Please help me.

    Thanks,

    - Gary

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Companies undertaking an IT project need to find the right balance between cost and functionality. It's important to start by determining whether to build a solution from scratch, buy an out-of-the-box solution, or a combination of both. In reality, most projects will require some system tailoring to meet business requirements. Decision-makers must understand how much software development is enough and craft a detailed implementation plan to ensure the project's success. This white paper examines the different …

  • According to technology research firm Gartner, cloud computing will become the bulk of new IT spend by 20161. By the end of 2017, Gartner predicts that nearly half of large enterprises will have hybrid cloud deployments1. Learn how you can use these trends to your advantage by offering cloud and hybrid data solutions to your customers.

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date