Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Setfocus / killfocus on cbutton

1,941 views
Skip to first unread message

mfc

unread,
Dec 2, 2010, 10:32:13 AM12/2/10
to
Hi,

do you know how is it possible to change the focus from one button to
another button?

When I use the TAB control to go from one button to the next button,
the focus will be changed, so that the next button will get the focus.
Everything works correct.

But if I use the mouse to click on another button I also want to
acchieve this behaviour. At the moment the focus will stay on the last
button. Which message handler do I have to use to change the focus
using the mouse?

BEGIN_MESSAGE_MAP(CImageTextButton, CButton)

ON_WM_ERASEBKGND()
ON_WM_MOUSEMOVE()
ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
ON_WM_KILLFOCUS()
ON_WM_SETFOCUS()

END_MESSAGE_MAP()

I`m not sure if kill focus and set focus will be the right place for
this? And furthermore which code I have to place in these handlers?

void CImageTextButton::OnSetFocus(CWnd* pOldWnd)
{
CButton::OnSetFocus(pOldWnd);
}

void CImageTextButton::OnKillFocus(CWnd * pNewWnd)
{
CButton::OnKillFocus(pNewWnd);
} // End of OnKillFocus


In my DrawItem method I will use a different image for the button, if
the button has the focus rectangle.

void CImageTextButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct )
{
CDC *pDC = CDC::FromHandle( lpDrawItemStruct->hDC );

// The color inside the HBITMAP that should be transparent
COLORREF cTransparentColor = RGB(0,0,0);

BOOL bIsFocused = (lpDrawItemStruct->itemState & ODS_FOCUS);
BOOL bIsDisabled = (lpDrawItemStruct->itemState & ODS_DISABLED);

HBITMAP m_Btn;

if(((bIsFocused) && (!bIsDisabled)))
m_Btn = m_btnFocus;
else if(bIsDisabled)
m_Btn = m_btnDis;
else
m_Btn = m_btnStandard;
//and so on
}


best regards
Hans

David Lowndes

unread,
Dec 2, 2010, 10:48:47 AM12/2/10
to
>do you know how is it possible to change the focus from one button to
>another button?
>
>When I use the TAB control to go from one button to the next button,
>the focus will be changed, so that the next button will get the focus.
>Everything works correct.
>
>But if I use the mouse to click on another button I also want to
>acchieve this behaviour.

By default, clicking a control/button with the mouse should move focus
to it (and then invoke the button click).

>At the moment the focus will stay on the last
>button.

That's not normal - some of your code must be causing this.

Dave

mfc

unread,
Dec 2, 2010, 10:57:33 AM12/2/10
to
I`ve installed the OnlButtonDown() method where I will call
Setfocus(), and then the button which will be clicked by the mouse
gets the focus.

But another question according to the setfocus method.

I`ve some buttons installled to control the value of a specific
cstatic field. In my example, I`ve one button to the left and one
button to the right of the cstatic field to control this field. My
button class knows the maximum and minimum value of the cstatic field.
If the button will reach one of these two values (minimum or maximum)
the specific button (with the focus) will be disabled. At this time,
no control on my dialog will have the focus....

How is it possible to set the focus to the next item on the dialog in
this situation?

best regards
Hans

mfc

unread,
Dec 2, 2010, 11:17:53 AM12/2/10
to

Using
CWnd *wnd = GetParent();
wnd->PostMessage(WM_SETFOCUS);

in my cbuttonclass to get the window of the dialog containing all
these controls... the first item in the dialog will get the focus but
not the next item to the disabled button....

David Lowndes

