1

The below question discusses how inheritance chain between TButton and TBitBtn has changed, namely the introduction of a common ancestor, TCustomButton.

What happened to TBitBtn and TButton inheritance chain?

In the past, those who wanted graphics on their button would opt for a TBitBtn, as it offered usage of bitmap glyphs.

In more recent versions of Delphi, the standard TButton allows to specify a TImageList, for png / jpg support, while TBitBtn does not.

My (long-time) button subclass inherits from TBitBtn, and changing that would mean a potential application-wide button redesign. No time for that. I want the ability to migrate things over time, in baby steps, without deleting and re-creating buttons (chance to miss the migration of a property or an event). Optimally, pick a button, wipe its glyph, put a png in its imagelist, over.

Looking at the sources of VCL.Buttons and VCL.StdCtrls, the bulk of the work for ImageList, ImageIndex ... is done at the common ancestor, TCustomButton. TButton only publishes those properties to be used at design-time.

Great, I can simply make my TBitBtn subclass publish those same properties. The properties are now visible in the object inspector. I can select an imagelist component, even pick an image index from the available images.

And this is where things stop working: the selected image doesn't get displayed at design-time, nor at runtime. Assign a glyph, it gets displayed without any problems. I tried looking at other properties which may interfere in the painting of the png (maybe some kind of transparency option), in vain.

EDIT:

This link mentions that TBitBtn purposely disables the usage of ImageList in favor of its own drawing:

http://codeverge.com/embarcadero.delphi.graphics/tbitbtn-with-png-on-d2009/1077124

Nonetheless, any suggestions on how I can achieve my "baby steps" migration described above from bmp icons to png ones, as smoothly as possible?

Version info: 10.3.1

Thanks!

Khorkhe
  • 1,024
  • 1
  • 11
  • 26
  • There is a significant difference between `TBitBtn` and `TButton` that will make your "baby steps" very hard. You see unlike standard `TButton` which is always showing just one image `TBitBton` can actually contain up to four Glyphs stored in same image. These four images are used to show different state of the button (Up, Disabled, Clicked, Down). In order to draw the needed image it uses ImageList code to extract the needed Glyph from the provided image. This makes loading of image/s to TBitBtn from image list not possible. – SilverWarior Dec 06 '20 at 12:41
  • Now looking at documentation for Delphi 10.4 it seems that there were significant changes in `TCustomButton` class which introduces additional properties to determine Image Index for different button states essentially allowing any Button to have multiple images for multiple states. – SilverWarior Dec 06 '20 at 12:46
  • So whatever "baby steps" you make in current version be aware that they could get broken when you decide to migrate to Delphi 10.4 or later. In your case I would consider upgrading to latest version of Delphi before making any changes to your custom version of `TButBtn`. Maybe the new changes to `TCustomButton` will also help you simply the code of your custom buttons. – SilverWarior Dec 06 '20 at 12:50
  • right, so a migration to 10.4 will effectively allow me to alter one button at a time, with no possible side-effects across the application. That seems indeed like the optimal way to go. Now, does that make the difference between TButton and TBitBtn a historical one, or are there still any use-cases where one would choose this over that? – Khorkhe Dec 06 '20 at 15:36
  • Also, if you wouldn't mind writing that up as an answer so I can accept it. Thanks! – Khorkhe Dec 06 '20 at 15:37

3 Answers3

1

I tested with Delphi 10.4.1 and it works perfectly.

At first, I tought about an interposer class ot make ImageIndex and Images published. This is not even required. TBitBtn in Delphi 10.4.1 has both properties already published. I had not noticed that before !

Here is the code:

unit BitBtnInheritenceDemoMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Buttons,
  System.ImageList, Vcl.ImgList;

