Delphi: show assigned Frame to Node of Tree View - delphi

I have no experience with frames.
How to use a Tree View with frames?
I need to switch among nods of the Tree View and show assigned Frame to the selected node.
Big big thank for help!!!

It doesn't really make any difference if the Data of nodes hold a pointer to a frame or any other kind of object, typecast the pointer to the type of object it holds.
Below code adds two frames ('Frame2' and 'Frame3', created by the IDE - much like a new form), as nodes of a TreeView, and sets the visibility of the selected node's frame to true and the deselected one's to false.
type
TForm1 = class(TForm)
TreeView1: TTreeView;
procedure FormCreate(Sender: TObject);
procedure TreeView1Change(Sender: TObject; Node: TTreeNode);
procedure TreeView1Changing(Sender: TObject; Node: TTreeNode;
var AllowChange: Boolean);
procedure FormDestroy(Sender: TObject);
end;
var
Form1: TForm1;
implementation
uses Unit2, Unit3;
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
TreeView1.Align := alLeft;
with TFrame(TreeView1.Items.AddObject(nil, 'Frame2', TFrame2.Create(nil)).Data) do begin
Visible := False;
Parent := Self;
Align := alClient;
end;
with TFrame(TreeView1.Items.AddObject(nil, 'Frame3', TFrame3.Create(nil)).Data) do begin
Visible := False;
Parent := Self;
Align := alClient;
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
var
i: Integer;
begin
for i := 0 to TreeView1.Items.Count - 1 do
TFrame(TreeView1.Items[i].Data).Free;
end;
procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode);
begin
TFrame(Node.Data).Visible := True;
end;
procedure TForm1.TreeView1Changing(Sender: TObject; Node: TTreeNode;
var AllowChange: Boolean);
begin
if Assigned((Sender as TTreeView).Selected) then
TFrame(TTreeView(Sender).Selected.Data).Visible := False;
end;

Related

"Listener" for detect changes on mouse icon

I seen here a code that retrieves the current icon of mouse as string, but this code had uses a TTimer for make it.
So, i want know if exist some event (Listener) for detect these change on mouse cursor icon.
Below is code that uses a TTimer:
const
HighCursor = 13;
type
TForm1 = class(TForm)
Timer1: TTimer;
Label1: TLabel;
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
FCursorHandles: array [0..HighCursor] of HCURSOR;
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
OEMCursors: array [0..HighCursor] of Integer = (OCR_NORMAL, OCR_IBEAM,
OCR_WAIT, OCR_CROSS, OCR_UP, OCR_SIZENWSE, OCR_SIZENESW, OCR_SIZEWE,
OCR_SIZENS, OCR_SIZEALL, OCR_NO, OCR_HAND, OCR_APPSTARTING,
32651 {OCR_HELP?});
CursorNames: array [0..HighCursor] of string = ('OCR_NORMAL', 'OCR_IBEAM',
'OCR_WAIT', 'OCR_CROSS', 'OCR_UP', 'OCR_SIZENWSE', 'OCR_SIZENESW',
'OCR_SIZEWE', 'OCR_SIZENS', 'OCR_SIZEALL', 'OCR_NO', 'OCR_HAND',
'OCR_APPSTARTING', 'OCR_HELP');
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
begin
for i := 0 to HighCursor do
FCursorHandles[i] := LoadImage(0, MakeIntResource(OEMCursors[i]),
IMAGE_CURSOR, 0, 0, LR_DEFAULTCOLOR or LR_DEFAULTSIZE or LR_SHARED);
end;
procedure TForm1.Timer1Timer(Sender: TObject);
function GetCursorName(Cursor: HCURSOR): string;
var
i: Integer;
begin
for i := 0 to HighCursor do
if Cursor = FCursorHandles[i] then begin
Result := CursorNames[i];
Exit;
end;
Result := 'Unknown Cursor'; // A custom cursor.
end;
var
CursorInfo: TCursorInfo;
begin
CursorInfo.cbSize := SizeOf(CursorInfo);
if GetCursorInfo(CursorInfo) then
Label1.Caption := GetCursorName(CursorInfo.hCursor)
else
Label1.Caption := 'Fail: ' + SysErrorMessage(GetLastError);
end;
The way that applications listen for events is via Windows messages. There is no message sent when the cursor image is changed, so there is nothing to listen for; your code using a timer is the only possibility.
See Cursors at MSDN for the functions and notifications that Windows provides for cursors.

Communicating between frames in Delphi

