Link to home
Start Free TrialLog in
Avatar of javigil1
javigil1

asked on

Buttons. Easy, very easy.

How can I enable/disable a button? I mean, within a programm (not with the resource editor)  Which code do I have to use?
Avatar of mikeblas
mikeblas

In MFC, assuming your coding a member function of your CDialog-derived class where the button lives:

   CWnd* pWnd = GetDlgItem(IDC_YOURBUTTONID);

   // to disable
   pWnd->Enable(FALSE);

   // to enable
   pWnd->Enable(TRUE);

In Plain Windows SDK without MFC:

   HWND hw;
   hw = GetDlgItem(hwndDialog, IDC_YOURBUTTONID);

   // to disable
   EnableWindow(hw, FALSE);

   // to enable
   EnableWindow(hw, TRUE);

..B ekiM
Avatar of javigil1

ASKER

Uppsss!!

The compiler says that "Enable" is not a component of the CWind class.

(Error C2039)
..of the CWnd class, sorry!
Try this (which is kinda like the same as mikeb answer)

CButton *pBtn = (CButton *)GetDlgItem(ID_BUTTON);

then, to enable :

pBtn->EnableWindow(TRUE);

and to this disable:
pBtn->EnableWindow(FALSE);

No need to cast to CButton*.

In fact (strictly speaking) it is not correct to do so, because GetDlgItem returns a pointer to a CWnd object, not a pointer to a CButton object.

However, as you are calling a CWnd function anyway, it won't hurt.

Genreally, I use ClassWizard / DDX_Control to associate member variables (eg a CButton member).

In that case, assuming that you associate CButton m_mybutton, then you'd simply say:

  m_mybutton.EnableWindow(true);

no need for GetDlgItem etc, as that is all done for you by MFC.

MIKE: You've basically provided the answer .. how about you submit again with the corrections.

The corrected code (in case Mike doesn't respond) should be

   CWnd* pWnd = GetDlgItem(IDC_YOURBUTTONID);
   // to disable
   pWnd->EnableWindow(FALSE);
   // to enable
   pWnd->EnableWindow(TRUE);

In Plain Windows SDK without MFC:

   HWND hw = ::GetDlgItem(hwndDialog, IDC_YOURBUTTONID);
   // to disable
   ::EnableWindow(hw, FALSE);
   // to enable
   ::EnableWindow(hw, TRUE);

And (my preference), use class wizard to associate a CButton member variable with the control, then you get:

   // to disable
   m_mybutton.EnableWindow(FALSE);
   // to enable
   m_mybutton.EnableWindow(TRUE);


ASKER CERTIFIED SOLUTION
Avatar of mikeblas
mikeblas

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
anytime, Mike .. your answer was correct (in spirit at least).  We all make typos and slip-ups :-)


RONSLOW,

Just asking, is it that bad to cast the pointer to the type the window really is?  I mean, isn't CEdit (and all others controls) a specialisation of CWnd?  I know you said "strictly speaking", just wondering if there are any implications to doing so, other than the fact GetDlgItem returns a CWnd pointer?  (Anyway i use member variables usually, but sometimes i use this method to reduce the number of them when control is only used in one function or so).

Bye!

Alex
In this case, you'll never have a problem casting to CButton* if you know that ID_SOMETHING refers to a window of class "BUTTON".

..B ekiM
asaward:

GetDlgItem will only return a pointer to a CTempWnd (directly derived from CWnd) UNLESS you have alreday subclassed the control yourself (eg. used DDX_Control).  It will NOT return a pointer to a CButton at all.

For most simple MFC control classes, you can get away with it because there are no virtual functions being called and no data members in those classes.  However, anything other than simple MFC-defined CWnd-derived simple control classes, you can get into trouble.

Strictly speaking what is going on there is bad code.  It is exactly equivalent to this:

class Base {
 ...
};
class Derived : public Base {
 ...
 void f();
};

Base base;
(Derived*) p = (Derived*)(&base);
p->f();

so you are calling a member function of class Derived on an object that is NOT of type class Derived.

If f() were a virtual function, or class Derived had memeber variables accessed in f(), then you'd be in big trouble.

Fortunately, as I said, the simple MFC control classes don't add any virtual functions or member variables.  However, some of the more complicated (like CCheckListBox), do.  But, if you were using these classes, you'd almost have to be subclassing anyway.

Unless you REALLY need a CButton-defined function, use CWnd* instead of CButton*. In your case, EnableWindow is a CWnd function, so make store the value returned by GetDlgItem as a CWnd*.

MIKE: The window class makes little difference (other than whether the control knows how to handle the specific message).  Few if any MFC classes check the window class.  What DOES make a difference is the class you cast the return of GetDlgItem to.  If you cast it to CButton* then you are safe (with the current implementation of MFC) .. indeed, you could happily write the code as

  CEdit* p = (CEdit*)GetDlgItem(ID_MYBUTTON);
  p->EnableWindow(TRUE);

and everything would still be fine.  If you called a CEdit defined function, the button would just not respond to the message.  But this cast is just bad code.  The CButton cast is only a little better, but not technically good code either.

To be techincaly correct, if you need to use anything other then CWnd-defined functions, then you should subclass the control .. whether by DDX_Control or directly.
> Few if any MFC classes check the window class.  

Actually, they all will end up sending window class-specific messages to the target window.  Since message IDs overlap, it's possible to send a message to a window and have that message do something unexpected when it reaches a window that's actually of a different type. There doesn't need to be an test for the actual window class name to have a problem.

 > indeed, you could happily write the code as

     CEdit* p = (CEdit*)GetDlgItem(ID_MYBUTTON);
     p->EnableWindow(TRUE);

 > and everything would still be fine.  

Sure. But, what if you code

   p->SetCurSel(35, 125);

or

   p->GetLine(strFoo);

?

On second thought, I guess it's not a problem. Now that I look at WINUSER.H, these messages seem to be defined to not overlap; I must be remembering Win16, where I thought they were of the form (WM_USER + x), and the values used for (x) overlapped for all the different styles.

I guess, again, the worst thing which'll happen is that your code just won't work.

..B ekiM
>Actually, they all will end up sending window class-specific messages to the
....

Quite true .. but only for WM_USER messages (some control use these internally i think).

>Sure. But, what if you code ...

I certainly wasn't for a moment suggesting that it was something you SHOULD do .. just an example of some bad code that happens to work.

The point I was making is that even if some code does work given the current definition/implementation of MFC etc, that doesn't make it good code.

Of course, good and bad code isn't a black and white issue .. especially when you have to work with / around the limitations of a framework such as MFC.  Sometime you have to do something not quite kosher .. or it is just simpler to do so (and sometimes simple and clear code is better than doing the 'right' thing).