Click here to Skip to main content
15,891,033 members
Articles / Desktop Programming / MFC

One-To-Many Relationship between Two List Controls

Rate me:
Please Sign up or sign in to vote.
4.08/5 (9 votes)
27 Sep 2003CPOL3 min read 61.2K   338   29   2
In this article the realization of one-to-many relationship between two list controls is described.

Image 1

Introduction

The typical task for constructing relationships between two data structures, one of which is master and the other is slave, is the realization of one-to-many relationship. To exclude the possibility of terminological ambiguity we shall specify that under 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 relationships and the possible variant of its visualization.

Data Structures for Realization of 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 is 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 arrays m_ptrMasterArray and m_ptrChildArray we shall use the class CMapWordToOb. 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 array m_ptrChildArray 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 index of 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 One-To-Many Relationship

To display the elements of both arrays we will use two list controls.

For the array m_ptrMasterArray we will use the list control:

CListCtrl  m_ctrlFirstList;

And for the array m_ptrChildArray 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 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 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 of visualization of one-to-many relationship: display for every element of parent array of only related to it in child array ("Show Related" mode) and highlighting of elements of child array that are related to the current element of parent array ("Select Related" mode).

For completeness we also show the realization of "Show All" mode.

To switch the modes of visualization we use 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 list control m_ctrlSecondList will be shown only those elements of array m_ptrChildArray that are child elements for element m_ptrMasterArray 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 list control m_ctrlSecondList will be highlighted those elements of array m_ptrChildArray that are child elements for element m_ptrMasterArray 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::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 list control m_ctrlSecondList is filled with all the elements of array m_ptrChildArray.

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);
  }

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

Switching of 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 master list control m_ctrlFirstList 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 realization of 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Russian Federation Russian Federation
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralExmple code leaks memory Pin
Dave Cullen9-Feb-07 7:55
Dave Cullen9-Feb-07 7:55 
GeneralRe: Exmple code leaks memory - fixed Pin
kezhu4-Oct-07 16:35
kezhu4-Oct-07 16:35 
Hello:

This is a nice example though it has some memory leaks. Following codes fixed the memory leaks.

1. Modify RelOneToManyDlg.h
...
public: // ADD THIS LINE
virtual ~CRelOneToManyDlg(); // ADD THIS LINE
// Construction
public:
void Clear(); // ADD THIS LINE

2. Add destructor codes to RelOneToManyDlg.cpp and codes for Clear()

CRelOneToManyDlg::~CRelOneToManyDlg()
{
Clear();
}

.....

void CRelOneToManyDlg::Clear()
{
int i;

for ( i = 0; i < m_idxChildIndex.GetCount(); i++)
delete (CItemPtrArray *)m_idxChildIndex[i];

for ( i = 0; i < m_ptrMasterArray.GetSize(); i++ )
delete (CItemInfo *)m_ptrMasterArray[i];

for ( i = 0; i < m_ptrChildArray.GetSize(); i++ )
delete (CItemInfo *)m_ptrChildArray[i];

m_idxChildIndex.RemoveAll();
m_ptrMasterArray.RemoveAll();
m_ptrChildArray.RemoveAll();
}

Cheers,

Ke

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.