3

I'm deriving from TClientdataset and trying to define an 'always run' AfterPost event. I have tried assign my AfterPost event at the constructor, but the derived component does not seem to pick it up

TMyClientDataset = class(TClientDataset)
private
  FInserting: Boolean;    //set to True at the MyOnNewRecord event
  property FuserAfterPost: TDataSetNotifyEvent;
...
public
  constructor Create(AOwner: TComponent);
...

implementation
constructor TMyClientDataset.Create(AOwner: TComponent)
begin
  ...
  if Assigned(AfterPost) then
    FuserAfterPost := AfterPost;
  Afterpost := MyAfterPost;
...
end;

procedure TMyClientDataset.MyAfterPost(Dataset: TDataset);
begin
  If Assigned(FuserAfterPost) then
    FuserAfterPost(Dataset);
  FInserting := False;
end;

What I'm trying to do: On new record, set Finserting := True; On after post, run the user supplied OnAfterPost and set FInserting := False; But the MyAfterpost event won't even run. I'm assuming the constructor is not the right place to do AfterPost := MyAfterPost;? Where should it go?

Disillusioned
  • 14,635
  • 3
  • 43
  • 77
JeffP
  • 539
  • 1
  • 5
  • 19
  • I agree with @CraigYoung. If you are wanting to ensure that some code of yours is always executed after a Post operation, why not override `TClientDataSet.Post` and put your code in there, after a call to `inherited`? – MartynA Oct 19 '16 at 12:02

2 Answers2

3

There's no good place for what you want to do. Because a user of the component may attach a handler or nil to the event handler anytime while the program is running, not just at design time. May detach an existing handler too. Then your code will not run.

For this reason, VCL employs a two step call to event handlers. First is a virtual procedure which, generally, does nothing more than to call a possible event handler. In your case, this is DoAfterPost.

TMyClientDataset = class(TClientDataset)
  ..
protected
  procedure DoAfterPost; override;

...

procedure TMyClientDataset.DoAfterPost;
begin
  inherited;
  FInserting := False;
end;


For a case when no such opportunity exists, there would be no chance but properly document and hope for the user of the component reads and complies with it. Overriding Loaded would be the right place to backup an existing design-time attached handler.

Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169
  • I did as suggested, overriding DoAfterPost and it worked to solve my problem. I will be doing the same for DoBeforePost and DoNewRecord as I have same issues with these. – JeffP Oct 20 '16 at 21:58
0

Sertac's answer is excellent guidance on this type of problem. And yes it does answer the question you asked, but it's missing something.

It seems to me that you have an XY problem, and failed to ask the correct question. There is no need for you to try to manually track FInserting. Delphi already does this. Take a look at TDataSet.State and TDataSetState.

Basically your FInserting is equivalent to State = dsInsert.
Although, as you have pointed out, your FInserting flag is True in OnAfterPost (which makes it misleading, and on that basis is technically a bug).

Community
  • 1
  • 1
Disillusioned
  • 14,635
  • 3
  • 43
  • 77
  • `TDataSet.State` would not be `dsInsert` when the app arrives at the OnAfterPost event. – JeffP Oct 19 '16 at 07:08
  • @JeffP Which is _correct_ because when the _**After**Post_ event fires, you are by definition no longer 'inserting'. And this brings me back to your XY problem. You have a "root problem" in which you've conceived of a so-called "solution" that requires you to pretend you're in an "inserting state" when you're not. While this approach may hack around your existing problem, you may wish to ask another question about your root problem; _you may find a far more practical approach that simpler does doesn't require a misleading `FInserting` flag_. – Disillusioned Oct 19 '16 at 08:15
  • FInserting is confusing, I see that. I should have named it FLastPostedState or FIHaveJustInsertedARecord; that's what I'm trying to do – JeffP Oct 20 '16 at 21:52
  • @JeffP It's more than just the name. Why do you feel the need to differentiate the between whether you ***were*** editing or ***were*** inserting at the point ***after*** you 'saved your changes'? I strongly suspect you a missing something in your approach, and believe you'll get a much better design if step back and ask a new question. – Disillusioned Oct 21 '16 at 04:23
  • thanks for your interest. This is the issue: after inserting a sales record, I save the last number used as it is user supplied. I do this on the AfterPost event, but only if the record was inserted (dsInsert), but not when edited (dsEdit). So I set `FInsering := True` at the OnNewRecord, do my stuff at AfterPost and reset `FInserting := False`. This was fine for the first 2 tables, the 3rd started to get me. So I thought I just subclass TClientDataset and do all the rest of overriding the concerned events, but not successfully, hence this question. – JeffP Oct 21 '16 at 09:48
  • @JeffP Yes. I'd say you've definitely missed an easier option. But comments in not the place for Q&A as it would diminish the value of this site for future users. So please post a [new question](http://stackoverflow.com/questions/ask). I'll give you an answer there unless someone else does so before me. – Disillusioned Oct 23 '16 at 01:24
  • Sorry I'm so dense today but, I don't even know what to ask. My initial problem's solved, but you seem to say there is a better way? Or is that what you mean? – JeffP Oct 24 '16 at 09:49