I just started using Frames in Delphi.
That Frames are in FrameBar1 and they both are visible. Just for testing, first one contains one Button and second Frame contains one Edit.
I want to change text in Edit with click on Button (which are controls on two different frames).
How to communicate between frames?
The same way you would if the controls were in the same Form. Just prefix the Edit control with the Frame object that owns it, eg:
uses
Frame1Unit, Frame2Unit;
procedure TForm1.FormCreate(Sender: TObject);
begin
Frame1 := TFrame1.Create(Self);
Frame1.Parent := ...;
...
Frame2 := TFrame2.Create(Self);
Frame2.Parent := ...;
...
end;
uses
Frame2Unit;
procedure TFrame1.Button1Click(Sender: TObject);
begin
Frame2.Edit1.Text := '...';
end;
A better design would be to encapsulate the logic so Frame1 and Frame2 do not know about each other. Have Frame1 expose an event that it fires when the button is clicked, and then the parent Form can assign a handler to that event and assign the text on the Frame2, eg:
uses
Frame1Unit, Frame2Unit;
procedure TForm1.FormCreate(Sender: TObject);
begin
Frame1 := TFrame1.Create(Self);
Frame1.Parent := ...;
Frame1.OnNewText := Frame1Text;
...
Frame2 := TFrame2.Create(Self);
Frame2.Parent := ...;
...
end;
procedure TForm1.Frame1Text(Sender: TObject; const NewText: string);
begin
Frame2.EditText := NewText;
end;
type
TFrame1TextEvent = procedure(Sender: TObject; const NewText; string) of object;
TFrame1 = class(TFrame)
Button1: TButton;
procedure Button1Click(Sender: TObject);
public
OnNewText: TFrame1TextEvent;
end;
procedure TFrame1.Button1Click(Sender: TObject);
begin
if Assigned(OnNewText) then
OnNewText(Self, '...');
end;
type
TFrame2 = class(TFrame)
Edit1: TEdit;
private
function GetEditText: string;
procedure SetEditText(const Value: string);
public
property EditText: string read GetEditText write SetEditText;
end;
function TFrame2.GetEditText: string;
begin
Result := Edit1.Text;
end;
procedure TFrame2.SetEditText(const Value: string);
begin
Edit1.Text := Value;
end;

How can a control be notified when its parent receives and loses focus in Delphi?

As the title says, I'd like a component (say, a label) to be notified when it's parent (say, a panel) receives and loses focus. I wandered a bit in Delphi source, in hope of using TControl.Notify, but it's only used to notify child controls of some property changes like font and color. Any suggestions?
Whenever the active control in an application changes, a CM_FOCUSCHANGED message is broadcast to all controls. Simply intercept it, and act accordingly.
Also, I assumed that by when it's parent (say, a panel) receives and loses focus you mean whenever a (nested) child control on that parent/panel receives or loses focus.
type
TLabel = class(StdCtrls.TLabel)
private
function HasCommonParent(AControl: TWinControl): Boolean;
procedure CMFocusChanged(var Message: TCMFocusChanged);
message CM_FOCUSCHANGED;
end;
procedure TLabel.CMFocusChanged(var Message: TCMFocusChanged);
const
FontStyles: array[Boolean] of TFontStyles = ([], [fsBold]);
begin
inherited;
Font.Style := FontStyles[HasCommonParent(Message.Sender)];
end;
function TLabel.HasCommonParent(AControl: TWinControl): Boolean;
begin
Result := False;
while AControl <> nil do
begin
if AControl = Parent then
begin
Result := True;
Break;
end;
AControl := AControl.Parent;
end;
end;
If you don't like to subclass TJvGradientHeader, then it is possible to design this generically by the use of Screen.OnActiveControlChange:
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FHeaders: TList;
procedure ActiveControlChanged(Sender: TObject);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FHeaders := TList.Create;
FHeaders.Add(Label1);
FHeaders.Add(Label2);
Screen.OnActiveControlChange := ActiveControlChanged;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FHeaders.Free;
end;
function HasCommonParent(AControl: TWinControl; AMatch: TControl): Boolean;
begin
Result := False;
while AControl <> nil do
begin
if AControl = AMatch.Parent then
begin
Result := True;
Break;
end;
AControl := AControl.Parent;
end;
end;
procedure TForm1.ActiveControlChanged(Sender: TObject);
const
FontStyles: array[Boolean] of TFontStyles = ([], [fsBold]);
var
I: Integer;
begin
for I := 0 to FHeaders.Count - 1 do
TLabel(FHeaders[I]).Font.Style :=
FontStyles[HasCommonParent(Screen.ActiveControl, TLabel(FHeaders[I]))];
end;
Note that I chose TLabel to demonstrate this works also for TControl derivatives.

How to create a TButton or other controls inside a THintWindow?

