IainHere
asked on
ActiveX property array
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.
MyControl1.Caption(1).Font
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.
ASKER
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.
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
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.
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
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
ASKER
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.
Thanks.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
That's simply wonderful. Thanks.
//header file
#if !defined(AFX_PROPERTYLIST_
#define AFX_PROPERTYLIST_H__742053
#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*
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CPropertyL
public:
virtual void MeasureItem(LPMEASUREITEMS
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
protected:
virtual BOOL PreCreateWindow(CREATESTRU
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_
//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::CPropertyLi
{
}
CPropertyList::~CPropertyL
{
}
BEGIN_MESSAGE_MAP(CPropert
//{{AFX_MSG_MAP(CPropertyL
ON_WM_CREATE()
ON_CONTROL_REFLECT(LBN_SEL
ON_WM_LBUTTONUP()
ON_WM_KILLFOCUS()
ON_WM_LBUTTONDOWN()
ON_WM_MOUSEMOVE()
//}}AFX_MSG_MAP
ON_CBN_KILLFOCUS(IDC_PROPC
ON_CBN_SELCHANGE(IDC_PROPC
ON_EN_KILLFOCUS(IDC_PROPED
ON_EN_CHANGE(IDC_PROPEDITB
ON_BN_CLICKED(IDC_PROPBTNC
END_MESSAGE_MAP()
//////////////////////////
// CPropertyList message handlers
BOOL CPropertyList::PreCreateWi
{
if (!CListBox::PreCreateWindo
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->itemH
}
void CPropertyList::DrawItem(LP
{
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
dc.DrawEdge(rect2,EDGE_SUN
dc.DrawEdge(rect,EDGE_SUNK
//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_propN
rect2.right-3,rect2.bottom
DT_LEFT | DT_SINGLELINE);
//write the initial property value in the second rectangle
dc.DrawText(pItem->m_curVa
rect.right+3,rect.bottom+3
DT_LEFT | DT_SINGLELINE);
}
dc.Detach();
}
int CPropertyList::AddItem(CSt
{
int nIndex = AddString(txt);
return nIndex;
}
int CPropertyList::AddPropItem
{
int nIndex = AddString(_T(""));
SetItemDataPtr(nIndex,pIte
return nIndex;
}
int CPropertyList::OnCreate(LP
{
if (CListBox::OnCreate(lpCrea
return -1;
m_bDivIsSet = FALSE;
m_nDivider = 0;
m_bTracking = FALSE;
m_hCursorSize = AfxGetApp()->LoadStandardC
m_hCursorArrow = AfxGetApp()->LoadStandardC
m_SSerif8Font.CreatePointF
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_HI
if (pItem->m_nItemType==PIT_C
{
//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_DROPDO
rect,this,IDC_PROPCMBBOX);
m_cmbBox.SetFont(&m_SSerif
}
//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))
{
m_cmbBox.AddString(cmbItem
i=i2+1;
}
m_cmbBox.ShowWindow(SW_SHO
m_cmbBox.SetFocus();
//jump to the property's current value in the combo box
int j = m_cmbBox.FindStringExact(0
if (j != CB_ERR)
m_cmbBox.SetCurSel(j);
else
m_cmbBox.SetCurSel(0);
}
else if (pItem->m_nItemType==PIT_E
{
//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_SSeri
}
lBoxSelText = pItem->m_curValue;
m_editBox.ShowWindow(SW_SH
m_editBox.SetFocus();
//set the text in the edit box to the property's current value
m_editBox.SetWindowText(lB
}
else
DisplayButton(rect);
}
void CPropertyList::DisplayButt
{
//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(regio
else
{
m_btnCtrl.Create("...",BS_
region,this,IDC_PROPBTNCTR
m_btnCtrl.SetFont(&m_SSeri
}
m_btnCtrl.ShowWindow(SW_SH
m_btnCtrl.SetFocus();
}
void CPropertyList::OnKillFocus
{
//m_btnCtrl.ShowWindow(SW_
CListBox::OnKillFocus(pNew
}
void CPropertyList::OnKillfocus
{
m_cmbBox.ShowWindow(SW_HID
Invalidate();
}
void CPropertyList::OnKillfocus
{
CString newStr;
m_editBox.ShowWindow(SW_HI
Invalidate();
}
void CPropertyList::OnSelchange
{
CString selStr;
if (m_cmbBox)
{
m_cmbBox.GetLBText(m_cmbBo
CPropertyItem* pItem = (CPropertyItem*) GetItemDataPtr(m_curSel);
pItem->m_curValue = selStr;
}
}
void CPropertyList::OnChangeEdi
{
CString newStr;
m_editBox.GetWindowText(ne
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,%
GetGValue(selClr),GetBValu
m_btnCtrl.ShowWindow(SW_HI
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.lpstrInitial
currPath.GetLength() - currPath.ReverseFind('\\')
if(IDOK == FileDlg.DoModal())
{
SelectedFile = FileDlg.GetPathName();
m_btnCtrl.ShowWindow(SW_HI
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_HI
pItem->m_curValue = faceName;
Invalidate();
}
}
}
void CPropertyList::OnLButtonUp
{
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(poin
//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(nFla
}
}
void CPropertyList::OnLButtonDo
{
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_HID
if (m_editBox)
m_editBox.ShowWindow(SW_HI
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_nO
//capture the mouse
SetCapture();
}
else
{
m_bTracking = FALSE;
CListBox::OnLButtonDown(nF
}
}
void CPropertyList::OnMouseMove
{
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_nO
//draw new divider line
InvertLine(&dc,CPoint(poin
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(nFla
}
void CPropertyList::InvertLine(
{
int nOldMode = pDC->SetROP2(R2_NOT);
pDC->MoveTo(ptFrom);
pDC->LineTo(ptTo);
pDC->SetROP2(nOldMode);
}
void CPropertyList::PreSubclass
{
m_bDivIsSet = FALSE;
m_nDivider = 0;
m_bTracking = FALSE;
m_curSel = 1;
m_hCursorSize = AfxGetApp()->LoadStandardC
m_hCursorArrow = AfxGetApp()->LoadStandardC
m_SSerif8Font.CreatePointF
}
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,*pro
CPropertyItem *propItem6,*propItem7,*pro
CPropertyItem *propItem11,*propItem12,*p
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(pro
propItem2 = new CPropertyItem("Enabled","t
m_propList.AddPropItem(pro
propItem3 = new CPropertyItem("Visible","t
m_propList.AddPropItem(pro
propItem4 = new CPropertyItem("Fore. Color","",PIT_COLOR,"");
m_propList.AddPropItem(pro
propItem5 = new CPropertyItem("Back. Color","",PIT_COLOR,"");
m_propList.AddPropItem(pro
propItem6 = new CPropertyItem("Opaque","fa
m_propList.AddPropItem(pro
propItem7 = new CPropertyItem("Auto. Scroll","true",PIT_COMBO,"
m_propList.AddPropItem(pro
propItem8 = new CPropertyItem("Double Buffered","true",PIT_COMBO
m_propList.AddPropItem(pro
propItem9 = new CPropertyItem("Font","",PI
m_propList.AddPropItem(pro
propItem10 = new CPropertyItem("Text","",PI
m_propList.AddPropItem(pro
propItem11 = new CPropertyItem("Horiz. Align","CENTER",PIT_COMBO,
m_propList.AddPropItem(pro
propItem12 = new CPropertyItem("Vert. Align","CENTER",PIT_COMBO,
m_propList.AddPropItem(pro
propItem13 = new CPropertyItem("Icon","",PI
m_propList.AddPropItem(pro
propItem14 = new CPropertyItem("Border Painted","",PIT_COMBO,"tru
m_propList.AddPropItem(pro
propItem15 = new CPropertyItem("Fill Content Area","",PIT_COMBO,"true|f
m_propList.AddPropItem(pro
propItem16 = new CPropertyItem("Focus Painted","",PIT_COMBO,"tru
m_propList.AddPropItem(pro
propItem17 = new CPropertyItem("Horiz. Text Pos.","RIGHT",PIT_COMBO,"R
m_propList.AddPropItem(pro
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