Apply button handling in dialog windows (Delphi)

From LiteratePrograms

Jump to: navigation, search

Apply button handling in dialog windows. The purpose of this unit is to automatically control an enabled/disabled state of "Apply" button in dialog windows. The main idea is that if certain controls' properties changed from its defaults, then "Apply" button becomes enabled, otherwise it stays disabled.

Main unit

<<ChangeFinder.pas>>=
unit ChangeFinder;
interface
uses SysUtils, Classes, Controls, Forms, Contnrs;
type
  IStateHolder = interface
    ['{335B2FA7-678D-4B3B-976A-9D139590575B}']
    procedure SaveState (AComponent: TComponent; PropName: string);
    procedure SaveStatesFor (AForm: TForm; AComponentClass: TComponentClass;
      PropName: string);
    function IsChanged: Boolean;
  end;
function CreateStateHolder: IStateHolder;
implementation
uses TypInfo;
type
  TComponentState = class
  private
    FComponent: TComponent;
    FPropertyName: string;
    FValue: Variant;
  public
    constructor Create (AComponent: TComponent; APropertyName: string); reintroduce;
    property Component: TComponent read FComponent;
    property PropertyName: string read FPropertyName;
    function Equal (Another: TComponentState): Boolean;
  end;
  TStateHolder = class (TInterfacedObject, IStateHolder)
  private
    FList: TObjectList;
  public
    constructor Create;
    destructor Destroy; override;
    procedure SaveState (AComponent: TComponent; PropName: string);
    function IsChanged: Boolean;
    procedure SaveStatesFor (Form: TForm; ComponentClass: TComponentClass;
      PropName: string);
  end;
function CreateStateHolder: IStateHolder;
begin
  Result := TStateHolder.Create
end;
function GetPropertyValue (Component: TComponent; PropertyName: string): Variant;
begin
 if GetPropInfo(Component,PropertyName) = nil then
   raise Exception.CreateFmt ('Property %s not found',[PropertyName]);
 Result := GetPropValue (Component,PropertyName);
end;
constructor TStateHolder.Create;
begin
  inherited Create();
  FList := TComponentList.Create(True);
end;
destructor TStateHolder.Destroy;
begin
  FList.Free;
  inherited;
end;
function TStateHolder.IsChanged: Boolean;
var
  I: Integer;
  State1,
  State2: TComponentState;
begin
  Result := False;
  for I := 0 to FList.Count - 1 do
  begin
    State1 := TComponentState(FList[I]);
    State2 := TComponentState.Create(State1.Component,State1.PropertyName);
    try
       if not State1.Equal(State2) then
       begin
         Result := True;
         Break
       end
    finally
      State2.Free
    end
  end
end;
procedure TStateHolder.SaveState(AComponent: TComponent; PropName: string);
begin
  FList.Add( TComponentState.Create(AComponent,PropName))
end;
procedure TStateHolder.SaveStatesFor(Form: TForm;
  ComponentClass: TComponentClass; PropName: string);
var
  I: Integer;
begin
  for I := 0 to Form.ComponentCount-1 do
    if Form.Components[I] is ComponentClass then
      SaveState (Form.Components[I], PropName)
end;
constructor TComponentState.Create(AComponent: TComponent;
  APropertyName: string);
begin
  inherited Create();
  FValue := GetPropertyValue(AComponent,APropertyName);
  FComponent := AComponent;
  FPropertyName := APropertyName;
end;
function TComponentState.Equal(Another: TComponentState): Boolean;
begin
  Result := (FComponent=Another.FComponent) and
            (FPropertyName=Another.FPropertyName) and
            (FValue=Another.FValue);
end;
end.


Usage


You have dialog form with some controls on it and OK, Cancel, Apply buttons. Also, place TActionList on the form. Create an action in TActionList for Apply button and associate it with this button.

The test unit for main form:

<<Unit17.pas>>=
unit Unit17;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ActnList, StdCtrls, ChangeFinder;
type
  TForm17 = class(TForm)
    Button1: TButton;
    ActionList1: TActionList;
    acApply: TAction;
    Edit1: TEdit;
    Edit2: TEdit;
    CheckBox1: TCheckBox;
    CheckBox2: TCheckBox;
    Button2: TButton;
    Button3: TButton;
    procedure ActionList1Update(Action: TBasicAction; var Handled: Boolean);
    procedure FormCreate(Sender: TObject);
    procedure acApplyExecute(Sender: TObject);
  private
    FStateHolder: IStateHolder; // an instance of interface implementation
  public
  end;
var
  Form17: TForm17;
implementation
{$R *.dfm}
procedure TForm17.acApplyExecute(Sender: TObject);
begin
  //
  // Place appropriate code for apply changes
  //
end;
procedure TForm17.ActionList1Update(Action: TBasicAction; var Handled: Boolean);
begin
  acApply.Enabled :=
    FStateHolder.IsChanged;
end;
procedure TForm17.FormCreate(Sender: TObject);
begin
  // Create an instance of state holder
  FStateHolder := CreateStateHolder;
  //
  // Here you may set default values for some controls.
  // Or just set them in Object Inspector at design time.
  //
  with FStateHolder do
  begin
    // Save values of Text property
    // for all TEdit controls (and its descedants) on form
    SaveStatesFor(Self,TEdit,'Text');
    // the same for all TCheckBoxes
    SaveStatesFor(Self,TCheckBox,'Checked');
    // do the same for all other controls' classes
    // or you may use SaveState to save state
    // of individual controls instead of specifying a class
  end;
end;
end.

DFM-file for main form:

<<Unit17.dfm>>=
object Form17: TForm17
  Left = 355
  Top = 349
  Caption = 'Form17'
  ClientHeight = 174
  ClientWidth = 378
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 295
    Top = 141
    Width = 75
    Height = 25
    Action = acApply
    TabOrder = 0
  end
  object Edit1: TEdit
    Left = 16
    Top = 16
    Width = 121
    Height = 21
    TabOrder = 1
    Text = 'Edit1'
  end
  object Edit2: TEdit
    Left = 16
    Top = 43
    Width = 121
    Height = 21
    TabOrder = 2
    Text = 'Edit2'
  end
  object CheckBox1: TCheckBox
    Left = 16
    Top = 70
    Width = 97
    Height = 17
    Caption = 'CheckBox1'
    TabOrder = 3
  end
  object CheckBox2: TCheckBox
    Left = 16
    Top = 93
    Width = 97
    Height = 17
    Caption = 'CheckBox2'
    TabOrder = 4
  end
  object Button2: TButton
    Left = 133
    Top = 141
    Width = 75
    Height = 25
    Caption = 'OK'
    TabOrder = 5
  end
  object Button3: TButton
    Left = 214
    Top = 141
    Width = 75
    Height = 25
    Caption = 'Cancel'
    TabOrder = 6
  end
  object ActionList1: TActionList
    OnUpdate = ActionList1Update
    Left = 200
    Top = 24
    object acApply: TAction
      Caption = 'Apply'
      OnExecute = acApplyExecute
    end
  end
end
Views