Owner Drawing the Submenu Arrow | CodeGuru

Owner Drawing the Submenu Arrow

I was looking for info on how to do some custom drawing with respect to the submenu arrow recently, only to find that each question about it went unanswered. So, because I have solved the problem much to my liking, I would like to share the solution with whomever might find it useful. My goal […]

Written By
CodeGuru Staff
CodeGuru Staff
Dec 19, 2006
2 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

I was looking for info on how to do some custom drawing with respect to the submenu arrow recently, only to find that each question about it went unanswered. So, because I have solved the problem much to my liking, I would like to share the solution with whomever might find it useful. My goal was to simply make disabled custom drawn submenus have disabled looking submenu arrows, but there seemed to be no obvious way to do this. In finding a solution, I also found a way to simply do custom submenu drawing if that is what you want to do also.

First, start with the owner draw handling code:

LRESULT CMyMenuClass::MyDrawMenuItem(MSG &iMsg)
{
   LRESULT result = 0;

   DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*)iMsg.lParam;
   HDC hDC = dis->hDC;

   if(dis->CtlType == ODT_MENU)
   {
      CMyMenuItem *menuItem = (CMyMenuItem*)dis->itemData;
      RECT textR            = dis->rcItem;
      bool isSubmenu        = menuItem->IsSubmenu();
      bool isEnabled        = menuItem->IsEnabled();

      //Draw custom submenu arrow
      if(isSubmenu)
      {
         HBITMAP hBitmap = (HBITMAP)::LoadImage(NULL,
            MAKEINTRESOURCE(OBM_MNARROW), IMAGE_BITMAP, 0, 0,
            LR_DEFAULTSIZE | LR_SHARED);
         BITMAP bm;
         ::GetObject(hBitmap, sizeof(BITMAP), (LPSTR) &bm);
         RECT arrowR  = textR;
         textR.right -= bm2.bmWidth;
         arrowR.left  = textR.right;
         int arrowW   = (arrowR.right - arrowR.left);
         int arrowH   = (arrowR.bottom - arrowR.top);
         ::DeleteObject(hBitmap);

         //Center the arrow rect vertically
         if(arrowH > bm2.bmHeight)
         {
            int offsetH   = (bm2.bmHeight - arrowH) / 2;
            arrowR.top   += offsetH;
            arrowR.bottom = arrowR.top + bm2.bmHeight;
         }

         //Draw the arrow
         this->MyDrawMenuArrow(hDC, arrowR, isEnabled);
      }

      //Do your custom menu drawing here
      this->MyDrawMenuItem(menuItem, textR);
   }

   RECT tmpR = dis->rcItem;
   ::ExcludeClipRect(hDC, tmpR.left, tmpR.top, tmpR.right,
                     tmpR.bottom);
}

So in the function above, start with the last line of code first. This call basically is what stops the OS from drawing the submenu item for you, so now you will have a “clear” palette to work with. In the section pertaining to the submenu drawing, I retrieve the submenu and enabled flags that I stored with each item during menu creation in the item’s data (code not shown). If the item is a submenu, I calculate the proper arrow rect (taking note to shorten the text rect also so there will be no overlap), and then pass that rect, along with the output HDC and the enabled state, into my draw arrow function.

In the draw arrow function, you could do anything you want to customize the submenu arrows, so if you wanted to do something non-OS looking, here is where you do that. I just wanted to have an OS look, but specific functionality that is, for some reason, not available.

Here is my draw arrow function:

void CMyMenuClass::MyDrawMenuArrow(HDC inHDC, RECT &inDestR,
                                   bool inIsEnabled)
{
   //Create the DCs and Bitmaps we will need
   HDC arrowDC = ::CreateCompatibleDC(inHDC);
   HDC fillDC  = ::CreateCompatibleDC(inHDC);
   int arrowW  = inDestR.right - inDestR.left;
   int arrowH  = inDestR.bottom - inDestR.top;
   HBITMAP arrowBitmap    = CreateDIBBitmap(inHDC, arrowW, arrowH);
   HBITMAP oldArrowBitmap = (HBITMAP)::SelectObject(arrowDC, arrowBitmap);
   HBITMAP fillBitmap     = CreateDIBBitmap(inHDC, arrowW, arrowH);
   HBITMAP oldFillBitmap  = (HBITMAP)::SelectObject(fillDC, fillBitmap);

   //Set the offscreen arrow rect
   RECT tmpArrowR;
   ::SetRect(&tmpArrowR, 0, 0, arrowW, arrowH);

   //Draw the frame control arrow (The OS draws this as a black on
   //                              white bitmap mask)
   ::DrawFrameControl(arrowDC, &tmpArrowR, DFC_MENU, DFCS_MENUARROW);

   //Set the arrow color
   HBRUSH arrowBrush = inIsEnabled ? ::GetSysColorBrush(COLOR_MENUTEXT) :
                                     ::GetSysColorBrush(COLOR_GRAYTEXT);

   //Fill the fill bitmap with the arrow color
   ::FillRect(fillDC, &tmpArrowR, arrowBrush);

   //Blit the items in a masking fashion
   ::BitBlt(inHDC, inDestR.left, inDestR.top, arrowW, arrowH, fillDC,
            0, 0, SRCINVERT);
   ::BitBlt(inHDC, inDestR.left, inDestR.top, arrowW, arrowH, arrowDC,
            0, 0, SRCAND);
   ::BitBlt(inHDC, inDestR.left, inDestR.top, arrowW, arrowH, fillDC,
            0, 0, SRCINVERT);

   //Clean up
   ::SelectObject(fillDC, oldFillBitmap);
   ::DeleteObject(fillBitmap);
   ::SelectObject(arrowDC, oldArrowBitmap);
   ::DeleteObject(arrowBitmap);
   ::DeleteDC(fillDC);
   ::DeleteDC(arrowDC);
}

In the function above, CreateDIBBitmap is just a utility function I wrote; it’s used to create a compatible DIB bitmap. You can use whatever method you prefer to create your bitmap.

I decided that, because I just wanted a simple arrow, I would stick to using DrawFrameControl. For me, making sure that I could use the disabled color was the most important thing, and to my dismay, state flags have no effect on how the submenu item is drawn. Basically, the documentation states that it will just create a black on white mask for you, and that is it. If I wanted to take it to the next level, I might check for a theme, and use theme drawing if applicable.

Here is what the disabled submenu arrows look like afterwards:

CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.