4

I'm looking for an out-of-the-box way of configuring a flat borderless button. So that I can add a button from palette and configure it in design-time, without runtime overrides. I can use DevExpress components, however I would like to avoid LookAndFeel overrides or creating a custom theme for that.

The problem is that some controls allow to edit properties, but miss others.

Here's what I have tried:

Component      TabStop    FocusRect     Text V.Align    Borderless    Color
----------------------------------------------------------------------------
TButton        V          V             V               -             -
TSpeedButton   V          V             V               -             -
TLabel         -          -             V               V             V
TPanel         V          -             V               V             V
TStaticText    V          -             -               V             V
TcxButton      V          V             V               -             V
TcxLabel       -          -             V               V             V
----------------------------------------------------------------------------
* Text V.Align - vertical text alignment to center
* Borderless - no borders in default/unfocused state
** Color - ability to set face color

Another approach could be to override a TButton class via some OwnerDraw magic and put that unit first into every forms' uses clause?

Do you know of any alternatives that would allow to create/configure such a flat button in designtime having only standard Delphi 7 and basic DevExpress components?

EDIT: To address downvoters, who presumably think this is a bad question because it asks how to do something with existing tools without reinventing the bicycle.

  • This is not a garage project. There are half a dozen developers and build-machines, meaning that installing an updated component containing custom button each time for each one of them is a noticeable trouble overall.
  • The question is not about looking for a 3rd party component. I'm asking if there's a known way to configure existing controls to suit the need. You don't create a new control each time you need a hotlink label right?

P.S. Target OS is Windows XP and up

Kromster
  • 7,181
  • 7
  • 63
  • 111
  • And your requirements are that you need all those 5 properties? – Jan Doggen Jun 19 '14 at 08:33
  • 1
    Surely this is a classic case of needing to create your own component? It's not that difficult to do. – Andy_D Jun 19 '14 at 08:33
  • @Andy_D: There are many developers, thus installing a design-time package is a noticeable effort. It's better to use existing component that can be configured that way instead of creating a custom replacement for every small need ;) – Kromster Jun 19 '14 at 08:42
  • Windows Vista introduced a CommandLink Button and later versions of Delphi implement this as a property TButton.Style := bsCommandLink. I am unfamiliar with DevExpress if there is support there. Here's an image http://stackoverflow.com/questions/5007243/how-make-stylistic-buttons-windows-7 – Lars Jun 19 '14 at 09:45
  • Strange to want to have a different color than the background for a borderless control, it will sure look like a border. Same for the focus rectangle. – Sertac Akyuz Jun 19 '14 at 10:14
  • @SertacAkyuz: Background color is blue. Button color is plain Orange. Focus rect or focus color can be Black/Red. Don't see nothing odd about that. – Kromster Jun 19 '14 at 10:19
  • Sorry for asking... how do you vertically text align a TButton? – Lars Jun 19 '14 at 13:02
  • @Lars: Please read * item below, "vertical text alignment **to center**". – Kromster Jun 19 '14 at 13:20
  • Half a dozen developers is a small team, and distributing a few component source files and a package file to them and your build servers is a relatively minor task. You somehow manage to install the DevEx stuff for them (including updates and bug fixes), don't you? Nobody said "Create a new control each time you need a hotlink label", but you can create a *single* hotlink label that meets your needs and then reuse it multiple times. – Ken White Jun 19 '14 at 13:51
  • It is indeed really easy to install components. You put the code into a designtime package that is in revision control. You provide a build script that will automate building. Then you update, close ide, build, open ide. Takes no time at all. You make life far harder by fighting. – David Heffernan Jun 20 '14 at 20:24
  • Thanks for the downvotes. Does anyone cares to explain why exactly this is a bad question? There are hundreds of "How to do/get something.." questions on SO. If the answer is "no" why dont just write it without going into "-1 You are doing it wrong from the start". Theoreticaly, what if I were just missing TBitBtn that could be configured that way? – Kromster Jun 20 '14 at 22:23

1 Answers1

8

Below is an interposer class example that modifies TButton to be of BS_OWNERDRAW style. As you noted, it can be put into a unit that is to be used after 'stdctrls'.

type
  TButton = class(stdctrls.TButton)
  protected
    procedure SetButtonStyle(ADefault: Boolean); override;
    procedure CNCtlcolorbtn(var Message: TMessage); message CN_CTLCOLORBTN;
    procedure CNDrawitem(var Message: TWMDrawItem); message CN_DRAWITEM;
    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  end;

...

procedure TButton.SetButtonStyle(ADefault: Boolean);
begin
  if HandleAllocated then
    Perform(BM_SETSTYLE, BS_OWNERDRAW, 1);
end;

procedure TButton.CNCtlcolorbtn(var Message: TMessage);
begin
  DWORD(Message.Result) := CreateSolidBrush($79FF);
end;

procedure TButton.CNDrawitem(var Message: TWMDrawItem);
var
  DC: HDC;
  SaveObj: HGDIOBJ;
  R: TRect;
begin
  R := ClientRect;
  DC := Message.DrawItemStruct.hDC;
  SaveObj := SelectObject(DC, Font.Handle);
  SetBkMode(DC, TRANSPARENT);
  DrawText(DC, PChar(Caption), -1, R, DT_SINGLELINE or DT_CENTER or DT_VCENTER);
  SelectObject(DC, SaveObj);
  Message.Result := 1;
end;

procedure TButton.WMPaint(var Message: TWMPaint);
var
  DC: HDC;
  R: TRect;
begin
  inherited;
  if GetFocus = Handle then begin
    DC := GetDC(Handle);
    SelectObject(DC, GetStockObject(DC_BRUSH));
    SetDCBrushColor(DC, $FF);
    R := ClientRect;
    InflateRect(R, -2, -2);
    FrameRect(DC, R, DC_BRUSH);
    ReleaseDC(Handle, DC);
  end;
end;

Looks on W7 like this:

enter image description here

Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169