One-To-Many Relationship Between Two List Controls

Environment: Visual C++ 6.0 with Service Pack 5.

Introduction

The typical task for constructing of relationship between two data structures, one of which is the master and the other is the slave, is the realization of a one-to-many relationship. To exclude the possibility of terminological ambiguity, we shall specify that under a one-to-many relationship, we understand the relationship between two arrays of data structures, when one element of the master array can be related to one or more elements of slave array. In this article, you'll find the example of data structures for realization of such a relationship and the possible variant of its visualization.

Data Structures for Realization of a One-To-Many Relationship

For storing data about the elements of the parent and child array, the class CItemInfo with the data members key_id, parent_id, and item_name are used. We shall specify the parent and child array as the pointers to elements CItemInfo and for this purpose we define the next type:

typedef CTypedPtrArray<CPtrArray, CItemInfo*> CItemPtrArray;

First, we define the parent array:

CItemPtrArray m_ptrMasterArray;

And then, the child array:

CItemPtrArray m_ptrChildArray;

To set the relationship between the elements of the m_ptrMasterArray and m_ptrChildArray arrays, we shall use the CMapWordToOb class. The idea is to set the second element of the map for every unique key key_id of the element of array m_ptrMasterArray as the array CItemPtrArray, the elements of which will be the pointers to the elements of the m_ptrChildArray array that corresponds to this key. We define the copy CMapWordToOb as the index of relationship between parent and child arrays:

CMapWordToOb m_idxChildIndex;

Creation of index element:

CItemInfo* pItemMaster = new CItemInfo();
...
m_idxChildIndex.SetAt(pItemMaster->key_id, new CItemPtrArray);

Adding to the index of a pointer to the related child element:

CItemInfo* pItemChild = new CItemInfo();
...
CItemPtrArray* pItm;
m_idxChildIndex.Lookup(pItemMaster->key_id, (CObject*&)pItm);
pItm->Add(pItemChild);

Visualization of a One-To-Many Relationship

To display the elements of both arrays, we will use two list controls. For the m_ptrMasterArray array, we will use the list control:

CListCtrl m_ctrlFirstList;

And, for the m_ptrChildArray array, we will use:

CListCtrl m_ctrlSecondList;

We fill in the arrays and list controls with test data; parallel to it, we form the index for the relationship between our arrays:

void CRelOneToManyDlg::FillList()
{
  int master_count = 0;
  int child_count  = 0;
  CString str;

 for (int i = 1; i <= 12; i++)
  {
    CItemInfo* pItemMaster = new CItemInfo();
    pItemMaster->key_id = master_count;
    str.Format("%d", pItemMaster->key_id);
    pItemMaster->item_name = "Master Item #" + str;

    m_ctrlFirstList.InsertItem(pItemMaster->key_id,
                               pItemMaster->item_name);
    str.Format("%d", pItemMaster->key_id);
    m_ctrlFirstList.SetItemText(pItemMaster->key_id, 1, str);
    str.Format("%d", pItemMaster->parent_id);
    m_ctrlFirstList.SetItemText(pItemMaster->key_id, 2, str);

    m_ptrMasterArray.Add(pItemMaster);

    // create an element of index
    m_idxChildIndex.SetAt(pItemMaster->key_id, new CItemPtrArray);

    // child elements - only for odd parent elements
    if (pItemMaster->key_id % 2 == 1)
    {
      for (int j = 1; j <= 4; j++)
      {
        CItemInfo* pItemChild = new CItemInfo();
        pItemChild->key_id = child_count;
        pItemChild->parent_id = pItemMaster->key_id;
        str.Format("Sub #%d Master #%d", pItemChild->key_id,
                   pItemChild->parent_id);
        pItemChild->item_name = "Child Item: " + str;

        m_ctrlSecondList.InsertItem(pItemChild->key_id,
                                    pItemChild->item_name);
        str.Format("%d", pItemChild->key_id);
        m_ctrlSecondList.SetItemText(pItemChild->key_id, 1, str);
        str.Format("%d", pItemChild->parent_id);
        m_ctrlSecondList.SetItemText(pItemChild->key_id, 2, str);

        m_ptrChildArray.Add(pItemChild);

        // add a child element to the array
        CItemPtrArray* pItm;
        m_idxChildIndex.Lookup(pItemMaster->key_id,
                               (CObject*&)pItm);
        pItm->Add(pItemChild);

        child_count++;
      }
    }

    master_count++;
  }
}

