• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 266
  • Last Modified:

A Simple? Component

Hello Experts,

I cannot make a component, can you give me a simple framework for it that I can then add to please.

It must show a modal form that has a label and a timer, the timer is enabled when the form is shown and then when the timer times out the form is closed.

I think it would be best if you start from scratch instead of trying to fix my mess, but this is what I have . . .

In the Component Show I can -
  create the form, create the label and position it, size the form, create the timer, assign the timer interval, enable the timer.

I can assign properties to the component and they are shown in the property editor at design time, things like label caption, timer time.

From that I am sure I can expand your very simple example to do what I want it to do.

The problem is that I cannot make the timer time out.
Evidence that the timer doesn't time out (as opposed to it timing out but not closing the form) is that in the TimerTime procedure in the component I put a messagedlg and that message is never shown.


0
frog
Asked:
frog
  • 7
  • 3
1 Solution
 
wimmeyvaertCommented:
I think you just need to setup a fully working Form with all the functionality you need and then Add it to the repository.

If you then want to make use of this kind of Form in another project, just inherit from this Form.

To add the Form to the Repository do :
     - Right click the Form.
     - Choose 'Add to Repository'
     - Fill in a Title, Desccription, Page and Author
     - Click 'OK'.

If you want to make use of this 'MasterForm' in a fututr project, do :
     - File - New
     - Select the Tab you saved your MasterForm in.
     - Select the MasterForm, but be aware that 'Inherit' is selected.
     - Click 'OK'
     - A New Form will now be added that inherits from your MasterForm.


That's it.

Best regards,

The Mayor.
0
 
wimmeyvaertCommented:
I just made a small Form with 1 Timer and a 'Counter' declared as a Property.
Every second the Timer-Event is triggered and then the Form's Caption is updated ('closing in x seconds'). When is the Counting is reached 0, trhen the Form is Closed (Self.Closed).

Here is the code (DFM + PAS Files) :

DFM-File (FORM_BASE_AUTOCLOSE.dfm):
object frmAutoCloseForm: TfrmAutoCloseForm
  Left = 192
  Top = 153
  Width = 696
  Height = 480
  Caption = 'frmAutoCloseForm'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object tmrActiveCounting: TTimer
    OnTimer = tmrActiveCountingTimer
    Left = 8
  end
end




PAS-File (FORM_BASE_AUTOCLOSE.pas):
unit FORM_BASE_AUTOCLOSE;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls;

type
  TfrmAutoCloseForm = class(TForm)
    tmrActiveCounting: TTimer;
    procedure tmrActiveCountingTimer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    FActiveTimeSec: Integer;
    strFrmOrigCaption : String;
    procedure SetActiveTimeSec( const Value: Integer );
    procedure SetFormCaption( Seconds: Integer );
    { Private declarations }
  public
    { Public declarations }
    Property ActiveTimeSec: Integer read FActiveTimeSec write SetActiveTimeSec;
  end;

var
  frmAutoCloseForm: TfrmAutoCloseForm;

implementation

{$R *.DFM}

{ TForm1 }

procedure TfrmAutoCloseForm.SetActiveTimeSec( const Value: Integer );
begin
  FActiveTimeSec := Value;
end;

procedure TfrmAutoCloseForm.tmrActiveCountingTimer(Sender: TObject);
begin
  SetFormCaption( ActiveTimeSec );
  ActiveTimeSec := ActiveTimeSec - 1;
  if ActiveTimeSec < 0 then
    Self.Close;
end;

procedure TfrmAutoCloseForm.FormCreate(Sender: TObject);
begin
  strFrmOrigCaption := Self.Caption;
  SetFormCaption( ActiveTimeSec );
end;

procedure TfrmAutoCloseForm.SetFormCaption(Seconds: Integer);
begin
  Self.Caption := strFrmOrigCaption + ' [Closing in ' + IntToStr( Seconds ) + ' sec.]';
end;

end.


If you Add this form to the Repository (see my previous comment), you can inherit from this form in future projects and the Form is fully functional without any line of extra code. If you want to extend the functionality of the Master)Form, just add the specific code to this Form and recompile all applications that make use of this MasterForm. From then on every application that is recompiled will have the new functionality too.

