How to make TDBCheckBox update its DataField immediately after click?

2.5k views Asked by At

I have a form with several controls where the first one is a TDBCheckBox that is bound to DataField := 'enabled'.

When the checkbox is clicked I want all the remaining controls to be enabled / disabled.

procedure TMyAdapter.DataSourceDataChange(Sender: TObject; Field: TField);
var
  Enabled: Boolean;
begin
  Enabled := FModel.DataSet['enabled'].AsBoolean;
  FView.Label1.Enabled   := Enabled;
  FView.DBEdit1.Enabled  := Enabled;
  FView.Label2.Enabled   := Enabled;
  FView.DBEdit2.Enabled  := Enabled;
  FView.Label3.Enabled   := Enabled;
  FView.DBEdit3.Enabled  := Enabled;
  FView.Label4.Enabled   := Enabled;
  FView.DBEdit4.Enabled  := Enabled;
end;

This only works when the focus leaves the checkbox or when the dataset is scrolled (I have a navigator on this form as well).

Is there a way to make the checkbox update its datafield immediately?

Or is there even a better alternative to achieve what I have described?

3

There are 3 answers

3
MartynA On BEST ANSWER

A couple of problems with using the DataChange event to do things like this are that

  • It's called a lot more frequently than you actually need, to react to your DBCheckBox being clicked and

  • Doing a .Post to the dataset is going to change its state, which is generally a bad idea inside an event which can itself be triggered by a change of dataset state.

Ime, it's best to use a standardised way of dealing with these sorts of problems and the one I use is to write a custom message handler that does the work you want, and to call it using PostMessage from your DBCheckBox1Click handler, as shown below:

const
      WM_AutoPost = WM_User + 1;
type
  TForm1 = class(TForm)
    [...]
  private
    procedure DoAutoPost;
    procedure WMAutoPost(var Msg : TMessage); message WM_Autopost;
  [...]
  end;

var
  Form1: TForm1;

implementation

[...]

procedure TForm1.DBCheckBox1Click(Sender: TObject);
begin
  PostMessage(Self.Handle, WM_AutoPost, 0, 0);
end;

procedure TForm1.DoAutoPost;
begin
  if CDS1.State in [dsEdit, dsInsert] then begin
    CDS1.Post;
    //  Update other controls here
  end;
end;

procedure TForm1.WMAutoPost(var Msg: TMessage);
begin
  DoAutoPost;
end;
0
Jens Mühlenhoff On

This is the solution I build from input of the answers of Uwe and MartynA:

procedure TMyAdapter.EnabledClick(Sender: TObject);
begin
  PostMessage(FView.Handle, WM_ENABLED_CLICKED, 0, 0);
end;

procedure TMyAdapter.WMEnabledClicked(var Msg: TMessage);
var
  DataSet: TDataSet;
begin
  DataSet := FView.EnabledCheckBox.Field.DataSet;
  if not (DataSet.State in [dsInsert, dsEdit]) then 
    DataSet.Edit;
  DataSet.UpdateRecord;
end;

procedure TMyAdapter.DataSourceDataChange(Sender: TObject; Field: TField);
var
  Enabled: Boolean;
begin
  if (Field = nil) or (Field = FView.EnabledCheckBox.Field) then
  begin
    Enabled := FView.EnabledCheckBox.Field.AsBoolean;
    FView.Label1.Enabled   := Enabled;
    FView.DBEdit1.Enabled  := Enabled;
    // etc.
  end;
end;
1
Uwe Raabe On

You can call the DataSets UpdateRecord method to make any linked DB control store its data into the underlying field.