WebMemo OnChange event not firing correctly

Hello,

The OnChange event handler for TWebMemo only fires when the memo is updated by typing something into the memo box.

If the memo is updated programmatically, the OnChange event never fires.

See attached project. The VCL version works correctly, while the Web version works just partially.

Please fix.

Memo OnChange Event.zip (58.6 KB)

We believe the proper way for OnChange to work is that it is exclusively triggered by UI changes and not from programmatic changes.
1)
When it is triggered in both circumstances, the code cannot differentiate between programmatic changes and UI changes
2)
When your code performs a change, your code knows there is a change, so if there is an action to happen for these changes, your code can directly call this instead of doing a roundtrip via an event handler.

I know the VCL does this but we believe this is old and not good design from the original VCL framework.

There is no need to differentiate. The event is called "OnChange". That literally means: "when a change occurs". It shouldn't matter why the change occurred, or what triggered the change. The point is that the memo changed.

If you feel it's really so important to differentiate between how the change occurs, then you should create two different events to handle them.

And what about a situation where the memo changes, but it's not my code that makes the change? How am I supposed to know that a change has occurred? What if the memo changes because of code from TMS WEB Core? Please continue reading, and you'll see what I mean.

Borland has done it this way from day one, and it's definitely the right way to do it.

Consider my current situation:

I'm using a WebOpenDialog to load a text file into a memo. Thanks to Julio, I finally got it working.

So now that the user has selected a text file and the memo has been populated with the text, I need to know about it. The OnChange event handler is where I would expect to be notified. But because TMS decided that Delphi's implementation is "old and not good design", I can't rely on the OnChange event handler. So even though the memo did, in fact, change, I won't know about it.

To work around it, I have to introduce a WebTimer into my program to constantly monitor the contents of my memo several times per second, thereby wasting resources.

When you make changes - especially breaking changes - to how Delphi has worked for 25 years, it will always create problems for your users, even if you can't foresee those problems.

Either the memo is changed via keyboard/mouse, i.e. a UI interaction or via code, i.e. by changing WebMemo.Lines. There is no other way.

When a change is made via the UI, OnChange is triggered.

When a change is made in code via WebMemo.Lines, your code will always know this as there is code at application level responsible for it.

Using the WebOpenDialog, the code is

TWebOpenDialog(Sender).Files[0]
.GetFileAsText(
procedure (txt : string)
begin
webmemo1.Lines.Text := txt;
--> YOUR CODE KNOWS HERE THE MEMO CHANGED, SO CAN NOTIFY WHEN NEEDED
end
);

The code doesn't know anything, because WebOpenDialog doesn't operate the same way in WEB Core as in VCL.

With the VCL implementation, I can do:

if OpenDialog1.Execute then Memo1.Lines.LoadFromFile(OpenDialog1.FileName);

Therefore, the memo would only change if a file was actually selected. If the user pressed "Cancel" during the OpenDialog, the memo would not be updated.

In WEB Core, I can't do that because WebOpenDialog's Execute method is a procedure, not a function. And because of that, I have no way of knowing if the user actually selected a file or if he pressed cancel.

So just because we say "webmemo1.Lines.Text := txt;", doesn't mean that the memo changed. But if we had a proper OnChange event, we would know.

I'm sorry but I don't know how I can better explain as you do not seem to understand it.

If the OnChange event handler was working as it should, then none of this would be an issue because you wouldn't have to explain anything, and I wouldn't have to be accused of being too stupid to understand.

It is working as expected by design and I did not accuse anyone. You should not interpret my failure to explain as an accusation!

If the line
webmemo1.Lines.Text := txt;"
is executed, then the memo changes, so your code knows.

The old VCL pattern is wrong and we do not wish to be bound for the next 25 years to an old wrong pattern.
Please consider this my final answer.

This would imply that you have no better way of explaining it.

This would imply that I'm too stupid to understand.

So far, you haven't even tried to explain why it's "wrong". The only thing you said was that it's "old and not good design".

From my point of view, you're doing a Microsoft. You're changing things just for the sake of changing things. Not because anything was wrong with how it was done up until now, but because somebody inside the company decided (probably out of boredom), that something needs to be changed, even though it was working perfectly fine the way it was.

Now how should I interpret that we do something out of boredom or for the sake of change?!
This sort of discussion is disrespectful and does not belong here.

I have explained it already. This is NOT out of boredom. We have 25 years of experience in writing component code for VCL,FMX,LCL and seeing the code of these frameworks. This is because of the need of a CLEAR SEPARATION of knowing when something happens from the UI and when something happens due to code executed.

Again, I think this is now the 3rd time:

When a change is made via the UI due to user interaction, OnChange is triggered.

When a change is made in code via for for example WebMemo.Lines, your code will always know this as there is code at application level responsible for it. There is NO NEED to trigger an event, your code knows it. Triggering an event is superfluous and prevents discoverability of the cause of the change. If you really need to do something in response to this change, your code can always directly from there invoke this code.

Chill out...
It's just as simple as TMS Web Core isn't a direct convertion from VCL to Web (for good). Period.
Even IW, 15 years ago, didn't fire onchange events for programmatically made changes. Maybe they've added it later, but I really could not fit on using IW for a second time, so I don't know exactly. One IW project had already been too much! :wink:

It's a good practice is to keep as much code as posible out of event triggers, so you can do a better maintenance and reuse more.

...

type
  TEditChangeMode = (ecmDoesntMatter, ecmByUser, ecmByCode);

...

    procedure DoMemoReset;
    procedure DoShowOpenDialog;
    procedure DoDialogOpenFile;
    procedure DoMemoChange(AMode : TEditChangeMode = ecmDoesntMatter);

...

implementation
uses StrUtils;

...

{UI events}

procedure TForm1.WebFormCreate(Sender: TObject);
begin
  DoMemoReset;
end;

procedure TForm1.WebButton1Click(Sender: TObject);
begin
  DoShowOpenDialog;
end;

procedure TForm1.WebButton2Click(Sender: TObject);
begin
  DoMemoReset;
end;

procedure TForm1.WebMemo1Change(Sender: TObject);
begin
  DoMemoChange(ecmByUser);
end;

procedure TForm1.WebOpenDialog1Change(Sender: TObject);
begin
  DoDialogOpenFile;
end;

...

{Control blocks}

procedure TForm1.DoMemoReset;
begin
  webmemo1.Lines.Text := 'Text reset';
  DoMemoChange;   // ecmDoesntMatter as default mode
end;

procedure TForm1.DoShowOpenDialog;
begin
  WebOpenDialog1.accept := '.txt';
  WebOpenDialog1.Execute;
end;

procedure TForm1.DoDialogOpenFile;
begin
  WebOpenDialog1.Files[0].GetFileAsText(
    procedure (oTxt : string)
      begin
        webmemo1.Lines.Text := oTxt;
        DoMemoChange(ecmByCode);
      end
    );
end;

procedure TForm1.DoMemoChange(AMode: TEditChangeMode);
begin
  if AMode = ecmByCode then
    MessageDlg('Hey, user! We opened the file you selected!', mtInformation, [mbOk]);

  weblabel1.Caption := 
    WebMemo1.Lines.Text.CountChar(' ').ToString 
    + ' space(s) in your text'
    + ifthen(AMode = ecmByUser, ' (edited)', '');
end;

...
1 Like