change width SysIPAddress32 - delphi

I'm trying implement IP edit. This my code:
unit Main;
interface
uses
System.SysUtils, System.Classes,
Winapi.Windows, Winapi.Messages, Winapi.CommCtrl,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.StdCtrls, Vcl.ComCtrls;
type
TIpEdit = class(TWinControl)
strict protected
procedure CreateParams(var Params: TCreateParams); override;
procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
procedure WMSetFont(var Message: TWMSetFont); message WM_SETFONT;
end;
TMainForm = class(TForm)
Btn1: TButton;
procedure FormCreate(Sender: TObject);
procedure Btn1Click(Sender: TObject);
private
FIpEdit: TIpEdit;
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
{ TIPEdit }
procedure TIPEdit.CreateParams(var Params: TCreateParams);
begin
InitCommonControl(ICC_INTERNET_CLASSES);
inherited CreateParams(Params);
CreateSubClass(Params, WC_IPADDRESS);
Params.Style := Params.Style or WS_TABSTOP or WS_CHILD;
end;
procedure TIPEdit.WMGetDlgCode(var Message: TWMGetDlgCode);
begin
inherited;
Message.Result := Message.Result or DLGC_WANTARROWS;
end;
procedure TIPEdit.WMSetFont(var Message: TWMSetFont);
var
LF: LOGFONT;
begin
if GetObject(Message.Font, SizeOf(LF), #LF) <> 0 then
begin
Message.Font := CreateFontIndirect(LF);
inherited;
end;
end;
{ TMainForm }
procedure TMainForm.Btn1Click(Sender: TObject);
begin
FIpEdit.Width := FIpEdit.Width + 100;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
FIpEdit := TIpEdit.Create(Self);
FIpEdit.Parent := Self;
FIpEdit.SetBounds(10, 10, 120, 21);
end;
end.
Before Btn1.Click
After Btn1.Click
As a result: Control change own width but not change width internal edits.
I try fix by two ways
Use RecreateWnd. This works, but IMHO its ugly solution.
Fix internal edits width manualy. This works, but it is difficult to
implement, due to the internal work of the control
Maybe I missed something and there is a simpler solution?
Edit:
I tested the RecreateWnd, but it can not be used when using DevExpress layout control. It seems that layout control uses the direct call of the API bypassing the SetBounds method. In this case RecreateWnd can not be using.
The final conclusion is:
RecreateWnd is simple solution with some ristrictions
RepeatUntil answer more difficult, but always working

The control does not provide a mechanism to update its layout after it has been created. Yes you could hack at the internal edit controls, but that is dangerous. You'd be relying on undocumented implementation detail that is subject to change.
In summary, I would say that recreating the window is the best solution available.

You can use TPanel with child TEdit controls to create your own IP Edit, That will give you more control of their property.
This how its look at run time:
I wrote this time ago, its not completed work but may give an idea of what i mean.
unit IPEdit;
interface
uses
System.SysUtils, System.Classes, Vcl.Controls, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Forms,
Vcl.Graphics, System.UITypes;
type
TExitType = (etNone, etNext);
type
TIPEdit = class(TCustomPanel)
private
FPart1 : TEdit;
FPart2 : TEdit;
FPart3 : TEdit;
FPart4 : TEdit;
FSplitter1 : TPanel;
FSplitter2 : TPanel;
FSplitter3 : TPanel;
FRiseErr : Boolean;
FErrMsg : string;
FExitType : TExitType;
FBevelInner : TPanelBevel;
FLeadingzero: Boolean;
procedure SetExitType(Value : TExitType);
procedure SetBevelInner(Value: TPanelBevel);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
protected
procedure EditOnKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure EditOnChange(Sender: TObject);
procedure OnPanelResize(Sender: TObject);
procedure EditOnExit(Sender: TObject);
published
property ShowError: Boolean read FRiseErr write FRiseErr default False;
property Leadingzero: Boolean read FLeadingzero write FLeadingzero default False;
property ErrorText: string read FErrMsg write FErrMsg;
property ExitType: TExitType read FExitType write SetExitType default etNext;
property BevelInner: TPanelBevel read FBevelInner write SetBevelInner default bvNone;
end;
implementation
const
Msg_Err_Value_Exceeded = 'Value cannot be greater than 255';
SplitterWidth = 5;
{ TIPEdit }
constructor TIPEdit.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
if AOwner is TWinControl then
Parent := TWinControl(AOwner);
{ Main Panle Style }
ParentBackground := False;
BevelKind := bkFlat;
BevelOuter := bvNone;
Color := clWindow;
Height := 25;
Width := 165;
Caption := '';
/////////////////////////////
{ Set Handlers}
OnResize := OnPanelResize;
{ Create child controls }
FPart1 := TEdit.Create(Self);
FPart1.Name := 'IPEditPart1';
FPart1.Visible := False;
FSplitter1 := TPanel.Create(Self);
FSplitter1.Name := 'IPSplitter1';
FSplitter1.Visible:= False;
FPart2 := TEdit.Create(Self);
FPart2.Name := 'IPEditPart2';
FPart2.Visible := False;
FSplitter2 := TPanel.Create(Self);
FSplitter2.Name := 'IPSplitter2';
FSplitter2.Visible:= False;
FPart3 := TEdit.Create(Self);
FPart3.Name := 'IPEditPart3';
FPart3.Visible := False;
FSplitter3 := TPanel.Create(Self);
FSplitter3.Name := 'IPSplitter3';
FSplitter3.Visible:= False;
FPart4 := TEdit.Create(Self);
FPart4.Name := 'IPEditPart4';
FPart4.Visible := False;
FPart1.Align := alLeft;
FSplitter1.Align := alLeft;
FPart2.Align := alLeft;
FSplitter2.Align := alLeft;
FPart3.Align := alLeft;
FSplitter3.Align := alLeft;
FPart4.Align := alLeft;
/////////////////////////////
{ Set Child Style }
// This order is very important //
FPart1.Parent := TWinControl(Self);
FSplitter1.Parent := TWinControl(Self);
FPart2.Parent := TWinControl(Self);
FSplitter2.Parent := TWinControl(Self);
FPart3.Parent := TWinControl(Self);
FSplitter3.Parent := TWinControl(Self);
FPart4.Parent := TWinControl(Self);
FPart1.Visible := True;
FSplitter1.Visible := True;
FPart2.Visible := True;
FSplitter2.Visible := True;
FPart3.Visible := True;
FSplitter3.Visible := True;
FPart4.Visible := True;
//////////////////////////////////
FPart1.Alignment := taCenter;
FPart2.Alignment := taCenter;
FPart3.Alignment := taCenter;
FPart4.Alignment := taCenter;
FPart1.Margins.Left := 0;
FPart2.Margins.Left := 0;
FPart3.Margins.Left := 0;
FPart4.Margins.Left := 0;
FSplitter1.Margins.Left := 0;
FSplitter2.Margins.Left := 0;
FSplitter3.Margins.Left := 0;
FPart1.Margins.Right := 0;
FPart2.Margins.Right := 0;
FPart3.Margins.Right := 0;
FPart4.Margins.Right := 0;
FSplitter1.Margins.Right := 0;
FSplitter2.Margins.Right := 0;
FSplitter3.Margins.Right := 0;
FPart1.AlignWithMargins := True;
FSplitter1.AlignWithMargins := True;
FPart2.AlignWithMargins := True;
FSplitter2.AlignWithMargins := True;
FPart3.AlignWithMargins := True;
FSplitter3.AlignWithMargins := True;
FPart4.AlignWithMargins := True;
FPart1.AutoSize := False;
FPart2.AutoSize := False;
FPart3.AutoSize := False;
FPart4.AutoSize := False;
FPart1.BorderStyle := bsNone;
FPart2.BorderStyle := bsNone;
FPart3.BorderStyle := bsNone;
FPart4.BorderStyle := bsNone;
FPart1.NumbersOnly := True;
FPart2.NumbersOnly := True;
FPart3.NumbersOnly := True;
FPart4.NumbersOnly := True;
FPart1.MaxLength := 3;
FPart2.MaxLength := 3;
FPart3.MaxLength := 3;
FPart4.MaxLength := 3;
FPart1.Width := 36;
FPart2.Width := 36;
FPart3.Width := 36;
FPart4.Width := 36;
FSplitter1.Alignment := taCenter;
FSplitter2.Alignment := taCenter;
FSplitter3.Alignment := taCenter;
FSplitter1.Caption := '.';
FSplitter2.Caption := '.';
FSplitter3.Caption := '.';
FSplitter1.BevelOuter := bvNone;
FSplitter2.BevelOuter := bvNone;
FSplitter3.BevelOuter := bvNone;
FSplitter1.Color := clWindow;
FSplitter2.Color := clWindow;
FSplitter3.Color := clWindow;
FSplitter1.ParentBackground := False;
FSplitter2.ParentBackground := False;
FSplitter3.ParentBackground := False;
FSplitter1.TabStop := False;
FSplitter2.TabStop := False;
FSplitter3.TabStop := False;
FSplitter1.Width := SplitterWidth;
FSplitter2.Width := SplitterWidth;
FSplitter3.Width := SplitterWidth;
FSplitter1.Font.Style := FSplitter1.Font.Style + [fsBold];
FSplitter2.Font.Style := FSplitter2.Font.Style + [fsBold];
FSplitter3.Font.Style := FSplitter3.Font.Style + [fsBold];
//////////////////////////////
{Set Child handlers}
FPart1.OnChange := EditOnChange;
FPart2.OnChange := EditOnChange;
FPart3.OnChange := EditOnChange;
FPart4.OnChange := EditOnChange;
FPart1.OnKeyDown := EditOnKeyDown;
FPart2.OnKeyDown := EditOnKeyDown;
FPart3.OnKeyDown := EditOnKeyDown;
FPart4.OnKeyDown := EditOnKeyDown;
FPart1.OnExit := EditOnExit;
FPart2.OnExit := EditOnExit;
FPart3.OnExit := EditOnExit;
FPart4.OnExit := EditOnExit;
{Set Child control tab order for the handlers work}
FPart1.TabOrder := 0;
FPart2.TabOrder := 1;
FPart3.TabOrder := 2;
FPart4.TabOrder := 3;
FPart1.Text := '1';
FPart2.Text := '2';
FPart3.Text := '3';
FPart4.Text := '4';
FExitType := etNext;
FErrMsg := Msg_Err_Value_Exceeded;
end;
destructor TIPEdit.Destroy;
begin
FPart1.Free;
FPart2.Free;
FPart3.Free;
FPart4.Free;
FSplitter1.Free;
FSplitter2.Free;
FSplitter3.Free;
inherited;
end;
procedure TIPEdit.EditOnChange(Sender: TObject);
var
iValue : Integer;
bValGrater : Boolean;
I: Integer;
begin
if NOT (Sender is TEdit) then Exit;
bValGrater := False;
if TryStrToInt(TEdit(Sender).Text, iValue) then begin
if (iValue > 255) then begin
iValue := 255;
bValGrater := True;
TEdit(Sender).Text := iValue.ToString;
if (FRiseErr and bValGrater) then
raise Exception.Create(FErrMsg);
end;
end;
if Length(TEdit(Sender).Text) = 3 then begin
case FExitType of
etNone: ;
etNext: FindNextControl(TEdit(Sender), True, False, False).SetFocus;
end;
end;
end;
procedure TIPEdit.EditOnKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
const
vkReturn = $0D;
begin
if not (Sender is TEdit) then Exit;
if Key = vkReturn then
FindNextControl(TEdit(Sender), True, False, False).SetFocus;
end;
procedure TIPEdit.OnPanelResize(Sender: TObject);
const
EditCount = 4;
SplitterCount = 3;
var
EditWidth : Integer;
FSplitterWidth : Integer;
EditMargin : Integer;
begin
EditWidth := Trunc(((Width) / EditCount) - (SplitterCount * (SplitterWidth)));
FPart1.Width := EditWidth;
FPart2.Width := EditWidth;
FPart3.Width := EditWidth;
FPart4.Width := EditWidth;
FSplitterWidth := Trunc(( (Width) - ( EditWidth * EditCount) ) / SplitterCount);
FSplitter1.Width := FSplitterWidth;
FSplitter2.Width := FSplitterWidth;
FSplitter3.Width := FSplitterWidth;
{Center edits text vertically == this is a temporary workaround}
FPart1.Margins.Top := 0;
FPart2.Margins.Top := 0;
FPart3.Margins.Top := 0;
FPart4.Margins.Top := 0;
EditMargin := Round( ((Height / 5)));
FPart1.Margins.Top := EditMargin;
FPart2.Margins.Top := EditMargin;
FPart3.Margins.Top := EditMargin;
FPart4.Margins.Top := EditMargin;
FSplitter1.Margins.Top := EditMargin + SplitterWidth; { the +lblWidth to make it lower than the edits}
FSplitter2.Margins.Top := EditMargin + SplitterWidth;
FSplitter3.Margins.Top := EditMargin + SplitterWidth;
end;
procedure TIPEdit.SetBevelInner(Value: TPanelBevel);
begin
TPanel(Self).BevelInner := Value;
end;
procedure TIPEdit.SetExitType(Value: TExitType);
begin
FExitType := Value;
end;
procedure TIPEdit.EditOnExit(Sender: TObject);
var
I: Integer;
begin
if not (Sender IS TEdit) then Exit;
if FLeadingzero then begin
if Length(TEdit(Sender).Text) >= 1 then
for I := Length(TEdit(Sender).Text) to 2 do begin
TEdit(Sender).Text := '0' + TEdit(Sender).Text;
end;
end;
end;
end.

Related

Delphi: How do you control multiple alike objects?

Say that I have five TRectangle objects, and a function is going to pass a parameter in to make one of them blink.
I know how to control one object like the following code:
procedure TForm1.TimerTimer(Sender: TObject);
begin
if rect1.Visible then
rect1.Visible := false
else
rect1.Visible := true;
end;
procedure TForm1.Blink_Square;
begin
Timer := TTimer.Create(nil);
Timer.OnTimer := TimerTimer;
rect1.Fill.Color := TAlphacolors.Red;
rect1.fill.Kind := TBrushKind.bkSolid;
rect1.Stroke.Thickness := 1;
rect1.Stroke.Color := Talphacolors.Darkgray;
Timer.Interval := 500;
Timer.Enabled := True;
end;
But I really wonder if there is a way that I can use the blink square repeatedly like having a procedure as procedure TForm1.Blink_Square(rec_number: integer); And we can call Blink_Square(5); to make rect5 blink.
Thanks in Advance
You can store your objects in an array or list, then use your procedure parameter to index into it.
var
Blinks: array[1..5] of record
Rectangle: TRectangle;
Timer: TTimer;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Blinks[1].Rectangle := Rect1;
Blinks[1].Timer := nil;
Blinks[2].Rectangle := Rect2;
Blinks[2].Timer := nil;
Blinks[3].Rectangle := Rect3;
Blinks[3].Timer := nil;
Blinks[4].Rectangle := Rect4;
Blinks[4].Timer := nil;
Blinks[5].Rectangle := Rect5;
Blinks[5].Timer := nil;
end;
procedure TForm1.TimerTimer(Sender: TObject);
var
Timer: TTimer;
begin
Timer := TTimer(Sender);
Blinks[Timer.Tag].Visible := not Blinks[Timer.Tag].Visible;
end;
procedure TForm1.Blink_Square(Number: Integer);
begin
Blinks[Number].Rectangle.Fill.Color := TAlphacolors.Red;
Blinks[Number].Rectangle.fill.Kind := TBrushKind.bkSolid;
Blinks[Number].Rectangle.Stroke.Thickness := 1;
Blinks[Number].Rectangle.Stroke.Color := Talphacolors.Darkgray;
if Blinks[Number].Timer = nil then
begin
Blinks[Number].Timer := TTimer.Create(Self);
Blinks[Number].Timer.OnTimer := TimerTimer;
Blinks[Number].Timer.Interval := 500;
Blinks[Number].Timer.Tag := Number;
Blinks[Number].Timer.Enabled := True;
end;
end;
Alternatively:
var
Rects: array[1..5] of TRectangle;
procedure TForm1.FormCreate(Sender: TObject);
begin
Rects[1] := Rect1;
Rects[2] := Rect2;
Rects[3] := Rect3;
Rects[4] := Rect4;
Rects[5] := Rect5;
end;
procedure TForm1.TimerTimer(Sender: TObject);
begin
TRectangle(Sender).Visible := not TRectangle(Sender).Visible;
end;
procedure TForm1.Blink_Square(Number: Integer);
var
Rec: TRectangle;
Timer: TTimer;
M: TNotifyEvent;
begin
Rec := Rects[Number];
Rec.Fill.Color := TAlphacolors.Red;
Rec.fill.Kind := TBrushKind.bkSolid;
Rec.Stroke.Thickness := 1;
Rec.Stroke.Color := Talphacolors.Darkgray;
if Rec.Tag = 0 then
begin
M := TimerTimer;
TMethod(M).Data := Rec;
Timer := TTimer.Create(Rec);
Timer.OnTimer := M;
Timer.Interval := 500;
Timer.Enabled := True;
Rec.Tag := NativeInt(Timer);
end;
end;

Get component text based on component name

So I creat some TEdit components like this
var
lb : TLabel;
topLabel, i: Integer;
dbedit : TEdit;
begin
inherited;
topLabel := 40;
i := 0;
lb := TLabel.Create(nil);
lb.Parent := GroupBox2;
lb.Left := 245;
lb.Top := 20;
lb.Caption := 'ASD';
with DataModule.myStoredProc do begin
Close;
ParamByName('#Param1').AsInteger := 1;
ExecProc;
Open;
SetLength(nrPozitiiDinctionar,RecordCount);
First;
while not Eof do begin
lb := TLabel.Create(nil);
lb.Parent := GroupBox2;
lb.Left := 7;
lb.Top := topLabel ;
lb.Caption := FieldByName('X').AsString;
dbedit := TEdit.Create(nil);
dbedit.Name := 'Edit'+IntToStr(FieldByName('Poz').AsInteger);
dbedit.Text := '';
dbedit.Parent := GroupBox2;
dbedit.Height := 21;
dbedit.Width := 40;
dbedit.Left := 240;
dbedit.Top := lb.Top-3 ;
topLabel := topLabel + 30;
nrPozitiiDinctionar[i] := FieldByName('Poz').AsInteger;
i := i + 1;
Next;
end;
end;
end;
Then after the user add his input I run a function with this code
var
IDPoz, I : Integer;
dbedit : TEdit;
pctj,nume : string;
begin
for I := Low(nrPozitiiDinctionar) to High(nrPozitiiDinctionar) do
begin
nume := 'Edit'+IntToStr(nrPozitiiDinctionar[i]);
pctj := TEdit(FindComponent('Edit'+IntToStr(nrPozitiiDinctionar[i]))).Text;
with DateCOFurnizori.spCOFCmzFurnizoriEvaluarePozitii_Edit do begin
ParamByName('#IDEvaluare').AsInteger := StrToInt(Edit1.Text);
ParamByName('#IDPozitie').AsInteger := IDPoz;
ParamByName('#DictionarID').AsInteger := 9103;
ParamByName('#DictionarPozitiiID').AsInteger := nrPozitiiDinctionar[i];
ParamByName('#Punctaj').AsFloat := 1 ;//StrToFloat(pctj) ;
ParamByName('#DataEvaluare').AsDateTime := Now;
ExecProc;
IDPoz := IDPoz + 1;
end;
end;
This is only a portion of the code but this should relate to my problem.
When I use the debugger there is no value in pctj, what am I doing wrong? I try to get the value of the TEdits based on their names. What am I doing wrong with the FindComponent function?
You are not assigning an Owner to the TEdit controls, so that is why FindComponent() cannot find them. Either assign Self as the Owner (since you are calling Self.FindComponent()), or else store the TEdit pointers in a TList or TObjectList that you can loop through when needed.

Creating a new btnClick procedure for a dynamic button

I'm having trouble creating a new procedure for a dynamic button. When the button is clicked, it supposed to send items to a RichEdit.
I have declared all my objects and my procedures.
Here's my code:
type
TfrmSale = class(TForm)
redOutput: TRichEdit;
btnBuy: TButton;
procedure btnBuyClick(Sender: TObject);
private
pnlSale : TPanel;
edtQuantity : TEdit;
rgpOptions : TRadioGroup;
btnSelect : TButton;
procedure btnSelectCick(Sender: TObject);
public
{ Public declarations }
end;
var
frmSale: TfrmSale;
implementation
{$R *.dfm}
procedure TfrmSale.btnBuyClick(Sender: TObject);
begin
pnlSale := TPanel.Create(frmSale);
pnlSale.Parent := frmSale;
pnlSale.Left := 10;
pnlSale.Top := 10;
pnlSale.Width := 350;
pnlSale.Height := 250;
pnlSale.Visible := True;
edtQuantity := TEdit.Create(pnlSale);
edtQuantity.Parent := pnlSale;
edtQuantity.Left := 50;
edtQuantity.Top := 60;
edtQuantity.Width := 80;
edtQuantity.Height := 20;
edtQuantity.Text := 'Quantity';
edtQuantity.Visible := True;
rgpOptions := TRadioGroup.Create(pnlSale);
rgpOptions.Parent := pnlSale;
rgpOptions.Left := 30;
rgpOptions.Top := 100;
rgpOptions.Width := 300;
rgpOptions.Height := 140;
rgpOptions.Visible := True;
rgpOptions.Caption := 'Options';
rgpOptions.Items.Add('Screws 12mm');
rgpOptions.Items.Add('Canned Peaches 250g');
rgpOptions.Items.Add('Tomatoes');
rgpOptions.Items.Add('Spaghetti 500g');
rgpOptions.Items.Add('Twin Flex 5m');
rgpOptions.Items.Add('Clear Glue 250ml');
btnSelect := TButton.Create(frmSale);
btnSelect.Parent := frmSale;
btnSelect.Left := 130;
btnSelect.Top := 260;
btnSelect.Width := 80;
btnSelect.Height := 40;
btnSelect.Caption := 'Select';
end;
procedure TfrmSale.btnSelectClick(Sender: TObject);
case rgpOptions.ItemIndex of
1 := redOutput.Lines.Add('Screws 12mm');
2 := redOutput.Lines.Add('Canned Peaches 250g');
3 := redOutput.Lines.Add('Refil Blue Pen');
4 := redOutput.Lines.Add('Tomatoes');
5 := redOutput.Lines.Add('Spaghetti 500g');
6 := redOutput.Lines.Add('Twin Flex 5m');
7 := redOutput.Lines.Add('Clear Glue 250ml');
end
Please can someone help me make a OnClick procedure.
AS TLama said. If you're going to create your buttons in code,you need to also assign the OnClick handler in code.
type
TfrmSale = class(TForm)
redOutput: TRichEdit;
btnBuy: TButton;
procedure btnBuyClick(Sender: TObject); <<--- link made in designer
private VVVVV-- from here on it's
pnlSale : TPanel; your responsibility
edtQuantity : TEdit;
rgpOptions : TRadioGroup;
btnSelect : TButton;
procedure btnSelectCick(Sender: TObject); <<-- link must be made in code
public
{ Public declarations }
end;
var
frmSale: TfrmSale;
implementation
{$R *.dfm}
procedure TfrmSale.btnBuyClick(Sender: TOBject);
begin
pnlSale := TPanel.Create(frmSale);
pnlSale.Parent := frmSale;
pnlSale.Left := 10;
pnlSale.Top := 10;
pnlSale.Width := 350;
pnlSale.Height := 250;
pnlSale.Visible := True;
edtQuantity := TEdit.Create(pnlSale);
edtQuantity.Parent := pnlSale;
edtQuantity.Left := 50;
edtQuantity.Top := 60;
edtQuantity.Width := 80;
edtQuantity.Height := 20;
edtQuantity.Text := 'Quantity';
edtQuantity.Visible := True;
rgpOptions := TRadioGroup.Create(pnlSale);
rgpOptions.Parent := pnlSale;
rgpOptions.Left := 30;
rgpOptions.Top := 100;
rgpOptions.Width := 300;
rgpOptions.Height := 140;
rgpOptions.Visible := True;
rgpOptions.Caption := 'Options';
rgpOptions.Items.Add('Screws 12mm');
rgpOptions.Items.Add('Canned Peaches 250g');
rgpOptions.Items.Add('Tomatoes');
rgpOptions.Items.Add('Spaghetti 500g');
rgpOptions.Items.Add('Twin Flex 5m');
rgpOptions.Items.Add('Clear Glue 250ml');
btnSelect := TButton.Create(frmSale);
btnSelect.OnClick:= btnSelectCick; <<-- Assign the button handler
btnSelect.Parent := frmSale;
btnSelect.Left := 130;
btnSelect.Top := 260;
btnSelect.Width := 80;
btnSelect.Height := 40;
btnSelect.Caption := 'Select';
end;
procedure TfrmSale.btnSelectClick(Sender: TOBject);
begin
redOutput.Lines.Add(rgpOptions.Lines[rgpOptions.ItemIndex]); <<-- DRY
end;
Note that I've tweaked btnSelectClick so that it reuses the data you've stored into rgpOptions previously.
See also: http://en.wikipedia.org/wiki/Don%27t_repeat_yourself

(Delphi THintWindow) How to draw a transparent PNG?

I have this delphi 2010 code:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Math, ExtCtrls, pngimage;
type
TMyHintWindow = class(THintWindow)
private
FBitmap : TBitmap;
ThePNG : TPngImage;
FRegion : THandle;
procedure FreeRegion;
protected
procedure CreateParams(var Params : TCreateParams); override;
procedure Paint; override;
procedure Erase(var Message : TMessage); message WM_ERASEBKGND;
public
constructor Create(AOwner : TComponent); override;
destructor Destroy; override;
procedure ActivateHint(Rect : TRect; const AHint : String); Override;
end;
type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender : TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1 : TForm1;
implementation
{$R *.dfm}
// --------------------------------------------------------------------------- //
constructor TMyHintWindow.Create(AOwner : TComponent);
begin
inherited Create(AOwner);
FBitmap := TBitmap.Create;
FBitmap.PixelFormat := pf32bit;
FBitmap.HandleType := bmDIB;
FBitmap.Transparent := True;
FBitmap.TransparentMode := tmAuto; // }tmFixed;
FBitmap.TransparentColor := clWhite;
FBitmap.AlphaFormat := {afPremultiplied; // }afDefined;
ThePNG := TPngImage.Create;
ThePNG.Transparent := True;
ThePNG.TransparentColor := clWhite;
ThePNG.LoadFromFile('D:\project-1\tooltip.png');
FBitmap.LoadFromFile('D:\project-1\tooltip.bmp');
end;
// --------------------------------------------------------------------------- //
destructor TMyHintWindow.Destroy;
begin
FBitmap.Free;
FreeRegion;
inherited;
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.CreateParams(var Params : TCreateParams);
const
CS_DROPSHADOW = $20000;
begin
inherited;
Params.Style := Params.Style - WS_BORDER;
Params.WindowClass.Style := Params.WindowClass.Style or CS_DROPSHADOW;
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.FreeRegion;
begin
if FRegion <> 0 then
begin
SetWindowRgn(Handle, 0, True);
DeleteObject(FRegion);
FRegion := 0;
end;
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.ActivateHint(Rect : TRect; const AHint : String);
var
i : Integer;
begin
Caption := AHint;
Canvas.Font := Screen.HintFont;
FBitmap.Canvas.Font := Screen.HintFont;
DrawText(Canvas.Handle, PChar(Caption), Length(Caption), Rect, DT_CALCRECT or DT_NOPREFIX);
Width := 230; // (Rect.Right - Rect.Left) + 16;
Height := 61; // (Rect.Bottom - Rect.Top) + 10;
FBitmap.Width := Width;
FBitmap.Height := Height;
Left := Rect.Left;
Top := Rect.Top;
FreeRegion;
with Rect do
FRegion := CreateRoundRectRgn(1, 1, Width, Height, 3, 3);
if FRegion <> 0 then
SetWindowRgn(Handle, FRegion, True);
AnimateWindowProc(Handle, 300, AW_BLEND);
SetWindowPos(Handle, HWND_TOPMOST, Left, Top, 0, 0, SWP_SHOWWINDOW or SWP_NOACTIVATE or SWP_NOSIZE);
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.Paint;
var
CaptionRect : TRect;
begin
with FBitmap.Canvas do
begin
Font.Color := clWindowText;
Brush.Style := bsClear;
end; // with
CaptionRect := Rect(25, 26, Width - 10, Height - 10);
SetBkMode(Canvas.Handle, TRANSPARENT);
DrawText(FBitmap.Canvas.Handle, PChar(Caption), Length(Caption), CaptionRect, DT_WORDBREAK OR DT_NOPREFIX);
BitBlt(Canvas.Handle, 0, 0, Width, Height, FBitmap.Canvas.Handle, 0, 0, SRCERASE{SRCCOPY});
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.Erase(var Message : TMessage);
begin
Message.Result := 0;
end;
// --------------------------------------------------------------------------- //
procedure TForm1.FormCreate(Sender : TObject);
begin
HintWindowClass := TMyHintWindow;
Button1.Hint := 'This is a nice fake tooltip!';
end;
// --------------------------------------------------------------------------- //
end.
This example has two issues:
I need to draw the PNG with the transparent borders. The image itself is here
If you run this project (form has just a button called Button1), and show the hint few times, you should realize that caption becomes bolder every time the hint is shown. I'm pretty sure I forgot a background I forgot to clear/erase, but I'm not sure how to fix that.
Can someone please tell me how to fix these two issues?
You will to have to perform adaption for position and png in cas of hint needed above, but the "engine" should work as expected. I didn't use GDI+ which would i have made much easier.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Math, ExtCtrls, pngimage;
type
TMyHintWindow = class(THintWindow)
private
FBitmap : TBitmap;
ThePNG : TPngImage;
FCurrAlpha:Integer;
FTimer:TTimer;
FActivated:Boolean;
FLastActive:Cardinal;
procedure PrepareBitmap;
procedure IncAlpha(Sender:TObject);
protected
procedure CreateParams(var Params : TCreateParams); override;
procedure Paint; override;
procedure Erase(var Message : TMessage); message WM_ERASEBKGND;
public
constructor Create(AOwner : TComponent); override;
destructor Destroy; override;
procedure ActivateHint(Rect : TRect; const AHint : String); Override;
end;
type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender : TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1 : TForm1;
implementation
{$R *.dfm}
// --------------------------------------------------------------------------- //
constructor TMyHintWindow.Create(AOwner : TComponent);
begin
inherited Create(AOwner);
FBitmap := TBitmap.Create;
FCurrAlpha := 1;
FTimer := TTimer.Create(self);
FTimer.Interval := 20;
Ftimer.OnTimer := IncAlpha;
Ftimer.Enabled := false;
ThePNG := TPngImage.Create;
ThePNG.Transparent := True;
ThePNG.TransparentColor := clWhite;
ThePNG.LoadFromFile('C:\temp\0o36B.png');
end;
// --------------------------------------------------------------------------- //
destructor TMyHintWindow.Destroy;
begin
FBitmap.Free;
ThePNG.Free;
inherited;
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.IncAlpha(Sender:TObject);
begin
FCurrAlpha := FCurrAlpha + 10;
if FCurrAlpha >= 254 then
begin
FCurrAlpha := 254;
Ftimer.Enabled := false;
FActivated := false;
end;
invalidate;
end;
procedure TMyHintWindow.CreateParams(var Params : TCreateParams);
const
CS_DROPSHADOW = $20000;
begin
inherited;
Params.Style := Params.Style - WS_BORDER;
Params.WindowClass.Style := Params.WindowClass.Style or CS_DROPSHADOW;
end;
// --------------------------------------------------------------------------- //
type
pRGBQuadArray = ^TRGBQuadArray;
TRGBQuadArray = ARRAY[0..$effffff] OF TRGBQuad;
Procedure SetAlpha(bmp:TBitMap;Alpha:Byte);
var
pscanLine32 : pRGBQuadArray;
i,j:Integer;
begin
Bmp.PixelFormat := pf32Bit;
bmp.HandleType := bmDIB;
bmp.ignorepalette := true;
bmp.alphaformat := afDefined;
for i := 0 to bmp.Height -1 do
begin
pscanLine32 := bmp.Scanline[i];
for j := 0 to bmp.Width -1 do
begin
pscanLine32[j].rgbReserved := Alpha;
pscanLine32[j].rgbBlue := 0;
pscanLine32[j].rgbRed := 0;
pscanLine32[j].rgbGreen := 0;
end;
end;
end;
Procedure ResetSetAlpha(bmp:TBitMap;r:Trect;Alpha:Byte);
var
pscanLine32 : pRGBQuadArray;
i,j:Integer;
begin
for i := 0 to bmp.Height -1 do
begin
pscanLine32 := bmp.Scanline[i];
for j := 0 to bmp.Width -1 do
begin
if (i>=r.Top) and (i<=r.Bottom) and (j>=r.Left) and (j<=r.Right) then
pscanLine32[j].rgbReserved := Alpha;
end;
end;
end;
procedure TMyHintWindow.PrepareBitmap;
var
r:TRect;
begin
r := Clientrect;
r.Top := r.Top + 10;
InflateRect(r,-10,-10);
FreeAndNil(FBitmap);
FBitmap := TBitmap.Create;
FBitmap.Width := 230;
FBitmap.Height := 61;
SetAlpha(FBitmap, 0);
FBitmap.Canvas.Font := Screen.HintFont;
FBitmap.Canvas.Brush.Style := bsClear;
FBitmap.Canvas.Draw(0, 0, ThePNG);
DrawText(FBitmap.Canvas.Handle, PChar(Caption), Length(Caption), r,DT_Center or DT_Wordbreak or DT_NOPREFIX);
ResetSetAlpha(FBitmap,r,255);
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.ActivateHint(Rect : TRect; const AHint : String);
var
i : Integer;
begin
if (GetTickCount - FLastActive > 250) and (Length(AHint) < 100) then
if not FActivated then
begin
FCurrAlpha := 1;
FActivated := true;
Caption := AHint;
Canvas.Font := Screen.HintFont;
Width := 230; // (Rect.Right - Rect.Left) + 16;
Height := 61; // (Rect.Bottom - Rect.Top) + 10;
Left := rect.Left - Width div 2;
Top := Rect.Top;
Ftimer.Enabled := true;
ShowWindow(Handle, SW_SHOWNOACTIVATE);
SetWindowPos(Handle, HWND_TOPMOST, Left, Top, 0, 0, SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
invalidate;
end;
FLastActive := GetTickCount;
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.Paint;
var
DestPoint, srcPoint:TPoint;
winSize:TSize;
DC : HDC;
blendfunc : BLENDFUNCTION;
Owner : HWnd;
curWinStyle:Integer;
exStyle:Dword;
begin
PrepareBitmap;
DC := GetDC(0);
try
winSize.cx := width;
winSize.cy := Height;
srcPoint.x := 0;
srcPoint.y := 0;
DestPoint := BoundsRect.TopLeft;
exStyle := GetWindowLongA(handle, GWL_EXSTYLE);
if (exStyle and WS_EX_LAYERED) = 0 then
SetWindowLong(handle, GWL_EXSTYLE, exStyle or WS_EX_LAYERED );
With blendFunc do
begin
AlphaFormat := 1; //=AC_SRC_ALPHA;
BlendFlags := 0;
BlendOp := AC_SRC_OVER;
SourceConstantAlpha := FCurrAlpha; // here you can set Alpha
end;
UpdateLayeredWindow(Handle, DC, #DestPoint, #winSize, FBitmap.Canvas.Handle, #srcPoint,clBlack, #blendFunc, 2);//=ULW_ALPHA
finally
ReleaseDC(0, DC);
end;
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.Erase(var Message : TMessage);
begin
Message.Result := 0;
end;
// --------------------------------------------------------------------------- //
procedure TForm1.FormCreate(Sender : TObject);
begin
HintWindowClass := TMyHintWindow;
Button1.Hint := 'This is a nice fake tooltip!';
ReportMemoryLeaksOnShutDown := true;
end;
// --------------------------------------------------------------------------- //
end.

How to display a table in ShowMessage?

I am trying to display a table using ShowMessage that looks like this:
short | Description for "short"
verylongtext | Description for "verylongtext"
How do I get two correctly aligned columns like that in a simple message dialog?
I tried to align the columns using spaces, but the font of ShowMessage is variable. Then I tried to align them using tab characters, but I do not know how to calculate the proper tab count for each row.
Is there a reliable way to calculate the tab count?
PS: I would like to avoid writing a custom dialog for this purpose.
You could use a list view in a custom dialog box, as well.
My class supports the standard Windows icons (and sounds): information, warning, error, confirmation, none. Here is the icon-less version:
It is easy to use:
TTableDialog.ShowTable
(
Self,
'Audio Properties',
['Duration', 'Samples per channel', 'Total data size', 'Channels', 'Bits per sample', 'Sample rate', 'Bitrate'],
['1 h 15 min 0 s', '216 000 000', '824 MB', '1', '32', '48 kHz', '1 536 kbit/sec'],
mtInformation
)
It supports DPI scaling (high DPI) and all Windows versions from Windows XP (it might work on Windows 2000 as well, I just haven't tested that) to Windows 10:
The table is a list view, so you get all its benefits, like a scrollbar, truncation ellipses, and tooltips:
You can also specify the dialog's size to make it fit the contents:
TTableDialog.ShowTable
(
Self,
'Audio Properties',
['Duration', 'Samples per channel', 'Total data size', 'Channels', 'Bits per sample', 'Sample rate', 'Bitrate', 'Maximum fractional sample value'],
['1 h 15 min 0 s', '216 000 000', '824 MB', '1', '32', '48 kHz', '1 536 kbit/sec', '0.1'],
mtInformation,
360,
240
)
Of course, the OK button is both Default and Cancel, so you can dismiss the dialog with Enter or Escape.
Finally, pressing Ctrl+C will copy the table to clipboard.
Full source code:
uses
ComCtrls, Math, Clipbrd;
type
TTableDialog = class
strict private
type TFormData = class(TComponent)
public
ListView: TListView;
IconKind: PWideChar;
Icon: HICON;
LIWSD: Boolean;
end;
class function Scale(X: Integer): Integer;
class procedure FormShow(Sender: TObject);
class procedure FormDestroy(Sender: TObject);
class procedure FormPaint(Sender: TObject);
class procedure FormKeyPress(Sender: TObject; var Key: Char);
class procedure LVToClipboard(AListView: TListView);
public
class procedure ShowTable(AOwner: TCustomForm; const ACaption: string;
const ANames, AValues: array of string;
ADialogType: TMsgDlgType = mtInformation;
const AWidth: Integer = 360; const AHeight: Integer = 200);
end;
class procedure TTableDialog.FormShow(Sender: TObject);
var
FormData: TFormData;
ComCtl: HMODULE;
LoadIconWithScaleDown: function(hinst: HINST; pszName: LPCWSTR; cx: Integer;
cy: Integer; var phico: HICON): HResult; stdcall;
begin
if not (Sender is TForm) then
Exit;
if not (TObject(TForm(Sender).Tag) is TFormData) then
Exit;
TForm(Sender).OnShow := nil;
FormData := TFormData(TForm(Sender).Tag);
if FormData.IconKind = nil then
Exit;
ComCtl := LoadLibrary('ComCtl32.dll');
if ComCtl <> 0 then
begin
try
LoadIconWithScaleDown := GetProcAddress(ComCtl, 'LoadIconWithScaleDown');
if Assigned(LoadIconWithScaleDown) then
FormData.LIWSD := Succeeded(LoadIconWithScaleDown(0, FormData.IconKind,
Scale(32), Scale(32), FormData.Icon));
finally
FreeLibrary(ComCtl);
end;
end;
if not FormData.LIWSD then
FormData.Icon := LoadIcon(0, FormData.IconKind);
end;
class procedure TTableDialog.FormDestroy(Sender: TObject);
var
FormData: TFormData;
begin
if not (Sender is TForm) then
Exit;
if not (TObject(TForm(Sender).Tag) is TFormData) then
Exit;
FormData := TFormData(TForm(Sender).Tag);
if (FormData.Icon <> 0) and FormData.LIWSD then
DestroyIcon(FormData.Icon);
end;
class procedure TTableDialog.FormKeyPress(Sender: TObject; var Key: Char);
var
FormData: TFormData;
begin
if not (Sender is TForm) then
Exit;
if not (TObject(TForm(Sender).Tag) is TFormData) then
Exit;
FormData := TFormData(TForm(Sender).Tag);
case Key of
^C:
LVToClipboard(FormData.ListView);
end;
end;
class procedure TTableDialog.FormPaint(Sender: TObject);
var
FormData: TFormData;
Frm: TForm;
Y: Integer;
begin
if not (Sender is TForm) then
Exit;
if not (TObject(TForm(Sender).Tag) is TFormData) then
Exit;
Frm := TForm(Sender);
FormData := TFormData(TForm(Sender).Tag);
Y := Frm.ClientHeight - Scale(25 + 8 + 8);
Frm.Canvas.Brush.Color := clWhite;
Frm.Canvas.FillRect(Rect(0, 0, Frm.ClientWidth, Y));
Frm.Canvas.Pen.Color := $00DFDFDF;
Frm.Canvas.MoveTo(0, Y);
Frm.Canvas.LineTo(Frm.ClientWidth, Y);
if FormData.Icon <> 0 then
DrawIconEx(Frm.Canvas.Handle, Scale(8), Scale(8), FormData.Icon,
Scale(32), Scale(32), 0, 0, DI_NORMAL);
end;
class procedure TTableDialog.LVToClipboard(AListView: TListView);
function GetRow(AIndex: Integer): string;
begin
if InRange(AIndex, 0, AListView.Items.Count - 1) and (AListView.Items[AIndex].SubItems.Count = 1) then
Result := AListView.Items[AIndex].Caption + #9 + AListView.Items[AIndex].SubItems[0]
else
Result := '';
end;
var
S: string;
i: Integer;
begin
if AListView = nil then
Exit;
S := GetRow(0);
for i := 1 to AListView.Items.Count - 1 do
S := S + sLineBreak + GetRow(i);
Clipboard.AsText := S;
end;
class function TTableDialog.Scale(X: Integer): Integer;
begin
Result := MulDiv(X, Screen.PixelsPerInch, 96);
end;
class procedure TTableDialog.ShowTable(AOwner: TCustomForm; const ACaption: string;
const ANames, AValues: array of string;
ADialogType: TMsgDlgType = mtInformation;
const AWidth: Integer = 360; const AHeight: Integer = 200);
const
Sounds: array[TMsgDlgType] of Integer =
(MB_ICONWARNING, MB_ICONERROR, MB_ICONINFORMATION, MB_ICONQUESTION, 0);
Icons: array[TMsgDlgType] of MakeIntResource =
(IDI_WARNING, IDI_ERROR, IDI_INFORMATION, IDI_QUESTION, nil);
var
dlg: TForm;
lv: TListView;
btn: TButton;
i: Integer;
snd: Integer;
begin
if Length(ANames) <> Length(AValues) then
raise Exception.Create('The lengths of the columns don''t match.');
dlg := TForm.Create(AOwner);
try
dlg.BorderStyle := bsDialog;
dlg.Caption := ACaption;
dlg.Width := Scale(AWidth);
dlg.Height := Scale(AHeight);
dlg.Position := poOwnerFormCenter;
dlg.Scaled := False;
dlg.Font.Name := 'Segoe UI';
dlg.Font.Size := 9;
dlg.Tag := NativeInt(TFormData.Create(dlg));
TFormData(dlg.Tag).IconKind := Icons[ADialogType];
dlg.OnShow := FormShow;
dlg.OnDestroy := FormDestroy;
dlg.OnPaint := FormPaint;
dlg.OnKeyPress := FormKeyPress;
dlg.KeyPreview := True;
btn := TButton.Create(dlg);
btn.Parent := dlg;
btn.Caption := 'OK';
btn.Default := True;
btn.Cancel := True;
btn.ModalResult := mrOk;
btn.Width:= Scale(75);
btn.Height := Scale(25);
btn.Left := dlg.ClientWidth - btn.Width - Scale(8);
btn.Top := dlg.ClientHeight - btn.Height - Scale(8);
lv := TListView.Create(dlg);
TFormData(dlg.Tag).ListView := lv;
lv.Parent := dlg;
lv.DoubleBuffered := True;
lv.ReadOnly := True;
lv.BorderStyle := bsNone;
lv.Left := Scale(8) + IfThen(Icons[ADialogType] <> nil, Scale(32 + 8));
lv.Top := Scale(8);
lv.Width := dlg.ClientWidth - Scale(16) - IfThen(Icons[ADialogType] <> nil, Scale(32 + 8));
lv.Height := dlg.ClientHeight - Scale(16 + 8 + 4) - btn.Height;
lv.ViewStyle := vsReport;
lv.RowSelect := True;
lv.ShowColumnHeaders := False;
with lv.Columns.Add do
begin
Caption := 'Name';
Width := Scale(150);
end;
with lv.Columns.Add do
begin
Caption := 'Value';
Width := lv.ClientWidth - lv.Columns[0].Width -
GetSystemMetricsForWindow(SM_CXVSCROLL, dlg.Handle) - scale(2);
end;
for i := 0 to High(ANames) do
with lv.Items.Add do
begin
Caption := ANames[i];
SubItems.Add(AValues[i]);
end;
snd := Sounds[ADialogType];
if snd <> 0 then
MessageBeep(snd);
dlg.ShowModal;
finally
dlg.Free;
end;
end;
If you're not writing a custom dialog for this, when will you? It's not that hard. Just create a form, drop a TMemo on it and make that memo readonly. You can set a monospaced font like Courier New, and your problem is solved. You got the advantage of scrollbars and selection too, and you can choose to make it non-modal.
I would even recommend showing this type of data in a grid (like TStringGrid) instead of a memo or label.
Calculating how to display this text in a messagebox will require much more effort than just creating a custom dialog.
Just created something that shows a popup like this:
Just call the procedure below, and add a TStringList as a parameter.
Of course you could pimp this by using a TListView, icons, scrollbars, etc.
Put it in a separate unit, and you'll always be able to easily show stuff like this.
uses ..., StdCtrls, ExtCtrls;
procedure ShowTablePopup(SL:TStringList);
var
LButtonOK: TButton;
LMemo: TMemo;
LPanel: TPanel;
LForm: TForm;
begin
LForm := TForm.Create(Application);
LMemo := TMemo.Create(LForm);
LPanel := TPanel.Create(LForm);
LButtonOK := TButton.Create(LForm);
LForm.Left := 0;
LForm.Top := 0;
LForm.Caption := 'Values';
LForm.ClientHeight := 250;
LForm.ClientWidth := 400;
LMemo.Parent := LForm;
LMemo.AlignWithMargins := True;
LMemo.Left := 3;
LMemo.Top := 3;
LMemo.Width := 295;
LMemo.Height := 226;
LMemo.Align := alClient;
LMemo.Font.Name := 'Courier New';
LMemo.Lines.Assign(SL);
LPanel.Parent := LForm;
LPanel.Caption := '';
LPanel.Left := 0;
LPanel.Top := 232;
LPanel.Width := 301;
LPanel.Height := 37;
LPanel.Align := alBottom;
LPanel.BevelOuter := bvNone;
LButtonOK.Parent := LPanel;
LButtonOK.AlignWithMargins := True;
LButtonOK.Left := 223;
LButtonOK.Top := 3;
LButtonOK.Width := 75;
LButtonOK.Height := 31;
LButtonOK.Align := alRight;
LButtonOK.Caption := '&OK';
LButtonOK.ModalResult := mrOk;
LButtonOK.Default := True;
LForm.ShowModal;
end;
Example on how to use it:
var
SL:TStringList;
begin
SL := TStringList.Create;
try
SL.Add('short | Description for "short"');
SL.Add('verylongtext | Description for "verylongtext"');
ShowTablePopup(SL);
finally
SL.Free;
end;
end;

Resources