Consider two ways to visualize a one-to-many relationship: Display for every element of parent array of only those related to it in the child array ("Show Related" mode), and highlighting the elements of the child array that are related to the current element of the parent array ("Select Related" mode). For completeness, we also show the realization of the "Show All" mode. To switch the modes of visualization, we use a combo box

CComboBox m_ctrlRelMode;

filled with the following elements:

m_ctrlRelMode.AddString("Show All");
m_ctrlRelMode.AddString("Show Related");
m_ctrlRelMode.AddString("Select Related");
m_ctrlRelMode.SetCurSel(1);

To trace the current mode of visualization, we define the variable:

int m_nRelMode;

"Show Related" Mode

In this mode, in the m_ctrlSecondList list control will be shown only those elements of array m_ptrChildArray that are child elements for the m_ptrMasterArray element that is selected at this moment in the list control m_ctrlFirstList. This mode is realized with the help of the following function:

void CRelOneToManyDlg::ShowRel(int nCurItem)
{
  m_ctrlSecondList.SetRedraw(FALSE);
  CWaitCursor wait;
  m_ctrlSecondList.DeleteAllItems();

  if (nCurItem == -1)
  {
    m_ctrlSecondList.UpdateWindow();
    m_ctrlSecondList.SetRedraw(TRUE);
    return;
  }

  CItemPtrArray* pItm;

  if (m_idxChildIndex.Lookup(nCurItem, (CObject*&)pItm))
  {
    int cnt = pItm->GetSize();

    if (cnt == 0)
    {
      m_ctrlSecondList.UpdateWindow();
      m_ctrlSecondList.SetRedraw(TRUE);
      return;
    }

    CItemInfo* pItemChild;
    CString str;
    int child_count = 0;

    for (int i = 0; i < cnt; i++)
    {
      pItemChild = pItm->GetAt(i);
      m_ctrlSecondList.InsertItem(child_count,
                                  pItemChild->item_name);
      str.Format("%d", pItemChild->key_id);
      m_ctrlSecondList.SetItemText(child_count, 1, str);
      str.Format("%d", pItemChild->parent_id);
      m_ctrlSecondList.SetItemText(child_count, 2, str);

      child_count++;
    }
  }

  m_ctrlSecondList.UpdateWindow();
  m_ctrlSecondList.SetRedraw(TRUE);
}

"Select Related" Mode

In this mode, in the m_ctrlSecondList list control will be highlighted those elements of the m_ptrChildArray array that are child elements for the m_ptrMasterArray element that is selected at this moment in the m_ctrlFirstList list control. This mode is realized with the help of the following function:

void CRelOneToManyDlg::SelectRel(int nCurItem)
{
  if (nCurItem == -1)
    return;

  POSITION pos = m_ctrlSecondList.GetFirstSelectedItemPosition();

  if (pos != NULL)
  {
    while (pos)
    {
      int nItem = m_ctrlSecondList.GetNextSelectedItem(pos);
      m_ctrlSecondList.SetItemState(nItem, ~LVIS_SELECTED,
                                    LVIS_SELECTED);
    }
  }

  CItemPtrArray* pItm;

  if (m_idxChildIndex.Lookup(nCurItem, (CObject*&)pItm))
  {
    int cnt = pItm->GetSize();

    if (cnt == 0)
      return;

    CItemInfo* pItemChild;

    for (int i = 0; i < cnt; i++)
    {
      pItemChild = pItm->GetAt(i);
      m_ctrlSecondList.SetItemState(pItemChild->key_id,
                                    LVIS_SELECTED, LVIS_SELECTED);
    }

    m_ctrlSecondList.EnsureVisible(pItemChild->key_id, 0);
  }

}

"Show All" Mode

In this mode, the m_ctrlSecondList list control is filled with all the elements of the m_ptrChildArray array. It is realized with the help of the following function:

