Solved

User pointer in GUI controls

Posted on 2000-02-13
18
276 Views
Last Modified: 2010-04-10
Do GUI controls in Visual C++ have a spare pointer/property that the programmer can use to point to his own object?

In Delphi, there is - and its called a 'tag' property.

It would typically be available for each GUI control.
0
Comment
Question by:abulka
18 Comments
 
LVL 1

Expert Comment

by:mahno
Comment Utility
Right mouse button click on the control -> Class Wizard -> Member Variables -> Add Variable

mahno
0
 
LVL 30

Expert Comment

by:Zoppo
Comment Utility
Hi abulka,

each control is a windowsm so each control has at least a unique window handle of type HWND.

You can achive this handle for controls using GetDlgItem() passing the parent's window handle and the control's id.

mahno showed a way how this can be done for dialogs or form views in MFC by subclassing the controls ...

In MFC you can also use CWnd::GetDlgItem() to obtain a CWnd-pointer to the control, but this MUST not be stored for later use, because it's a temporary CWnd pointer which later is released in framework's idle time processing. But, you can also get the HWND handle from a CWnd object via CWnd::GetSafeHwnd() and create a 'safe' (none-temporary) CWnd pointer with CWnd::FromHandlePermanent().

hope that helps,

ZOPPO
0
 
LVL 6

Expert Comment

by:graham_k
Comment Utility
why are you using MS VC-- ? Use Borland C++ Builder and have all the comfort of Delphi (including Delphi component reuse) _and_ you'll get your tag field.
0
 
LVL 1

Expert Comment

by:ScottyDawg
Comment Utility
Hi,

Yes most do. For instance, for each item you insert into a listctrl you can associate a user defined item (DWORD) with it. Normally, the name of the function is SetItemData(...) which accepts as its parameters the item whose data you wish to set and the data item itself.

i.e. ListCtrl.SetItemData(10, 1001);

Which would set the 10th item in the list to have a data item = 1001.

Of course, you're more likely to put something meaningful into here, such as a pointer to a user defined item.
0
 

Author Comment

by:abulka
Comment Utility
graham_k - I do actualy use Delphi / C++ Builder and recognise the ease of that.  I have to understand how to emulate the tag feature for VC++ programmers, too, however.

ScottyDawg - associating data with each element in a list is great - but wouldn't you have to cast/uncast the value into an integer?  Also, could your technique be used for a static text box, edit control or button?

Mahno - this subclassing trick sounds very clean.  Though it seems like you can only add one variable per control, and for a IDC_BUTTON1 the type must be CButton.  So how would I add a property called m_tag to a button which could hold a pointer to any object - or is it up to me to do some casting?

EVERYONE: I also got this technique as a possible solution from a friend - any comments on its validity?:  Apparently you can simulate a "tag" property in VC++ using SetWindowLong (and GetWindowLong) to store a required pointer. SetWindowLong(HWND hWnd, int index, LONG lData) where index = GWL_USERDATA 

thanks so far guys,
-Andy
0
 

Author Comment

by:abulka
Comment Utility
I'd like a more comprehensive soution, that can be used with any GUI control, not just list items - though thanks for that tip anyway.
0
 
LVL 30

Expert Comment

by:Zoppo
Comment Utility
hi abulka,

I'm not sure what this 'tag' is with Delphi/C++ Builder, but if I understand you correct, I think you want to be able to save some custom data with a control. Is this correct?

If so, there are generally two possibilities:

1. Derive your own class from the control's class, i.e.:
class CMyButton : public CButton
{
....
}
and add any data and functionality you need. Then you only will have to subclass the control with an object of this class. How this can be done is similar to mahno's description, just select the new type (CMyButton) in the 'Variable Type' combo box of the 'Add Member Variable' dialog and make sure the needed files are included. Or you can use CWnd::SubclassWindow() to subclass it directly.
This method has the advantage that you don't have to care about memory allocation/freeing.

