Solved

Help writing "helper class" for UI Controls.

Posted on 2004-07-30
23
354 Views
Last Modified: 2010-04-01
This question relates to accessing UI controls from a within a class that isn't derived from a prop sheet or dialog.

edit: Using VC++ 6.0

I've written a class CUIControlManager that has a method defined as:

UIToggleGroup(int* p_aID, BOOL a_bVISIBLE, BOOL a_bENABLE)
    int iIndex = 0;
    while (p_aID[iIndex] != -1)
    {
        CWnd* pControl = GetDlgItem(p_aID[iIndex]);
 //.. etc..


p_aID is a static array of control ID values. I basically want any property sheet classes throughout my app to be able to call this "helper" in order to facilitate all of my UI control operations. (my prop sheet classes have static arrays defined which basically model groups of controls. I can then pass a pointer to an array to this method to turn on/off enable/disable groups..

Anyway - this line:
    CWnd* pControl = GetDlgItem(a_ControlID);

crashes.
edit: the crash is an assertion failure:
      ASSERT(::IsWindow(m_hWnd));
inside WINOCC.CPP


I think this is because my CUIControlManager class perhaps isn't derived from an appropriate class? Do I have to derive it from a PropertySheet or CDialog? What if I don't want to do that, is there another paramater I can pass in or grab within the method in order to use the GetDlgItem(int nID,  HWND *pHWnd) interface instead of GetDlgItem(int nID) ?

I think the problem is that this method has no idea what window to go to to find the specified control ID?  I'm still very new to this, so I think I have the concept down, but the implementation is alluding me.  I thought about adding a 4th paramater to UIToggleGroup that took the ID of the Dialog that contains the control, but if that's a viable approach, how would I turn the ID of the dialog into an HWND pointer for GetDlgItem() ?

thanks
-Paul


0
Comment
Question by:PMH4514
  • 13
  • 10
23 Comments
 
LVL 13

Expert Comment

by:SteH
ID: 11677149
When is this helper function called the first time? The error seems to be related with not having created the controls yet which is true for the constructor. Dialogs (and property pages) have OnInitDialog () to do some initializing before displaying but when the control windows have be created.
0
 

Author Comment

by:PMH4514
ID: 11677391
within my InitDialog method.

BOOL CVideoPage::OnInitDialog()
{
    CPropertyPage::OnInitDialog();

    // a bunch of other stuff.
   
    if ( ERROR_SUCCESS != ( dwReturn = m_pSBehavior->Initialize( m_pView ) ) )

that Initialize call triggers some other calls, ultimately a few levels down where a thread is launched, within which HideUI() is called for the first time.  So, it's after CPropertyPage::OnInitDialog() has been called

The CVideoPage constructor looks like this:
CVideoPage::CVideoPage() : CPropertyPage(CScopePage::IDD)

--------

but I'd like to use it anywhere (anywhere after controls have been created via. InitDialog that is.) I guess the gist of this is, knowing the ID of a property sheet and a Control, how can I successfully call GetDlgItem which requires the handle to a window? Get a handle from an ID.  or am I completely mis-understanding this??

thanks




0
 

Author Comment

by:PMH4514
ID: 11677402
whoops. the constructor looks like:
CVideoPage::CVideoPage() : CPropertyPage(CVideoPage::IDD)
0
 
LVL 13

Expert Comment

by:SteH
ID: 11677516
Two problems might be the reason:
MFC classes are not thread safe: a map from windows handles (HWND) to MFC classes is kept in thread local storage. From a HWND you can get the control variable CWnd* using FromHandlePermanent (). To get this to work you need to store the HWND instead of the control IDs of your controls in the array.

Or GetDlgItem can return a temporary object (when FromHandlePermanent would return NULL) which won't react like you want.

I guess the best solution would be to have the enabling done inside the property page class. It can be triggered from the helper function and latter can provide the controls to disable/enable but the actual function should sit inside the class. And associate control variables to each of those controls you want to enable/disable. (Perhaps this will resolve your problem already?)
0
 

Author Comment

by:PMH4514
ID: 11677630
well I did have this working when the method was within the property page class, but I had hoped to abstract it into its own class that any of my non prop-sheet or dialog derived classes can call, so that I don't have to have multiple copies of what amounts to the same code in each class. Maybe I'll all together abstracting this wrong. I basically have one property sheet that I'm primarilly concerned with. But there is a lot going on within this, different classes (non visual) which are controlling different parts of the system each have a visual representation within a given area on the single property sheet. I could write one class that models this property sheet, but that to me seems improper because I'd rather have classes be able to control their own UI devices, regardless that they are all within the same property sheet. (did that make sense?)

I do sorta like the idea of having an array of handles, that way I could pass a pointer to that array to my helper class and it wouldn't need to know about the parent window and it's being in a seperate thread would make no difference. right?  How would I re-write this:

static int RecordControlIDArray[] = { IDC_BUTTON_RECORD, IDC_BUTTON_STOP_RECORD, -1 };

to store the handles to those controls instead?

0
 

Author Comment

by:PMH4514
ID: 11677675
or.. just thinking outloud.. maybe each of my non-visual functional classes, rather than controlling it's UI elements itself, perhaps I could create a UI class.. so if I have CVideoCapture, I could also have CVideoCaptureUI - which itself would be derived from CPropertySheet pointing to the single prop sheet that encompasses the entire system?

CVideoCaptureUI::CVideoCaptureUI() : CPropertyPage(CMainProp::IDD)

or something to that effect.. thoughts?
0
 

Author Comment

by:PMH4514
ID: 11677699
oh and can you expand on this please:
>>And associate control variables to each of those controls you want to enable/disable. (Perhaps this will resolve your problem already?)
0
 
LVL 13

Expert Comment

by:SteH
ID: 11677767
In a dialog or propery sheet you can create control variable using the class wizard. (Member variables tab) Select an ID and create a variable. Be sure to select control and not value as category. If I want to change control parameters like window text or color I normally use a control variable. And if I understand the help to FromHandlePermanent correctly it is the only way to get the correct control CWnd and not a temporay one.
0
 
LVL 13

Expert Comment

by:SteH
ID: 11677800
The abstraction you want to do could be done using a base class you derive all your property sheets from that way the function is part of the class but has not to be written for each class seperatly:
CPropertySheet -> CYourUIPropertySheet -> CVideoCapture

Would this work too?
0
 

Author Comment

by:PMH4514
ID: 11677812
oh, I see what you mean. Actually I already do have member variables for all of my controls.  Are you saying that if I had an array of those member variables, I could pass a pointer to that array to my helper class and enable/disable those controls from my non dialog class?
0
 

Author Comment

by:PMH4514
ID: 11677845
>>CPropertySheet -> CYourUIPropertySheet -> CVideoCapture
>>Would this work too?

hmm.. that's a good idea. Will this allow me to have one physical property sheet, for the sake of argument, break it into 4 quadrants, each quadrant showing feedback based on functionality happening within entirely seperate classes?
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:PMH4514
ID: 11677885
is it a valid statement that any functional classes that have any kind of UI representation should be derived from CPropertySheet or CDialog?
0
 
LVL 13

Expert Comment

by:SteH
ID: 11677895
>>hmm.. that's a good idea. Will this allow me to have one physical property sheet, for the sake of argument, break it into 4 quadrants, >>each quadrant showing feedback based on functionality happening within entirely seperate classes?

No. It allows you to have different classes have the same functionality (functions) without having to write them for eah one seperatly. In your case you should create an array of the controls in the constructor/OnInitDialog which will be controlled in this base class function. (The array needs to be a member var of the base class I guess).
0
 
LVL 13

Expert Comment

by:SteH
ID: 11677922
>> Actually I already do have member variables for all of my controls.  Are you saying that if I had an array of those member variables, I >>could pass a pointer to that array to my helper class and enable/disable those controls from my non dialog class?

As long as those control variables are not accessed from a seperate thread it should be OK. Generally you should not change the GUI from another thread. Either race conditions or deadlocks will happen; If you don't synchronize you will have race conditions otherwise possible deadlocks. A better way is to send a message from the thread to the main window. In the handler you then change the UI from within the main thread.
0
 
LVL 13

Expert Comment

by:SteH
ID: 11677976
>> is it a valid statement that any functional classes that have any kind of UI representation should be derived from CPropertySheet or   >> CDialog?

I don't see why. The problem you are facing is more related to the fact that you want to access members of those classes outside. For windows sake these members are normally not encapsulated with all the problems arising from it: it is not always clear where they are changed.

And there are more UI interface classes beside the controls used within a dialog or a property sheet: CFormView, CToolBar, CStatusBar and more?
0
 

Author Comment

by:PMH4514
ID: 11678057
>>No. It allows you to have different classes have the same functionality (functions) without having to write them for eah one seperatly.
yes, I understand that part. basic inheritance.

>>In your case you should create an array of the controls in the constructor/OnInitDialog
yeah, see I think this is what I am (I guess wrongly) trying to get away from. OnInitDialog would imply that I've derived my class from CPropertyPage (right?) I've been thinking that I could have a class that is NOT derived from CPropertyPage or CDialog  that itself contains the array of control ID's that it cares about. If that was a poor assumption, I was then thinking that multiple classes could be derived from the same visible property page.  In a nutshell I want to keep functional areas as seperated and stand-alone as possible with their own classes, while keeping in mind that their visual representations are running physically in different areas of the same window at the same time.

>>As long as those control variables are not accessed from a seperate thread it should be OK
yeah, they would be.

>> A better way is to send a message from the thread to the main window. In the handler you then change the UI from within the main thread.
I'm beginning to think this is what I'll need to do.  So you think all GUI related code should exist in a single CPropertySheet instance, regardless how unrelated the functionality the various elements represent actually are? Seperate well modeled classes can exist to drive this functionality and only send messages to this main property page to do all UI updates?

That would make sense to me, but from a purely OO standpoint I would prefer modules control thier own UI elements..

>>For windows sake these members are normally not encapsulated with all the problems arising from it: it is not always clear where they are changed.

yeah, I guess it's that kind of thinking that's new to me being so new to C++ and windows programming. I guess encapsulating "The UI" as a single entity, regardless the fact that independant elements within "The UI" represent such disparate functional areas would be a valid design decision in light of the windows issues. right?

>>And there are more UI interface classes beside the controls used within a dialog or a property sheet: CFormView, CToolBar, CStatusBar
yeah, but in my case all I'm concerned with is a single property sheet with alot of buttons, sliders, radio groups, progress indicators and such.

hmmm...

0
 
LVL 13

Expert Comment

by:SteH
ID: 11678287
You can have several dialogs embedded in a dialog as childs for example. It is something I am doing in my current project. So different functionality resides in different windows (if those are dialogs, tab controls, property sheets I don't care). Look at different apps a lot of them group their functionality into different kind of windows. Some have dockable tools which might float other have popup dialogs. That part you can choose to your needs (and to your knowlegde, whichever is the more limiting fact).

Only if threads come into the game you need to seperate them from the GUI. A thread does its work but should not alter the GUI directly unless you make sure that the main thread is not touching these variables/controls. Espiacally framegrabbers and video drivers use the latter approach. The thread acquiring the images is directly writing to the window (HWND) on the main dialog for speed reasons but the main thread doesn't touch it anymore. If you mix access to the GUI from a thread and the main thread care has to be taken that no variables (like controls) are touched simultaniously. And the effect from synchronisation together with thread switching penalty could make the advantage of using a thread turn into a disadvantage.

If you have time look at the very useful articles on
http://www.flounder.com (MVP tips)
to get some insights on how windows/MFC works and what pitfalls are ahead of you.

0
 

Author Comment

by:PMH4514
ID: 11678436
>>A thread does its work but should not alter the GUI directly unless you make sure that the main thread is not touching these variables/controls

Ok that I hadn't thought of, it makes sense. But for the sake of argument, assume I know and can ensure through whatever locking mechanisms are appropriate, that the main thread doesn't touch the UI variables/controls that the worker thread updates . My worker thread is a class that is not derived from a dialog or property sheet.  How would I update a control on a property sheet from within the thread knowing only it's ID? or is that just a can of worms I shouldn't be going near? This is a threaded app, there is framegrabbing going on in a thread, which uses DirextX features to paint a large image on screen. There is external hardware which controls various movements and a thread is constantly polling that for changes and then updates the UI accordingly.. the video capture now is another thread, which is constantly buffering and writing out these captures frames, and it needs to update the record/stop/pause/save progress indicators on the screen as well. All of this is on the same Property Sheet.

Anyway, if there is an actual code answer for enabling a control from within a non visual class, I'd love to see it, but otherwise this has side-tracked to a very good design discussion.
:)
0
 

Author Comment

by:PMH4514
ID: 11678492
wow.. I'll be spending alot of time reading those essays on founder.com :)

thanks
0
 
LVL 13

Accepted Solution

by:
SteH earned 50 total points
ID: 11678654
I would have the thread polling the movements send either registered or WM_APP+x messages (there are articles about that on flounder.com as well) to the main thread. And in the handler to these messages you update the controls. Exactly this is (in my view) a no go for threads. Threads are used to do work to keep the GUI responsive. If they are controlling the GUI as well you loose this most importatn feature of threads and only thread context switching remains.

A simple message like UWM_VIDEO_ENABLE_CONTROLS or UWM_SCOPE_ENABLE_CONTROLS are then handled inside their respective dialogs/property sheets. Like that you can change the controls state from any part of the program in a known manner.
0
 
LVL 13

Expert Comment

by:SteH
ID: 11678669
>> wow.. I'll be spending alot of time reading those essays on founder.com :)

found them 1.5 years ago but still reading them from time to time.
0
 

Author Comment

by:PMH4514
ID: 11679700
>>A simple message like UWM_VIDEO_ENABLE_CONTROLS or UWM_SCOPE_ENABLE_CONTROLS are then handled inside their respective >>dialogs/property sheets. Like that you can change the controls state from any part of the program in a known manner.

yup, that makes total sense. to this point I've acta
0
 

Author Comment

by:PMH4514
ID: 11679782
whoops.. I meant to end that last statement after "sense."  
0

Featured Post

Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

Join & Write a Comment

Templates For Beginners Or How To Encourage The Compiler To Work For You Introduction This tutorial is targeted at the reader who is, perhaps, familiar with the basics of C++ but would prefer a little slower introduction to the more ad…
Introduction This article is the first in a series of articles about the C/C++ Visual Studio Express debugger.  It provides a quick start guide in using the debugger. Part 2 focuses on additional topics in breakpoints.  Lastly, Part 3 focuses on th…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

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

12 Experts available now in Live!

Get 1:1 Help Now