0

I want to use an image as a button that has no caption, no background and no border with a tabstop.

I have tried the speedbutton with the flat property set which is perfect except it has no tabstop.

I have tried the bitbtn which also accepts an image and has a tabstop but has no way to remove the button border. I have unchecked seBorder in StyleElements which does nothing and there is no flat option so cannot remove the border.

Rohit Gupta
  • 4,022
  • 20
  • 31
  • 41
Delphify
  • 13
  • 4
  • 1
    The purpose of having a border is to visualize to the user it can be focused/clicked at all. What's the reason that you want to challenge the user? Please end your sentences with punctuation - a line wrap alone is no subtitution for a dot. – AmigoJack Mar 14 '23 at 12:20
  • Just wondering, how is the user going to know that an image is focused, if you did manage to get the tabstop to work. – Rohit Gupta Mar 14 '23 at 12:57
  • @RohitGupta: Presumably with a classic Win32 focus rectangle. – Andreas Rejbrand Mar 14 '23 at 13:23
  • Maybe it's a version issue (Windows, Delphi, VCL or FMX) which you did not include in your question. For me a `TBitBtn` appears to fulfill your request if the image is big enough to cover the whole button. You did not say anything about the focus rectangle, should it be invisible or visible? – Tom Brunberg Mar 14 '23 at 13:23
  • I tried the `TBitBtn` trick with an oversized image, and it seems to kind of work, but it's maybe not very elegant to draw a button and then an image that completely covers the button. I also see that the button flickers (for instance, if I have a form with only the button and an edit control and then keep the Tab key depressed), even though the button has `DoubleBuffered = True`. – Andreas Rejbrand Mar 14 '23 at 13:28
  • It is Delphi VCL. I have no problem with a border on focus. I do not want a border on normal image button. i want it to look like an image rather than a button with an image if that makes sense. The buttons will be very small, an example would be a folder icon to select a directory. TBitBtn does not fulfil my request because images are not square and look really strange in places with a border around the image – Delphify Mar 14 '23 at 13:31
  • @Delphify: You can easily make your own button control by starting from the `Vcl.Buttons.TBitBtn` code. All you need to to is to remove most of the code from `DrawItem`. – Andreas Rejbrand Mar 14 '23 at 13:33
  • I guess you addressed me in response to my previous comment, when you said: *It is Delphi VCL*. Then you say: *I do not want a border on normal image button.* I don't understand, I have not suggested anything like that. Then you say: *... because images are not square and look really strange in places with a border around the image.* Again, I don't understand. You do know that you can resize the button to be just as small as the image, so no border around the image. But hey, maybe it is better you do as has been suggested, create your own button. – Tom Brunberg Mar 14 '23 at 14:52
  • 1
    Tom: Yes I realize I can resize to suit but as images are transparent and not completely square the border and background looks strange in the non square parts which are smaller in places. I wanted the same look as a flat speedbutton. @Andreas, thanks for the suggestion for custom button control based off original bitbtn code. It's not something I had considered or done before but have done as you suggested and all looks great. No more button borders unless button has focus. Thanks all for your suggestions :) – Delphify Mar 14 '23 at 16:59

1 Answers1

0

We all have personal styles and preferences, but when it comes to UI design, it is important to stick with the platform standard. And note that your UI may become an unusable headache in the future. Things like the introduction of the Aero interface and the use of high-density monitors can easily mess up your personalized UI.

With that said, TSpeedButton and TBitBtn are two options to implement what you want. By design, Delphi has two types of TControls. One is TGraphicControl, like TSpeedButton, which cannot receive the focus. The other is TWinControl, like TBitBtn, which can be focused. So, one way to do it is to place a TSpeedButton on a focusable container, make the container transparent by overriding CreateParam, and handle its keypress/keydown events. It is not easy though. Alternatively, you can sub-class TBitBtn and override its drawing, by handling the CN_DRAWITEM message.

Below is a minimal working example, tested on Delphi 2009 and 10.4. Since you want only the image, I skipped and didn't deal with theme. I also didn't handle button down, enabled, and hovering. Read the source code (Buttons.pas) for how to deal with them.

Add a TButton and a TbitBtn to a new form and try this code

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Buttons, StdCtrls;

type
  // This changes all TBitBtn controls in this form, including the one from the plaette
  TBitBtn = class(Buttons.TBitBtn)
  private
    procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
    procedure DrawItem(const DrawItemStruct: TDrawItemStruct);
  end;

  TForm1 = class(TForm)
    BitBtn1: TBitBtn;
    Button1: TButton;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TBitBtn }

procedure TBitBtn.CNDrawItem(var Message: TWMDrawItem);
begin
  DrawItem(Message.DrawItemStruct^);
end;

procedure TBitBtn.DrawItem(const DrawItemStruct: TDrawItemStruct);
var
  R: TRect;
  FCanvas: TCanvas;
begin
  FCanvas := TCanvas.Create;
  try
    FCanvas.Handle := DrawItemStruct.hDC;
    R := ClientRect;
    if Focused then
      FCanvas.DrawFocusRect(R);
    Glyph.Transparent := True;
    FCanvas.Draw((R.Left + R.Right - GlyPh.Width) div 2,
                 (R.Top + R.Bottom - GlyPh.Height) div 2,
                 GlyPh);
  finally
    FCanvas.Free;
  end;
end;

end.

Edit: 26 Mar 2023: TBitBtn's Glyph is drawn with transparent background, although its Transparent property is false. A line was added above to make the drawing transparent, like TBitBtn does.

W. Chang
  • 494
  • 3
  • 10
  • While your example shows a border only on focus it does not show an image at all – Delphify Mar 25 '23 at 09:27
  • I just tested it again on both Delphi 2009 and 10.4 on a Windows 10 PC. It is working. Is it because BitBtn1's Glyph is empty? Have you loaded an image in the Object Inspector? – W. Chang Mar 26 '23 at 11:03
  • An issue I forgot to mention is that `GlyPh` is drawn without transparency. This can be easily fixed by setting `Glyph.Transparent`. – W. Chang Mar 26 '23 at 11:08
  • My test image was a transparent image loaded from an TImageList? – Delphify Mar 29 '23 at 16:32
  • @Delphify: That explains. As you can see in the above code, the drawing is done only with `GlyPh`. It is easy to add support to image lists. Just use `ImageList.draw` or `ImageList_DrawEx`. Something like `ImageList.Draw(FCanvas, X, Y, ImageIndex, dsTransparent, itImage, Enabled);` should work. Of course, check if ImageList and `ImageIndex` are valid first. – W. Chang Mar 29 '23 at 18:17
  • @ W. Chang I have already solved my problem as per my comment 14 March from Andreas suggestion. It was far easier to remove the border code and leave border on focus than to add your minimal example and then add button down, hover, enabled, theme, TImageList etc. I did initially try your solution but it seemed a backwards way of solving an easy solution. Thanks for your solution anyway :) – Delphify Mar 30 '23 at 08:45
  • @Delphify: Since you are using image lists, it's not difficult. Just choose from `ImageIndex`, `HotImageIndex`, `DisabledImageIndex`, `SelectedImageIndex`, and `PressedImageIndex` according to the state. But, of course, to keep it simple, using oversize images may work when the background of its parent is single-colored and not themed. Just remember to test your UI on both high- and low-DPI monitors. Oversize images may become smaller on some monitors. – W. Chang Mar 30 '23 at 15:34