locked
Bitmap Buttons - "Assertion Failed!" - "subclass" my button class RRS feed

  • Question

  • Hi!

     

    Why do I get a

     

    I’ve tried to "subclass" my button class using below code. It seems to work fine when I debug it. The button resides on a non-modal dialogbox.

     

    My implementation file *.cpp

     

    BOOL CAddEntityIntoLayerDlg::OnInitDialog()

    {

              CDialog::OnInitDialog();

     

              // We need a handle to each

              CButton *bmpOuterContour;

     

              // Get a handle to each of the existing buttons

              bmpOuterContour = reinterpret_cast<CButton *>(GetDlgItem(IDC_BUTTON_ADD_TO_OUTER_LAYER));

     

              // Get the style of the button(s)

              LONG GWLOK = GetWindowLong(bmpOuterContour->m_hWnd, GWL_STYLE);

     

              // Change the button's style to BS_OWNERDRAW

              SetWindowLong(bmpOuterContour->m_hWnd, GWL_STYLE, GWLOK | BS_OWNERDRAW);

     

              // Subclass each button

              m_btnBmpOuterContour.SubclassDlgItem(IDD_DIALOG_ADD_ENTITY_TO_LAYER, this);

     

              // TODO:  Add extra initialization here

     

              return TRUE;  // return TRUE unless you set the focus to a control

              // EXCEPTION: OCX Property Pages should return FALSE

    }

     

    Here you have a part of my heaader *.h

     

    class CAddEntityIntoLayerDlg : public CDialog

    {

              DECLARE_DYNAMIC(CAddEntityIntoLayerDlg)

     

    public:

              CAddEntityIntoLayerDlg(CWnd* pParent = NULL);   // standard constructor

              virtual ~CAddEntityIntoLayerDlg();

     

    // Dialog Data

              enum { IDD = IDD_DIALOG_ADD_ENTITY_TO_LAYER };

     

    private:

              CBitmapButton m_btnBmpOuterContour;

     

    protected:

              virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

     

              DECLARE_MESSAGE_MAP()

    public:

              afx_msg void OnBnClickedButtonAddToOuterLayer();

     

    ...

    ...

    ...

    }

     

    Here the tree last lines from my call stack

     

    mfc80d.dll!CButton::DrawItem(tagDRAWITEMSTRUCT * __formal=0x0012f258)  Line 84 + 0x14 bytes

     

    mfc80d.dll!CButton::OnChildNotify(unsigned int message=43, unsigned int wParam=1054, long lParam=1241688, long * pResult=0x00000000)  Line 96

     

    mfc80d.dll!CWnd::SendChildNotifyLastMsg(long * pResult=0x00000000)  Line 2898

     

    ...

     

    Here is where it halts ( winctrl1.cpp )

     

    // Derived class is responsible for implementing all of these handlers

    //   for owner/self draw controls

    void CButton::DrawItem(LPDRAWITEMSTRUCT)

    {

              ASSERT(FALSE);

    }

     

    If this makes any sence to anyone reading this then please help!

     

    Regards

    Kennet

    PS. Yes I've tried to use "Insert code block" but it just don't work out correctly

    • Edited by KentaH Wednesday, June 10, 2009 9:37 AM Again code-block looks like sh*t!
    Wednesday, June 10, 2009 9:29 AM

Answers

  • I'm not really sure what your question is, or if it is related to your original question. If your original question is answered, it would be best to mark it as answered and ask a new question.

    To use multiple inheritance with MFC, only the left-most base class can derive from CObject (hence from CWnd).

    But why are you trying to use both  CBitmapButton base and CBitmapButton* member anyway?

    David Wilkinson | Visual C++ MVP
    • Edited by davewilk Thursday, June 11, 2009 10:14 AM format
    • Marked as answer by KentaH Thursday, June 11, 2009 11:08 AM
    Thursday, June 11, 2009 10:13 AM