type   
  TForm1 = class(TForm)
    BitBtn1: TBitBtn;
    ImageList1: TImageList;
    procedure FormCreate(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
    // Properties could also be assigned thru object inspector
    BitBtn1.Images     := ImageList1;
    BitBtn1.ImageIndex := 1;
end;

end.
fpiette
  • 11,983
  • 1
  • 24
  • 46
  • I tried the same in Delphi 10.3.1, it didn't work. Could it be that in 10.4.1, the Images property of TBitBtn is available at design time without using an interposer class? – Khorkhe Dec 06 '20 at 08:23
  • 1
    There were significant changes to `TBitBtn` and underlying `TCustomButton` as well introduced in Delphi 10.4 that are not present in Delphi 10.3. Most notably the ability to naturally connect `TbitBtn` to external image list and then specify Image Index for each button state. this isn't present in Delphi 10.3 or older where `TBitBtn` stores one image which can optionally store multiple Glyphs. In order to render correct Glyph based on `TBitBtn` state it then uses `ImageList` code internally to extract needed Glyph from the stored image. – SilverWarior Dec 06 '20 at 12:56
  • @SilverWarior i see, so the ImageList, previously only for internal usage, has been exposed to the user in Delphi 10.4. That is inline with fpiette's answer. – Khorkhe Dec 06 '20 at 15:34
  • @Khorkhe I edited my answer to remove the interposer class. TBitBtn works out of the box with image list in Delphi 10.4.1. The interposer I wrote before is essentially a noop with Delphi 10.4.1. I suggest you upgrade your Delphi to the latest version. – fpiette Dec 06 '20 at 18:15
  • That's good news for PNG support. Wonder if that makes the two classes interchangeable at this point (minus glyph) – Khorkhe Dec 06 '20 at 19:01
  • TButton inherite from TCustomButton without adding anything. TBitBtn inherit from TCustomButton and add salt and pepper. See the source code. – fpiette Dec 06 '20 at 20:04
1

There is a significant difference between TBitBtn and TButton that will make your "baby steps" very hard. You see unlike standard TButton which is always showing just one image TBitBtn can actually contain up to four Glyphs stored in same image. These four images are used to show different state of the button (Up, Disabled, Clicked, Down). In order to draw the needed image TBitBtn uses ImageList code to extract the needed Glyph from the provided image. This makes loading of image/s to TBitBtn from image list not possible.

Fun fact:

Did you know that in older versions of Delphi basic TButton didn't support showing any icons at all. So in the past if you wanted to have button with an icon on it you had to use TBitBtn instead.

Now looking at documentation for Delphi 10.4 it seems that there were significant changes in TCustomButton class which introduces additional properties to determine Image Index for different button states essentially allowing any Button to have multiple images for multiple states. Same goes for standard TButton in Delphi 10.4.

While this brings standard TButton functionality much closer to the one from TBitBtn there are still many cases were you would prefer to use TBitBtn instead. For instance one notable drawback of standard TButton is that you can't fully change font property of its caption like changing Color of the Button text. But this is fully supported on TBitBtn.


So whatever "baby steps" you make in current version be aware that they might get broken when you decide to migrate to Delphi 10.4 or later.

In your case I would strongly advice you to consider upgrading to the latest version of Delphi before making any changes to your custom version of TBitBtn. Maybe the new changes to TCustomButton and subsequently to TBitBtn itself will also help you simplify the code of your custom buttons.

SilverWarior
  • 7,372
  • 2
  • 16
  • 22
  • this confirms the reason why our button subclasses from TBitBtn and not TButton in the old days. As @fpiette suggests in his answer, it seems in 10.4 specifying both a bmp and a png gives priority to the imagelist. With that, I should be able to easily change one button icon at a time, without the risk of introducing any breaking changes across the application. Wish i could accept both answers, as they both add valuable information. – Khorkhe Dec 08 '20 at 08:35
0

I am using Delphi XE5, in my case I tried with this code and it works:

ImageList1.GetBitmap(0, BitBtn1.Glyph);
Dedy Chaidir
  • 767
  • 6
  • 15