unread,
Dec 2, 2010, 12:01:01 PM12/2/10
to
>I`ve installed the OnlButtonDown() method where I will call
>Setfocus(),

Why? Remove it, you don't need it.

>How is it possible to set the focus to the next item on the dialog in
>this situation?

Have a look at the NextDlgCtrl method.

Dave

David Lowndes

unread,
Dec 2, 2010, 12:01:47 PM12/2/10
to
>Using
>CWnd *wnd = GetParent();
>wnd->PostMessage(WM_SETFOCUS);

Noooo - see my last reply!

Dave

Joseph M. Newcomer

unread,
Dec 2, 2010, 12:49:41 PM12/2/10
to
Not sure I really understand the question.

Yes, it is possible to change the focus from one button to another, using APIs to move to
the next tab item in the dialog, for example. This is the API that the TAB key calls to
change the focus.

It is normal for a mouse click to move the focus. In fact, I'm not sure how it is
possible to defeat this. What are you basing your assertion on? If you are relying on
the state you see as the result of your OnDrawItem code, then you have the question about
whether or not you are actually drawing the right thing, which is a different question
than whether or not you have changed the focus. If you change the focus, the button
should redraw, so your drawitem handler should be called once for the button you just left
and once for the button you just moved into (assuming both are your owner-draw buttons). I
love owner-draw buttons, and not once have I had a problem with this.

Unfortunately, your question does not make it clear if the focus changes or not, or if the
*image* of focus is drawn properly. Example: having clicked the button with the mouse,
try hitting the space bar. If the button you just clicked into activates again, then
indeed it has the focus; if it doesn't look like it has the focus, your drawing code is
wrong. OTOH, if you hit the space bar and the PREVIOUS button activates, then IT had the
focus, and your drawing code is correct, but focus in fact did not change. You will need
to indicate which of these actually occurs before we can begin to guess what might be
happening.

I have never used a SetFocus or KillFocus handler in any of my owner-drawn buttons.

****
One of the first things I do is change that horrible unwieldy variable name
"lpDrawItemStruct" to "dis" so I can use it without developing cramps in my fingers.
****


>{
> CDC *pDC = CDC::FromHandle( lpDrawItemStruct->hDC );
>
> // The color inside the HBITMAP that should be transparent
> COLORREF cTransparentColor = RGB(0,0,0);
>
> BOOL bIsFocused = (lpDrawItemStruct->itemState & ODS_FOCUS);
> BOOL bIsDisabled = (lpDrawItemStruct->itemState & ODS_DISABLED);
>
> HBITMAP m_Btn;

****
If you don't know how to use the notation correctly DO NO USE IT AT ALL! The m_ prefix
means "This is a member variable", and here you are declaring what is clearly a LOCAL
variable! This will only confuse most people trying to read your code!
****


>
> if(((bIsFocused) && (!bIsDisabled)))
> m_Btn = m_btnFocus;
> else if(bIsDisabled)
> m_Btn = m_btnDis;
> else
> m_Btn = m_btnStandard;
> //and so on
>}
>
>
>best regards
>Hans

Joseph M. Newcomer [MVP]
email: newc...@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm

Joseph M. Newcomer

unread,
Dec 2, 2010, 1:08:28 PM12/2/10
to
See below...

****
ARRRGH! Seeing code like this causes us much pain! Note that what you have done is send
a message that the focus has changed, WITHOUT ACTUALLY CHANGING THE FOCUS! WM_SETFOCUS is
a message which tells that the focus *has* changed, the consequence of a SetFocus call. It
does NOT change the focus. Therefore, you are lying about the focus changing, and in any
case the message you are sending is deeply erroneous (WM_SETFOCUS requires a WPARAM value
which you are not supplyihg). Before flailing about dropping random bits of code into
your program, it is critical to (a) RTFM (b) read about focus changes. Here, you are
telling the PARENT of the control that IT has received the focus, received it from a
situation where there had been NO previous window that had focus. You are not actually
CHANGING the focus, merely lying to the parent about it! Since there is never a situation
in which you would WANT change the focus to the parent of the button, it is not clear why
you would attempt to do so, or pretend that you have done so and lie about it.

If you want to set the focus to a window, you MUST call SetFocus() on that window. A side
effect of this call is that the window which is losing the focus gets a WM_KILLFOCUS
message and the window which receives the focus gets a WM_SETFOCUS message, both of which
are informing the window about the current state. Neither message actually changes any
state!

Read carefully the documentation of WM_SETFOCUS: it VERY CAREFULLY AND EXPLICITLY TELLS
YOU WHAT THIS MESSAGE MEANS! At no point does it say "This message changes the focus"; it
says that "This message is received by a window ***after it has received the keyboard
focus***". [emphasis added] It even contains a hyperlink to the SetFocus function. You
cannot just start randomly sending built-in messages because you THINK you know what they
do; you MUST read the manual and see what these messages do, and realize that 99% of the
time when you want to send or post a Microsoft-defined Windows message to a window that is
not one of the messages defined as something a user should send a window, such as a
control-class message (e.g., LB_ADDSTRING, CB_SETCURSEL, etc.) that you are completely and
utterly wrong. The 1% of the time are situations in which you are, for example, writing
your own control and want to send the parent a notification message (WM_COMMAND or
WM_NOTIFY), or post a WM_CLOSE to a window, or...well, I'm running out of valid
situations. The rest of the time, sending or posting a Microsoft-defined message is a
mistake. Three messages you must NEVER, EVER send yourself are WM_SETFOCUS, WM_KILLFOCUS
and WM_PAINT. There may be lots more.
joe
>
>Dave

Joseph M. Newcomer

unread,
Dec 2, 2010, 1:09:44 PM12/2/10
to
But, of course, that makes sense only if the program is supposed to be changing the focus!
Here, I think we have a much different problem, and I don't know what it is, but the
horrible code shown in another message suggests that no focus changes are actually
occurring.
joe

mfc

unread,
Dec 2, 2010, 2:07:40 PM12/2/10
to
On 2 Dez., 18:49, Joseph M. Newcomer <newco...@flounder.com> wrote:
> Not sure I really understand the question.
>
> Yes, it is possible to change the focus from one button to another, using APIs to move to
> the next tab item in the dialog, for example. This is the API that the TAB key calls to
> change the focus.
>
> It is normal for a mouse click to move the focus.  In fact, I'm not sure how it is
> possible to defeat this.  What are you basing your assertion on?  If you are relying on
> the state you see as the result of your OnDrawItem code, then you have the question about
> whether or not you are actually drawing the right thing, which is a different question
> than whether or not you have changed the focus.  If you change the focus, the button
> should redraw, so your drawitem handler should be called once for the button you just left
> and once for the button you just moved into (assuming both are your owner-draw buttons). I
> love owner-draw buttons, and not once have I had a problem with this.  
>
> Unfortunately, your question does not make it clear if the focus changes or not, or if the
> *image* of focus is drawn properly.  Example: having clicked the button with the mouse,
> try hitting the space bar.  If the button you just clicked into activates again, then
> indeed it has the focus; if it doesn't look like it has the focus, your drawing code is
> wrong.  OTOH, if you hit the space bar and the PREVIOUS button activates, then IT had the
> focus, and your drawing code is correct, but focus in fact did not change.  You will need
> to indicate which of these actually occurs before we can begin to guess what might be
> happening.
>
thanks for your answer. Unfortunately I was very unclear with my
question: and I missed an important point which I`ve figured out now.

