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.
Related
Using Delphi 10.4.
I am hoping someone can explain what I am doing wrong with my FMX TTreeView that is causing an EArgumentOutOfRangeException. I am trying to create a custom TTreeViewItem class that allows me to associate some data with each node, as well as provide an in-place editor to allowing changing the node text.
The code below is a stripped down version of what I am doing. The FMX form has a TTreeview and two buttons on it, with the form's Onshow set to FormShow and the buttons set to the two button events.
The TVLinkTreeViewItem is my custom TTreeViewItem where I add a background and edit component for my in-place editor, which is displayed when a node is double clicked.
When you run the code as is, the program will throw the exception when the logic gets to the TreeView1.EndUpdate call at the end of the FormShow routine. The exception is thrown in FMX.Controls in the TControl.EndUpdate procedure.
If you comment out the ExpandAll call, the exception is not thrown, but if you mess with the expanding and collapsing of the nodes and resizing of the form, sooner or later the exception gets thrown. I left the ExpandAll line in the code below, as I assume the exception is being caused by the same error.
From what I can tell, the problem appears to be how I am setting up the fBackground and fEditor. If I don't call the AddObject routine and not set the Parent properties, I get no exception.
So can anybody tell me what I am doing wrong? Or is there a better way to do an in-place editor for the FMX TTreeViewItems component?
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.TreeView, FMX.Layouts, FMX.Controls.Presentation,
FMX.MultiView, FMX.Edit, FMX.Objects, FMX.StdCtrls;
type
TForm1 = class(TForm)
TreeView1: TTreeView;
Button1: TButton;
Button2: TButton;
procedure FormShow(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
type
TVLinkTreeViewItem = class(TTreeViewItem)
private
fData: string;
fEditor: TEdit;
fBackground: TRectangle;
procedure TreeViewItem1DblClick(Sender: TObject);
procedure EditorExit(Sender: TObject);
procedure EditorKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
public
property Editor: TEdit read fEditor write fEditor;
property Data: string read fData write fData;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TreeView1.ExpandAll;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
TreeView1.CollapseAll;
end;
procedure TForm1.FormShow(Sender: TObject);
var
I, c, r, s: Integer;
vNode1,
vNode2,
vNode3,
vNode4: TVLinkTreeViewItem;
begin
TreeView1.BeginUpdate;
TreeView1.Clear;
for I := 0 to 4 do
begin
vNode1 := TVLinkTreeViewItem.Create(TreeView1);
vNode1.Text := 'Level 1 - '+ IntToStr(I);
TreeView1.AddObject(vNode1);
for c := 0 to 4 do
begin
vNode2 := TVLinkTreeViewItem.Create(vNode1);
vNode2.Text := 'Level 2 - '+ IntToStr(c);
vNode1.AddObject(vNode2);
for r := 0 to 4 do
begin
vNode3 := TVLinkTreeViewItem.Create(vNode2);
vNode3.Text := 'Level 3 - '+ IntToStr(r);
vNode2.AddObject(vNode3);
// for s := 0 to 4 do
// begin
// vNode4 := TVLinkTreeViewItem.Create(vNode3);
// vNode4.Text := 'Level 4 - '+ IntToStr(s);
// vNode3.AddObject(vNode4);
// end;
end;
end;
end;
//ExpandAll works when no parent is set for fBackGround and fEditor is not set in "TVLinkTreeViewItem.Create" below"
//If the Parents are set below, ExpandAll/EndUpdate causes "Augument out of range" exception.
TreeView1.ExpandAll;
treeView1.EndUpdate;
end;
{ TVLinkTreeViewItem }
constructor TVLinkTreeViewItem.Create(AOwner: TComponent);
begin
inherited;
fData := '';
fBackground := TRectangle.Create(AOwner);
//When ExpandAll is not called in FormShow,
// Calling "AddObject" or setting parent, as shown below, make all the code work,
// but will get intermident "Augument out of range" exceptions when resizing form,
// or when expanding or collapsing nodes using the buttons.
self.AddObject(fBackGround);
//fBackGround.Parent := self;
fBackGround.Visible := false;
fEditor := TEdit.Create(AOwner);
fBackGround.AddObject(fEditor);
//fEditor.Parent := fBackGround;
fEditor.Visible := false;
fEditor.Align := TAlignLayout.Client;
fEditor.OnKeyDown := EditorKeyUp;
self.OnDblClick := TreeViewItem1DblClick;
fEditor.OnExit := EditorExit;
end;
destructor TVLinkTreeViewItem.Destroy;
begin
inherited;
end;
procedure TVLinkTreeViewItem.TreeViewItem1DblClick(Sender: TObject);
begin
fBackGround.Visible := true;
fBackGround.Width := self.Width - 20;
fBackGround.Height := self.Height;
fBackGround.Position.X := 20;
fEditor.Enabled := true;
fEditor.Visible := true;
fEditor.Opacity := 1;
fBackGround.BringToFront;
fEditor.BringToFront;
fEditor.Text := Text;
fEditor.SetFocus;
fEditor.SelectAll;
end;
procedure TVLinkTreeViewItem.EditorKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
begin
inherited;
if Key = vkReturn then
begin
Text := fEditor.Text;
fBackGround.Visible := false;
fEditor.Enabled := false;
end
else if Key in [vkEscape, vkCancel, vkTab, vkHardwareBack] then
begin
fBackGround.Visible := false;
fEditor.Enabled := false;
end;
end;
procedure TVLinkTreeViewItem.EditorExit(Sender: TObject);
begin
fBackGround.Visible := false;
fEditor.Enabled := false;
fEditor.Visible := false;
end;
end.
Here's the fmx content:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 480
ClientWidth = 640
FormFactor.Width = 320
FormFactor.Height = 480
FormFactor.Devices = [Desktop]
OnShow = FormShow
DesignerMasterStyle = 0
object TreeView1: TTreeView
Align = Left
Size.Width = 269.000000000000000000
Size.Height = 480.000000000000000000
Size.PlatformDefault = False
TabOrder = 0
Viewport.Width = 265.000000000000000000
Viewport.Height = 476.000000000000000000
end
object Button1: TButton
Position.X = 356.000000000000000000
Position.Y = 68.000000000000000000
TabOrder = 2
Text = 'Expand'
OnClick = Button1Click
end
object Button2: TButton
Position.X = 354.000000000000000000
Position.Y = 102.000000000000000000
TabOrder = 1
Text = 'Collapse'
OnClick = Button2Click
end
end
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.
My application can download one picture from every url in memo1.
It uses idhttp.get and has a skipbutton. After skip it downloads the next picture.
Q1: Do you have code to put into the destructor and what is the code for " terminate" and "waitfor"?
I found this on another website:
destructor thread.destroy;
begin
try
Terminate;
If HTTP.Connected then HTTP.Disconnect;
finally
WaitFor;
FreeAndNil(HTTP);
end;
inherited;
end;
Q2: How do I call the destructor and make it work?
Q3: Do you have hints (especially security concerns) and additional lines of code?
the code of my application:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection,
IdTCPClient, IdHTTP;
type
thread = class
public
Constructor Create; overload;
Destructor Destroy; override;
end;
TForm1 = class(TForm)
IdHTTP1: TIdHTTP;
Memo1: TMemo;
Memo2: TMemo;
Memo3: TMemo;
startbutton: TButton;
skipbutton: TButton;
procedure startbuttonClick(Sender: TObject);
procedure skipbuttonClick(Sender: TObject);
procedure IdHTTP1Work(Sender: TObject; AWorkMode: TWorkMode;
const AWorkCount: Integer);
end;
var
Form1: TForm1;
http: tidhttp;
s: boolean;
implementation
{$R *.dfm}
constructor thread.Create;
begin
HTTP := TIdHTTP.Create(nil);
inherited ;
end;
destructor thread.destroy;
begin
try
If HTTP.Connected then HTTP.Disconnect;
finally
FreeAndNil(HTTP);
end;
inherited;
end;
procedure TForm1.startbuttonClick(Sender: TObject);
var
i: integer;
fs : TFileStream ;
begin
for i:= 0 to memo1.lines.count-1 do begin
s:= false;
fs := TFileStream.Create(inttostr(i)+'abc.jpg', fmCreate);
http:= idhttp1;
try
try
HTTP.Get(memo1.lines[i],fs);
memo2.Lines.add(memo1.Lines[i]);
except
on E: Exception do
begin
memo3.lines.add(' ha ha ha not working '+syserrormessage(getlasterror));
end;
end;
finally
fs.free;
end;
end;
end;
procedure TForm1.skipbuttonClick(Sender: TObject);
begin
s:=true;
end;
procedure TForm1.IdHTTP1Work(Sender: TObject; AWorkMode: TWorkMode;
const AWorkCount: Integer);
begin
application.ProcessMessages;
if s = true then
http.Disconnect;
end;
end.
Since your are using IdHttp from the GUI (= main thread) and Indy is blocking, you have two options: a) use IdAntifreeze in combination with messages (just drop the component on the form), b) use threads.
Do NOT use Application.Processmessages as it will lead to strange side effects.
now to answer your questions:
Q1: the code you found on the internet implemented solution b) so this is not applicable for your current code
Q2: same as Q1
Q3 : here is a version that correctly implements solution a)
This code is still not 100% perfect as it does not implement logic for disabling/enabling the starttransfer and skiptransfer buttons (I leave that as an exercise for you :) ).
unit Unit16;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdAntiFreezeBase, IdAntiFreeze, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP;
const
WM_TRANSFER = WM_USER + 1;
type
TForm1 = class(TForm)
IdHTTP1: TIdHTTP;
IdAntiFreeze1: TIdAntiFreeze;
Memo1: TMemo;
Btn_start: TButton;
Btn_skip: TButton;
Memo2: TMemo;
procedure IdHTTP1Work(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
procedure Btn_startClick(Sender: TObject);
procedure Btn_skipClick(Sender: TObject);
private
{ Private declarations }
Transferring : Boolean;
UrlIndex : Integer;
procedure NextTransfer(var msg : TMessage); message WM_TRANSFER;
procedure StartTransfer;
procedure DoTransfer;
procedure SkipTransfer;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.NextTransfer(var msg: TMessage);
begin
DoTransfer;
end;
procedure TForm1.SkipTransfer;
begin
Transferring := false;
end;
procedure TForm1.StartTransfer;
begin
UrlIndex := 0;
DoTransfer;
end;
procedure TForm1.DoTransfer;
var
Url : String;
Stream : TStringStream;
begin
if UrlIndex < Memo1.Lines.Count then
begin
Url := Memo1.Lines[UrlIndex];
Memo2.Lines.Add(Format('getting data from URL: %s', [Url]));
Inc(UrlIndex);
Transferring := True;
try
Stream := TStringStream.Create;
try
IdHttp1.Get(Url, Stream);
Memo2.Lines.Add(Format('Data: "%s"',[Stream.DataString]));
finally
Stream.Free;
end;
except
on E: Exception do
begin
Memo2.Lines.Add(Format('error during transfer: %s', [E.Message]));
end;
end;
Transferring := False;
PostMessage(Handle, WM_TRANSFER, 0, 0);
end;
end;
procedure TForm1.Btn_startClick(Sender: TObject);
begin
Memo2.Lines.Add('starting transfer');
StartTransfer;
end;
procedure TForm1.Btn_skipClick(Sender: TObject);
begin
Memo2.Lines.Add('skipping current transfer');
SkipTransfer;
end;
procedure TForm1.IdHTTP1Work(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
begin
Memo2.Lines.Add('work event');
if not Transferring and (AWorkMode = wmRead) then
try
Memo2.Lines.Add('disconnecting peer');
IdHttp1.Disconnect;
except
end;
end;
end.
DFM file:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 290
ClientWidth = 707
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Memo1: TMemo
Left = 92
Top = 12
Width = 213
Height = 257
Lines.Strings = (
'http://stackoverflow.com'
'http://www.google.com'
'http://www.hardocp.com'
'')
TabOrder = 0
WordWrap = False
end
object Btn_start: TButton
Left = 8
Top = 128
Width = 75
Height = 25
Caption = 'Btn_start'
TabOrder = 1
OnClick = Btn_startClick
end
object Btn_skip: TButton
Left = 8
Top = 159
Width = 75
Height = 25
Caption = 'Btn_skip'
TabOrder = 2
OnClick = Btn_skipClick
end
object Memo2: TMemo
Left = 320
Top = 12
Width = 373
Height = 257
TabOrder = 3
WordWrap = False
end
object IdHTTP1: TIdHTTP
OnWork = IdHTTP1Work
AllowCookies = True
ProxyParams.BasicAuthentication = False
ProxyParams.ProxyPort = 0
Request.ContentLength = -1
Request.ContentRangeEnd = -1
Request.ContentRangeStart = -1
Request.ContentRangeInstanceLength = -1
Request.Accept = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
Request.BasicAuthentication = False
Request.UserAgent = 'Mozilla/3.0 (compatible; Indy Library)'
Request.Ranges.Units = 'bytes'
Request.Ranges = <>
HTTPOptions = [hoForceEncodeParams]
Left = 24
Top = 16
end
object IdAntiFreeze1: TIdAntiFreeze
Left = 16
Top = 72
end
end
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?.
I'm in the process of reproducing Project Page Options IDE add-in¹. Particularly, this add-in replaces default behavior² of Open action in the Project Manager with its own behavior - to open a HTML page in the same internal browser which is used to display a Welcome Page. So, i want to do the same, but currently i failed to reach this menu.
I tried IOTAProjectManager interface, which facilitates an adding Project Manager's menu items³, but i learned what its notifiers are isolated from each other, so most probably this API is useless for my purpose.
Also, i tried to hook into application-wide action processing. It gave me absolutely no results, probably action list(s) are not used there at all.
I guess, disposition above leave me no choice but to resort to a hacks, which makes hackish solutions really welcome here. So, any idea please?
¹ For more info about that see this Q.
² There are 3 relevant items: Open, Show Markup, Show Designer. Open defaults to Show Designer without an add-in.
³ In the fact, this API allows adding items on-the-fly, and it probably makes things even more complicated.
Context menus illustrated:
As TOndrej mentioned in comment below, behavior of Open menu item changed only for HTML document configured as "Project Page" in the corresponding dialog.
I think the original Project Page extension does it by installing an IDE Notifier (see TProjectPageNotifier below). I don't think it has anything to do with the Project Manager. It simply listens to notifications about files which are being opened in the IDE and if it's the project page it will open it in the embedded browser instead of the default HTML designer. Here's my attempt to reproduce this functionality for Delphi 2007.
1) package:
package projpageide;
{$R *.res}
// ... some compiler options snipped for brevity
{$DESCRIPTION '_Project Page Options'}
{$LIBSUFFIX '100'}
{$DESIGNONLY}
{$IMPLICITBUILD ON}
requires
rtl,
designide;
contains
Projectpagecmds in 'Projectpagecmds.pas',
ProjectPageOptionsDlg in 'ProjectPageOptionsDlg.pas';
end.
2) data module with an action and a menu item to add to 'Project' menu:
unit ProjectPageCmds;
interface
uses
Windows,SysUtils, Classes, ActnList, Menus, Controls, Forms, Dialogs;
type
TProjectPageCmds = class(TDataModule)
ActionList1: TActionList;
PopupMenu1: TPopupMenu;
ProjectWelcomeOptions: TAction;
ProjectWelcomeOptionsItem: TMenuItem;
procedure ProjectWelcomeOptionsExecute(Sender: TObject);
procedure ProjectWelcomeOptionsUpdate(Sender: TObject);
private
public
end;
implementation
{$R *.dfm}
uses
XMLIntf, Variants, ToolsApi,
ProjectPageOptionsDlg;
type
IURLModule = interface(IOTAModuleData)
['{9D215B02-6073-45DC-B007-1A2DBCE2D693}']
function GetURL: string;
procedure SetURL(const URL: string);
property URL: string read GetURL write SetURL;
end;
TOpenNewURLModule = procedure(const URL: string; EditorForm: TCustomForm);
TProjectPageNotifier = class(TNotifierObject, IOTAIDENotifier)
procedure FileNotification(NotifyCode: TOTAFileNotification; const FileName: string; var Cancel: Boolean);
procedure BeforeCompile(const Project: IOTAProject; var Cancel: Boolean); overload;
procedure AfterCompile(Succeeded: Boolean); overload;
end;
const
sWelcomePageFile = 'WelcomePageFile';
sWelcomePageFolder = 'WelcomePageFolder';
var
DataModule: TProjectPageCmds = nil;
NotifierIndex: Integer = -1;
function FindURLModule: IURLModule;
var
I: Integer;
begin
Result := nil;
with BorlandIDEServices as IOTAModuleServices do
for I := 0 to ModuleCount - 1 do
if Supports(Modules[I], IURLModule, Result) then
Break;
end;
procedure OpenURL(const URL: string; UseExistingView: Boolean = True);
{$IFDEF VER220} // Delphi XE
const
SStartPageIDE = 'startpageide150.bpl';
SOpenNewURLModule = '#Urlmodule#OpenNewURLModule$qqrx20System#UnicodeStringp22Editorform#TEditWindow';
{$ENDIF}
{$IFDEF VER185} // Delphi 2007
const
SStartPageIDE = 'startpageide100.bpl';
SOpenNewURLModule = '#Urlmodule#OpenNewURLModule$qqrx17System#AnsiStringp22Editorform#TEditWindow';
{$ENDIF}
var
Module: IURLModule;
EditWindow: INTAEditWindow;
Lib: HMODULE;
OpenNewURLModule: TOpenNewURLModule;
begin
EditWindow := nil;
Module := nil;
if UseExistingView then
Module := FindURLModule;
if Assigned(Module) then
begin
Module.URL := URL;
(Module as IOTAModule).Show;
end
else
begin
{$IFDEF VER220}
EditWindow := (BorlandIDEServices as INTAEditorServices).TopEditWindow;
{$ENDIF}
{$IFDEF VER185}
if Assigned((BorlandIDEServices as IOTAEditorServices).TopView) then
EditWindow := (BorlandIDEServices as IOTAEditorServices).TopView.GetEditWindow;
{$ENDIF}
if not Assigned(EditWindow) or not Assigned(EditWindow.Form) then
Exit;
Lib := GetModuleHandle(SStartPageIDE);
if Lib = 0 then
Exit;
OpenNewURLModule := GetProcAddress(Lib, SOpenNewURLModule);
if #OpenNewURLModule <> nil then
OpenNewURLModule(URL, EditWindow.Form);
end;
end;
function ReadOption(const Project: IOTAProject; const SectionName, AttrName: WideString): WideString;
var
Node: IXMLNode;
begin
Result := '';
Node := (BorlandIDEServices as IOTAProjectFileStorage).GetProjectStorageNode(Project, SectionName, False);
if Assigned(Node) and (Node.HasAttribute(AttrName)) then
Result := Node.Attributes[AttrName];
end;
procedure WriteOption(const Project: IOTAProject; const SectionName, AttrName, Value: WideString);
var
Node: IXMLNode;
begin
Node := (BorlandIDEServices as IOTAProjectFileStorage).GetProjectStorageNode(Project, SectionName, False);
if not Assigned(Node) then
Node := (BorlandIDEServices as IOTAProjectFileStorage).AddNewSection(Project, SectionName, False);
Node.Attributes[AttrName] := Value;
Project.MarkModified;
end;
function GetCurrentProjectPageFileName: string;
var
Project: IOTAProject;
begin
Result := '';
Project := (BorlandIDEServices as IOTAModuleServices).GetActiveProject;
if Assigned(Project) then
Result := ReadOption(Project, sWelcomePageFile, 'Path');
end;
procedure TProjectPageCmds.ProjectWelcomeOptionsExecute(Sender: TObject);
var
Project: IOTAProject;
Dlg: TDlgProjectPageOptions;
I: Integer;
ModuleInfo: IOTAModuleInfo;
begin
Project := (BorlandIDEServices as IOTAModuleServices).GetActiveProject;
if not Assigned(Project) then
Exit;
Dlg := TDlgProjectPageOptions.Create(nil);
try
for I := 0 to Project.GetModuleCount - 1 do
begin
ModuleInfo := Project.GetModule(I);
if ModuleInfo.CustomId = 'HTMLTool' then
Dlg.cmbWelcomePage.Items.Add(ExtractRelativePath(ExtractFilePath(Project.FileName), ModuleInfo.FileName));
end;
Dlg.cmbWelcomePage.Text := ReadOption(Project, sWelcomePageFile, 'Path');
Dlg.edWelcomeFolder.Text := ReadOption(Project, sWelcomePageFolder, 'Path');
if Dlg.ShowModal = mrOK then
begin
WriteOption(Project, sWelcomePageFile, 'Path', Dlg.cmbWelcomePage.Text);
WriteOption(Project, sWelcomePageFolder, 'Path', Dlg.edWelcomeFolder.Text);
end;
finally
Dlg.Free;
end;
end;
procedure TProjectPageCmds.ProjectWelcomeOptionsUpdate(Sender: TObject);
var
Project: IOTAProject;
begin
Project := (BorlandIDEServices as IOTAModuleServices).GetActiveProject;
with (Sender as TAction) do
begin
Enabled := Assigned(Project);
Visible := Enabled;
end;
end;
procedure TProjectPageNotifier.FileNotification(NotifyCode: TOTAFileNotification; const FileName: string;
var Cancel: Boolean);
var
Project: IOTAProject;
begin
if (NotifyCode = ofnFileOpening) then
begin
Project := (BorlandIDEServices as IOTAModuleServices).GetActiveProject;
if not Assigned(Project) then
Exit;
if SameText(ReadOption(Project, sWelcomePageFile, 'Path'), ExtractRelativePath(ExtractFilePath(Project.FileName), FileName)) then
begin
Cancel := True;
OpenURL(FileName);
end;
end;
end;
procedure TProjectPageNotifier.AfterCompile(Succeeded: Boolean);
begin
// do nothing
end;
procedure TProjectPageNotifier.BeforeCompile(const Project: IOTAProject; var Cancel: Boolean);
begin
// do nothing
end;
procedure Initialize;
var
NTAServices: INTAServices;
Services: IOTAServices;
begin
if not BorlandIDEServices.GetService(INTAServices, NTAServices) or not BorlandIDEServices.GetService(IOTAServices, Services) then
Exit;
DataModule := TProjectPageCmds.Create(nil);
try
NTAServices.AddActionMenu('ProjectDependenciesItem', DataModule.ProjectWelcomeOptions, DataModule.ProjectWelcomeOptionsItem);
NotifierIndex := Services.AddNotifier(TProjectPageNotifier.Create);
except
FreeAndNil(DataModule);
raise;
end;
end;
procedure Finalize;
begin
if NotifierIndex <> -1 then
(BorlandIDEServices as IOTAServices).RemoveNotifier(NotifierIndex);
FreeAndNil(DataModule);
end;
initialization
Initialize;
finalization
Finalize;
end.
3) the data module's dfm:
object ProjectPageCmds: TProjectPageCmds
OldCreateOrder = False
Left = 218
Top = 81
Height = 150
Width = 215
object ActionList1: TActionList
Left = 32
Top = 8
object ProjectWelcomeOptions: TAction
Category = 'Project'
Caption = 'Pro&ject Page Options...'
HelpContext = 3146
OnExecute = ProjectWelcomeOptionsExecute
OnUpdate = ProjectWelcomeOptionsUpdate
end
end
object PopupMenu1: TPopupMenu
Left = 96
Top = 8
object ProjectWelcomeOptionsItem: TMenuItem
Action = ProjectWelcomeOptions
end
end
end
4) project page options dialog:
unit ProjectPageOptionsDlg;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TDlgProjectPageOptions = class(TForm)
bpCancel: TButton;
bpHelp: TButton;
bpOK: TButton;
cmbWelcomePage: TComboBox;
edWelcomeFolder: TEdit;
Label1: TLabel;
Label2: TLabel;
procedure bpOKClick(Sender: TObject);
procedure bpHelpClick(Sender: TObject);
private
procedure Validate;
public
end;
implementation
{$R *.dfm}
uses
ShLwApi, ToolsApi;
resourcestring
sProjectPageDoesNotExist = 'Project page ''%s'' does not exist';
sProjectFolderDoesNotExist = 'Project folder ''%s'' does not exist';
function CanonicalizePath(const S: string): string;
var
P: array[0..MAX_PATH] of Char;
begin
Win32Check(PathCanonicalize(P, PChar(S)));
Result := P;
end;
procedure TDlgProjectPageOptions.Validate;
var
Project: IOTAProject;
WelcomePagePath, WelcomeFolderPath: string;
begin
Project := (BorlandIDEServices as IOTAModuleServices).GetActiveProject;
if not Assigned(Project) then
Exit;
if cmbWelcomePage.Text <> '' then
begin
WelcomePagePath := CanonicalizePath(ExtractFilePath(Project.FileName) + cmbWelcomePage.Text);
if not FileExists(WelcomePagePath) then
begin
ModalResult := mrNone;
raise Exception.CreateFmt(sProjectPageDoesNotExist, [WelcomePagePath]);
end;
end;
if edWelcomeFolder.Text <> '' then
begin
WelcomeFolderPath := CanonicalizePath(ExtractFilePath(Project.FileName) + edWelcomeFolder.Text);
if not FileExists(WelcomeFolderPath) then
begin
ModalResult := mrNone;
raise Exception.CreateFmt(sProjectFolderDoesNotExist, [WelcomeFolderPath]);
end;
end;
ModalResult := mrOK;
end;
procedure TDlgProjectPageOptions.bpHelpClick(Sender: TObject);
begin
Application.HelpContext(Self.HelpContext);
end;
procedure TDlgProjectPageOptions.bpOKClick(Sender: TObject);
begin
Validate;
end;
end.
5) the dialog's dfm:
object DlgProjectPageOptions: TDlgProjectPageOptions
Left = 295
Top = 168
HelpContext = 3146
BorderIcons = [biSystemMenu]
BorderStyle = bsDialog
Caption = 'Project Page Options'
ClientHeight = 156
ClientWidth = 304
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
Position = poScreenCenter
DesignSize = (
304
156)
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 8
Top = 6
Width = 65
Height = 13
Caption = '&Project page:'
FocusControl = cmbWelcomePage
end
object Label2: TLabel
Left = 8
Top = 62
Width = 80
Height = 13
Caption = '&Resource folder:'
FocusControl = edWelcomeFolder
end
object edWelcomeFolder: TEdit
Left = 8
Top = 81
Width = 288
Height = 21
Anchors = [akLeft, akTop, akRight]
TabOrder = 1
end
object bpOK: TButton
Left = 59
Top = 123
Width = 75
Height = 25
Anchors = [akRight, akBottom]
Caption = 'OK'
Default = True
ModalResult = 1
TabOrder = 2
OnClick = bpOKClick
end
object bpCancel: TButton
Left = 140
Top = 123
Width = 75
Height = 25
Anchors = [akRight, akBottom]
Cancel = True
Caption = 'Cancel'
ModalResult = 2
TabOrder = 3
end
object bpHelp: TButton
Left = 221
Top = 123
Width = 75
Height = 25
Anchors = [akRight, akBottom]
Caption = 'Help'
TabOrder = 4
OnClick = bpHelpClick
end
object cmbWelcomePage: TComboBox
Left = 8
Top = 25
Width = 288
Height = 21
Anchors = [akLeft, akTop, akRight]
ItemHeight = 13
TabOrder = 0
Text = 'cmbWelcomePage'
end
end
However, I don't know what effect the "Resource Folder" has. The option can be stored in and read from the .dproj file, it's also checked that it exists but I don't know how the original extension uses the folder path. If you find out please let me know, I'll include it in the code.
Also, part of this code is copied from my answer to another question of yours, which I compiled (and briefly tested) in Delphi 2007 and Delphi XE. This code was only compiled and briefly tested in Delphi 2007.
Hope this helps as a starting point, at least.