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

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds