Solved

ActiveX property array

Posted on 2001-08-21
6
351 Views
Last Modified: 2013-11-20
I want my control (MFC) to expose a property for use in VB like this:

MyControl1.Caption(1).Font = Whatever

Where there are lots of captions, and each caption itself can have several properties.  I've seen this in commercial controls (without source code), but can't work out how to do it.
0
Comment
Question by:IainHere
  • 3
  • 2
6 Comments
 
LVL 42

Expert Comment

by:sedgwick
ID: 6410344
here is a wrapper class for VB-like property listbox:

//header file
#if !defined(AFX_PROPERTYLIST_H__74205380_1B56_11D4_BC48_00105AA2186F__INCLUDED_)
#define AFX_PROPERTYLIST_H__74205380_1B56_11D4_BC48_00105AA2186F__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// PropertyList.h : header file
//

#define PIT_COMBO      0  //PIT = property item type
#define PIT_EDIT      1
#define PIT_COLOR      2
#define PIT_FONT      3
#define PIT_FILE      4

#define IDC_PROPCMBBOX   712
#define IDC_PROPEDITBOX  713
#define IDC_PROPBTNCTRL  714


/////////////////////////////////////////////////////////////////////////////
//CPropertyList Items
class CPropertyItem
{
// Attributes
public:
      CString m_propName;
      CString m_curValue;
      int m_nItemType;
      CString m_cmbItems;

public:
      CPropertyItem(CString propName, CString curValue,
                          int nItemType, CString cmbItems)
      {
            m_propName = propName;
            m_curValue = curValue;
            m_nItemType = nItemType;
            m_cmbItems = cmbItems;
      }
};

/////////////////////////////////////////////////////////////////////////////
// CPropertyList window

class CPropertyList : public CListBox
{
// Construction
public:
      CPropertyList();

// Attributes
public:

// Operations
public:
      int AddItem(CString txt);
      int AddPropItem(CPropertyItem* pItem);

// Overrides
      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(CPropertyList)
      public:
      virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
      virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
      protected:
      virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
      virtual void PreSubclassWindow();
      //}}AFX_VIRTUAL

// Implementation
public:
      virtual ~CPropertyList();

      // Generated message map functions
protected:
      //{{AFX_MSG(CPropertyList)
      afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
      afx_msg void OnSelchange();
      afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
      afx_msg void OnKillFocus(CWnd* pNewWnd);
      afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
      afx_msg void OnMouseMove(UINT nFlags, CPoint point);
      //}}AFX_MSG
      afx_msg void OnKillfocusCmbBox();
      afx_msg void OnSelchangeCmbBox();
      afx_msg void OnKillfocusEditBox();
      afx_msg void OnChangeEditBox();
      afx_msg void OnButton();

      DECLARE_MESSAGE_MAP()

      void InvertLine(CDC* pDC,CPoint ptFrom,CPoint ptTo);
      void DisplayButton(CRect region);

      CComboBox m_cmbBox;
      CEdit m_editBox;
      CButton m_btnCtrl;
      CFont m_SSerif8Font;
      
      int m_curSel,m_prevSel;
      int m_nDivider;
      int m_nDivTop;
      int m_nDivBtm;
      int m_nOldDivX;
      int m_nLastBox;
      BOOL m_bTracking;
      BOOL m_bDivIsSet;
      HCURSOR m_hCursorArrow;
      HCURSOR m_hCursorSize;
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_PROPERTYLIST_H__74205380_1B56_11D4_BC48_00105AA2186F__INCLUDED_)




//cpp file
// PropertyList.cpp : implementation file
//

#include "stdafx.h"
//#include "PropListBox.h"
#include "PropertyList.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CPropertyList

CPropertyList::CPropertyList()
{
}

CPropertyList::~CPropertyList()
{
}


BEGIN_MESSAGE_MAP(CPropertyList, CListBox)
      //{{AFX_MSG_MAP(CPropertyList)
      ON_WM_CREATE()
      ON_CONTROL_REFLECT(LBN_SELCHANGE, OnSelchange)
      ON_WM_LBUTTONUP()
      ON_WM_KILLFOCUS()
      ON_WM_LBUTTONDOWN()
      ON_WM_MOUSEMOVE()
      //}}AFX_MSG_MAP
      ON_CBN_KILLFOCUS(IDC_PROPCMBBOX, OnKillfocusCmbBox)
      ON_CBN_SELCHANGE(IDC_PROPCMBBOX, OnSelchangeCmbBox)
      ON_EN_KILLFOCUS(IDC_PROPEDITBOX, OnKillfocusEditBox)
      ON_EN_CHANGE(IDC_PROPEDITBOX, OnChangeEditBox)
      ON_BN_CLICKED(IDC_PROPBTNCTRL, OnButton)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CPropertyList message handlers

