tags:

views:

105

answers:

4

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...

+1  A: 

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;
M Schenkel
ThanksThat's good, and solves one of my questions (unstated) as to how I store the values as well as the command.However what I still can't see is how I call the stored function - in your example you are using it to decide which ShowMessage to use... I would need to call the procedure and pass the stored parameter...
Dan Kelly
Actually I'm being dense - one answer is use if / case statements to make the decision in the GoBackClick routine...(Can you do the other option though?)
Dan Kelly
A: 

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.

skamradt
A: 

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;
PA
+1  A: 

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.
M Schenkel
That's exactly what I had in mind.I had also considered the pointer route, but had even less idea of where to start down that route (pointers, unfortunately, remain one of my programming blind spots)
Dan Kelly