I`ve one class derrived from Cbutton which is called CImageTextButton.
This class has the DrawItems method installed and so on. Moreover I`ve
another class derrived from this class (CImageTextButton) which is
called CSpinCtrlButton. I`ve installed this class for all buttons
which change the cstatic fields.

In this class is only a OnLButtonDown() handler installed. All
CSpinCtrlButtons will increase / decrease the values of the cstatic
fields; the CImageTextButton will open further dialogs when they get
hit by the mouse or by the spacebar. Therefore I made another subclass
from my imagebuttonclass. And in this situation the focus will be only
properly set if I add a SetFocus() call in the OnLButtonDown() method.
My CImageTextButton class didn`t have any OnLButtonDown() method
installed.

class CSpinCtrlButton :
public CImageTextButton
{
public:
CSpinCtrlButton(void);
virtual ~CSpinCtrlButton(void);

enum ChangeValue
{
DECREASE = 0, //erniedrigen
INCREASE = 1, //erhöhen
};

void SetValues(CImageStatic *pStatField, BOOL change, UINT min, UINT
max);

protected:

CImageStatic *m_pField; //static field which is affected by the
spinbuttons
UINT m_Min; //min value of the cstatic field
UINT m_Max; //max value of the cstatic field
BOOL m_Change; //increase/decrease value
BOOL m_BtnSpinCtrlMode;

protected:

virtual BOOL PreTranslateMessage(MSG* pMsg);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

DECLARE_MESSAGE_MAP()
};

best regards
Hans

mfc

unread,
Dec 2, 2010, 2:17:48 PM12/2/10
to
the second question is: if I have a CImageButton button with this
onclick-handler:

void CPortPage::OnBnClickedBtnPatvalueR()
{
m_Btn.EnableWindow(FALSE);
}

If I click on this button (with the mouse or with the spacebar) the
button will be disabled, but no other button in my dialog will get the
focus. So I`m no longer able to jump from one button to the next
button by using the TAB key....

Which method can I use to acchieve this?

Joseph M. Newcomer

unread,
Dec 2, 2010, 5:50:40 PM12/2/10
to
I once had this problem with disabling radio buttons. What you have to do is consider one
of two approaches: in the OnBnClickedBntPatvalueR rourinte, ask if the control you are
about to disable has the focus, and if it does, GetNextDlgTabItem and SetFocus to it;
alternatively, the disable handler in the button moves the focus to the next control.
joe

mfc

unread,
Dec 3, 2010, 10:41:32 AM12/3/10
to
On 2 Dez., 23:50, Joseph M. Newcomer <newco...@flounder.com> wrote:
> I once had this problem with disabling radio buttons.  What you have to do is consider one
> of two approaches: in the OnBnClickedBntPatvalueR rourinte, ask if the control you are
> about to disable has the focus, and if it does, GetNextDlgTabItem and SetFocus to it;
> alternatively, the disable handler in the button moves the focus to the next control.
>                                 joe
>
> On Thu, 2 Dec 2010 11:17:48 -0800 (PST), mfc <mfcp...@googlemail.com> wrote:
> >the second question is: if I have a CImageButton button with this
> >onclick-handler:
>
> >void CPortPage::OnBnClickedBtnPatvalueR()
> >{
> >   m_Btn.EnableWindow(FALSE);
> >}
>
> >If I click on this button (with the mouse or with the spacebar) the
> >button will be disabled, but no other button in my dialog will get the
> >focus. So I`m no longer able to jump from one button to the next
> >button by using the TAB key....
>
> >Which method can I use to acchieve this?
>
> Joseph M. Newcomer [MVP]
> email: newco...@flounder.com

Thank you for the hint with GetNextDlgTabItem() - it`s working.

According to the cbutton classes; I think the best would be to
integrate the spincontrol-button mode in the Cimagetextbutton class
and add a small method where I can activate or not activate the
spincontrol mode.

If you have many buttons installed on your dialog ("<<" and ">>" which
increase/decrease the value of a specific cstatic field), each button
will need a OnButtonClick eventhandler (and these buttons do not have
identical names).

Is there a better solution instead of adding all these onclick
eventhandlers in the dialog class? Maybe is it possible to add the
OnClick-handler of these buttons to the specific button-class of these
buttons? Or is this a bad design?

best regards
Hans

Joseph M. Newcomer

unread,
Dec 3, 2010, 10:58:48 AM12/3/10
to
Generally, I don't worry about adding OnClick handlers; most of them have the body
updateControls();
and updateControls understands how to deal with all kinds of factors, including
enable/disable, show/hide, interactions with other controls, etc., so there is no
complexity at all; updateControls() will simply do whatever is correct, each time, all the
time.
joe

email: newc...@flounder.com

mfc

unread,
Dec 3, 2010, 11:33:52 AM12/3/10
to
> MVP Tips:http://www.flounder.com/mvp_tips.htm- Zitierten Text ausblenden -
>
> - Zitierten Text anzeigen -

and how is this updatecontrols method called if a user clicks on a
button? Or do you call the updatecontrol method from the
onbuttonclick-handler?

Joseph M. Newcomer

unread,
Dec 3, 2010, 2:35:34 PM12/3/10
to
See below...

****
I said I don't worry about adding OnClick handlers. I didn't say I didn't add them, only
that adding them doesn't bother me in the slightest, because all they do is call
updateControls(). Note that if you are ever going to enable/disable or show/hide
controls, you must do it from only ONE function, which is solely responsible for all
enabling/disabling showing/hiding. This is the only way to make it manageable. I came up
with this solution after a client sent me a first-order disaster (back in the early 90s)
in which there were 23 locations that had code to enable/disable show/hide controls, using
six different algorithms. I consolidated it into one place (the code was truly
unmaintainable!)

If I got the slightest bit concerned about the number of handlers, I might set all my
buttons into a single range of numbers and add a single ON_CONTROL_RANGE handler, but that
takes more work than I care for. But the only thing the ON_CONTROL_RANGE handler would do
would be to call updateControls(). See my essay on dialog control management on my MVP
Tips site.
joe

Joseph M. Newcomer [MVP]
email: newc...@flounder.com

mfc

unread,
Dec 5, 2010, 11:01:34 AM12/5/10
to
> >> MVP Tips:http://www.flounder.com/mvp_tips.htm-Zitierten Text ausblenden -

Alright now I understand your approach. I`ve installed one public
method in my cbutton class (SetParams) which gets all required
specific values in the OnInitDialog().