BOOL CPropertyList::PreCreateWindow(CREATESTRUCT& cs)
{
      if (!CListBox::PreCreateWindow(cs))
            return FALSE;

      cs.style &= ~(LBS_OWNERDRAWVARIABLE | LBS_SORT);
      cs.style |= LBS_OWNERDRAWFIXED;

      m_bTracking = FALSE;
      m_nDivider = 0;
      m_bDivIsSet = FALSE;

      return TRUE;
}

void CPropertyList::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
      lpMeasureItemStruct->itemHeight = 20; //pixels
}


void CPropertyList::DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
      CDC dc;
      dc.Attach(lpDIS->hDC);
      CRect rectFull = lpDIS->rcItem;
      CRect rect = rectFull;
      if (m_nDivider==0)
            m_nDivider = rect.Width() / 2;
      rect.left = m_nDivider;
      CRect rect2 = rectFull;
      rect2.right = rect.left - 1;
      UINT nIndex = lpDIS->itemID;

      if (nIndex != (UINT) -1)
      {
            //draw two rectangles, one for each row column
            dc.FillSolidRect(rect2,RGB(192,192,192));
            dc.DrawEdge(rect2,EDGE_SUNKEN,BF_BOTTOMRIGHT);
            dc.DrawEdge(rect,EDGE_SUNKEN,BF_BOTTOM);

            //get the CPropertyItem for the current row
            CPropertyItem* pItem = (CPropertyItem*) GetItemDataPtr(nIndex);

            //write the property name in the first rectangle
            dc.SetBkMode(TRANSPARENT);
            dc.DrawText(pItem->m_propName,CRect(rect2.left+3,rect2.top+3,
                                                                  rect2.right-3,rect2.bottom+3),
                              DT_LEFT | DT_SINGLELINE);

            //write the initial property value in the second rectangle
            dc.DrawText(pItem->m_curValue,CRect(rect.left+3,rect.top+3,
                                                                  rect.right+3,rect.bottom+3),
                              DT_LEFT | DT_SINGLELINE);
      }
      dc.Detach();
}

int CPropertyList::AddItem(CString txt)
{
      int nIndex = AddString(txt);
      return nIndex;
}

int CPropertyList::AddPropItem(CPropertyItem* pItem)
{
      int nIndex = AddString(_T(""));
      SetItemDataPtr(nIndex,pItem);
      return nIndex;
}

int CPropertyList::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
      if (CListBox::OnCreate(lpCreateStruct) == -1)
            return -1;

      m_bDivIsSet = FALSE;
      m_nDivider = 0;
      m_bTracking = FALSE;

      m_hCursorSize = AfxGetApp()->LoadStandardCursor(IDC_SIZEWE);
      m_hCursorArrow = AfxGetApp()->LoadStandardCursor(IDC_ARROW);

      m_SSerif8Font.CreatePointFont(80,_T("MS Sans Serif"));

      return 0;
}