void CRelOneToManyDlg::ShowAll()
{
  m_ctrlSecondList.SetRedraw(FALSE);
  CWaitCursor wait;
  m_ctrlSecondList.DeleteAllItems();

  CString str;

  int cnt = m_ptrChildArray.GetSize();
  for (int i = 0; i < cnt; i++)
  {
    CItemInfo* pItem = m_ptrChildArray.GetAt(i);
    m_ctrlSecondList.InsertItem(pItem->key_id, pItem->item_name);
    str.Format("%d", pItem->key_id);
    m_ctrlSecondList.SetItemText(pItem->key_id, 1, str);
    str.Format("%d", pItem->parent_id);
    m_ctrlSecondList.SetItemText(pItem->key_id, 2, str);
  }

  _ctrlSecondList.UpdateWindow();
  m_ctrlSecondList.SetRedraw(TRUE);
}

Switching Visualization Modes

The following function provides the switching of visualization modes:

void CRelOneToManyDlg::OnSelchangeRel()
{
  int nNewMode = m_ctrlRelMode.GetCurSel();

  if (nNewMode == m_nRelMode)
    return;

  if (nNewMode == 1)    // Show all
  {
    if (m_nRelMode == 3)
    {
      POSITION pos =
               m_ctrlSecondList.GetFirstSelectedItemPosition();
      if (pos != NULL)
      {
        while (pos)
        {
          int nItem = m_ctrlSecondList.GetNextSelectedItem(pos);
          m_ctrlSecondList.SetItemState(nItem, ~LVIS_SELECTED,
                                        LVIS_SELECTED);
        }
      }
      m_nRelMode = nNewMode;
      return;
    }
    else
    {
      m_nRelMode = nNewMode;
      ShowAll();
      return;
    }
  }

  POSITION pos = m_ctrlFirstList.GetFirstSelectedItemPosition();
  int nItem;

  if (pos != NULL)
    nItem = m_ctrlFirstList.GetNextSelectedItem(pos);
  else
    nItem = -1;

  switch (nNewMode)
  {
    case 2:    // Show Related
      m_nRelMode = nNewMode;
      ShowRel(nItem);
      break;
    case 3:    // Select Related
      if (m_nRelMode == 2)
        ShowAll();
      m_nRelMode = nNewMode;
      SelectRel(nItem);
      break;
  }
}

Changing of the Current Item of Master List Control

To change the current item in the m_ctrlFirstList master list control, the following function is realized:

void CRelOneToManyDlg::OnItemchangedFirstList(NMHDR* pNMHDR,
                                              LRESULT* pResult)
{
  NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;

  if (pNMListView->uNewState & LVIS_FOCUSED)
  {
    int cur_item = pNMListView->iItem;

    if (m_nRelMode == 2)
      ShowRel(cur_item);

    if (m_nRelMode == 3)
      SelectRel(cur_item);
  }

  *pResult = 0;
}

Conclusion

The described method of realizing a one-to-many relationship doesn't need considerable overhead expenses concerning the use of memory and provides sufficient speed of search of related data. The above described methods were successfully used in the project Search of Similar Files.

Downloads

Download demo project - 12 Kb


Comments

  • Interesting

    Posted by Legacy on 10/17/2003 12:00am

    Originally posted by: Mark R.

    Just wanted to say thanks for your thoughts on this topic. I've recently been experimenting with such relationships in a project I'm working on, and your article caused me to consider options I hadn't previously.

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

Top White Papers and Webcasts

  • Live Event Date: March 19, 2015 @ 1:00 p.m. ET / 10:00 a.m. PT The 2015 Enterprise Mobile Application Survey asked 250 mobility professionals what their biggest mobile challenges are, how many employees they are equipping with mobile apps, and their methods for driving value with mobility. Join Dan Woods, Editor and CTO of CITO Research, and Alan Murray, SVP of Products at Apperian, as they break down the results of this survey and discuss how enterprises are using mobile application management and private …

  • Remember getting your first box of LEGOS as a kid? How fun it was putting the pieces together, collaborating with your friends to create something new? Now, as an IT professional, assembling and maintaining a Lego-like collaboration infrastructure isn't what you signed up for. Piecing together disparate systems of record for email, web meetings and other applications is about as painful as stepping on a pile of Legos. Download the e-book to learn how implementing a collaboration system connects systems of …

Most Popular Programming Stories

More for Developers

RSS Feeds

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