Best regards,

The Mayor.
0
 
Eddie ShipmanAll-around developerCommented:
If this is just to show a message, why not use the undocumented MessageBoxTimeout function?

unit MsgBoxTimeOut;

interface

uses Windows;

const
 MB_TIMEDOUT = 32000;

function MessageBoxTimeOut(hWnd: HWND; lpText: PChar; lpCaption: PChar;
                           uType: UINT; wLanguageId: WORD; dwMilliseconds: DWORD): Integer; stdcall;

function MessageBoxTimeOutA(hWnd: HWND; lpText: PChar; lpCaption: PChar;
                           uType: UINT; wLanguageId: WORD; dwMilliseconds: DWORD): Integer; stdcall;

function MessageBoxTimeOutW(hWnd: HWND; lpText: PWideChar; lpCaption: PWideChar;
                           uType: UINT; wLanguageId: WORD; dwMilliseconds: DWORD): Integer; stdcall;

implementation


function MessageBoxTimeOut; external user32 name 'MessageBoxTimeoutA';
function MessageBoxTimeOutA; external user32 name 'MessageBoxTimeoutA';
function MessageBoxTimeOutW; external user32 name 'MessageBoxTimeoutW';

end.


Usage:

uses  ..., MsgBoxTimeout;


procedure TForm1.Button1Click(Sender: TObject);
var
  iRet: Integer;
  iFlags: Integer;
begin
  iFlags := MB_OK or MB_SETFOREGROUND or MB_SYSTEMMODAL or MB_ICONINFORMATION;
  iRet   := MessageBoxTimeout(Application.Handle, 'Test a timeout of 2 seconds.',
                             'MessageBoxTimeout Test', iFlags, 0, 2000);
  //iRet will = 1 (IDOK)
  ShowMessage(IntToStr(iRet));

  iFlags := MB_YESNO or MB_SETFOREGROUND or MB_SYSTEMMODAL or MB_ICONINFORMATION;
  iRet   := MessageBoxTimeout(Application.Handle, 'Test a timeout of 5 seconds.',
                              'MessageBoxTimeout Test', iFlags, 0, 5000);
  //iRet = MB_TIMEDOUT if no buttons clicked, otherwise
  // iRet will return the value of the button clicked
  case iRet of
  IDYES:  // Pressed Yes button
    ShowMessage('Yes');
  IDNO:  // Pressed the No button
    ShowMessage('No');
  MB_TIMEDOUT: // MessageBox timed out
    ShowMessage('TimedOut');
  end;
end;
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
frogAuthor Commented:
Thankyou for your replies, a wealth of knowledge as usual.

Thanks EddieShipman I will use your code for other times when I need a MessageBoxTimeout,  but for this one it needs to do more than just display a message. The label is only an example for this question.

Thanks wimmeyvaert, that will be good for making several custom forms that will save time in developing future applications.
Why do you say to make a form and add it to the repository instead of making a component?
I would rather have a component that I can just drop onto a form.

0
 
wimmeyvaertCommented:
Well, Everything in Delphi is a component, so a Form too.
Only Forms ar not added to the component-Pallette, but in the Repository.
But for the rest it is the same.

I think I know why you ask for a component. Probably because you already have several Forms that should be extended with this functionality, right ?

Weel, in this case you can convert all the 'normal' Forms into your 'own' Form like this :
Assume my MasterForm (BaseForm) is called 'frmAutoCloseForm' (Name of the Form) and is saved as 'FORM_BASE_AUTOCLOSE' (on Disk). THen I can convert a normal Form as follows :

Original header of my Form :
unit Form_ModalConverted;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
  TfrmModalConverted = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmModalConverted: TfrmModalConverted;

implementation
....



Change it into :
unit Form_ModalConverted;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, FORM_BASE_AUTOCLOSE;

type
  TfrmModalConverted = class(TfrmAutoCloseForm)
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmModalConverted: TfrmModalConverted;

implementation




Next, from your Form, pres Alt-F12 to switch to 'textmode' and change the word 'object' into 'inhertied' on the first line of code.