I am trying to create a THintWindow and place a TButton or a TFrame on it. here is my code:
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
HintWindow: THintWindow;
public
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
HintWindow := THintWindow.Create(Self);
HintWindow.Color := clInfoBk;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
P: TPoint;
R: TRect;
Control: TControl;
begin
Control := Button1;
P := Control.ClientToScreen(Point(0, Control.Height));
R := Rect(P.X, P.Y, P.x + 100, P.Y + 100);
with TButton.Create(HintWindow) do
begin
Parent := HintWindow;
Caption := 'My Button';
end;
HintWindow.ActivateHint(R, 'My Hint');
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
HintWindow.ReleaseHandle;
end;
The Hint window is shown but I don't see the TButton. it seems that there are no child windows inside the Hint window (I tested with Spy++ for "first child").
I also tried to subclass THintWindow with new CreateParams ie:
procedure TMyHintWindow.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.Style := Params.Style or WS_CLIPCHILDREN;
Params.ExStyle := Params.ExStyle or WS_EX_CONTROLPARENT;
end;
When I create a TFrame as child on the Hint window, Spy++ shows that there is a child on the hint window but I cant see it (even after I force it to be visible).
Any feed-backs on this?
Don't ask me why, but you can make this work in old versions of Delphi by setting the ParentWindow to Application.Handle immediately after you create the THintWindow instance:
HintWindow := THintWindow.Create(Self);
HintWindow.ParentWindow := Application.Handle;
This answer was inspired by the modern versions of the Delphi VCL source.

Frames and Browse History in Delphi

