Search code examples
c++buildervcl

Custom Drawing TPopupMenu doesn't seem to work


Using C++ Builder 2009. Experimenting to see if I can improve TPopupMenu (also TMainMenu) for Windows 11

My AdvancedDrawItem function is never called. I'm not sure why. Am I missing something silly?

image

//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
    // Init done here for the sake of small code sample
    PopupMenu1->OwnerDraw = true ;
    PopupMenu1->Items->OnAdvancedDrawItem = AdvancedDrawItem ;
    
    
    PopupMenu1->Popup(Left + Button2->Left + 50, Top + Button2->Top + 50) ;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::AdvancedDrawItem(TObject *Sender, TCanvas *ACanvas, const TRect &ARect, TOwnerDrawState State)
{
    TMenuItem *Item = (TMenuItem*)Sender ;
    
    TTextFormat TF = TTextFormat() << tfVerticalCenter << tfSingleLine << tfEndEllipsis << tfLeft ;
    
    ACanvas->TextRect((TRect&)ARect, (String&)Item->Caption, TF) ;
}
//---------------------------------------------------------------------------

PS. In a different project with a lot more complexity, I do get it to fire in an object that inherits from TPopupMenu. I'm thoroughly confused at the moment as to why I can't get this simple code to work. However, I'm starting to realize that this is a task and a half since DrawThemeBackground() doesn't appear to do anything during this event, and I was sort of relying on that. Also, keeping into account shortcuts, icons, etc while painting is going to be a PITA.


Solution

  • PopupMenu1->Items->OnAdvancedDrawItem = AdvancedDrawItem ;

    This is wrong. You need to assign the OnAdvancedDrawItem handler to each individual visible TMenuItem in the menu (ie, test1 and test2), not to the TPopupMenu::Items collection that owns the items.

    ACanvas->TextRect((TRect&)ARect, (String&)Item->Caption, TF) ;

    Both typecasts are wrong.

    Since you are not using a TF flag that allows TextRect() to modify the parameter values (tfCalcRect or tfModifyString), you can use const_cast to replace the first one:

    const_cast<TRect&>(ARect)

    But, there is technically no legal way to convert an rvalue (such as the return value of TMenuItem::Caption) to an lvalue reference, a variable or function call is required, so just use a variable:

    String str = Item->Caption;
    ACanvas->TextRect(const_cast<TRect&>(ARect), str, TF);
    

    In which case, you may as well use a variable for the 1st parameter, too:

    TRect rect = ARect;
    String str = Item->Caption;
    ACanvas->TextRect(rect, str, TF);