m_MyButtonL.SetParams(&m_StatCtrl, CImageTextButton::DECREASE,
minvalue, maxvalue);
m_MyButtonR.SetParams(&m_StatCtrl, CImageTextButton::INCREASE,
minvalue, maxvalue);

These two buttons will increase/decrease the value of a specifc
cstatic field (in this example - m_StatCtrl ). Minvalue / maxvalue
will be either a static definition (located in a specific header file)
or the start / end number of a CString in the string table.

Dependent from the value of the cstatic field, it is possible that
some other controls in the dialog should be disable/enabled. For
example if you click on button1, UpdateControls() for button1 will be
called where I change the value of the specific cstatic field. Now
according to this change, it could be possible that for example
button2 and button3 should be disabled.

Is there a better solution than this:
1) including all depending button controls to this call in the
OnInitDialog() (sometimes up to six other controls affected by the
spcific button)
m_Button1.SetParams(&m_StatCtrl, CImageTextButton::INCREASE, minvalue,
maxvalue, &m_Button2, &m_Button3);

How can I add these controls to one member-variable of the button
class? - using a clist (m_list.Add(m_Button2) and so on.... ?

2) check in the m_Button1.UpdateControl() if other controls are
depended from this button and call their UpdateControls() methods??

void CImageButton::UpdateControls()
{
//change the value of the specific cstatic field

//check if other controls affected by this control - if so call
their Updatecontrol() method

Invalidate();
}

3) in the end of their UpdateControls() method call Invalidate() to
draw these buttons again


best regards
Hans

mfc

unread,
Dec 5, 2010, 1:55:38 PM12/5/10
to
It sounds very complicated but maybe each button member needs a
std::map or cvector which includes all conditions under which this
button must be disabled.

for example for button1:

std::map list

key | value
(button2 | disabled)
(button3 | disabled)
(button4 | enabled)

void CImageButton::UpdateControls()
{
//check if this button must be disabled (according to the std::map)
ButtonDisabled();

}

Joseph M. Newcomer

unread,
Dec 5, 2010, 2:29:08 PM12/5/10
to
See below...

****
What I would do is invent a code, such as CITB_CHANGED, and use an ON_CONTROL handler to
handle this notification in the parent. The child button would do

GetParent()->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlId(), CITB_CHANGED),
(LPARAM)m_hWnd);
then I would have, in the parent's message map,
ON_CONTROL(CIBT_CHANGED, IDC_WHATEVER_BUTTON, OnWhateverChanged)

void CMyDialog::OnWhateverChanged()
{
updateaControls();
}
****


>
>Is there a better solution than this:
>1) including all depending button controls to this call in the
>OnInitDialog() (sometimes up to six other controls affected by the
>spcific button)
>m_Button1.SetParams(&m_StatCtrl, CImageTextButton::INCREASE, minvalue,
>maxvalue, &m_Button2, &m_Button3);
>
>How can I add these controls to one member-variable of the button
>class? - using a clist (m_list.Add(m_Button2) and so on.... ?

****
Key here is how you encode it. I don't think there is a better way than having the
SetParams method (or some variant) for each button, but I might consider, if I have a lot
of buttons, having something of the form:

static const struct {
CImageTextButton * ctl,
CStatic * target,
CImageTextButton::Direction dir,
int minvalue,
int maxvalue} Buttons[] = {
{&mIncreaseSomeValue, &m_SomeControl, CImageTextButton::INCREASE, 0, 1 },
{&m_OtherControl, CImageTextButton::DECREASE, 0, 1 },
{NULL, 0, 0, 0} // EOT
};

void CMyDialog::AddButtons()
{
for(int i = 0; Buttons[i].ctl != NULL; i++)
{
Buttons[i].ctl->SetParameters(Buttons[i].target,
Buttons[i].dir,
Buttons[i].minvalue,
Buttons[i].maxvalue);
}
}

and just call "AddButtons" in OnInitDialog. Note that it doesn't change the fact that you
have to specify all the buttons, but it handles it in a different way.
****


>
>2) check in the m_Button1.UpdateControl() if other controls are
>depended from this button and call their UpdateControls() methods??