I am currently developing a delphi application that will need a browse history and am trying to work out how exactly to implement this.
The application has 2 modes. Browse and Details. Both designed as Frames.
After a search an appropriate number of Browse Frames are created in Panel 1 and populated.
From a Browse Frame we can either open the Detail Frame, replacing the contents of Panel 1 with the contents of the Detail Frame. Alternatively a new search can be spawned, replacing the current set of results with a new set.
From the Detail Frame we can either edit details, or spawn new searches. Certain searches are only available from the Detail Frame. Others from either the Browse Frames or the Detail Frame.
Each time a user displays the Detail Frame, or spawns a new search I want to record that action and be able to repeat it. Other actions like edits or "more details" won't be recorded. (Obviously if a user goes back a few steps then heads down a different search path this will start the history fresh from this point)
In my mind I want to record the procedure calls that were made in a list e.g.
SearchByName(Search.Text);
SearchByName(ArchName.Text);
DisplayDetails(JobID);
SearchByName(EngineerName.Text);
DisplayDetails(JobID);
Then I can just (somehow) call each item in order as I go bak and forward...
In response to Dan Kelly's request to store the function:
However what I still can't see is how I call the stored function -
What you are referring to is storing a method handler. The code below demonstrates this. But, as you indicated your self, you could do a big if..then or case statement.
This all will works. But an even more "eloquent" way of doing all this is to store object pointers. For example, if a search opens another search, you pass a pointer of the first to the 2nd. Then in the 2nd if you want to refer back to it, you have a pointer to it (first check that it is not nil/free). This is a much more object oriented approach and would lend itself better to situations where someone might close one of the frames out of sequence.
unit searchit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TSearchObject = class
FSearchValue: String;
FOnEventClick: TNotifyEvent;
constructor Create(mSearchValue: string; mOnEventClick: TNotifyEvent);
procedure FireItsEvent;
end;
type
TForm1 = class(TForm)
SearchByName: TButton;
GoBack: TButton;
DisplayDetails: TButton;
searchfield: TEdit;
jobid: TEdit;
procedure FormCreate(Sender: TObject);
procedure SearchByNameClick(Sender: TObject);
procedure GoBackClick(Sender: TObject);
procedure DisplayDetailsClick(Sender: TObject);
private
{ Private declarations }
SearchObjectsList: TStringList;
procedure DisplayDetailFunction(Sender: TObject);
procedure SearchByNameFunction(Sender: TObject);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
constructor TSearchObject.Create(mSearchValue: string;mOnEventClick: TNotifyEvent);
begin
FOnEventClick := mOnEventClick;
FSearchValue := mSearchValue;
end;
{$R *.dfm}
procedure TSearchObject.FireItsEvent;
begin
if Assigned(FOnEventClick) then
FOnEventClick(self);
end;
procedure TForm1.SearchByNameClick(Sender: TObject);
var
mSearchObject: TSearchObject;
begin
mSearchObject := TSearchObject.Create(SearchField.Text,SearchByNameFunction);
SearchObjectsList.AddObject(SearchField.Text,mSearchObject);
end;
procedure TForm1.DisplayDetailFunction(Sender: TObject);
var
mSearchObject: TSearchObject;
begin
mSearchObject := TSearchObject(Sender);
ShowMessage('This is the Display Detail Event. The value of the JobID is '+mSearchObject.FSearchValue);
end;
procedure TForm1.SearchByNameFunction(Sender: TObject);
var
mSearchObject: TSearchObject;
begin
mSearchObject := TSearchObject(Sender);
ShowMessage('This is the SearchByName Event. The value of the Search Field is '+mSearchObject.FSearchValue);
end;
procedure TForm1.DisplayDetailsClick(Sender: TObject);
var
mSearchObject: TSearchObject;
begin
mSearchObject := TSearchObject.Create(jobid.text,DisplayDetailFunction);
SearchObjectsList.AddObject(jobid.text,mSearchObject);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
SearchObjectsList := TStringList.Create;
end;
procedure TForm1.GoBackClick(Sender: TObject);
var
mSearchObject: TSearchObject;
begin
if SearchObjectsList.count=0 then
showmessage('Cannot go Back!')
else begin
mSearchObject := TSearchObject(SearchObjectsList.Objects[SearchObjectsList.count-1]);
mSearchObject.FireItsEvent;
SearchObjectsList.Delete(SearchObjectsList.count-1);
end;
end;
end.
Keep track of everything in a TStringList; when they go "Back" you delete from the string list. This is a sort of prototype:
type
TSearchObject = class
FSearchFunction,FSearchValue: String;
constructor Create(mSearchFunction,mSearchValue: string);
end;
type
TForm1 = class(TForm)
SearchByName: TButton;
GoBack: TButton;
DisplayDetails: TButton;
searchfield: TEdit;
procedure FormCreate(Sender: TObject);
procedure SearchByNameClick(Sender: TObject);
procedure GoBackClick(Sender: TObject);
procedure DisplayDetailsClick(Sender: TObject);
private
{ Private declarations }
SearchObjectsList: TStringList;
jobid: String; //not sure how you get this
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
constructor TSearchObject.Create(mSearchFunction,mSearchValue: string);
begin
FSearchFunction := mSearchFunction;
FSearchValue := mSearchValue;
end;
{$R *.dfm}
procedure TForm1.SearchByNameClick(Sender: TObject);
var
mSearchObject: TSearchObject;
begin
mSearchObject := TSearchObject.Create('SearchByName',SearchField.Text);
SearchObjectsList.AddObject(SearchField.Text,mSearchObject);
end;
procedure TForm1.DisplayDetailsClick(Sender: TObject);
var
mSearchObject: TSearchObject;
begin
mSearchObject := TSearchObject.Create('DisplayDetails',JobID);
SearchObjectsList.AddObject(JobId,mSearchObject);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
SearchObjectsList := TStringList.Create;
end;
procedure TForm1.GoBackClick(Sender: TObject);
var
mSearchObject: TSearchObject;
begin
if SearchObjectsList.count=0 then
showmessage('Cannot go Back!')
else begin
mSearchObject := TSearchObject(SearchObjectsList.Objects[SearchObjectsList.count-1]);
if mSearchObject.FSearchFunction ='SearchByName' then
ShowMessage('Value of Search Field:'+mSearchObject.FSearchValue)
else
ShowMessage('Value of JobID:'+mSearchObject.FSearchValue);
SearchObjectsList.Delete(SearchObjectsList.count-1);
end;
end;
Another option would be to use my wizard framework, which does this with TForms but can easily also be adjusted to use frames. The concept is that each summary form knows how to create its appropriate details. In your case the framework is more of an example of how to do it, rather than a plug and play solution.
Complementing MSchenkel answer.
To persist the list between program runs, use an ini file.
Here is the idea. You have to adapt it. Specially, you have to figure out the way to convert object to string and string to object, sketched here as ObjectToString(), StringToStringID and StringToObject().
At OnClose event, write the list out to the ini file.
const
IniFileName = 'MYPROG.INI';
MaxPersistedObjects = 10;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
ini: TIniFile;
i: integer;
cnt: integer;
begin
ini:=TIniFile.Create(iniFileName);
cnt:=SearchObjectsList.Count;
if cnt>MaxPersistedObjects then
cnt:=MaxPersistedObjects;
for i:=1 to MaxPersistedObjects do
if i>cnt then
ini.WriteString('SearchObjects','SearchObject'+intToStr(i),'');
else
ini.WriteString('SearchObjects','SearchObject'+intToStr(i),
ObjectToString(SearchObjectsList[i-1],SearchObjectsList.Objects[i-1]) );
ini.Free;
end;
and read it back at OnCreate event.
procedure TForm1.FormCreate(Sender: TObject);
var
ini: TIniFile;
i: integer;
begin
SearchObjectsList := TStringList.Create;
ini:=TIniFile.Create(IniFileName);
for i:=1 to MaxPersistedObjects do
begin
s:=ini.ReadString('SearchObjects','SearchObject'+intToStr(i),'');
if s<>'' then
SearchObjectsList.AddObject(StringToID(s),StringToObject(s));
end;
ini.Free;
end;

Resources