C++ | MFC | CMFCToolbar | Set tool button image as EMF file

Hi Experts,

I would like to know how can I set an EMF file on a disk as image to the CMFCToolBarButton's image.
We have the images of the tool bar buttons given as emf file which we are not supposed to convert to BMP and use it.
Instead we are suppose to directly use the EMF file to set as Tool bar button's image.

Please help and advise.

Thanks,
Karrtik
LVL 15
Karrtik IyerSoftware ArchitectAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

sarabandeCommented:
the class  CMFCToolBarButton is defined in afxtoolbarbutton.h and implementation is in afxtoolbarbutton.cpp.

the class has virtual member function OnDraw, so you could derive from it and use an overloaded OnDraw to draw .emf files instead of one bitmap file containing all toolbar button images.

I would assume it is simpler to convert to bitmap but you may make up your own decision.

Sara
0
Karrtik IyerSoftware ArchitectAuthor Commented:
Hi Sara,
I already tried this approach. I could derive my custom class from CMFCToolBarButton, however with this approach, two issues occur.
1> The InsertButton call on CMFCToolBar fails with instance of my custom class derived from CMFCToolBarButton. The create object call inside Insert function fails for my custom class.
CMFCToolBarButtonEx mybutton( MY_ID, -1, _T("My Tool") );                        
m_nToolBar.InsertButton(mybutton  );
2>  Also CMFCToolBarButton does not allow message map to defined for the derived class. The below code fails to compile:
BEGIN_MESSAGE_MAP(CMFCToolBarButtonEx, CMFCToolBarButton)
END_MESSAGE_MAP()

Thanks,
Karrtik
0
Karrtik IyerSoftware ArchitectAuthor Commented:
I forgot to update in my question that I could override the DrawButton function of CMFCToolBar and do a playMetaFile using DC object. However I was not sure about the limitation of this DrawButton approach. I would like to know if because of using this DrawButton option, will it cause any default behavior provided by CMFCToolBarButton to get overridden except the image being redrawn, also are there any chances of memory or performance issues?

Sample code:
BOOL CMFCToolBarEx::DrawButton(
      CDC* pDC,
      CMFCToolBarButton* pButton,
      CMFCToolBarImages* pImages,
      BOOL bHighlighted,
      BOOL bDrawDisabledImages
      ){
if(pButton->m_nID == MY_ID){
                  HENHMETAFILE hMetaFile = NULL;
                  hMetaFile = ::GetEnhMetaFile(L"C:\\scan_dash_alert_d.emf");
                  BOOL retval = pDC->PlayMetaFile(hMetaFile,pButton->Rect());      
                  return retval;
            }
else
                  return CMFCToolBar::DrawButton(pDC,pButton,pImages,bHighlighted,bDrawDisabledImages);
}
0
CompTIA Cloud+

The CompTIA Cloud+ Basic training course will teach you about cloud concepts and models, data storage, networking, and network infrastructure.

sarabandeCommented:
(1) i would assume that InsertButton is not a virtual function (i will verify that tomorrow). so you better would provide an empty or dummy bitmap and only use the OnDraw to get what you want.
(2) between BEGIN_MESSAGE_MAP and END_MESSAGE_MAP you would define an variable sized array by means of macros like ON_MESSAGE(...), .... i would assume that an empty array lets the compiler complain. beside of that, you should try to go without a message map cause this is designed for customizing of classes which belong to the mfc framework and less for tool helpers.

Sara
0
ZoppoCommented:
Hi. Some time ago I had to analyze the code of MFCToolBar and related a  lot and found there are things which are extremely hard to do. For example internally all images of all toolbars are hold within one image list (a global CMFCToolBarImages), this i.e. means you cannot use different color depths for different toolbars.

It is possible to override CMFCToolBar::InsertButton to replace the buttons by some instances of CMFCToolBarButton-derived instances. And it's possible to do own drawing in an overridden OnDraw in the CMFCToolBarButton-derived class.

But there are some problems, especially if you want to keep the visual effect from any of MFC's VisualManager - lot of code is internally used to draw all the gradients, shadows, highlighting, gray-coloring. If you simply do your drawing without the base class functionality you'll have to do all of these yourself.