void CPropertyList::OnSelchange()
{
      CRect rect;
      CString lBoxSelText;

      //m_curSel = GetCurSel();

      GetItemRect(m_curSel,rect);
      rect.left = m_nDivider;

      CPropertyItem* pItem = (CPropertyItem*) GetItemDataPtr(m_curSel);

      if (m_btnCtrl)
            m_btnCtrl.ShowWindow(SW_HIDE);

      if (pItem->m_nItemType==PIT_COMBO)
      {
            //display the combo box.  If the combo box has already been
            //created then simply move it to the new location, else create it
            m_nLastBox = 0;
            if (m_cmbBox)
                  m_cmbBox.MoveWindow(rect);
            else
            {      
                  rect.bottom += 100;
                  m_cmbBox.Create(CBS_DROPDOWNLIST | CBS_NOINTEGRALHEIGHT | WS_VISIBLE | WS_CHILD | WS_BORDER,
                                          rect,this,IDC_PROPCMBBOX);
                  m_cmbBox.SetFont(&m_SSerif8Font);
            }

            //add the choices for this particular property
            CString cmbItems = pItem->m_cmbItems;
            lBoxSelText = pItem->m_curValue;
            
            m_cmbBox.ResetContent();
            m_cmbBox.AddString("");            
            int i,i2;
            i=0;
            while ((i2=cmbItems.Find('|',i)) != -1)
            {
                  m_cmbBox.AddString(cmbItems.Mid(i,i2-i));
                  i=i2+1;
            }

            m_cmbBox.ShowWindow(SW_SHOW);
            m_cmbBox.SetFocus();

            //jump to the property's current value in the combo box
            int j = m_cmbBox.FindStringExact(0,lBoxSelText);
            if (j != CB_ERR)
                  m_cmbBox.SetCurSel(j);
            else
                  m_cmbBox.SetCurSel(0);
      }
      else if (pItem->m_nItemType==PIT_EDIT)
      {
            //display edit box
            m_nLastBox = 1;
            m_prevSel = m_curSel;
            rect.bottom -= 3;
            if (m_editBox)
                  m_editBox.MoveWindow(rect);
            else
            {      
                  m_editBox.Create(ES_LEFT | ES_AUTOHSCROLL | WS_VISIBLE | WS_CHILD | WS_BORDER,
                                          rect,this,IDC_PROPEDITBOX);
                  m_editBox.SetFont(&m_SSerif8Font);
            }

            lBoxSelText = pItem->m_curValue;

            m_editBox.ShowWindow(SW_SHOW);
            m_editBox.SetFocus();
            //set the text in the edit box to the property's current value
            m_editBox.SetWindowText(lBoxSelText);
      }
      else
            DisplayButton(rect);
}

void CPropertyList::DisplayButton(CRect region)
{
      //displays a button if the property is a file/color/font chooser
      m_nLastBox = 2;
      m_prevSel = m_curSel;

      if (region.Width() > 25)
            region.left = region.right - 25;
      region.bottom -= 3;

      if (m_btnCtrl)
            m_btnCtrl.MoveWindow(region);
      else
      {      
            m_btnCtrl.Create("...",BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD,
                                    region,this,IDC_PROPBTNCTRL);
            m_btnCtrl.SetFont(&m_SSerif8Font);
      }

      m_btnCtrl.ShowWindow(SW_SHOW);
      m_btnCtrl.SetFocus();
}

void CPropertyList::OnKillFocus(CWnd* pNewWnd)
{
      //m_btnCtrl.ShowWindow(SW_HIDE);

      CListBox::OnKillFocus(pNewWnd);
}

void CPropertyList::OnKillfocusCmbBox()
{
      m_cmbBox.ShowWindow(SW_HIDE);

      Invalidate();
}

void CPropertyList::OnKillfocusEditBox()
{
      CString newStr;
      m_editBox.ShowWindow(SW_HIDE);

      Invalidate();
}

void CPropertyList::OnSelchangeCmbBox()
{
      CString selStr;
      if (m_cmbBox)
      {
            m_cmbBox.GetLBText(m_cmbBox.GetCurSel(),selStr);
            CPropertyItem* pItem = (CPropertyItem*) GetItemDataPtr(m_curSel);
            pItem->m_curValue = selStr;
      }
}

void CPropertyList::OnChangeEditBox()
{
      CString newStr;
      m_editBox.GetWindowText(newStr);
      
      CPropertyItem* pItem = (CPropertyItem*) GetItemDataPtr(m_curSel);
      pItem->m_curValue = newStr;
}

