2

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?

Jens Mühlenhoff
  • 14,565
  • 6
  • 56
  • 113

3 Answers3

3

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

Uwe Raabe
  • 45,288
  • 3
  • 82
  • 130
  • This still leaves the question when to call `UpdateRecord`. I have now combined this with the answer from MartynA, see my own answer. – Jens Mühlenhoff Jun 22 '15 at 07:20
2

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;
MartynA
  • 30,454
  • 4
  • 32
  • 73
  • There is still a problem with your solution: Since you do the updating in response of the window message it doesn't get triggered when the DataSet is scrolled. – Jens Mühlenhoff Jun 22 '15 at 07:10
  • Glad you got something working, though I'm not sure I follow your comment that " it doesn't get triggered when the DataSet is scrolled." Since the WM_AutoPost message is posted as soon as the DBCheckBox is clicked, the change ought already to have been posted by the time the dataset scrolls? If not, then personally, I'd call my DoAutoPost from the dataset's BeforeScroll event. – MartynA Jun 22 '15 at 11:18
  • For example when the Form is loaded in the first place and the DataSet already contains enabled = False. It's not enough to update the Enabled state when the Checked stated of the CheckBox changes. – Jens Mühlenhoff Jun 22 '15 at 14:04
1

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;
Jens Mühlenhoff
  • 14,565
  • 6
  • 56
  • 113