DanAsh
DanAsh

Reputation: 67

How to draw buttons in TStringGrid cells

I am trying to customize a TStringGrid by adding visual objects to cells within the grid. One column needs to contain standard windows push buttons in each row and another column needs to contain a drop down with pre-defined options.

From what I have read the best way to achieve this is to draw the buttons manually in the OnDrawCell event handler. All of the examples I have found use DrawFrameControl() which does not draw a themed button like you would expect in Windows 7 or later.

Is there an equivalent function to DrawFrameControl() that will allow me to draw a themed button and if so can someone please give an example of how I might use it?

I also tried creating a vector of TButtons and setting the parent of each button to be the StringGrid and placing each button within the relevant cell. This also works but does now allow for scrolling of the grid when there are more cells that can be displayed visible area.

I am using RAD Studio 10.2 C++ builder and using the BCC32C compiler (clang-enhanced). It is a VCL WIN32 application.

Upvotes: 0

Views: 752

Answers (2)

DanAsh
DanAsh

Reputation: 67

Just for completeness here is the code I got working in order to draw a Windows Themed button in a TStringGrid cell:

void __fastcall TForm_Controller::StringGrid1DrawCell(TObject *Sender, int ACol,
          int ARow, TRect &Rect, TGridDrawState State)
{
    TStringGrid *grid;
    bool ButtonDown = false, ButtonHot = false, ButtonInFocus = false;
    TThemedElementDetails LDetails;
    TTextFormatFlags LTextFormat;
    TColor LColor, TempColor;
    TCustomStyleServices *LStyle;
    int XPos, YPos;
    TPoint points[3];

    grid = (TStringGrid *)Sender;

    //a cell with a button ('+' or '-')
    if((ACol == 0) && (ARow > 0) && grid->Cells[ACol][ARow].Length())
    {
        Rect.Left -= 4;  //override padding so button fills entire cell


        if ((FocusCell.X == ACol) && (FocusCell.Y == ARow))
            ButtonHot = true;
        if ((CellDown.X == ACol) && (CellDown.Y == ARow))
            ButtonDown = true;

        LStyle = StyleServices();
        if (LStyle->Enabled && LStyle->Available)
        {
            if (ButtonDown)
                LDetails = LStyle->GetElementDetails(tbPushButtonPressed);
            else if (ButtonHot)
                LDetails = LStyle->GetElementDetails(tbPushButtonHot);
            else if (ButtonInFocus)
                LDetails = LStyle->GetElementDetails(tbPushButtonDefaulted);
            else
                LDetails = LStyle->GetElementDetails(tbPushButtonNormal);
            LStyle->DrawElement(grid->Canvas->Handle, LDetails, Rect);

            if (LStyle->GetElementColor(LDetails, ecTextColor, LColor))
                grid->Canvas->Font->Color = LColor;
            grid->Canvas->Font->Size = 13;
            LTextFormat = (tfCenter);

            LStyle->DrawTextA(grid->Canvas->Handle, LDetails, grid->Cells[ACol][ARow], Rect, TTextFormat() << tfCenter << tfVerticalCenter);           
        }
        else    //themed drawing not available so revert to old style
            DrawButtonFace(grid->Canvas, Rect, 1, bsAutoDetect, true, ButtonDown, ButtonInFocus);
    }
}

I then use the OnMouseDown, OnMouseMove, OnMouseLeave and OnMouseUp events to determine what state the buttons need to be in using 3 TPoint objects FocusCell, PrevCell, CellDown.

Upvotes: 0

Remy Lebeau
Remy Lebeau

Reputation: 595412

Is there an equivalent function to DrawFrameControl() that will allow me to draw a themed button

The Win32 DrawFrameControl() function is for drawing non-themed UI controls. To draw themed UI controls, you need to use the Win32 Theming functions instead - DrawThemeBackground(), DrawThemeEdge(), DrawThemeText(), etc. These functions are wrapped for you by the VCL's Vcl.Themes unit. In particular, use the TThemeServices class, which has various Draw...() methods that you can use when TThemeServices.Available and TThemeServices.Enabled are both true.

I also tried creating a vector of TButtons and setting the parent of each button to be the StringGrid and placing each button within the relevant cell. This also works but does now allow for scrolling of the grid when there are more cells that can be displayed visible area.

Correct. You would have to subclass the StringGrid to intercept the scrolling so you can reposition the buttons manually.

Upvotes: 2

Related Questions