void CPropertyList::OnButton()
{
      CPropertyItem* pItem = (CPropertyItem*) GetItemDataPtr(m_curSel);

      //display the appropriate common dialog depending on what type
      //of chooser is associated with the property
      if (pItem->m_nItemType == PIT_COLOR)
      {
            COLORREF initClr;
            CString currClr = pItem->m_curValue;
            //parse the property's current color value
            if (currClr.Find("RGB") > -1)
            {
                  int j = currClr.Find(',',3);
                  CString bufr = currClr.Mid(4,j-4);
                  int RVal = atoi(bufr);
                  int j2 = currClr.Find(',',j+1);
                  bufr = currClr.Mid(j+1,j2-(j+1));
                  int GVal = atoi(bufr);
                  int j3 = currClr.Find(')',j2+1);
                  bufr = currClr.Mid(j2+1,j3-(j2+1));
                  int BVal = atoi(bufr);
                  initClr = RGB(RVal,GVal,BVal);
            }
            else
                  initClr = 0;
            
            CColorDialog ClrDlg(initClr);
            
            if (IDOK == ClrDlg.DoModal())
            {
                  COLORREF selClr = ClrDlg.GetColor();
                  CString clrStr;
                  clrStr.Format("RGB(%d,%d,%d)",GetRValue(selClr),
                                    GetGValue(selClr),GetBValue(selClr));
                  m_btnCtrl.ShowWindow(SW_HIDE);

                  pItem->m_curValue = clrStr;
                  Invalidate();
            }
      }
      else if (pItem->m_nItemType == PIT_FILE)
      {
            CString SelectedFile;
            CString Filter("Gif Files (*.gif)|*.gif||");
      
            CFileDialog FileDlg(TRUE, NULL, NULL, NULL,
                  Filter);
            
            CString currPath = pItem->m_curValue;
            FileDlg.m_ofn.lpstrTitle = "Select file";
            if (currPath.GetLength() > 0)
                  FileDlg.m_ofn.lpstrInitialDir = currPath.Left(
                        currPath.GetLength() - currPath.ReverseFind('\\'));

            if(IDOK == FileDlg.DoModal())
            {
                  SelectedFile = FileDlg.GetPathName();
                  
                  m_btnCtrl.ShowWindow(SW_HIDE);
                  
                  pItem->m_curValue = SelectedFile;
                  Invalidate();
            }
      }
      else if (pItem->m_nItemType == PIT_FONT)
      {      
            CFontDialog FontDlg(NULL,CF_EFFECTS | CF_SCREENFONTS,NULL,this);
            
            if(IDOK == FontDlg.DoModal())
            {
                  CString faceName = FontDlg.GetFaceName();
                  
                  m_btnCtrl.ShowWindow(SW_HIDE);
                  
                  pItem->m_curValue = faceName;
                  Invalidate();
            }
      }
}

void CPropertyList::OnLButtonUp(UINT nFlags, CPoint point)
{
      if (m_bTracking)
      {
            //if columns were being resized then this indicates
            //that mouse is up so resizing is done.  Need to redraw
            //columns to reflect their new widths.
            
            m_bTracking = FALSE;
            //if mouse was captured then release it
            if (GetCapture()==this)
                  ::ReleaseCapture();

            ::ClipCursor(NULL);

            CClientDC dc(this);
            InvertLine(&dc,CPoint(point.x,m_nDivTop),CPoint(point.x,m_nDivBtm));
            //set the divider position to the new value
            m_nDivider = point.x;

            //redraw
            Invalidate();
      }
      else
      {
            BOOL loc;
            int i = ItemFromPoint(point,loc);
            m_curSel = i;
            CListBox::OnLButtonUp(nFlags, point);
      }
}

void CPropertyList::OnLButtonDown(UINT nFlags, CPoint point)
{
      if ((point.x>=m_nDivider-5) && (point.x<=m_nDivider+5))
      {
            //if mouse clicked on divider line, then start resizing

            ::SetCursor(m_hCursorSize);

            CRect windowRect;
            GetWindowRect(windowRect);
            windowRect.left += 10; windowRect.right -= 10;
            //do not let mouse leave the list box boundary
            ::ClipCursor(windowRect);
            
            if (m_cmbBox)
                  m_cmbBox.ShowWindow(SW_HIDE);
            if (m_editBox)
                  m_editBox.ShowWindow(SW_HIDE);

            CRect clientRect;
            GetClientRect(clientRect);

            m_bTracking = TRUE;
            m_nDivTop = clientRect.top;
            m_nDivBtm = clientRect.bottom;
            m_nOldDivX = point.x;

            CClientDC dc(this);
            InvertLine(&dc,CPoint(m_nOldDivX,m_nDivTop),CPoint(m_nOldDivX,m_nDivBtm));

            //capture the mouse
            SetCapture();
      }
      else
      {
            m_bTracking = FALSE;
            CListBox::OnLButtonDown(nFlags, point);
      }
}

void CPropertyList::OnMouseMove(UINT nFlags, CPoint point)
{      
      if (m_bTracking)
      {
            //move divider line to the mouse pos. if columns are
            //currently being resized
            CClientDC dc(this);
            //remove old divider line
            InvertLine(&dc,CPoint(m_nOldDivX,m_nDivTop),CPoint(m_nOldDivX,m_nDivBtm));
            //draw new divider line
            InvertLine(&dc,CPoint(point.x,m_nDivTop),CPoint(point.x,m_nDivBtm));
            m_nOldDivX = point.x;
      }
      else if ((point.x >= m_nDivider-5) && (point.x <= m_nDivider+5))
            //set the cursor to a sizing cursor if the cursor is over the row divider
            ::SetCursor(m_hCursorSize);
      else
            CListBox::OnMouseMove(nFlags, point);
}

