Recent

Author Topic: TTaskDialog bug?  (Read 1756 times)

howardpc

  • Hero Member
  • *****
  • Posts: 3042
TTaskDialog bug?
« on: February 28, 2019, 12:58:28 pm »
Anyone understand why the following code fails when setting the TTaskDialog.Buttons property?
Is this a bug in the LCL?
Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   SysUtils, Forms, Controls, Dialogs;
  9.  
  10. type
  11.  
  12.   TForm1 = class(TForm)
  13.     procedure FormCreate(Sender: TObject);
  14.   private
  15.     FTaskDialog: TTaskDialog;
  16.     FButtons: TTaskDialogButtons;
  17.     function ExecuteTaskDialog: Boolean;
  18.   end;
  19.  
  20. var
  21.   Form1: TForm1;
  22.  
  23. implementation
  24.  
  25. {$R *.lfm}
  26.  
  27. procedure TForm1.FormCreate(Sender: TObject);
  28. begin
  29.   case ExecuteTaskDialog of
  30.     True:  ShowMessage('TaskDialog returned True');
  31.     False: ShowMessage('TaskDialog returned False');
  32.   end;
  33. end;
  34.  
  35. function TForm1.ExecuteTaskDialog: Boolean;
  36. begin
  37.   FTaskDialog := TTaskDialog.Create(Self);
  38.   FButtons := TTaskDialogButtons.Create(Self, TTaskDialogButtonItem);
  39.   with FButtons.Add do
  40.     begin
  41.       Caption := 'Button 1';
  42.       ModalResult := mrOK;
  43.     end;
  44.   with FButtons.Add do
  45.     begin
  46.       Caption := 'Button 2';
  47.       ModalResult := mrCancel;
  48.     end;
  49.   with FTaskDialog do
  50.     begin
  51.       Caption := 'Test TaskDialog';
  52.       Title := 'Title';
  53.       Text := 'Test text'#10'line 2';
  54.       MainIcon := tdiInformation;
  55.       CommonButtons := [];
  56.       Buttons := FButtons;
  57.     end;
  58.   Result := FTaskDialog.Execute;
  59. end;
  60.  
  61. end.

lucamar

  • Hero Member
  • *****
  • Posts: 1808
Re: TTaskDialog bug?
« Reply #1 on: February 28, 2019, 01:09:58 pm »
I'm not completely sure but try with:
Code: Pascal  [Select]
  1. Buttons.Assign(FButtons);
instead of
Code: Pascal  [Select]
  1. Buttons := FButtons;
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 1.8.4 & 2.0.2 w/FPC 3.0.4 on:
(K|L)Ubuntu 12..16, Windows XP SP3, various DOSes.

GetMem

  • Hero Member
  • *****
  • Posts: 3467
Re: TTaskDialog bug?
« Reply #2 on: February 28, 2019, 01:27:45 pm »
Instead of:
Code: Pascal  [Select]
  1. FButtons := TTaskDialogButtons.Create(Self, TTaskDialogButtonItem);
You need:
Code: Pascal  [Select]
  1. FButtons := TTaskDialogButtons.Create(FTaskDialog, TTaskDialogButtonItem);

But I don't see a good reason to create FButtons separately. Perhaps the following code will suffice:
Code: Pascal  [Select]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.   case ExecuteTaskDialog of
  4.     mrOk:  ShowMessage('TaskDialog returned True');
  5.     mrCancel: ShowMessage('TaskDialog returned False');
  6.   end;
  7. end;
  8.  
  9. function TForm1.ExecuteTaskDialog: TModalResult;
  10. var
  11.   I: Integer;
  12. begin
  13.   FTaskDialog := TTaskDialog.Create(Self);
  14.   with FTaskDialog do
  15.     begin
  16.       Caption := 'Test TaskDialog';
  17.       Title := 'Title';
  18.       Text := 'Test text'#10'line 2';
  19.       MainIcon := tdiInformation;
  20.       CommonButtons := [];
  21.       Buttons.Clear;
  22.       with Buttons.Add do
  23.       begin
  24.         Caption := 'Button 1';
  25.         ModalResult := mrOK;
  26.       end;
  27.       with Buttons.Add do
  28.       begin
  29.         Caption := 'Button 2';
  30.         ModalResult := mrCancel;
  31.       end;
  32.     end;
  33.     FTaskDialog.Execute;
  34.     Result := FTaskDialog.ModalResult;
  35. end;
« Last Edit: February 28, 2019, 01:34:18 pm by GetMem »

howardpc

  • Hero Member
  • *****
  • Posts: 3042
Re: TTaskDialog bug?
« Reply #3 on: February 28, 2019, 01:48:02 pm »
@lucamar
Assign is what the internal setter code uses, so it is already called by setting the property.
@GetMem
You are right, of course, about the buttons' owner needing to be the dialog and not the form.However, changing the owner to the correct entity produces another exception  "Cannot assign a TTaskDialogButtonItem to a TTaskDialogButtonItem"
which is somewhat paradoxical compiler complaint.
The code you show does work (this is what the wiki example shows). But why does setting the property directly fail?

GetMem

  • Hero Member
  • *****
  • Posts: 3467
Re: TTaskDialog bug?
« Reply #4 on: February 28, 2019, 02:56:26 pm »
The exception occures inside fpc(collect.inc). The following line fails:
Code: Pascal  [Select]
  1. procedure TCollection.BeginUpdate;
  2. begin
  3.   inc(FUpdateCount); //<--this one
  4. end;
I have no idea why. The error message is indeed confusing.

