I found the following code in another post. Is it possible to capture Outlook task events in the same way? There does not appear to be an equivalent of TMailItem for tasks - just Outlook2000._TaskItem (and if someone could kindly explain the difference between Outlook2000.MailItem and Outlook2000._MailItem it might give me a clue). Thanks in advance.
uses
..., outlook2000;
type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
OutlookApplication: TOutlookApplication;
procedure OnMailSend(Sender: TObject; var Cancel: WordBool);
public
end;
[...]
procedure TForm1.FormCreate(Sender: TObject);
begin
OutlookApplication := TOutlookApplication.Create(Self);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
MailItem: _MailItem;
Mail: TMailItem;
begin
MailItem := OutlookApplication.CreateItem(olMailItem) as _MailItem;
Mail := TMailItem.Create(nil);
try
Mail.ConnectTo(MailItem);
Mail.OnSend := OnMailSend;
MailItem.Recipients.Add('username#example.com');
MailItem.Display(True);
finally
Mail.Free;
end;
end;
procedure TForm1.OnMailSend(Sender: TObject; var Cancel: WordBool);
begin
ShowMessage((Sender as TMailItem).Body);
end;
Related
I'm new at SO, so forgive me if my question isn't in the right place or been answered before.
The questions is about multi-threading with Delphi 10.4.
I'm getting Access Violation error on my app, here is a very simple example:
type
myThread = class(TThread)
protected
procedure Execute; override;
end;
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
mySideTask : myThread;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
with mySideTask.Create do
FreeOnTerminate:=True
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if mySideTask<>nil then
begin
mySideTask.Terminate;
mySideTask.WaitFor;
FreeAndNil(mySideTask);
end;
end;
{ myThread }
procedure myThread.Execute;
begin
Synchronize(
procedure
begin
Form1.Memo1.Lines.Add('running my side task')
end);
end;
No error if I don't create an instance of the thread (which is confusing me):
procedure TForm1.Button1Click(Sender: TObject);
begin
myThread.Create
end;
Can you please let me know what am I missing.
The code in Button1Click() is wrong. You are calling Create() as an instance method on your mySideTask variable, but it is not pointing at a valid object instance. You need to instead call Create() as a constructor on the class type itself.
Try this instead:
procedure TForm1.Button1Click(Sender: TObject);
begin
mySideTask := myThread.Create(False{True});
//mySideTask.FreeOnTerminate := True;
//mySideTask.Start;
end;
Notice I commented out the handling of FreeOnTerminate=True. The reason for that is because that setting is meant for create-and-forget type of threads. The thread will destroy itself after its Execute() method exits. So it is not safe to call WaitFor() or Free() on a thread that could destroy itself at any moment.
If you want to use FreeOnTerminate=True, then the code should look more like this instead:
type
myThread = class(TThread)
protected
procedure Execute; override;
end;
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
mySideTask : myThread;
procedure SideTaskTerminated(Sender: TObject);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
mySideTask := myThread.Create(True);
mySideTask.FreeOnTerminate := True;
mySideTask.OnTerminated := SideTaskTerminated;
mySideTask.Start;
end;
procedure TForm1.SideTaskTerminated(Sender: TObject);
begin
mySideTask := nil;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if mySideTask <> nil then
begin
mySideTask.FreeOnTerminate := False;
mySideTask.Terminate;
mySideTask.WaitFor;
FreeAndNil(mySideTask);
end;
end;
{ myThread }
procedure myThread.Execute;
begin
Synchronize(
procedure
begin
Form1.Memo1.Lines.Add('running my side task')
end);
end;
I have seen the below msghandler code in several places now as the solution to not being able to press Enter in a twebbrowser. This solution does work as long as you're only dealing with one twebbrowser. I've provided a complete unit here for discussion. If you take two twebbrowsers and make one of them the "active" browser (see code) and navigate them each to a site for example that has a username, password and button you can enter the data in the "active" browser and press Enter successfully. If you try to use the non "active" browser not only can you not press Enter but use of tab fails as well. Whichever browser you press Enter in first is the one that will continue to work so it seems to have nothing to do with order of creation of the browsers.
How do I make my additional browsers function?
unit Main_Form;
interface
uses
Winapi.Windows, Winapi.Messages, Vcl.Controls, Vcl.Forms,
ActiveX, Vcl.OleCtrls, SHDocVw, System.Classes, Vcl.StdCtrls;
type
TForm1 = class(TForm)
NavigateBrowsers: TButton;
WebBrowser1: TWebBrowser;
WebBrowser2: TWebBrowser;
MakeBrowser1Active: TButton;
MakeBrowser2Active: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormDeactivate(Sender: TObject);
procedure NavigateBrowsersClick(Sender: TObject);
procedure MakeBrowser1ActiveClick(Sender: TObject);
procedure MakeBrowser2ActiveClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure MsgHandler(var Msg: TMsg; var Handled: Boolean);
end;
var
Form1: TForm1;
ActiveBrowser: TWebBrowser;
FOleInPlaceActiveObject: IOleInPlaceActiveObject;
SaveMessageHandler: TMessageEvent;
implementation
{$R *.dfm}
procedure TForm1.MakeBrowser1ActiveClick(Sender: TObject);
begin
ActiveBrowser := WebBrowser1;
end;
procedure TForm1.MakeBrowser2ActiveClick(Sender: TObject);
begin
ActiveBrowser := WebBrowser2;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Application.OnMessage := SaveMessageHandler;
FOleInPlaceActiveObject := nil;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
//Handle messages
SaveMessageHandler := Application.OnMessage;
Application.OnMessage := MsgHandler;
end;
procedure TForm1.FormDeactivate(Sender: TObject);
begin
Application.OnMessage := SaveMessageHandler;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FOleInPlaceActiveObject := nil;
end;
procedure TForm1.NavigateBrowsersClick(Sender: TObject);
begin
WebBrowser1.Navigate(''); //supply own
WebBrowser2.Navigate(''); //supply own
end;
procedure TForm1.MsgHandler(var Msg: TMsg; var Handled: Boolean);
const
StdKeys = [VK_BACK, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT];
var
IOIPAO: IOleInPlaceActiveObject;
Dispatch: IDispatch;
begin
//Exit if webbrowser object is nil
if ActiveBrowser = nil then
begin
Handled := False;
Exit;
end;
Handled:=(IsDialogMessage(ActiveBrowser.Handle, Msg) = True);
if (Handled) and (not ActiveBrowser.Busy) then
begin
if FOleInPlaceActiveObject = nil then
begin
Dispatch := ActiveBrowser.Application;
if Dispatch <>nil then
begin
Dispatch.QueryInterface(IOleInPlaceActiveObject, iOIPAO);
if iOIPAO <>nil then
FOleInPlaceActiveObject := iOIPAO;
end;
end;
if FOleInPlaceActiveObject <>nil then
if ((Msg.message = WM_KEYDOWN) or (Msg.message = WM_KEYUP)) and
(Msg.wParam in StdKeys) then
//nothing - do not pass on StdKeys
else
FOleInPlaceActiveObject.TranslateAccelerator(Msg);
end;
end;
initialization
OleInitialize(nil);
finalization
OleUninitialize;
end.
I faced the same problem as you and I use a similar message handler, FOleInPlaceActiveObject is not really needed:
procedure TForm1.MsgHandler(var Msg: TMsg; var Handled: Boolean);
const
StdKeys = [VK_BACK, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT];
var
IOIPAO: IOleInPlaceActiveObject;
begin
try
if Assigned(ActiveBrowser) then
begin
Handled:=(IsDialogMessage(ActiveBrowser.Handle, Msg) = True);
if Handled then
begin
if ((Msg.message = WM_KEYDOWN) or (Msg.message = WM_KEYUP)) and (Msg.wParam in StdKeys) then
begin
//nothing - do not pass on Backspace, Left, Right, Up, Down arrows
end
else
begin
IOIPAO := (ActiveBrowser.Application as IOleInPlaceActiveObject);
if Assigned(IOIPAO)then
IOIPAO.TranslateAccelerator(Msg)
end;
end;
end;
except
Handled := False;
end;
end;
After days of searching for an answer it appears I have found something that works the same day I posted the question here. Go figure! For everyone's benefit, here is what worked.
All I had to do was assign the browser as the active control when either the user changed tabs or at the time of new tab creation. The reason for the count check in the pagecontrolchange procedure is to keep from getting a list index out of bounds on initial tab creation at startup. I do realize I probably need to change my ObjectLists over to Generics, ;)
procedure TForm1.PageControl1Change(Sender: TObject);
begin
if PageControl1.PageCount = MyBrowsersObjectList.Count then // Not adding a page
begin
ActiveBrowser := MyBrowsersObjectList[PageControl1.ActivePageIndex] as TWebBrowser;
ActiveControl := ActiveBrowser;
end;
end;
procedure TForm1.CreateBrowserTab(APage: TAdvOfficePage; NavigateTo: String);
begin
APage.Caption := 'Loading...';
ActiveBrowser := TWebBrowser.Create(nil);
MyBrowsersObjectList.Add(ActiveBrowser);
TControl(ActiveBrowser).Parent := APage;
ActiveBrowser.Align := alClient;
ActiveBrowser.RegisterAsBrowser := True;
ActiveBrowser.Tag := BrowserTabs.ActivePageIndex;
ActiveBrowser.Navigate(NavigateTo);
ActiveControl := ActiveBrowser;
end;
I firstly created a form that will show settings. Then i created a login box that will load a password from an ini file. I originally thought that it was an error with loading the ini file. Though I have isolated it to when I load the settings form. Here is the code for all of them.
The code for the settings screen:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, inifiles;
type
TForm1 = class(TForm)
SaveButton: TButton;
AEditA: TEdit;
AEditB: TEdit;
SEditB: TEdit;
PEditB: TEdit;
PLabelA: TLabel;
SLabelA: TLabel;
ALabelA: TLabel;
PEditA: TEdit;
SEditA: TEdit;
ExitButton: TButton;
Settings: TLabel;
ALabelB: TLabel;
SLabelB: TLabel;
PLabelB: TLabel;
AReserveLabel: TLabel;
BReserveLabel: TLabel;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
Label5: TLabel;
Label6: TLabel;
Label8: TLabel;
Label7: TLabel;
procedure SaveButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure ExitButtonClick(Sender: TObject);
procedure AEditAKeyPress(Sender: TObject; var Key: Char);
procedure AEditBKeyPress(Sender: TObject; var Key: Char);
procedure SEditAKeyPress(Sender: TObject; var Key: Char);
procedure SEditBKeyPress(Sender: TObject; var Key: Char);
procedure PEditAKeyPress(Sender: TObject; var Key: Char);
procedure PEditBKeyPress(Sender: TObject; var Key: Char);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
IniFile : TIniFile;
appINI : TIniFile;
APriceA : String;
SPriceA : String;
PPriceA : String;
APriceB : String;
SPriceB : String;
PPriceB : String;
implementation
{$R *.DFM}
procedure TForm1.SaveButtonClick(Sender: TObject);
//Save Button
begin
appINI := TIniFile.Create(ChangeFileExt(Application.ExeName,'.ini'));
APriceA := (AEditA.Text);
SPriceA := (SEditA.Text);
PPriceA := (PEditA.Text);
APriceB := (AEditB.Text);
SPriceB := (SEditB.Text);
PPriceB := (PEditB.Text);
appINI.WriteString('PricesA','Adult',APriceA);
appINI.WriteString('PricesA','Student',SPriceA);
appINI.WriteString('PricesA','Pensioner',PPriceA);
appINI.WriteString('PricesB','Adult',APriceB);
appINI.WriteString('PricesB','Student',SPriceB);
appINI.WriteString('PricesB','Pensioner',PPriceB);
appINI.Free;
ShowMessage('Settings Saved Successfully!');
end;
procedure TForm1.FormCreate(Sender: TObject);
//Displays values as the form is created
begin
{ appINI := TIniFile.Create(ChangeFileExt(Application.ExeName,'.ini'));
APriceA := appINI.ReadString('PricesA','Adult','');
SPriceA := appINI.ReadString('PricesA','Student','');
PPriceA := appINI.ReadString('PricesA','Pensioner','');
APriceB := appINI.ReadString('PricesB','Adult','');
SPriceB := appINI.ReadString('PricesB','Student','');
PPriceB := appINI.ReadString('PricesB','Pensioner','');
appINI.Free;
AEditA.Text := (APriceA);
SEditA.Text := (SPriceA);
PEditA.Text := (PPriceA);
AEditB.Text := (APriceB);
SEditB.Text := (SPriceB);
PEditB.Text := (PPriceB);}
end;
procedure TForm1.ExitButtonClick(Sender: TObject);
//Exit Button
begin
Close;
end;
procedure TForm1.AEditAKeyPress(Sender: TObject; var Key: Char);
var s:string;
begin
s := ('1234567890.'#8); //Add chars you want to allow
if pos(key,s) =0 then begin
Key:=#0;
showmessage('Only Numbers are allowed. Include cents!');
end;
end;
procedure TForm1.AEditBKeyPress(Sender: TObject; var Key: Char);
var s:string;
begin
s := ('1234567890.'#8); //Add chars you want to allow
if pos(key,s) =0 then begin
Key:=#0;
showmessage('Only Numbers are allowed. Include cents!');
end;
end;
procedure TForm1.SEditAKeyPress(Sender: TObject; var Key: Char);
var s:string;
begin
s := ('1234567890.'#8); //Add chars you want to allow
if pos(key,s) =0 then begin
Key:=#0;
showmessage('Only Numbers are allowed. Include cents!');
end;
end;
procedure TForm1.SEditBKeyPress(Sender: TObject; var Key: Char);
var s:string;
begin
s := ('1234567890.'#8); //Add chars you want to allow
if pos(key,s) =0 then begin
Key:=#0;
showmessage('Only Numbers are allowed. Include cents!');
end;
end;
procedure TForm1.PEditAKeyPress(Sender: TObject; var Key: Char);
var s:string;
begin
s := ('1234567890.'#8); //Add chars you want to allow
if pos(key,s) =0 then begin
Key:=#0;
showmessage('Only Numbers are allowed. Include cents!');
end;
end;
procedure TForm1.PEditBKeyPress(Sender: TObject; var Key: Char);
var s:string;
begin
s := ('1234567890.'#8); //Add chars you want to allow
if pos(key,s) =0 then begin
Key:=#0;
showmessage('Only Numbers are allowed. Include cents!');
end;
end;
//End of Settings
end.
The code for the login form:
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Mask, inifiles, Unit1;
type
TForm2 = class(TForm)
PassEdit: TMaskEdit;
LoginButton: TButton;
PassLabel: TLabel;
InvisiButton: TButton;
procedure PassEditClick(Sender: TObject);
procedure LoginButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
IniFile : TIniFile;
appINI : TIniFile;
Password : string;
implementation
{$R *.DFM}
procedure TForm2.PassEditClick(Sender: TObject);
begin
PassEdit.Text := '';
end;
procedure TForm2.LoginButtonClick(Sender: TObject);
begin
//if Password = PassEdit.Text then begin
Form2.Hide;
showmessage('test');
Form1.Show;
end;
//end;
procedure TForm2.FormCreate(Sender: TObject);
begin
appINI := TIniFile.Create(ChangeFileExt(Application.ExeName,'.ini'));
Password := appINI.ReadString('Login','Password','');
ShowMessage(Password);
appINI.Free;
end;
end.
This is the project:
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas' {Form2};
{$R *.RES}
begin
Application.Initialize;
//Application.CreateForm(TForm1, Form1);
Application.CreateForm(TForm2, Form2);
Application.Run;
end.
You've commented out the line of code in the .dpr file that createsForm1`:
//Application.CreateForm(TForm1, Form1);
But you're accessing that uncreated form in Unit1:
procedure TForm2.LoginButtonClick(Sender: TObject);
begin
//if Password = PassEdit.Text then begin
Form2.Hide;
showmessage('test');
Form1.Show; // <-- Accessing uncreated form here
end;
Uncomment the line in the project file so it gets created. Note that the first form that's created with Application.CreateForm becomes your application's main form, and when that form is closed your application terminates.
You also have another major flaw in your code. You should never reference the form itself by name from within one of it's own methods, like you do here from within TForm2.LoginButtonClick:
Form2.Hide;
This means that if you ever rename the form, it won't compile, and if you create more than one TForm2, your code will either access the wrong one or will cause access violations for accessing a non-created form (like the problem you're having now). You should either just use the form's method directly, like Hide;' from the method, or useSelf.Hide;` to refer to the instance currently running the method.
(For future reference: When you have a problem, it helps if you explain what that problem is when you ask for help solving it. "Program errors" with no other information about the error is meaningless by itself. When you type "error", the very next thing you should add is the exact error you're having, including the exact error message including any address information. We can't see your screen from where we sit, so we only have the info you provide us to go by in helping you.)
Borland Developer Studio 2006, Delphi:
I have a TOLEContainer object with AllowInPlace=False. When the external editor is closed and changed my OLE object I have to do something with this OLE object inside TOLeContainer.
The problem is I can't catch a moment when the external editor is closed. OnDeactivate event is not working.
Probably I should change the source code of TOLEContainer adding this event myself, but I don't know where is the best place for it.
Can you advice some method?
A simple example which does not need modifying the VCL sources;
uses
.., activex;
type
TForm1 = class(TForm, IAdviseSink)
..
Button1: TButton;
OleContainer1: TOleContainer;
procedure Button1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
Connection: Longint;
procedure CloseConnection;
procedure OnDataChange(const formatetc: TFormatEtc; const stgmed: TStgMedium);
stdcall;
procedure OnViewChange(dwAspect: Longint; lindex: Longint);
stdcall;
procedure OnRename(const mk: IMoniker); stdcall;
procedure OnSave; stdcall;
procedure OnClose; stdcall;
public
end;
implementation
procedure TForm1.OnDataChange(const formatetc: TFormatEtc;
const stgmed: TStgMedium);
begin
end;
procedure TForm1.OnRename(const mk: IMoniker);
begin
end;
procedure TForm1.OnSave;
begin
end;
procedure TForm1.OnViewChange(dwAspect, lindex: Integer);
begin
end;
procedure TForm1.OnClose;
begin
ShowMessage('not editing anymore!');
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if OleContainer1.InsertObjectDialog then begin
CloseConnection;
OleContainer1.OleObjectInterface.Advise(IAdviseSink(Self), Connection);
end;
end;
procedure TForm1.CloseConnection;
begin
if Connection <> 0 then
if OleContainer1.OleObjectInterface.Unadvise(Connection) = S_OK then
Connection := 0;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
CloseConnection;
end;
The OLE object calls OnShowWindow method of the IOleClientSite interface (implemented by TOleContainer). The fShow parameter indicates whether the object's window is being opened or closed.
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;