void CPropertyList::InvertLine(CDC* pDC,CPoint ptFrom,CPoint ptTo)
{
      int nOldMode = pDC->SetROP2(R2_NOT);
      
      pDC->MoveTo(ptFrom);
      pDC->LineTo(ptTo);

      pDC->SetROP2(nOldMode);
}

void CPropertyList::PreSubclassWindow()
{
      m_bDivIsSet = FALSE;
      m_nDivider = 0;
      m_bTracking = FALSE;
      m_curSel = 1;

      m_hCursorSize = AfxGetApp()->LoadStandardCursor(IDC_SIZEWE);
      m_hCursorArrow = AfxGetApp()->LoadStandardCursor(IDC_ARROW);

      m_SSerif8Font.CreatePointFont(80,_T("MS Sans Serif"));
}




example of usage:
in your dialog add a listbox and change its type to - CPropertyList      m_propList;

also change its properties as follow:
selection: single
Owner draw: variable

the follow properties to to be checked:
Has strings,
Notify,
Border,
Vertical scroll,
No integral Height,
Modal frame.


add the follow data members to your CDialog:

      CPropertyItem *propItem1,*propItem2,*propItem3,*propItem4,*propItem5;
      CPropertyItem *propItem6,*propItem7,*propItem8,*propItem9,*propItem10;
      CPropertyItem *propItem11,*propItem12,*propItem13,*propItem14,*propItem15;
      CPropertyItem *propItem16,*propItem17;


and add them in the CYourDialog::OnInitDialog() :

      //Add properties to the property list box

      propItem1 = new CPropertyItem("ToolTip Text","",PIT_EDIT,"");
      m_propList.AddPropItem(propItem1);

      propItem2 = new CPropertyItem("Enabled","true",PIT_COMBO,"true|false|");
      m_propList.AddPropItem(propItem2);

      propItem3 = new CPropertyItem("Visible","true",PIT_COMBO,"true|false|");
      m_propList.AddPropItem(propItem3);

      propItem4 = new CPropertyItem("Fore. Color","",PIT_COLOR,"");
      m_propList.AddPropItem(propItem4);
      
      propItem5 = new CPropertyItem("Back. Color","",PIT_COLOR,"");
      m_propList.AddPropItem(propItem5);
      
      propItem6 = new CPropertyItem("Opaque","false",PIT_COMBO,"true|false|");
      m_propList.AddPropItem(propItem6);
      
      propItem7 = new CPropertyItem("Auto. Scroll","true",PIT_COMBO,"true|false|");
      m_propList.AddPropItem(propItem7);
      
      propItem8 = new CPropertyItem("Double Buffered","true",PIT_COMBO,"true|false|");
      m_propList.AddPropItem(propItem8);
      
      propItem9 = new CPropertyItem("Font","",PIT_FONT,"");
      m_propList.AddPropItem(propItem9);

      propItem10 = new CPropertyItem("Text","",PIT_EDIT,"");
      m_propList.AddPropItem(propItem10);

      propItem11 = new CPropertyItem("Horiz. Align","CENTER",PIT_COMBO,"CENTER|LEFT|RIGHT|");
      m_propList.AddPropItem(propItem11);
      
      propItem12 = new CPropertyItem("Vert. Align","CENTER",PIT_COMBO,"CENTER|TOP|BOTTOM|");
      m_propList.AddPropItem(propItem12);

      propItem13 = new CPropertyItem("Icon","",PIT_FILE,"");
      m_propList.AddPropItem(propItem13);

      propItem14 = new CPropertyItem("Border Painted","",PIT_COMBO,"true|false|");
      m_propList.AddPropItem(propItem14);

      propItem15 = new CPropertyItem("Fill Content Area","",PIT_COMBO,"true|false|");
      m_propList.AddPropItem(propItem15);

      propItem16 = new CPropertyItem("Focus Painted","",PIT_COMBO,"true|false|");
      m_propList.AddPropItem(propItem16);

      propItem17 = new CPropertyItem("Horiz. Text Pos.","RIGHT",PIT_COMBO,"RIGHT|LEFT|CENTER|LEADING|TRAILING|");
      m_propList.AddPropItem(propItem17);