howardpc

  • Hero Member
  • *****
  • Posts: 3042
Re: TTaskDialog bug?
« Reply #5 on: February 28, 2019, 08:17:08 pm »
@GetMem
I think the exception you found was some other probably irrelevant artefact.
Thinking about the strange "can't Assign X to X" error message, I wondered if TTaskDialogButtons had been provided with a customised overridden Assign method. Investigation showed it has not (at least not in a recent trunk).
I tried writing one and, hey presto, my patched Lazarus accepts a valid TTaskDialogButtons property assignment, and the executed taskdialog sports the correct buttons. I think TTaskDialogButtons needs an overridden public Assign method added:
Code: Pascal  [Select]
  1. procedure Assign(Source: TPersistent); override;
and the implementation should be something like this
Code: Pascal  [Select]
  1. procedure TTaskDialogButtons.Assign(Source: TPersistent);
  2. var
  3.   tdbs: TTaskDialogButtons absolute Source;
  4.   i: Integer;
  5.   tdb: TTaskDialogBaseButtonItem;
  6. begin
  7.   if (Source is TTaskDialogButtons) then
  8.     begin
  9.       BeginUpdate;
  10.       try
  11.         Clear;
  12.         for i := 0 to tdbs.Count - 1 do
  13.           begin
  14.             tdb := Self.Add;
  15.             tdb.Caption := tdbs.Items[i].Caption;
  16.             tdb.ModalResult := tdbs.Items[i].ModalResult;
  17.             tdb.Default := tdbs.Items[i].Default;
  18.           end;
  19.         DefaultButton := tdbs.DefaultButton;
  20.       finally
  21.         EndUpdate;
  22.       end;
  23.     end
  24.   else inherited Assign(Source);
  25. end;

jamie

  • Hero Member
  • *****
  • Posts: 1656
Re: TTaskDialog bug?
« Reply #6 on: February 28, 2019, 11:18:54 pm »
Why not use the already property "Buttons" in the TaskDialog?

What you are doing is the same thing but you are separating it from the dialog..

TaskDialog.Buttons.Add…..

howardpc

  • Hero Member
  • *****
  • Posts: 3042
Re: TTaskDialog bug?
« Reply #7 on: March 01, 2019, 09:12:07 am »
You repeat the point made by GetMem earlier.
My question in the thread title arises because IMO no LCL class should offer a property which when set in a valid way gives an immediate exception.

Ondrej Pokorny

  • Full Member
  • ***
  • Posts: 214
Re: TTaskDialog bug?
« Reply #8 on: March 01, 2019, 09:41:01 pm »
It's always a matter of discussion what a valid way is.

But yes, you are right. The limitation is not necessary. Delete the FClient/Client from TTaskDialogBaseButtonItem and add Assign methods to both TTaskDialogButtons and TTaskDialogBaseButtonItem if you want.

(Important: use "tdb.Assign(tdbs.Items[ i ]" instead of "tdb.Caption := tdbs.Items[ i ].Caption;" etc.)

howardpc

  • Hero Member
  • *****
  • Posts: 3042
Re: TTaskDialog bug?
« Reply #9 on: March 01, 2019, 10:46:27 pm »
Yes, validity is not an absolute criterion which is always self-evident.
I suppose I had in mind the usual assignment pattern
Code: Pascal  [Select]
  1. SomeObject.SomeProperty := SomeValidValue;
which surprises when it leads to an exception.
It is clearly an issue which other LCL maintainers have come across, without always having a solution to satisfy everyone.

For instance, in ExtCtrls we see beside the declaration of TNotebook:
Code: Pascal  [Select]
  1.     property ActivePage: String read GetActivePage;// write SetActivePage; // should not be published because the read can raise an exception
  2.     property ActivePageComponent: TPage read GetActivePageComponent;// write SetActivePage; // should not be published because the read can raise an exception


An unrelated question about TTaskDialog:
Why is its ModalResult property not read-only?
Surely whatever value you assign to it is lost?

lucamar

  • Hero Member
  • *****
  • Posts: 1808
Re: TTaskDialog bug?
« Reply #10 on: March 01, 2019, 11:54:18 pm »
An unrelated question about TTaskDialog:
Why is its ModalResult property not read-only?
Surely whatever value you assign to it is lost?

I haven't looked to the source so I may be wrong but it may be to be able to get some value after Execute no matter how you exit the dialog. Remember that, unlike most other dialogs, it can have buttons that don't set ModalResult automatically.
« Last Edit: March 01, 2019, 11:57:06 pm by lucamar »
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 1.8.4 & 2.0.2 w/FPC 3.0.4 on:
(K|L)Ubuntu 12..16, Windows XP SP3, various DOSes.

Ondrej Pokorny

  • Full Member
  • ***
  • Posts: 214
Re: TTaskDialog bug?
« Reply #11 on: March 02, 2019, 08:34:59 am »
Yes, validity is not an absolute criterion which is always self-evident.
I suppose I had in mind the usual assignment pattern
Code: Pascal  [Select]
  1. SomeObject.SomeProperty := SomeValidValue;
which surprises when it leads to an exception.

Yes, you are absolutely right here, sorry. The Buttons have a setter, so it is valid to assign them from an external object, there is no doubt about that.


An unrelated question about TTaskDialog:
Why is its ModalResult property not read-only?
Surely whatever value you assign to it is lost?

I don't know. But it's Delphi-compatible: http://docwiki.embarcadero.com/Libraries/Rio/en/Vcl.Dialogs.TCustomTaskDialog.ModalResult