****
There is exactly ONE updateControls() method, one, and one only, in your dialog. This
one-and-only place understands interactions, and you can think of it as a place where you
solve a set of simultaneous constraint equations which are predicates on
enabling/disabling or showing/hiding the controls. For that matter, if you use the
table-driven approach, you can just iterate through the table, e.g.,

for(int i = 0; Buttons[i].ctl != NULL; i++)
{
if(Buttons[i].target.GetValue() <= Buttons[i].minvalue &&
Buttons[i].dir == CImageTextButton::DECREASE)
Buttons[i].ctl->EnableWindow(FALSE);
else
Buttons[i].ctl->EnableWindow(TRUE);
if(Buttons[i].target.GetValue() >= Buttons[i].maxvalue &&
Buttons[i].dir == CImageTextButton::INCREASE)
Buttons[i].ctl->EnableWindow(FALSE);
else
Buttons[i].ctl->EnableWindow(TRUE);
}

or something along those lines (I'm inventing this off the top of my head, so take it as a
schema, and doublecheck to see if my actual code makes any sense in your context)
****


>
>void CImageButton::UpdateControls()
>{
> //change the value of the specific cstatic field
>
> //check if other controls affected by this control - if so call
>their Updatecontrol() method
>
> Invalidate();
>}

****
There is no need to have an UpdateControls for the button; this is a function of the
parent dialog. Note that your sketch above makes no sense whatsoever, because no button
can know of the existence of any other button.
****


>
>3) in the end of their UpdateControls() method call Invalidate() to
>draw these buttons again

****
Since the UpdateControls method as shown should not exist, the presence or absence of
Invalidate() in it is irrelevant.
joe
****


>
>
>best regards
>Hans
Joseph M. Newcomer [MVP]

email: newc...@flounder.com

Joseph M. Newcomer

unread,
Dec 5, 2010, 2:29:56 PM12/5/10
to
See below....

On Sun, 5 Dec 2010 10:55:38 -0800 (PST), mfc <mfc...@googlemail.com> wrote:

>It sounds very complicated but maybe each button member needs a
>std::map or cvector which includes all conditions under which this
>button must be disabled.

****
No. In fact, this would be a total disaster. See my previous reply.
****


>
>for example for button1:
>
>std::map list
>
> key | value
>(button2 | disabled)
>(button3 | disabled)
>(button4 | enabled)
>
>void CImageButton::UpdateControls()
>{
> //check if this button must be disabled (according to the std::map)
> ButtonDisabled();
>
>}

****
See my previous reply.
joe
****
Joseph M. Newcomer [MVP]
email: newc...@flounder.com

Joseph M. Newcomer

unread,
Dec 6, 2010, 12:30:37 AM12/6/10
to
Another solution is to create a "package" of the static control and its two buttons, as an
ActiveX control, and just place copies of this "package" in place. Then the package
maintains its own state, and you provide interfaces for specifying its upper and lower
limits and setting and retrieving its current state. The static control that is operated
on is thus intrinsic to the "package" and does not have to be specified. Note that again
no updateControls interface has to be exported because each package is responsible for its
own internal state.
joe

Joseph M. Newcomer [MVP]
email: newc...@flounder.com

mfc

unread,
Dec 6, 2010, 10:53:12 AM12/6/10
to
On 5 Dez., 20:29, Joseph M. Newcomer <newco...@flounder.com> wrote:
> See below...
> ****
> What I would do is invent a code, such as CITB_CHANGED, and use an ON_CONTROL handler to
> handle this notification in the parent.  The child button would do
>
>         GetParent()->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlId(), CITB_CHANGED),
>  (LPARAM)m_hWnd);
> then I would have, in the parent's message map,
>         ON_CONTROL(CIBT_CHANGED, IDC_WHATEVER_BUTTON, OnWhateverChanged)
>
> void CMyDialog::OnWhateverChanged()
>    {
>     updateaControls();
>    }
> ****

In the child button class you specifiy the id (GetDlgCtrlId = which
child button is affected by this change) but in the parent class
(dialog class) this information is not further used? Or did I miss
something?