Now save your project and reopen it and the 'normal' Form is now 'inherited' from your base-form.

Can you live with that ?

Best regards,

The Mayor.


0
 
wimmeyvaertCommented:
Hi,

I just made a little component, derived from TTimer.
If placed on a Form, you can set 2 properties :
   - CountDownSec : Holds the count-down seconds still to go (by default starting at 5 seconds)
   - CountDownTitle: Text that can be showed in the Form's Caption.
                              Use %d parameter in this string to be able to show remaining seconds


Here is the complete code :

unit Comp_CloseFormTimer;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls;

type
  TCloseFormTimer = class(TTimer)
  private
    OrigFormCaption: String;  { Local var to store the original Form's Caption }
    FCountDownSec: Integer;
    FCountDownTitle: String;
    procedure SetCountDownSec(const Value: Integer);
    procedure SetCountDownTitle(const Value: String);
    { Private declarations }
  protected
    { Protected declarations }
    procedure Timer; override;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
  published
    { Published declarations }
    Property CountDownSec: Integer read FCountDownSec write SetCountDownSec;
    Property CountDownTitle: String read FCountDownTitle write SetCountDownTitle;
  end;

procedure Register;

implementation


procedure Register;
begin
  RegisterComponents('ECA', [TCloseFormTimer]);
end;


{ TCloseFormTimer }


constructor TCloseFormTimer.Create(AOwner: TComponent);
begin
  inherited;
  { Set some default-values at designtime (when component is dropped on the Form. }
  if (csDesigning in ComponentState) then
  begin
    FCountDownSec       := 5;
    FCountDownTitle     := ' [Closing in %d seconds]';
    OrigFormCaption     := '';
  end; { if (csDesigning in ComponentState) then }
end;


procedure TCloseFormTimer.SetCountDownSec( const Value: Integer );
begin
  FCountDownSec := Value;
end;


procedure TCloseFormTimer.SetCountDownTitle( const Value: String );
begin
  FCountDownTitle := Value;
end;


procedure TCloseFormTimer.Timer;
begin
  inherited;

  { If the OrigFormCaption is still blank, store it now for later use. }
  if ( OrigFormCaption = '' ) then
    OrigFormCaption  := ( Self.Owner as TForm ).Caption;

  { If the user wants to show a 'CountDownTitle' (= not blank), then do this here. }
  if ( FCountDownTitle <> '' ) then
  begin
    (Self.Owner as TForm).Caption := OrigFormCaption +
                                     StringReplace( FCountDownTitle,
                                                    '%d',
                                                    IntToStr( FCountDownSec ),
                                                    [rfReplaceAll, rfIgnoreCase] );
  end; { if ( FCountDownTitle <> '' ) then }

  { Decrease the CountDown-Counter by 1 }
  Dec( FCountDownSec );

  { If CountDown-Counter reaches 0, then Close the Form the Timer is on. }
  if ( FCountDownSec < 0 ) then
    (Self.Owner as TForm).Close;
end;

end.





Since it is dervied from the normal TTimer, You should set some code in the OnTimer-Event when dropped on a Form, otherwise this event is never triggered en the form will not be closed automatically.
Just place a ';' in the OnTimer-Event :

procedure TForm1.CloseFormTimer1Timer(Sender: TObject);
begin
;
end;



Hope this is what you are looking for.

Bes tregards,

The Mayor.

0
 
wimmeyvaertCommented:
I made some little changes to my previous comment to improve the component.
Here is the complete code again (Comp_CloseFormTimer.Pas) :

unit Comp_CloseFormTimer;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls;

type
  TCloseFormTimer = class(TTimer)
  private
    OrigFormCaption: String;  { Local var to store the original Form's Caption }
    FCountDownSec: Integer;
    FCountDownTitle: String;
    procedure SetCountDownSec(const Value: Integer);
    procedure SetCountDownTitle(const Value: String);
    { Private declarations }
  protected
    { Protected declarations }
    procedure Timer; override;
  public
    { Public declarations }
    procedure UpdateCaption;
    constructor Create(AOwner: TComponent); override;
  published
    { Published declarations }
    Property CountDownSec: Integer read FCountDownSec write SetCountDownSec;
    Property CountDownTitle: String read FCountDownTitle write SetCountDownTitle;
  end;

procedure Register;

implementation


procedure Register;
begin
  RegisterComponents('ECA', [TCloseFormTimer]);
end;


{ TCloseFormTimer }


constructor TCloseFormTimer.Create(AOwner: TComponent);
begin
  inherited;
  { Set some default-values at designtime (when component is dropped on the Form. }
  if (csDesigning in ComponentState) then
  begin
    FCountDownSec       := 5;
    FCountDownTitle     := ' [Closing in %d seconds]';
    OrigFormCaption     := '';
  end; { if (csDesigning in ComponentState) then }
end;


procedure TCloseFormTimer.SetCountDownSec( const Value: Integer );
begin
  FCountDownSec := Value;
end;


procedure TCloseFormTimer.SetCountDownTitle( const Value: String );
begin
  FCountDownTitle := Value;
end;


procedure TCloseFormTimer.Timer;
begin
  inherited;

  { Decrease the CountDown-Counter by 1 }
  Dec( FCountDownSec );

  {
  Update the Form's Caption to show remaining seconds before closing.
  If user blanks out the CountDownTitle, then nothing will be displayed.
  }
  UpdateCaption;

  { If CountDown-Counter reaches 0, then Close the Form the Timer is on. }
  if ( FCountDownSec <= 0 ) then
    (Self.Owner as TForm).Close;
end;


procedure TCloseFormTimer.UpdateCaption;
begin
  { If the OrigFormCaption is still blank, store it now for later use. }
  if ( OrigFormCaption = '' ) then
    OrigFormCaption  := ( Self.Owner as TForm ).Caption;

  (Self.Owner as TForm).Caption := OrigFormCaption +
                                   StringReplace( FCountDownTitle,
                                                  '%d',
                                                  IntToStr( FCountDownSec ),
                                                  [rfReplaceAll, rfIgnoreCase] );
end;


end.









From your application you could now do the following :

procedure TForm1.FormCreate(Sender: TObject);
begin
  { Initially update the Form's Caption to show remaining seconds. }
  CloseFormTimer1.UpdateCaption;
end;

procedure TForm1.CloseFormTimer1Timer(Sender: TObject);
begin
  { Do nothing , only executing the TCloseFormTimer's specific code }
  ;
end;


If you want I can send you the little project I made, together with the Component's pas-file.
Just leave your email-address here and I'll send you the project/component.

Best regards,

The Mayor.
0
 
frogAuthor Commented:
Thanks Very Much for all your work!

I have not made it clear enough, the component must show a form that is then closed by the timer.

I have tried based on your early compnent that Was a form byt I couldn't make it work.

I have had the best results with a component that creats the form in its OnShow.
The component was like this-
  TMessageTimer = class(TComponent)
  private
    { Private }
    MessageForm : TForm ;
    FormTimer : TTimer;
   
    FTitle : string ;
    FMessage : String ;
    FBackColor : TColor ;
    FTextColor : TColor;
    FSeconds : integer;

The timer was initialised and enabled in the OnShow.
The problem was that the timer could not be made to time out.

I will work on it over the weekend.

Thanks again for all your help.

0
 
wimmeyvaertCommented:
Then I really think you should go for 'Form-Inheritance' as I proposed in my first Comment (just make a Form that does all you need, and then add it to repository).
You can then add a Form to your project, which inherits from your BaseForm.

Have you tried my component ?
You just have to drop it on a Form and place a ';' in the OnTimer-Event.

Then, when you call this form from your application the timer is enabled and the Form will close itself in the predefined seconds.

Can you give me your email-address, then I will send you the complete code of the component, ans also a little demo-app I made using this component.

Best regards,

The Mayor.
0
 
frogAuthor Commented:
Thanks for your help, I have discovered what a handy thing the repository is.

I have tried your component code, thanks for it.
I will contact you soon.

0
 
wimmeyvaertCommented:
Thanks for the points and the grade !

I hope I was of some help.

Best regards,

The Mayor.
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 7
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now