Apply button handling in dialog windows (Delphi)
From LiteratePrograms
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