> >Is there a better solution than this:
> >1) including all depending button controls to this call in the
> >OnInitDialog() (sometimes up to six other controls affected by the
> >spcific button)
> >m_Button1.SetParams(&m_StatCtrl, CImageTextButton::INCREASE, minvalue,
> >maxvalue, &m_Button2, &m_Button3);
>
> >How can I add these controls to one member-variable of the button
> >class?  - using a clist  (m_list.Add(m_Button2) and so on.... ?
>
> ****
> Key here is how you encode it.  I don't think there is a better way than having the
> SetParams method (or some variant) for each button, but I might consider, if I have a lot
> of buttons, having something of the form:
>
> static const struct {
>             CImageTextButton * ctl,
>             CStatic * target,
>             CImageTextButton::Direction dir,
>             int minvalue,
>             int maxvalue} Buttons[] = {
>    {&mIncreaseSomeValue, &m_SomeControl, CImageTextButton::INCREASE, 0, 1 },
>    {&m_OtherControl, CImageTextButton::DECREASE, 0, 1 },
>   {NULL, 0, 0, 0} // EOT
>   };
>

Could you tell me how you will declare / define this static struct in
the dialog class (so that this struct will be known in some other
methods in this dialog too)? I like the approach using a table / array
(Button[]) without knowing the size of the array at startup. So that
I can add as many buttons as I like.


> void CMyDialog::AddButtons()
>    {
>     for(int i = 0; Buttons[i].ctl != NULL; i++)
>        {
>         Buttons[i].ctl->SetParameters(Buttons[i].target,
>                                        Buttons[i].dir,
>                        Buttons[i].minvalue,
>                        Buttons[i].maxvalue);
>       }
>   }
>
> and just call "AddButtons" in OnInitDialog.  Note that it doesn't change the fact that you
> have to specify all the buttons, but it handles it in a different way.


I`ve now two small structs to get all required values for updating all
buttons

struct Btn
{
CImageTextButton *ctlLeft; /* << */
CImageTextButton *ctlRight; /* >> */
CImageStatic *target; /* cstatic field */
CImageTextButton::Direction dir;
int minvalue;
int maxvalue;
};

struct BtnCtrl
{
Btn ctrl;
Btn subctrl;
CDWordArray dis_subctrl; //situations where the
subctrl is complete disabled
};


The struct BtnCtrl will include an additional subcontrol as well as a
wordarray which defines the situations where the subcontrol will be
disabled according to the value of the ctrl-staticfield. I`ve some
cstatic fields which can disable/enable another cstatic field and
their two buttons (<< and >>).

best regards
Hans

mfc

unread,
Dec 6, 2010, 11:03:39 AM12/6/10
to
Where in the Updatecontrols() method of the dialog page do you change
the value of the cstatic field? Or do you change this value in the
OnButton_click() handler (before calling UpdateControls())?

Joseph M. Newcomer

unread,
Dec 6, 2010, 5:15:34 PM12/6/10
to
Note the ON_CONTROL Message Map entry uses the control ID to dispatch to the handler.
joe

Joseph M. Newcomer

unread,
Dec 6, 2010, 5:16:33 PM12/6/10
to
I would change it in the OnBnClicked handler (which does whatever it does, that is,
compute a new value), and then call updateControls() to re-evaluate the constraint
equations.
joe

mfc

unread,
Dec 6, 2010, 5:39:32 PM12/6/10
to

Is it better to install this struct within the dialog class or should
I use another headerfile which will be included in all dialog-classes
using this struct? And how can I use your initialization in these
classes?

Buttons[] = {
{&mIncreaseSomeValue, &m_SomeControl, CImageTextButton::INCREASE,
0, 1 },
{&m_OtherControl, CImageTextButton::DECREASE, 0, 1 },
{NULL, 0, 0, 0} // EOT
};


>

> Hans- Zitierten Text ausblenden -

Joseph M. Newcomer

unread,
Dec 6, 2010, 9:30:04 PM12/6/10
to
See below...

****
Because it is a static struct, declared at the global scope level in this compilation
unit, it is known to all methods in the compilation unit. So nothing special needs to be
done. It is visible. You could also define it as a static member of your class, and the
syntax change is minor, and the effect is the same. So you could say

class CMyDialog : public CDialog {
protected:
typedef struct {
...stuff
} BUTTON_TABLE;
static const BUTTON_TABLE Buttons[];
...
};


in the .cpp file:

static const CMyDialog::BUTTON_TABLE Buttons[] = { ... };

****


>
>Is it better to install this struct within the dialog class or should
>I use another headerfile which will be included in all dialog-classes
>using this struct? And how can I use your initialization in these
>classes?

****
In general, you will only need this in the one dialog class, which is what I was assuming
in the above example code. Now, if you want to use this table in other dialog classes to
build other button tables for other dialogs, you would put this typedef in another header
file. You would then include it in other dialog compilations. Note the declaration
static const BUTTON_TABLE Buttons;
would remain the same, but instead of putting the typedef right ahead of it, you would
move it to the separate header file.
****


>
> Buttons[] = {
> {&mIncreaseSomeValue, &m_SomeControl, CImageTextButton::INCREASE,
>0, 1 },
> {&m_OtherControl, CImageTextButton::DECREASE, 0, 1 },
> {NULL, 0, 0, 0} // EOT
> };

****
Note that using the sentinel to mark the end is convenient, particularly if the function
doesn't have access to the definition. If you want to use the length of the struct, leave
the NULL terminator from the description and use _countof.

What I would do if I needed this in several places is create a separate class which has an
AddButtons method; then I could do something like
Buttons.AddButtons();
or, if I needed the dialog reference,
Buttons.AddButtons(this);
Note that you can refactor this code in as many ways as you want. It's Just Code.
****

mfc

unread,
Dec 7, 2010, 4:27:26 AM12/7/10
to
On 7 Dez., 03:30, Joseph M. Newcomer <newco...@flounder.com> wrote:
> See below...

The problem is that if I install the typedef struct as protected in
the same class, I`ve no access to this typedef in my static c++
declaration (cpp file) as well as if I try to add some class variables
to the struct in the cpp.

class CMyDialog : public CDialog
{
protected:
typedef struct {
...stuff
} BUTTON_TABLE;
static const BUTTON_TABLE Buttons[];

/* my specific controls in this dialog */
CStaticField m_Field;
CImageButton m_Btn;
};


in the .cpp file:

static const CMyDialog::BUTTON_TABLE Buttons[] = {{&m_Btn,
&m_Field, ....}}; //<- no access to class variables ;

It seems to me that there is no solution to solve this....


class CMyBtnStruct
{
public:
typedef struct {
...stuff
} BUTTON_TABLE;

void AddButtons(LPVOID ptr)
{
}
};


#include "BtnStruct.h"

class CMyDialog : public CDialog
{
protected:

static const BUTTON_TABLE Buttons[];

/* my specific controls in this dialog */
CStaticField m_Field;
CImageButton m_Btn;
};

//in cpp:

static const CMyBtnStruct::BUTTON_TABLE Buttons[] = {{&m_Btn,
&m_Field, ....}}; //<- no access to class variables ;

// same problems here according to the protected class members

void OnInitDialog()
{
///...

AddButtons(this);
}


best regards
Hans

> email: newco...@flounder.com
> Web:http://www.flounder.com
> MVP Tips:http://www.flounder.com/mvp_tips.htm- Zitierten Text ausblenden -
>
> - Zitierten Text anzeigen -- Zitierten Text ausblenden -

Joseph M. Newcomer

unread,
Dec 7, 2010, 10:31:15 AM12/7/10
to
See below...

****
My error; that should have been

const CMyDialog::BUTTON_TABLE CMyDialog::Buttons[] = ...;

with the variable declared as static in the class. But this is just simple C++ syntax,
and my error should have been obvious as soon as you got the error message.
****


>It seems to me that there is no solution to solve this....

****
It's just syntax. For example, you could also have made the typedef public.
****


>
>
>class CMyBtnStruct
>{
> public:
> typedef struct {
> ...stuff
> } BUTTON_TABLE;
>
> void AddButtons(LPVOID ptr)
> {
> }
>};

****
But why something this complex? You could have eliminated the internal typedef and just
used the class!
****


>
>
>#include "BtnStruct.h"
>
>class CMyDialog : public CDialog
>{
>protected:
>
> static const BUTTON_TABLE Buttons[];
>
> /* my specific controls in this dialog */
> CStaticField m_Field;
> CImageButton m_Btn;
>};
>
>//in cpp:
>
>static const CMyBtnStruct::BUTTON_TABLE Buttons[] = {{&m_Btn,
>&m_Field, ....}}; //<- no access to class variables ;
>
>// same problems here according to the protected class members

****
It's JUST SYNTAX! The name would be declared as

const CMyBntStruct::BUTTON_TABLE CMyDialog::Buttons[] = ...;

Note the keyword 'static' also needs to be omitted, since that is used for a C variable,
not a C++ variable.
****
>
>void OnInitDialog()
>{
> ///...
>
> AddButtons(this);
>}
****
But it's JUST SYNTAX! Note that AddButtons is a non-static member of CBtnStruct, and you
need a CBtnStruct member to invoke it. Now, had you declared it 'static' you could have
written
CBtnStruct::AddButtons(this);
and it would have compiled. You have to pay attention to the fine points of syntax (which
I failed to do in the original message!)
joe
****

email: newc...@flounder.com

mfc

unread,
Dec 7, 2010, 11:18:01 AM12/7/10
to
> >> unit, it is known to all methods in the compilation unit. �So nothing special needs to be
> >> done. � It is visible. �You could also define it as a static member of your class, and the
> >> syntax change is minor, and the effect is the same. �So you could say
>
> >> class CMyDialog : public CDialog {
> >> � � �protected:
> >> � � � � typedef struct {
> >> � � � � ...stuff
> >> � � � � } BUTTON_TABLE;
> >> � � � � static const BUTTON_TABLE Buttons[];

> >> ...
>
> >> };
>
> >> in the .cpp file:
>
> >> static const CMyDialog::BUTTON_TABLE Buttons[] = { ... };
>
> >> ****
>
> >> >Is it better to install this struct within the dialog class or should
> >> >I use another headerfile which will be included in all dialog-classes
> >> >using this struct? And how can I use your initialization in these
> >> >classes?
>
> >> ****
> >> In general, you will only need this in the one dialog class, which is what I was assuming
> >> in the above example code. �Now, if you want to use this table in other dialog classes to

> >> build other button tables for other dialogs, you would put this typedef in another header
> >> file. �You would then include it in other dialog compilations. Note the declaration
> >> � � � � static const BUTTON_TABLE Buttons;

> >> would remain the same, but instead of putting the typedef right ahead of it, you would
> >> move it to the separate header file.
> >> ****
>
> >> > � � Buttons[] = {
> >> > � �{&mIncreaseSomeValue, &m_SomeControl, CImageTextButton::INCREASE,
> >> >0, 1 },
> >> > � �{&m_OtherControl, CImageTextButton::DECREASE, 0, 1 },
> >> > � �{NULL, 0, 0, 0} // EOT
> >> > �};

>
> >> ****
> >> Note that using the sentinel to mark the end is convenient, particularly if the function
> >> doesn't have access to the definition. �If you want to use the length of the struct, leave

> >> the NULL terminator from the description and use _countof.
>
> >> What I would do if I needed this in several places is create a separate class which has an
> >> AddButtons method; then I could do something like
> >> � � � � Buttons.AddButtons();

> >> or, if I needed the dialog reference,
> >> � � � � Buttons.AddButtons(this);
> >> Note that you can refactor this code in as many ways as you want. �It's Just Code.

> >> ****
>
> >The problem is that if I install the typedef struct as protected in
> >the same class, I`ve no access to this typedef in my static c++
> >declaration (cpp file) as well as if I try to add some class variables
> >to the struct in the cpp.
>
> >class CMyDialog : public CDialog
> >{
> >protected:
> >        typedef struct {
> >        ...stuff
> >        } BUTTON_TABLE;
> >        static const BUTTON_TABLE Buttons[];
>
> >   /* my specific controls in this dialog */
> >   CStaticField m_Field;
> >   CImageButton m_Btn;
> >};
>
> >in the .cpp file:
>
> >static const CMyDialog::BUTTON_TABLE Buttons[] = {{&m_Btn,
> >&m_Field, ....}};    //<- no access to class variables ;
>
> ****
> My error; that should have been
>
> const CMyDialog::BUTTON_TABLE CMyDialog::Buttons[] = ...;
>

const CMyDialog::BUTTON_TABLE CMyDialog::Buttons[]= {{5,
&m_BtnRefreshL}, {6, &m_BtnRefreshR}};

error message: "a nonstatic member reference must be relative to a
specific object"

The problem is that m_BtnRefreshL, m_BtnRefreshR are members (derrived
from the cbutton class | names of the buttons in the dialog) of the
CMyDialog class.


So maybe the solution with the additional class is required to get it
working?


class CMyBtnStruct
{
public:
typedef struct {
...stuff
} BUTTON_TABLE;


void AddButtons(LPVOID ptr)
{
for(int i=0; Buttons[i].ctrl != NULL; i++)
{
How will I get the Buttons[i].ctrl to this class?
}
}
};

Where will you install the typedef struct - in the CMyDialog class or
in a separate headerfile which will be included in each dialog using
the BUTTON_TABLE?

class CMyDialog
{

protected:
CMyBtnStruct m_BtnStructMember;

}

void CMyDialog::OnInitDialog()
{

BtnStructMember.AddButtons(this);
}


> ****
> But it's JUST SYNTAX!  Note that AddButtons is a non-static member of CBtnStruct, and you
> need a CBtnStruct member to invoke it.  Now, had you declared it 'static' you could have
> written
>         CBtnStruct::AddButtons(this);
> and it would have compiled.  You have to pay attention to the fine points of syntax (which
> I failed to do in the original message!)
>                                         joe
> ****

but could I use this approach when I use some member-variables in the
BUTTON_TABLE?

best regards
Hans

mfc

unread,
Dec 7, 2010, 3:33:35 PM12/7/10
to
class CBtnStruct
{
public:
CBtnStruct(void);
~CBtnStruct(void);

typedef struct
{
int value;
CImageTextButton *ctl;
CImageStatic *target;


CImageTextButton::Direction dir;
int minvalue;
int maxvalue;

} BUTTON_TABLE;


void AddButtons(LPVOID Param, BUTTON_TABLE *pBtnTable)
{
for(int i=0; pBtnTable[i].ctl != NULL; i++)
{
//do something
pBtnTable[i].ctl->SetParameters(pBtnTable[i].target,
pBtnTable[i].dir,
pBtnTable[i].minvalue,
pBtnTable[i].maxvalue
);
}
}
};

If more than one CImageStatic (static-field) is affected by clicking
on a button, do you know a better solution than adding another
CImageStatic *target2; to the BUTTON_TABLE?

<< Selection >>
<< value >>

For example: if the CImageStatic static-field "Selection" will be
"none" by clicking on the left ("<<") button, both buttons ("<<")
(">>") from the static-field "value" must be disabled, too.

According to the declaration of the BUTTON_TABLE, static isn`t a
solution because of the class members in the struct BUTTON_TABLE. Do
you know a better solution than using a fix array BUTTON_TABLE
Buttons[12]; with the really long initialization

Buttons[0].ctrl = &m_BtnLeft1;
Button[0].target = &m_StatField1;
Button[0].minvalue = 0;
Button[0].maxvalue = 3;

and so on for all 12 items....

best regards
Hans


Joseph M. Newcomer

unread,
Dec 7, 2010, 8:45:05 PM12/7/10
to
See below...

****
Ah, yes, that is a problem. I forgot about that trick. What I had to do was but the IDC_
values in and obtain the object dynamically. One of the few valid cases where you can use
GetDlgItem.
****


>
>
>So maybe the solution with the additional class is required to get it
>working?
>
>
>class CMyBtnStruct
>{
> public:
> typedef struct {
> ...stuff
> } BUTTON_TABLE;
>
>
> void AddButtons(LPVOID ptr)
> {
> for(int i=0; Buttons[i].ctrl != NULL; i++)
> {
> How will I get the Buttons[i].ctrl to this class?
> }
> }
>};
>
>Where will you install the typedef struct - in the CMyDialog class or
>in a separate headerfile which will be included in each dialog using
>the BUTTON_TABLE?

****
Doesn't work; my error.
****


>
>class CMyDialog
>{
>
>protected:
> CMyBtnStruct m_BtnStructMember;
>
>}
>
>void CMyDialog::OnInitDialog()
>{
>
> BtnStructMember.AddButtons(this);
>}
>
>
>> ****
>> But it's JUST SYNTAX!  Note that AddButtons is a non-static member of CBtnStruct, and you
>> need a CBtnStruct member to invoke it.  Now, had you declared it 'static' you could have
>> written
>>         CBtnStruct::AddButtons(this);
>> and it would have compiled.  You have to pay attention to the fine points of syntax (which
>> I failed to do in the original message!)
>>                                         joe
>> ****
>
>but could I use this approach when I use some member-variables in the
>BUTTON_TABLE?
>
>best regards
>Hans

0 new messages