2. You can create an instance of an data object on heap and set its pointer as user data  using ::SetWindowLong() like this:
class CMyData
{
.... // implement data/functionality you need here
};
SetDataToControl( CWnd* pControl )
{
 CMyData* pData = new CMyData;
 ::SetWindowLong( pControl->m_hWnd, GWL_USERDATA, (LONG)pData );
}
CMyData* GetControlData( CWnd* pControl )
{
 return (CMyData)::GetWindowLong( pControl->m_hWnd, GWL_USERDATA );
}
(you'll need some error checking also)
The advantage with this that you can decide at runtime if a control needs the data or not, so somehow more effective memory usage, but you'll have to take care that the memory used is freed when the control is destroyed.

hope that helps,

ZOPPO
0
 

Author Comment

by:abulka
Comment Utility
thanks for your comment zoppo - I'm still digesting it.  

Regarding the subclassing technique:

Why, using VC++ Right mouse button click on the control -> Class Wizard -> Member Variables -> Add Variable technique, is there only ONE variable per control allowed, and for a IDC_BUTTON1 why is that type CButton rather than anything I damn well please?

0
 
LVL 30

Expert Comment

by:Zoppo
Comment Utility
There's only one CWnd-derived variable allowed, and in addition for some controls (i.e. static text or edit control) more additional variables can be added for easier access to the control's value.

if the control is i.e. a button, then class wizard scans all the code of the projects to find CButton derived classes and you can use one of them. To do this, first derive a class from CButton via the 'Add New Class' button in the ClassWizard. In the menu select 'New...', then in the 'New Class' dialog enter a 'Name', i.e. CMyButton and select as 'Base Class' CButton.
After that add a member variable for a button's ID as before. You can now select between CButton and CMyButton as type for the member.

ZOPPO
0
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

 

Author Comment

by:abulka
Comment Utility
Why all the fuss with the 'CWnd-derived variable' having to point to a CButton or CMyButton type?  In Delphi the tag property that is supplied with each GUI windows control is a pointer to TObject (the mother of all classes) and thus by judicious casting, a programmer can store anything they want in there.

Why does VC++ stuff restrict you so much?  Can't we make a pointer to ANYTHING (like TObject) and then leave it to the programmer to cast and use that property as he/she sees fit?
0
 

Author Comment

by:abulka
Comment Utility
Please respond as an answer & I'll accept tomorrow.
0
 
LVL 30

Accepted Solution

by:
Zoppo earned 70 total points
Comment Utility
Now, I'm not sure how this 'tags' are implemented, but I'm sure they even use one of these two methods.

I don't think that it's a restriction. You have at least two ways to implement custom data as you like without that overhead that each object has space reserved for custom data even if it doesn't use it.

BTW, it's not VC++ that 'restrict' anything, it's the MFC.

And I think it's a good OO-style to derive a new class to expand and object's data or functionality.

ZOPPO
0
 

Author Comment

by:abulka
Comment Utility
Zoppo - I understand your comments about the merits of subclassing.  But I still don't understand why in the technique you show, the new property of the new subclassed control MUST BE A CButton or CMyButton type RATHER THAN a generic POINTER TO ANYTHING.  What do I do if I want the new pointer/property to point to a class FRED or class MyInfo?

Am I missing something?  You may have already answered this?  Sorry if my understanding is slow on this very specific (but important) point...
0
 
LVL 30

Expert Comment

by:Zoppo
Comment Utility
Hi abulka,

Now, the subclassing object must be at least CWnd derived to have subclassing functionality at all. I just suggest to use a CButton derived class for use with buttons because you the inherit the whole functionality of CButton.

>>What do I do if I want the new pointer/property to point to a class FRED or class MyInfo?

Just as I told before:

derive your class (easy with ClassWizard), i.e. CMyButton, and add members as you need like this:

class CMyButton : public CButton
{
....
   FRED m_FRED; // instance used here
   CMyInfo* m_pMyInfo; // pointer used here
// if you need you can add Get/Set functions for these, i.e.
   FRED& GetFred(){ return m_FRED; }
// you should even initialize at least the pointers in the constuctor to NULL, i.e.
   CMyButton():CButton(){ m_pMyInfo = NULL; }
}

Then again use ClassWizard to add a member variable of type CMyButton for a button control, i.e. m_MyButton1 for IDC_BUTTON1.

Now you can do anywhere you need it some calls like:

m_MyButton.GetFRED().DoSomethingWithFred();
m_MyButton.m_pMyInfo = new CMyInfo();

and also standard CButton calls like:

m_MyButton.SetBitmap();
m_MyButton.GetState();

a.s.o.

ok?

ZOPPO
0
 

Author Comment

by:abulka
Comment Utility
Adjusted points to 75
0
 

Author Comment

by:abulka
Comment Utility
Thanks for the great detail.  I see how you subclass the button using
  class CMyButton : public CButton {
}
but then a further issue is how to get it onto the tool palette so as to be able to drop it onto any form any time.  Perhaps I should ask this in another question.

Also, when you say:
> Then again use ClassWizard to add
> a member variable of type
> CMyButton for a button control,
> i.e. m_MyButton1 for IDC_BUTTON1.

I feel like I'm asking the same question again and again and not getting through.  In the above quotation you say "use ClassWizard to add a member variable of type CMyButton".  !!!   I want to use the class wizard to add a member variable NOT OF type CMyButton but of TYPE FRED, but the class wizard doesn't let me - for some reason it is forcing me to add a member variable of type CMyButton.  


Let's check my assumptions...

I am assuming that the classwizard is being used as an alternative and GUI/easy way to subclass a regular CButton.  I am assuming that we are subclassing CButton in order to add a property which points to say, class FRED.  I am assuming that adding a member variable means the same thing as adding a property.  I am assuming a property can point to any class I want.

I am assuming that the code:

class CMyButton : public CButton
{
   ....
   FRED m_FRED; // instance used here

can be generated automatically by the class wizard, and that adding a member variable (using the class wizard) will generate the code
  FRED m_FRED;
for me.  That's why I am confused when I HAVE TO specify CButton but CANNOT specify FRED when using the class wizard.
0
 
LVL 30

Expert Comment

by:Zoppo
Comment Utility
Hi again,

this phrase 'add a member variable of type ...' means add a member to the class of the dialog or form view. This new member represents one special button. This 'adding' action includes some code added to the dialog's/form view's class which handles default data exchange and subclassing.

To add a member to any class DevStudio only has a very simple dialog where you can enter (but not select) the type, the name and the access (protected, public ...) for a new member. To open this dialog click with right mouse button onto the class in the class list of the workspace and select the menu item 'Add member variable'...

ZOPPO
0
 

Author Comment

by:abulka
Comment Utility
Got it - thanks.
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Suggested Solutions

When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

763 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

8 Experts available now in Live!

Get 1:1 Help Now