also in your dialog's destructor add this:
      delete propItem1;
      delete propItem2;
      delete propItem3;
      delete propItem4;
      delete propItem5;
      delete propItem6;
      delete propItem7;
      delete propItem8;
      delete propItem9;
      delete propItem10;
      delete propItem11;
      delete propItem12;
      delete propItem13;
      delete propItem14;
      delete propItem15;
      delete propItem16;
      delete propItem17;


good luck


0
 
LVL 4

Author Comment

by:IainHere
ID: 6410374
sedgwick,

That's a lot of info, and I might have missed it in there, but I'm looking for help with a VB interface for an MFC ActiveX control.

When I've dropped my control on the VB form, I want one of the properties to be an array of objects that contain several other properties.  Referring to my example above

MyControl1.Caption(1).Font = Whatever

there would be, say, 10 Captions available, and this would be the method for setting each of their Fonts, sizes, colo[u]rs etc.

Thanks for your help.
0
 
LVL 7

Expert Comment

by:peterchen092700
ID: 6410584
The general idea:
You expose each Caption as instance of another "Caption" COM object. Caption cannot created by a CLSID (i.e. it's not listed in the IDL's "library section", and deliberately removed from the place where items <are listed for GetClassObject - I know only in ATL); rather, Caption can only be created by the Listbox object.

You object holds a list of Caption objects, and the Caption array property just returns the requested one.

Further thing to take care of: Expose the "Captions" property as collection (dunno how in MFC), so client can "for each Caption in MyControl.Captions".

Peter
0
6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

 
LVL 4

Author Comment

by:IainHere
ID: 6412600
Excellent - I think I understand now:  the property that I want to be an array has itself to be a COM object.  Do you know of any links to example code / explanations of how to do it? (In ATL would be fine, because I think I'll use ATL for them)

Thanks.
0
 
LVL 7

Accepted Solution

by:
peterchen092700 earned 50 total points
ID: 6413081
I don't know any samples, but if you can get create normal object, you can go along this line, after creating your ListBox object:

1. Add a new Caption object (Add new ATL Object, Simple COM, and Dual Interface I guess)

2. remove the objects definition from the Library block in the .IDL file
3. add a line interface ICaption to the library block of the IDL
4. remove the Caption's entry from the OBJECT map in the <project.cpp> file.

2-4 make sure the object isn't creatable from outside (i.e. though CoCreateInstance), but only through the ListBox component

you need to decide on design:
a) the ListBox component always holds an Array of Caption COM objects, which your property implementation just hands out.

b) the Caption object holds a reference to it's ListBox, and an Index (or the like) for which caption it's responsible. The Caption would implement a function like SetTile(BSTR text) by simply calling a ListBox.SetText(long index, BSTR text) function.

In your scenario, a) sounds better.
In scenario a) however, you need to be careful not to create a reference lock, i.e. ListBox has a pointer to Caption, and Caption a pointer to ListBox. When both references are ref counted, the components will never go out of memory, so one of the references must be a weak reference (no AddRef called for this). Since the Caption depends on the list box, it sounds reasonable to have the Caption having a weak reference to the ListBox, and the ListBox having a strong reference to the Caption

to Create an instance of the Caption programatically in ATL, use:

HRESULT hr;
CComObject<CCaption> *pCaption;
hr = CComObject<CCaption>::CreateInstance(&pCaption);

In scenario a) it would go on like

pCaption->AddRef();  // "lock" the component
pCaption->SetListBox(this); // or whatever method you use for establishing the connection...

You would create one of these for each caption, and manage them in a list<CCaption*>.

To destroy one, you would call
pCaption->SetListBox(0); // "there is no more listbox for you"
pCaption->Release();

note that the Caption component, if it has no ListBox anymore, must "degrade gracefully" - either return an error code, or do nothing.

Your get_Caption(index, IUnknown ** itf) function would look like:

*itf = static_cast<IUnknown *>(m_captions[index]);
(*itf)->AddRef();

So far... hope this gets you started
Peter
0
 
LVL 4

Author Comment

by:IainHere
ID: 6413147
That's simply wonderful.  Thanks.
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Powershell based monitoring system 2 127
dog bark java program 15 82
maven archtype selection in eclipse 1 51
changeXy challenge 13 58
Introduction: Load and Save to file, Document-View interaction inside the SDI. Continuing from the second article about sudoku.   Open the project in visual studio. From the class view select CSudokuDoc and double click to open the header …
Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now