简体   繁体   中英

Delphi Live binding, error converting text to TStrings field

Delphi 10.4

I have an issue bidirectional Tstrings binding. I have a simple app with 2 controls and a bind source. I am trying to bind bidirectionally a TStrings property of an object to to a TMemo control. I am using a TPrototypeBindSource with a TObjectBindSourceAdapter as the bind source adapter.

I have the lines property on the TPrototypeBindSource set to a ftTStrings fieldtype

在此处输入图像描述

TFoo is defined as;

  TFoo = class  (TObject)
  private
    Fname: string;
    flines: TStrings;
  published
    constructor Create;
    destructor destroy; override;
    property name: string read Fname write Fname;
    property lines: Tstrings read Flines write Flines;
  end;

The Adapter is set with the on create event

procedure TForm1.PrototypeBindSource1CreateAdapter(Sender: TObject; var ABindSourceAdapter: TBindSourceAdapter);
begin
  ABindSourceAdapter := TObjectBindSourceAdapter<TFoo>.Create(Self, GetFoo, False);
end;

Using live bindings I have the

PrototypeBindSource name fielddef <---> edit text field. PrototypeBindSource lines fielddef <---> memo text field.

在此处输入图像描述

All is good when you are reading or updating the name <-> edit control and when setting the memo text field. It recognises the lines field in the TPrototypeBindSource as being a TStrings.

However, when I change the text in the memo and go to post it back to the TPrototypeBindSource I get this exception

EBindConverterError: unable to cast or find converters between types string and TObject

There is a TStrings to string and string to TStrings converters registered. But there seems a disconnect between the TString in the object and the adapter as it cannot seem to see its type.

Have I missed something or is the RTL got issues?

EDIT:

Thanks, I missed that setter. Even with it fixed the issue remains...

  TFoo = class  (TObject)
    ...
    procedure SetLines(const aValue: TStrings);
    ...
  end;

procedure TFoo.SetLines(const Value: TStrings);
begin
  Flines.Assign(Value);
end;

The converters don't go through the setter they use the TStrings text property

system.bindings.outputs

class procedure TConverterUtils.StringToStrings(const I: TValue; var O: TValue);
var
  oOut: TStrings;
begin
  oOut := TStrings(O.AsObject);                                            
  if oOut <> nil then
  begin
    if I.IsEmpty then
      oOut.Text := ''
    else
      oOut.Text := I.AsString;
  end;
end;

Tracing through the binding code, the converters get called in here

procedure TBindingOutput.SetValue(AExpression: TObject; const ValueFunc: TBindOutValueFunc);

begin
...
  if AllowConverter then
    CanConvert := ValueConverter.CanConvert(LFinalOutVal.TypeInfo, LocationRec.Location.GetType);
...
end

Which calls;

function TValueRefConverter.CanConvert(AFrom, ATo: PTypeInfo): Boolean;
begin
  Result := GetConverter(AFrom, ATo, False) <> nil;
end;

When going from the TStrings property to the Text prop on the TMemo, the TypeInfo for the AFrom shows as a TStringlist, but when it is going back the other way the ATo PTypeInfo shows TObject not TStringList.

As the converters are registered for TStrings it cannot find the appropriate converter.

I got lost trying to work out where the info is stored.

If you take a look at TMemo source code you will see that Lines property is defined differently as your Lines property

property Lines: TStrings read FLines write SetLines;

As you can see TMemo lines property relies on the use of writer method instead of directly writing to FLines field as you do in your code.

In that SetLines writer method TStrings.Assign method is called.

procedure TCustomMemo.SetLines(Value: TStrings);
begin
  FLines.Assign(Value);
end;

Using this method a copy of the strings from source is made to its destination.

The way how you code is set you simply assign the reference of the source TLines object to your flines field making both objects to share same TLines object. This is extremely dangerous because if the source object gets destroyed your objects ends up referencing to a no longer existing data which could lead to a whole lot of problems.

Fix your Lines property to match the design from TMemo and this should solve your problem

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM