delphi Creating Component template - delphi

I am working with Delphi application.I created one form shown as below:
I wanted to make component out of this controls through code. But not through component-->create component Template-->so on.
How do i make component template out of form contols through delphi code.??
Thanx in advance.

Or if you want to have that group of controls as one single component you can install unit like this into some package:
unit EditGroup;
interface
uses
SysUtils, Classes, Graphics, Controls, StdCtrls;
type
TEditGroup = class(TCustomControl)
private
FButton: TButton;
FFirstEdit: TEdit;
FFirstLabel: TLabel;
FSecondEdit: TEdit;
FSecondLabel: TLabel;
protected
procedure Paint; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Button: TButton read FButton;
property FirstEdit: TEdit read FFirstEdit;
property FirstLabel: TLabel read FFirstLabel;
property SecondEdit: TEdit read FSecondEdit;
property SecondLabel: TLabel read FSecondLabel;
end;
procedure Register;
implementation
{ TEditGroup }
constructor TEditGroup.Create(AOwner: TComponent);
begin
inherited;
Width := 213;
Height := 104;
Color := clWhite;
FFirstLabel := TLabel.Create(Self);
FFirstLabel.SetSubComponent(True);
FFirstLabel.Parent := Self;
FFirstLabel.Top := 11;
FFirstLabel.Left := 8;
FFirstLabel.Name := 'FirstLabel';
FFirstEdit := TEdit.Create(Self);
FFirstEdit.SetSubComponent(True);
FFirstEdit.Parent := Self;
FFirstEdit.Top := 8;
FFirstEdit.Left := 84;
FFirstEdit.Width := 121;
FFirstEdit.Name := 'FirstEdit';
FSecondLabel := TLabel.Create(Self);
FSecondLabel.SetSubComponent(True);
FSecondLabel.Parent := Self;
FSecondLabel.Top := 39;
FSecondLabel.Left := 8;
FSecondLabel.Name := 'SecondLabel';
FSecondEdit := TEdit.Create(Self);
FSecondEdit.SetSubComponent(True);
FSecondEdit.Parent := Self;
FSecondEdit.Top := 36;
FSecondEdit.Left := 84;
FSecondEdit.Width := 121;
FSecondEdit.Name := 'SecondEdit';
FButton := TButton.Create(Self);
FButton.SetSubComponent(True);
FButton.Parent := Self;
FButton.Top := 71;
FButton.Left := 69;
FButton.Width := 75;
FButton.Name := 'Button';
end;
destructor TEditGroup.Destroy;
begin
FButton.Free;
FFirstEdit.Free;
FFirstLabel.Free;
FSecondEdit.Free;
FSecondLabel.Free;
inherited;
end;
procedure TEditGroup.Paint;
begin
Canvas.Rectangle(ClientRect);
end;
procedure Register;
begin
RegisterComponents('Stack Overflow', [TEditGroup]);
end;
end.
Here's how it looks like at design time:

If you right-click on the form and choose View as Text, then you are already a long way. Simply replace all ='s by :='s, and create all components by adding .Create(Self).
So this text:
object Form1: TForm1
Left = 300
Top = 281
Width = 630
Height = 372
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 14
Top = 28
Width = 32
Height = 13
Caption = 'Label1'
end
object Edit1: TEdit
Left = 63
Top = 24
Width = 121
Height = 21
TabOrder = 0
Text = 'Edit1'
end
end
should be converted into something like:
type
TMyForm1 = class(TForm)
private
Label1: TLabel;
Edit1: TEdit;
public
constructor Create(AOwner: TComponent); override;
end;
constructor TMyForm1.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Width := 630;
Height := 372;
Caption := 'Form1';
Color := clBtnFace;
...
Label1 := TLabel.Create(Self);
with Label1 do
begin
Left := 14;
Top := 28;
Width := 32;
Height := 13;
Caption := 'Label1';
end;
Edit1 := TEdit.Create(Self);
with Edit1 do
...
end;
But there are also tools for this special task, see Are there any Delphi DFM to Delphi source code convertion tools?.

Related

Change the background of an area containing already written text

(Delphi DX 10.3)
I have a big blank (white) canvas (on a Tpanel's descendant) where I draw some text (using Textout(), let's suppose text is always black) and graphics (lines, rectangles, nothing of so complex).
After drawing, I need to change the color of the white background of some specific areas, from white to another colour.
The effect I want to reach is much like an excel sheet with coloured cells. In the attached example, all the columns are created blank (white) like "value" and "difference", then the yellow (price) and the red (result) columns have been coloured.
If I could fill in the areas before writing the text, I would use SetBkMode(TRANSPARENT) and get an optimal result. Unfortunately I need to fill in the areas after writing the text and graphics.
The first solution I thought of was pixel-by-pixel replacement (using the Pixels[] function), but it's dramatically slow and graphically unsatisfactory.
So my question is: how can I color the background of an area containing already written text?
Here a Minimal Reproducibile Example.
Button 1 execute pixels substitution, very slow and with unsatisfactory graphical result.
Button 2 fills areas after writing text, using SetBkMode(TRANSPARENT). Perfect result, but I can't do it.
program BK_mode;
uses
Vcl.Forms,
main in 'main.pas' {Form1};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
////////////////////////////////
unit main;
interface
uses Windows, Forms, SysUtils, Vcl.StdCtrls, UiTypes, System.Classes, Vcl.Controls, Vcl.ExtCtrls, Graphics, Dialogs;
type
TForm1 = class(TForm)
btn_01: TButton;
btn_02: TButton;
procedure btn_02Click(Sender: TObject);
procedure btn_01Click(Sender: TObject);
private
procedure write_text(canvas: TCanvas;x, y : integer;i_fontsize : smallint;const str_text: String;style : TFontStyles;bo_transparent : boolean);
procedure switch_color(canvas : TCanvas;color_source, color_target : TColor);
procedure prepare_example(bo_transparent : boolean);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
BASE_COLOR = clWhite;
ALTERNATIVE_COLOR = clRed;
procedure TForm1.write_text(canvas: TCanvas;x, y : integer;i_fontsize : smallint;const str_text: String;style : TFontStyles;bo_transparent : boolean);
begin
var lo_old_BK_color : TColor := canvas.Brush.Color;
var lo_old_BK_mode := GetBKMode(canvas.Handle);
if bo_transparent then SetBKMode(canvas.Handle, TRANSPARENT)
else begin
SetBKMode(canvas.Handle, OPAQUE);
canvas.Brush.Color := BASE_COLOR
end;
canvas.Font.Color := clBlack;canvas.Font.Size := i_fontsize;canvas.Font.Style := style;
TextOut(canvas.Handle, x, y, PChar(str_text), Length(str_text));
if NOT bo_transparent then canvas.Brush.Color := lo_old_BK_color;
SetBKMode(canvas.Handle, lo_old_BK_mode)
end;
procedure TForm1.switch_color(canvas : TCanvas;color_source, color_target : TColor);
begin
for var x := 0 to clientWidth-1 do
for var y := 0 to clientHeight - 1 do
if (canvas.Pixels[x, y] = color_source) then canvas.Pixels[x, y] := color_target
end;
procedure TForm1.prepare_example(bo_transparent : boolean);
begin
if bo_transparent then Color := ALTERNATIVE_COLOR else Color := BASE_COLOR;
invalidate;
application.MessageBox('Click me', 'Test');
canvas.Rectangle(10, 10, 200, 100);
canvas.MoveTo(10, 110);canvas.LineTo(200, 140);
canvas.MoveTo(10, 140);canvas.LineTo(200, 110);
write_text(canvas, 30, 30, 14, 'This is a text!', [], bo_transparent);
write_text(canvas, 30, 60, 11, 'This is another text!', [fsBold, fsItalic], bo_transparent)
end;
procedure TForm1.btn_01Click(Sender: TObject);
begin
prepare_example(FALSE);
switch_color(canvas, BASE_COLOR, ALTERNATIVE_COLOR)
end;
procedure TForm1.btn_02Click(Sender: TObject);
begin
prepare_example(TRUE)
end;
end.
/////////////////////////////////////////////////////////
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 283
ClientWidth = 208
Color = clWhite
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
DesignSize = (
208
283)
PixelsPerInch = 96
TextHeight = 13
object btn_01: TButton
Left = 17
Top = 161
Width = 178
Height = 51
Anchors = [akLeft, akRight, akBottom]
Caption = '1. write text and fill area'
Font.Charset = ANSI_CHARSET
Font.Color = clWindowText
Font.Height = -16
Font.Name = 'Arial'
Font.Style = [fsBold]
ParentFont = False
TabOrder = 0
WordWrap = True
OnClick = btn_01Click
end
object btn_02: TButton
Left = 17
Top = 220
Width = 178
Height = 51
Anchors = [akLeft, akRight, akBottom]
Caption = '2. fill area then write text'
Font.Charset = ANSI_CHARSET
Font.Color = clWindowText
Font.Height = -16
Font.Name = 'Arial'
Font.Style = [fsBold]
ParentFont = False
TabOrder = 1
WordWrap = True
OnClick = btn_02Click
end
end
What you are asking for is simply not doable this way. You need to redraw the entire Canvas, drawing your backgrounds BEFORE drawing lines+text on top. Once you have drawn, you need a repaint to do everything over. DO NOT draw on a Canvas from outside of a Paint event.
Your example draws on a TForm.Canvas, so use the TForm.OnPaint event. For a TPanel descendant, override the virtual Paint() method instead. Either way, keep some variables with the desired settings, use those variable while drawing, and call Invalidate() after updating the variables and you want to trigger a repaint.
For example:
unit main;
interface
uses Windows, Forms, SysUtils, Vcl.StdCtrls, UiTypes, System.Classes, Vcl.Controls, Vcl.ExtCtrls, Graphics, Dialogs;
type
TForm1 = class(TForm)
btn_01: TButton;
btn_02: TButton;
procedure btn_02Click(Sender: TObject);
procedure btn_01Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormPaint(Sender: TObject);
private
FDrawTransparent : Boolean;
FDrawColor : TColor;
procedure write_text(ACanvas: TCanvas; x, y : integer; i_fontsize : smallint; const str_text: String; style : TFontStyles; bo_transparent : boolean);
procedure prepare_example(bo_transparent : boolean; color_target : TColor);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
BASE_COLOR = clWhite;
ALTERNATIVE_COLOR = clRed;
procedure TForm1.FormCreate(Sender: TObject);
begin
FDrawTransparent := False;
FDrawColor := BASE_COLOR;
end;
procedure TForm1.FormPaint(Sender: TObject);
begin
Canvas.Brush.Color := Self.Color;
Canvas.Rectangle(10, 10, 200, 100);
Canvas.MoveTo(10, 110);
Canvas.LineTo(200, 140);
Canvas.MoveTo(10, 140);
Canvas.LineTo(200, 110);
write_text(Canvas, 30, 30, 14, 'This is a text!', [], FDrawTransparent);
write_text(Canvas, 30, 60, 11, 'This is another text!', [fsBold, fsItalic], FDrawTransparent);
end;
procedure TForm1.write_text(ACanvas: TCanvas; x, y : integer; i_fontsize : smallint; const str_text: String;style : TFontStyles; bo_transparent : boolean);
begin
var lo_old_BK_color := ACanvas.Brush.Color;
var lo_old_BK_mode := GetBKMode(ACanvas.Handle);
if bo_transparent then
SetBKMode(ACanvas.Handle, TRANSPARENT)
else begin
SetBKMode(ACanvas.Handle, OPAQUE);
ACanvas.Brush.Color := FDrawColor;
end;
ACanvas.Font.Color := clBlack;
ACanvas.Font.Size := i_fontsize;
ACanvas.Font.Style := style;
TextOut(ACanvas.Handle, x, y, PChar(str_text), Length(str_text));
if NOT bo_transparent then ACanvas.Brush.Color := lo_old_BK_color;
SetBKMode(ACanvas.Handle, lo_old_BK_mode);
end;
procedure TForm1.prepare_example(bo_transparent : boolean; color_target: TColor);
begin
FDrawTransparent := bo_transparent;
FDrawColor := color_target;
if bo_transparent then Color := ALTERNATIVE_COLOR else Color := BASE_COLOR;
Invalidate;
Application.MessageBox('Click me', 'Test');
end;
procedure TForm1.btn_01Click(Sender: TObject);
begin
prepare_example(False, ALTERNATIVE_COLOR);
end;
procedure TForm1.btn_02Click(Sender: TObject);
begin
prepare_example(True, BASE_COLOR);
end;
end.

Create, destroy and count values inside dynamic controls in Delphi

I have questions about how to create dynamic controls, how destroy and how get value inside newly created control.
Create and count edits create in form worked correctly, but where I create edits in panels with buttons to destroy chosen panel (Panel [Edit, button]), it's create correctly, but count doesnt work.
And I don't know how to destroy chosen by me panel with edit without error (I didn't make it yet in code below).
I have this code:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TfrmMain = class(TForm)
btnCreateNewObject: TButton;
btnCountValues: TButton;
btnCreateNewPanels: TButton;
btnAllEditsInPanels: TButton;
procedure btnCreateNewObjectClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure btnCountValuesClick(Sender: TObject);
procedure btnCreateNewPanelsClick(Sender: TObject);
procedure btnAllEditsInPanelsClick(Sender: TObject);
private
dynEdit: TEdit;
dynPanel: TPanel;
yposition: integer;
ypositionpanel: integer;
{ Private declarations }
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.btnCountValuesClick(Sender: TObject);
var
i: integer;
res: integer;
begin
res := 0;
for i := 0 to Self.ControlCount - 1 do
begin
if Controls[i] is TEdit then
begin
res := res + StrToInt((Controls[i] as TEdit).Text);
end;
end;
ShowMessage(IntToStr(res));
end;
procedure TfrmMain.btnCreateNewObjectClick(Sender: TObject);
begin
dynEdit := TEdit.Create(Self);
with dynEdit do
begin
Parent := frmMain;
Width := 80;
Height := 25;
Top := yposition;
Left := 3;
end;
yposition := yposition + 26
end;
procedure TfrmMain.btnCreateNewPanelsClick(Sender: TObject);
begin
dynPanel := TPanel.Create(Self);
with dynPanel do
begin
Parent := frmMain;
Width := 100;
Height := 40;
Top := ypositionpanel;
Left := 120;
dynEdit := TEdit.Create(Self);
with dynEdit do
begin
Parent := dynPanel;
Width := 80;
Height := 25;
Top := 3;
Left := 3;
end;
end;
ypositionpanel := ypositionpanel + 41;
end;
procedure TfrmMain.btnAllEditsInPanelsClick(Sender: TObject);
var
i, j: integer;
res: integer;
begin
res := 0;
for i := 0 to Self.ControlCount - 1 do
begin
for j := 0 to dynPanel.ControlCount - 1 do
begin
if dynPanel.Controls[j] is TEdit then
begin
res := res + StrToInt( (Controls[j] as TEdit).Text );
end;
end;
end;
ShowMessage(IntToStr(res));
end;
procedure TfrmMain.FormCreate(Sender: TObject);
begin
yposition := 1;
ypositionpanel := 1;
end;
end.
object frmMain: TfrmMain
Left = 0
Top = 0
Caption = 'frmMain'
ClientHeight = 500
ClientWidth = 888
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object btnCreateNewObject: TButton
Left = 775
Top = 475
Width = 113
Height = 25
Caption = 'Create new edit'
TabOrder = 0
OnClick = btnCreateNewObjectClick
end
object btnCountValues: TButton
Left = 775
Top = 444
Width = 113
Height = 25
Caption = 'Count all edits'
TabOrder = 1
OnClick = btnCountValuesClick
end
object btnCreateNewPanels: TButton
Left = 648
Top = 475
Width = 121
Height = 25
Caption = 'Create new panels'
TabOrder = 2
OnClick = btnCreateNewPanelsClick
end
object btnAllEditsInPanels: TButton
Left = 648
Top = 444
Width = 121
Height = 25
Caption = 'Count all edits in panels'
TabOrder = 3
OnClick = btnAllEditsInPanelsClick
end
end
You are iterating only through the Edit controls that are direct children of the Form itself, or of the last Panel created. You are not iterating through all of the Panels.
Use a TList or other suitable container to keep track of the Edits you create dynamically, then you can loop through that list/container when needed. And when you are ready to remove a Panel from the Form, simply Remove() its child TEdit from the list and then Free() the Panel, which will free the TEdit for you.
For example:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls,
System.Generics.Collections;
type
TfrmMain = class(TForm)
btnCreateNewObject: TButton;
btnCountValues: TButton;
btnCreateNewPanels: TButton;
btnAllEditsInPanels: TButton;
procedure btnCreateNewObjectClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure btnCountValuesClick(Sender: TObject);
procedure btnCreateNewPanelsClick(Sender: TObject);
procedure btnAllEditsInPanelsClick(Sender: TObject);
procedure DestroyPanel(Sender: TObject);
private
{ Private declarations }
AllEdits: TList<TEdit>;
yposition: integer;
ypositionpanel: integer;
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.btnCountValuesClick(Sender: TObject);
var
i, res: integer;
dynEdit: TEdit;
begin
res := 0;
for i := 0 to AllEdits.Count - 1 do
begin
dynEdit := AllEdits[i];
if dynEdit.Parent = Self then
res := res + StrToInt(dynEdit.Text);
end;
ShowMessage(IntToStr(res));
end;
procedure TfrmMain.btnCreateNewObjectClick(Sender: TObject);
var
dynEdit: TEdit;
begin
dynEdit := TEdit.Create(Self);
try
with dynEdit do
begin
Parent := Self;
Width := 80;
Height := 25;
Top := yposition;
Left := 3;
end;
AllEdits.Add(dynEdit);
except
dynEdit.Free;
raise;
end;
yposition := yposition + 26
end;
procedure TfrmMain.btnCreateNewPanelsClick(Sender: TObject);
var
dynPanel: TPanel;
dynEdit: TEdit;
dynButton: TButton;
begin
dynPanel := TPanel.Create(Self);
try
with dynPanel do
begin
Parent := Self;
Width := 200;
Height := 40;
Top := ypositionpanel;
Left := 120;
end;
dynEdit := TEdit.Create(dynPanel);
with dynEdit do
begin
Parent := dynPanel;
Width := 80;
Height := 25;
Top := 3;
Left := 3;
end;
dynButton := TButton.Create(dynPanel);
with dynButton do
begin
Parent := dynPanel;
Width := 100;
Height := 25;
Top := 3;
Left := 100;
Caption := 'Destroy this pnl';
onClick := DestroyPanel;
end;
AllEdits.Add(dynEdit);
except
dynPanel.Free;
raise;
end;
ypositionpanel := ypositionpanel + 41;
end;
procedure TfrmMain.DestroyPanel(Sender: TObject);
var
dynPanel: TPanel;
dynEdit: TEdit;
begin
dynPanel := TPanel(TButton(Sender).Owner);
dynEdit := TEdit(dynPanel.Controls[0]);
AllEdits.Remove(dynEdit);
dynPanel.Free;
end;
procedure TfrmMain.btnAllEditsInPanelsClick(Sender: TObject);
var
i, res: integer;
dynEdit: TEdit;
begin
res := 0;
for i := 0 to AllEdits.Count - 1 do
begin
dynEdit := AllEdits[i];
if dynEdit.Parent <> Self then
res := res + StrToInt(dynEdit.Text);
end;
ShowMessage(IntToStr(res));
end;
procedure TfrmMain.FormCreate(Sender: TObject);
begin
yposition := 1;
ypositionpanel := 1;
AllEdits := TList<TEdit>.Create;
end;
procedure TfrmMain.FormDestroy(Sender: TObject);
begin
AllEdits.Free;
end;
end.

FireMonkey tRectangle with tLabel child

I am coding a custom control based on tRectangle:
tMyRect = class (tRectangle)
On the tMyRect Constructor, I create a tLabel:
fRectLabel := tLabel.Create (Self);
and then set some properties for it.
At runtime, the tLabel is not showed according to the properties settings, neither responds to the speedkey.
Follows the complete code:
unit frmMyRect;
interface
uses FMX.Controls, FMX.Controls.Presentation, FMX.Forms, FMX.Layouts,
FMX.Objects, FMXFMX.StdCtrls, FMX.Types,System.Classes, System.UITypes;
type
tfrmMyRect = class (tForm)
procedure FormCreate (Sender: tObject);
end;
tMyRect = class (tRectangle)
fRectLabel : tLabel;
constructor Create (aOwner: tComponent);
end;
var formMyRect: tfrmMyRect;
implementation
{$R *.fmx}
var MyRect : tMyRect;
procedure tformMyRect.FormCreate (Sender: tObject);
begin
MyRect := tMyRect.Create (Self);
MyRect.Parent := frmMyRect;
end; { FormCreate }
constructor tMyRect.Create (aOwner: tComponent);
begin
inherited;
Align := tAlignLayout.Center;
CanFocus := True;
Height := 23;
Width := 80;
fRectLabel := tLabel.Create (Self);
with fRectLabel do begin
Align := tAlignLayout.Center;
AutoSize := True;
FocusControl := Self;
HitTest := True;
Parent := Self;
Text := 'Labe&l';
with TextSettings do begin
FontColor := TAlphaColorRec.Blue;
WordWrap := False;
Font.Style := [TFontStyle.fsBold];
end;
end;
end; { Create }
end.
I appreciate if someone can clarify why the tLabel does not behave as expected.
You need to alter the StyleSettings property of the TLabel so that the styling system does not apply those that you have changed, e.g.:
StyledSettings := StyledSettings - [TStyledSetting.FontColor, TStyledSetting.Style];
As to the "neither responds to the speedkey" part, you'll need to clarify what you mean, as you have not shown code related to that

How to Implement Text Box with Gray Text Hint?

In many website, I can see text box with hint in gray text. The hint will tell the user what should be inputted and whenever user tries to input anything, the hint will disappear.
Below is a sample of it:
I just wonder how to implement such a feature in Windows desktop application. I am using Delphi XE3 and it is possible to implement such a feature with TTextBox? Or is there such a VCL component available?
Here is an implementation that works for Windows XP (and also Windows 7 and 8.1, haven't tested it on Windows 10). Beware: I mostly use it in Windows XP and 8.1 and with Delphi 2007 and XE2. There may be bugs that I haven't seen yet.
It's also probably not the most elegant solution but it works and is easy to understand.
Prerequisites: Delphi Custom Containers Pack
Just save the following to u_dzCueEdit.dfm and u_dzCueEdit.pas, create a runtime package with it, create a corresponding design time package with a Register procedure and install it.
dfm file:
object dzCueEdit: TdzCueEdit
Left = 0
Top = 0
Width = 258
Height = 21
TabOrder = 0
OnResize = BoxResize
object ed_Cue: TEdit
Left = 1
Top = 1
Width = 256
Height = 19
Align = alClient
TabOrder = 0
OnChange = ed_CueChange
OnClick = ed_CueClick
OnEnter = ed_CueEnter
OnExit = ed_CueExit
end
object p_Cue: TPanel
Left = 64
Top = 0
Width = 242
Height = 21
BevelOuter = bvNone
Color = clMoneyGreen
ParentBackground = False
TabOrder = 1
OnClick = p_CueClick
OnEnter = p_CueEnter
object l_Cue: TLabel
AlignWithMargins = True
Left = 88
Top = 0
Width = 93
Height = 13
Margins.Left = 1
Margins.Top = 1
Margins.Right = 1
Margins.Bottom = 1
Caption = 'Cue text goes here'
Font.Charset = DEFAULT_CHARSET
Font.Color = clGray
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
ParentFont = False
OnClick = l_CueClick
end
end
end
pas file:
unit c_dzCueEdit;
interface
uses
Windows,
Messages,
SysUtils,
Classes,
Graphics,
Controls,
Forms,
Dialogs,
ExtCtrls,
StdCtrls,
ccBoxes;
type
TdzCueEdit = class(TBox)
p_Cue: TPanel;
l_Cue: TLabel;
procedure ed_CueClick(Sender: TObject);
procedure l_CueClick(Sender: TObject);
published
ed_Cue: TEdit;
procedure p_CueEnter(Sender: TObject);
procedure p_CueClick(Sender: TObject);
procedure ed_CueChange(Sender: TObject);
procedure ed_CueEnter(Sender: TObject);
procedure ed_CueExit(Sender: TObject);
procedure BoxResize(Sender: TObject);
private
procedure CheckCueBanner;
function GetCue: string;
function GetText: string;
procedure SetCue(const _Value: string);
procedure SetText(const _Value: string);
protected
public
constructor Create(_Owner: TComponent); override;
published
property Text: string read GetText write SetText;
property Cue: string read GetCue write SetCue;
end;
implementation
{$R *.DFM}
{ TdzCueEdit }
constructor TdzCueEdit.Create(_Owner: TComponent);
begin
inherited;
BevelOuter := bvNone;
l_Cue.Align := alClient;
p_Cue.Color := ed_Cue.Color;
end;
procedure TdzCueEdit.BoxResize(Sender: TObject);
var
Rect: TRect;
begin
Rect := ed_Cue.ClientRect;
// p_Cue.SetBounds(Rect.Left, Rect.Top, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top);
p_Cue.SetBounds(Rect.Left + 4, Rect.Top + 3, Rect.Right - Rect.Left - 2, Rect.Bottom - Rect.Top - 2);
end;
procedure TdzCueEdit.CheckCueBanner;
begin
if ed_Cue.Text <> '' then
p_Cue.Visible := False
else begin
// if ed_Cue.Focused then
// p_Cue.Visible := False
// else
p_Cue.Visible := True;
end;
end;
procedure TdzCueEdit.ed_CueChange(Sender: TObject);
begin
CheckCueBanner;
end;
procedure TdzCueEdit.ed_CueClick(Sender: TObject);
begin
CheckCueBanner;
end;
procedure TdzCueEdit.ed_CueEnter(Sender: TObject);
begin
CheckCueBanner;
end;
procedure TdzCueEdit.ed_CueExit(Sender: TObject);
begin
CheckCueBanner;
end;
function TdzCueEdit.GetCue: string;
begin
Result := l_Cue.Caption;
end;
procedure TdzCueEdit.SetCue(const _Value: string);
begin
l_Cue.Caption := _Value;
end;
function TdzCueEdit.GetText: string;
begin
Result := ed_Cue.Text;
end;
procedure TdzCueEdit.l_CueClick(Sender: TObject);
begin
ed_Cue.SetFocus;
CheckCueBanner;
end;
procedure TdzCueEdit.SetText(const _Value: string);
begin
ed_Cue.Text := _Value;
end;
procedure TdzCueEdit.p_CueClick(Sender: TObject);
begin
ed_Cue.SetFocus;
CheckCueBanner;
end;
procedure TdzCueEdit.p_CueEnter(Sender: TObject);
begin
ed_Cue.SetFocus;
CheckCueBanner;
end;
end.

Allow multiple child controls to detect when their parent control resizes

I'm writing a TSplitter descendant that proportionally resizes its aligned control when its parent control resizes. In order to detect the parent resize I subclass the parents WinProc procedure
FOldWindowProc := Parent.WindowProc;
Parent.WindowProc := SubclassedParentWndProc;
This works perfectly when there is a single splitter parented by the parent. However, when there are one or more splitters, only one of them works correctly.
How can I receive a notification to all the splitter controls that the parent has resized?
Here's my code
unit ProportionalSplitterU;
interface
uses
Windows, SysUtils, Controls, Messages, Classes, CommCtrl, ExtCtrls;
type
TSPlitterHelper = class helper for TSplitter
public
function FindControlEx: TControl;
end;
TProportionalSplitter = class(TSplitter)
private
FOldWindowProc: TWndMethod;
FControlRatio: Double;
FProportionalResize: Boolean;
procedure SubclassedParentWndProc(var Msg: TMessage);
procedure SetRatio;
procedure SetProportionalResize(const Value: Boolean);
protected
procedure SetParent(AParent: TWinControl); override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure StopSizing; override;
public
constructor Create(AOwner: TComponent); override;
published
property ProportionalResize: Boolean read FProportionalResize write SetProportionalResize;
end;
implementation
{ TProportionalSplitter }
constructor TProportionalSplitter.Create(AOwner: TComponent);
begin
inherited;
FProportionalResize := True;
end;
procedure TProportionalSplitter.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited;
if (Operation = opRemove) and
(AComponent = Parent) then
begin
Parent.WindowProc := FOldWindowProc;
FOldWindowProc := nil;
end;
end;
procedure TProportionalSplitter.SetParent(AParent: TWinControl);
begin
FControlRatio := -1;
if Assigned(Parent) then
begin
Parent.WindowProc := FOldWindowProc;
end;
inherited SetParent(AParent);
if Assigned(AParent) then
begin
FOldWindowProc := Parent.WindowProc;
Parent.WindowProc := SubclassedParentWndProc;
SetRatio;
end;
end;
procedure TProportionalSplitter.SetProportionalResize(const Value: Boolean);
begin
FProportionalResize := Value;
SetRatio;
end;
procedure TProportionalSplitter.SetRatio;
var
ActiveControl: TControl;
begin
if FProportionalResize then
begin
ActiveControl := FindControlEx;
if (Parent <> nil) and
(ActiveControl <> nil) then
begin
case Align of
alTop,
alBottom: FControlRatio := ActiveControl.Height / Parent.Height;
alLeft,
alRight: FControlRatio := ActiveControl.Width / Parent.Width;
end;
end;
end
else
begin
FControlRatio := -1;
end;
end;
procedure TProportionalSplitter.StopSizing;
begin
inherited;
SetRatio;
end;
procedure TProportionalSplitter.SubclassedParentWndProc(Var Msg: TMessage);
begin
FOldWindowProc(Msg);
if Msg.Msg = WM_SIZE then
begin
if FControlRatio <> -1 then
begin
case Align of
alTop,
alBottom: FindControlEx.Width := Round(Parent.Height * FControlRatio);
alLeft,
alRight: FindControlEx.Width := Round(Parent.Width * FControlRatio);
end;
end
else
begin
SetRatio;
end;
end;
end;
{ TSPlitterHelper }
function TSPlitterHelper.FindControlEx: TControl;
begin
Result := Self.FindControl;
end;
end.
Demo .pas
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls,
ProportionalSplitterU;
type
TForm2 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
Panel3: TPanel;
procedure FormCreate(Sender: TObject);
private
FSplitter: TProportionalSplitter;
FSplitter2: TProportionalSplitter;
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.FormCreate(Sender: TObject);
begin
FSplitter := TProportionalSplitter.Create(Self);
FSplitter.Parent := Self;
FSplitter.Align := alLeft;
FSplitter.Left := Panel1.Width + 1;
FSplitter.Width := 20;
FSplitter.ResizeStyle := rsUpdate;
FSplitter2 := TProportionalSplitter.Create(Self);
FSplitter2.Parent := Self;
FSplitter2.Align := alTop;
FSplitter2.Top := Panel3.Height + 1;
FSplitter2.Height := 20;
FSplitter2.ResizeStyle := rsUpdate;
end;
end.
Demo .dfm
object Form2: TForm2
Left = 0
Top = 0
Caption = 'Form2'
ClientHeight = 478
ClientWidth = 674
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 120
TextHeight = 16
object Panel1: TPanel
Left = 0
Top = 193
Width = 249
Height = 285
Align = alLeft
Caption = 'Panel1'
TabOrder = 0
ExplicitTop = 0
ExplicitHeight = 478
end
object Panel2: TPanel
Left = 249
Top = 193
Width = 425
Height = 285
Align = alClient
Caption = 'Panel2'
TabOrder = 1
ExplicitTop = 0
ExplicitHeight = 478
end
object Panel3: TPanel
Left = 0
Top = 0
Width = 674
Height = 193
Align = alTop
Caption = 'Panel3'
TabOrder = 2
end
end
You code is working perfectly correctly as far as intercepting parent window messages is concerned. There is however a problem in your window hook code which may have lead you to incorrectly conclude that this was not working as one of your panels in your test case was not being proportionally resized.
The problem is in this code:
case Align of
alTop, vvvvv
alBottom : FindControlEx.Width := Round(Parent.Height * FControlRatio);
^^^^^
alLeft,
alRight : FindControlEx.Width := Round(Parent.Width * FControlRatio);
end;
Notice that in both cases you are setting the WIDTH of the active control. For Top/Bottom aligned splitter you should instead be setting the HEIGHT.
case Align of
alTop, vvvvvv
alBottom : FindControlEx.Height := Round(Parent.Height * FControlRatio);
^^^^^^
alLeft,
alRight : FindControlEx.Width := Round(Parent.Width * FControlRatio);
end;
This is why your top panel was not resizing its height, even though the WM_SIZE message is being received.

Resources