如何在 TStringGrid 单元格中绘制按钮

How to draw buttons in TStringGrid cells

我正在尝试通过向网格内的单元格添加视觉对象来自定义 TStringGrid。一列需要在每行中包含标准 windows 按钮,另一列需要包含带有预定义选项的下拉列表。

据我所知,实现此目的的最佳方法是在 OnDrawCell 事件处理程序中手动绘制按钮。我发现的所有示例都使用 DrawFrameControl(),它不会像您在 Windows 7 或更高版本中所期望的那样绘制主题按钮。

是否有与 DrawFrameControl() 等效的功能,可以让我绘制主题按钮,如果可以,有人可以举例说明我如何使用它吗?

我还尝试创建一个 TButtons 向量并将每个按钮的父级设置为 StringGrid 并将每个按钮放在相关单元格中。这也有效,但现在允许在有更多单元格可以显示可见区域时滚动网格。

我正在使用 RAD Studio 10.2 C++ 构建器并使用 BCC32C 编译器(clang 增强)。它是一个 VCL WIN32 应用程序。

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

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 都是正确的。

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.

正确。您必须将 StringGrid 子类化以拦截滚动,以便您可以手动重新定位按钮。

为了完整起见,这里是我用来在 TStringGrid 单元格中绘制 Windows 主题按钮的代码:

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);
    }
}

然后我使用 OnMouseDownOnMouseMoveOnMouseLeaveOnMouseUp 事件来确定按钮需要处于什么状态使用 3 TPoint对象 FocusCellPrevCellCellDown