Another problem is the customizing feature (I don't know if you need or want use it) and, related to this the serialization which is used to store info about position and look of all toolbars.

After lot of testing I found a possible (allthough not perfect) way to implement a toolbar were each button can have its own bitmap - if you're interested you can try, just add the four files to your projects and implement your own toolbar derived from my classes i.e. in mainfrm.* like this:
// in mainfrm.h before class CMainFrame is declared:
#include "MyToolBar.h"

class CMainFrameToolBar : public CMyToolBarBase
{
	int	InsertButton( const CMFCToolBarButton &button, INT_PTR iInsertAt = -1 ) override;
};

Open in new window

Within CMainFrame declaration replace the
CMFCToolBar m_wndToolbar

Open in new window

r with
CMainFrameToolBar m_wndToolBar;

Open in new window

Then you'll have to implement the CMainFrameToolBar::InsertButton in order to assign bitmaps to the buttons, i.e. for test I did it like this to use a PNG and an EMF:
// I did this in mainfrm.cpp
int CMainFrameToolBar::InsertButton( const CMFCToolBarButton &button, INT_PTR iInsertAt /*= -1*/ )
{
	CString strPath;

	switch( button.m_nID )
	{
	case( ID_APP_ABOUT ):
		strPath = "d:/temp/btn1.png";
		break;
	case( ID_FILE_PRINT ):
		strPath = "d:/temp/btn2.emf";
		break;
	default:
		return CMFCToolBar::InsertButton( button, iInsertAt );
	}

	CMyToolBarButton imageButton;

	imageButton.CopyFrom( button );

	if ( false == imageButton.LoadBitmap( strPath ) )
	{
		return CMFCToolBar::InsertButton( button, iInsertAt );
	}

	return CMyToolBarBase::InsertButton( imageButton, iInsertAt );
}

Open in new window

Of course you can (any maybe should) create own files for the CMainFrameToolBar class.

I tested it and it works fine, the buttons can still be customized and are correctly restored after a re-start of the application, they can be reset correctly as long as the bitmap files still exist (their name is stored with the buttons, not the bitmaps, so don't delete the files).

But: this is only for the toolbar buttons - if you have menu items for the same command-IDs they will still use the images from the resources, the same is with the bitmaps shown in the Customize dialog. I think the menu-part can be solved but is even a lot of effort, I think you'd have to derive classes for the menu bar, the menus and the menu-buttons in order to get a similar convenient and easy to use solution. About the Customize dialog I have no experience up to now at all, but I guess it's probably possible to find a solution to allow use of own bitmaps - but it's extremely hard to say how much effort this will mean.

Ok, it's time for bed :o)

I hope this helps,

ZOPPO
MyToolBar.cpp
MyToolBar.h
MyToolBarButton.cpp
MyToolBarButton.h
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Karrtik IyerSoftware ArchitectAuthor Commented:
Hi Sara,

InsertButton calls fails at the below location (afxtoolbar.cpp): The button pointer returned is NULL.
CMFCToolBarButton* pButton = (CMFCToolBarButton*) pClass->CreateObject();
      ENSURE(pButton != NULL);

And here is the declaration for InsertButton (it is virtual) from afxtoolbar.h:
virtual int InsertButton(const CMFCToolBarButton& button, INT_PTR iInsertAt = -1);

Thanks,
Karrtik
0
sarabandeCommented:
you need to overload the CreateObject function which is some kind of "virtual" constructor or "factory" code to creating derived objects rather than base class objects.

Sara
0
sarabandeCommented:
the pClass is of type CRuntimeClass*  what is a means to create objects from and get runtime info of CObject derived classes.

to make this feature available for your class you have to use the DECLARE_DYNCREATE macro in the header and the IMPLEMENT_DYNCREATE macro in the cpp file of your derived class.

Sara
0
ZoppoCommented:
About my previous comment: I forgot to mention the attached code uses Gdiplus, so you (if you don't use it yet) will have to initialize it. You can take a look at http://www.codeproject.com/Articles/1112/Starting-with-GDI to see how this can be done in a MFC app.

ZOPPO
0
Karrtik IyerSoftware ArchitectAuthor Commented:
Thanks Zoppo, I am trying your code.
Thanks Sara.
 I got that working by overriding DrawButton of the CMFCToolbar, does not necessarily have to override the CMFCToolBarButton::OnDraw, I guess.
The question would be say either on OnDraw or DrawButton I playing the EMF file works, however I am unable to get the text below the button working/appearing once I do the PlayMetaFile. How do I get to display the text below the EMF file using this approach?
See code below.
void CMFCToolBarButtonEx::OnDraw(
      CDC* pDC,
      const CRect& rect,
      CMFCToolBarImages* pImages,
      BOOL bHorz,
      BOOL bCustomizeMode,
      BOOL bHighlight,
      BOOL bDrawBorder,
      BOOL bGrayDisabledButtons
      )
{
      HENHMETAFILE hMetaFile = NULL;
      hMetaFile = ::GetEnhMetaFile(L"C:\\scan_dash_alert_d.emf");
      CRect myrect = rect;
      myrect.DeflateRect(rect.Width()/4,rect.Height()/4);
      BOOL retval = pDC->PlayMetaFile(hMetaFile,myrect);            
      if(retval == TRUE)
      {
            int i = i + 1;
      }
      this->m_strText = L"Scan Alert"; //This does not work
      
}
0
Karrtik IyerSoftware ArchitectAuthor Commented:
I guess I shall have to use the CDC and draw my own custom text and custom location. May be because of customised on draw the text set for button doesn't work in the default way as shown in my last post.
0
sarabandeCommented:
I guess I shall have to use the CDC and draw my own custom text and custom location.
yes, on that level you have to use DrawText for placing text below the image.

Sara
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.