All replies

  • 1. This line looks wrong:

    m_btnBmpOuterContour.SubclassDlgItem(IDD_DIALOG_ADD_ENTITY_TO_LAYER, this);

    First parameter should be ID of the control.

    2. Why aren't you using the normal MFC method of adding a control variable for your derived button class? Then you would not have made the above mistake.

    3. The compiler is telling you that you should override DrawItem() for your derived button class.
    David Wilkinson | Visual C++ MVP
    Wednesday, June 10, 2009 9:48 AM
  • Basically most of the code have been grabbed from: http://www.functionx.com/visualc/controls/bmpbtn.htm
    Maybe they've been wrong all the time if so please guide to a link where I can read some more about this issue.

    The control ID is "IDC_BUTTON_ADD_TO_OUTER_LAYER" it was named "IDC..." default from VC when I made the button.

    Regards
    Kennet

    Wednesday, June 10, 2009 9:57 AM
  • I repeat

    m_btnBmpOuterContour.SubclassDlgItem(IDD_DIALOG_ADD_ENTITY_TO_LAYER, this);

    The first parameter should be ID of the control, not the dialog IDD.

    David Wilkinson | Visual C++ MVP
    Wednesday, June 10, 2009 10:49 AM
  • As i mentioned initially I have a non-modal dialog in which I have a number of buttons dragged directly from the tool menu. They get IDD_ default BUT maybe one can rename them using ID_ instead, if so it’s great! To me this has become more a question of naming convention that an explanation to why I can’t use the IDD_ which is the default ID from VS:s tool box.

     

    Let's say that I must live with the IDD_ (default) and the above procedure requires ID_ what are my options then?

     

    Regard

    Kennet

     

    Wednesday, June 10, 2009 11:06 AM
  • This is not a question of naming convention. ID_, IDC_, IDD_ are all just numbers defined in "resource.h".

    That said, controls are normally created with IDC_, not IDD_ or ID_. In other parts of your code it is clear that IDC_BUTTON_ADD_TO_OUTER_LAYER is the ID of your button, and that IDD_DIALOG_ADD_ENTITY_TO_LAYER is the idetifier of your dialog.

    SubclassDlgItem() should take the ID of the control, not that of the dialog.

    David Wilkinson | Visual C++ MVP
    Wednesday, June 10, 2009 11:46 AM
  • Okay, I'm sorry! I don't know how I could misunderstand you. It’s pretty obvious what you are talking about now after a cup of coffee J I’m not over the finish line yet but I’m closer I hope. Now I’ve got stuck on below code line. I get an Assertion Error. It’s my member variable (m_btnBmpOuterContour) that is empty I guess.


    m_btnBmpOuterContour.SubclassDlgItem(IDC_BUTTON_ADD_TO_OUTER_LAYER,
    this);


    Regards

    Kennet

     

    Wednesday, June 10, 2009 2:05 PM
  • Well, what does the assertion say? When you get a assertion you should always break and look at the cause. Very often there will be a comment there.

    Also, as i said before, you are not doing things "the MFC way":

    1. The Owner Draw style should be set in the dialog template

    2. The sub-classing should be done by adding a control variable of type CMyButton to the button object on the template

    If you do things this way you do not even need to override OnInitDialog().

    David Wilkinson | Visual C++ MVP
    Wednesday, June 10, 2009 3:02 PM
  • New approach!

     

    I’ve looked back at some old projects and found a different way of solving my problem. I run a test sample on my home computer and it works. The same stuff at the office give’s me an ambiguous call which I can understand the cause of. I know I must use the key word “using” to get this right, but how?

     

    It worked on my home computer without any problems, no "using"-key word, no ambiguous calls, strange.

     

    My *.h:

     

    class CAddEntityIntoLayerDlg : public CDialog, CBitmapButton

    {

    ...

    ...

    public:

              void SetArrowButton();

              CBitmapButton* m_pButton;

    ...

    ...

    }

     

    My *.cpp:

     

    void CAddEntityIntoLayerDlg::SetArrowButton()

    {        

              m_pButton = new CBitmapButton;        

              m_pButton->Create(NULL, WS_CHILD|WS_VISIBLE|BS_OWNERDRAW, CRect(10,10,20,20), this, IDC_BUTTON_ADD_TO_OUTER_LAYER );

              m_pButton->LoadBitmaps( IDB_DEFAULT, IDB_SELECTED, IDB_FOCUS, IDB_DISABLED );

              m_pButton->SizeToContent();

              m_pButton->MoveWindow(10,10,20,20);   

    }

     

    I call the ”SetArrowButton()” from the constructor. Not very surpricingly I get the ambigius from the “m_pButton->Create(NULL...” line.

     

    Regards

    Kennet

     

    Thursday, June 11, 2009 9:34 AM
  • I'm not really sure what your question is, or if it is related to your original question. If your original question is answered, it would be best to mark it as answered and ask a new question.

    To use multiple inheritance with MFC, only the left-most base class can derive from CObject (hence from CWnd).

    But why are you trying to use both  CBitmapButton base and CBitmapButton* member anyway?

    David Wilkinson | Visual C++ MVP
    • Edited by davewilk Thursday, June 11, 2009 10:14 AM format
    • Marked as answer by KentaH Thursday, June 11, 2009 11:08 AM
    Thursday, June 11, 2009 10:13 AM
  • I'm having the same problem and am sure that I'm using the right ID for the control. I get the assertion failed problem only when I have the SubclassDlgItem line:
    	CBitmapButton *btnMap = new CBitmapButton();
    	btnMap->Create(NULL, WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
    		CRect(0, 0, 0, 0), this, IDC_TESTBUTTON);
    	btnMap->LoadBitmaps(IDB_TEST, 0, 0, 0);
    	btnMap->SizeToContent();
    	btnMap->SubclassDlgItem(IDC_TESTBUTTON, this);

    The article at: http://www.functionx.com/visualc/controls/bmpbtn.htm
    tells you to add the CBitmapButtons as members, as suggested here, but then later on they pop out these IDs (IDOK, IDCANCEL, etc.) from nowhere.

    If I am to add the buttons as members, how am I supposed to map the ID to each button?

    Also, Kennet, it would be helpful if you posted your solution for others like me who find this.

    Friday, July 3, 2009 8:01 AM
  • I'm sorry to admit that I haven't gone any further with this problem. I've taken a checkbox and "turned" it into a button and added a bitmap on top of it. It’s a darn ugly solution but since my project watch is ticking I must get moving. I hope to be able to get back to this issue I hate to leave unsolved things.

    Friday, July 3, 2009 8:21 AM
  • The solution is to not call SubclassDlgItem!  It is unnecessary and incorrect to do so for a dynamically created control.  The purpose of SubclassDlgItem is to attach a control to your variable.  But calling Create attaches it!  This is true whether the control is a CBitmapButton member variable or is created with 'new'

    If you look at the code to see where and why it asserts you will see that the button object already has an HWND, so the assertion is saying, if effect, "you can't attach an HWND to an object that already has an HWND."

    Friday, July 3, 2009 1:04 PM