Почему моя реализация TCollections.CreateObservableList для интерфейсов не работает? - PullRequest
0 голосов
/ 23 мая 2019

Я пытался реализовать, как предложил @Stefan Glienke в Почему не генерируется событие SpringistDist IList OnChanged? ObservableList для интерфейсов.

Эта версия должна реализовывать наблюдаемый список интерфейсов. Компилируется, но не может найти обработчик события 'change' во время выполнения. Я изменил некоторые параметры процедуры с TObject на IInterface. Некоторые другие должны быть TObject из-за реализации в базовых классах.

Спасибо за любую помощь.

В этом примере показано поведение:

program Project62;

{$APPTYPE CONSOLE}

uses
  Spring,
  Spring.Events,
  Spring.Collections,
  Spring.Collections.Lists,
  SysUtils;

type
  // Like TObservableList but for interfaces not classes
  TObservableInterfaceList<T: IInterface> = class(TFoldedInterfaceList<T>, INotifyPropertyChanged)
  private
    fOnPropertyChanged: IEvent<TPropertyChangedEvent>;
    function GetOnPropertyChanged: IEvent<TPropertyChangedEvent>;
  protected
    // Sender must be TObject because of TPropertyChangedEvent
    procedure DoItemPropertyChanged(sender: TObject; const eventArgs: IPropertyChangedEventArgs);

    procedure DoPropertyChanged(const propertyName: string);
    procedure Changed(const value: IInterface; action: TCollectionChangedAction); override;
  public
    constructor Create; override;
    property OnPropertyChanged: IEvent<TPropertyChangedEvent> read GetOnPropertyChanged;
  end;

  TNotifyPropertyChangedBase = class(TInterfaceBase, INotifyPropertyChanged)
  private
    fOnPropertyChanged: Event<TPropertyChangedEvent>;
    function GetOnPropertyChanged: IPropertyChangedEvent;
  protected
    procedure PropertyChanged(const propertyName: string);
  end;

  IMyInterface = interface(IInterface)
    ['{D5966D7D-1F4D-4EA8-B196-CB9B39AF446E}']
    function GetName: String;
    procedure SetName(const Value: String);
    property Name: String read GetName write SetName;
  end;

  TMyInterfacedObject = class(TNotifyPropertyChangedBase, IMyInterface)
  private
    FName: string;
    function GetName: string;
    procedure SetName(const Value: string);
  public
    property Name: string read GetName write SetName;
  end;

  TMain = class
    procedure ListOfMyInterfaceChanged(Sender: TObject; const item: IMyInterface; action: TCollectionChangedAction);
  end;

constructor TObservableInterfaceList<T>.Create;
begin
  inherited Create;
  fOnPropertyChanged := TPropertyChangedEventImpl.Create;
end;

// Sender must be TObject because of TPropertyChangedEvent
procedure TObservableInterfaceList<T>.DoItemPropertyChanged(sender: TObject; const eventArgs: IPropertyChangedEventArgs);
var
  MyInterface: IMyInterface;
begin
  if Supports(sender, IMyInterface, MyInterface) then
    inherited Changed(MyInterface, caChanged);
end;

procedure TObservableInterfaceList<T>.DoPropertyChanged(const propertyName: string);
begin
  if Assigned(fOnPropertyChanged) and fOnPropertyChanged.CanInvoke then
    fOnPropertyChanged.Invoke(Self,
      TPropertyChangedEventArgs.Create(propertyName) as IPropertyChangedEventArgs);
end;

function TObservableInterfaceList<T>.GetOnPropertyChanged: IEvent<TPropertyChangedEvent>;
begin
  Result := fOnPropertyChanged;
end;

procedure TObservableInterfaceList<T>.Changed(const value: IInterface; action: TCollectionChangedAction);
var
  notifyPropertyChanged: INotifyPropertyChanged;
  propertyChanged: IEvent<TPropertyChangedEvent>; // TPropertyChangedEvent = procedure(Sender: TObject; const EventArgs: IPropertyChangedEventArgs) of object;

begin
  if Supports(value, INotifyPropertyChanged, notifyPropertyChanged) then
  begin
    propertyChanged := notifyPropertyChanged.OnPropertyChanged;
    case action of
      caAdded:
        propertyChanged.Add(DoItemPropertyChanged);
      caRemoved, caExtracted:
        propertyChanged.Remove(DoItemPropertyChanged);
    end;
  end;
   inherited Changed(value, action);
  DoPropertyChanged('Count');
end;

function TNotifyPropertyChangedBase.GetOnPropertyChanged: IPropertyChangedEvent;
begin
  Result := fOnPropertyChanged;
end;

procedure TNotifyPropertyChangedBase.PropertyChanged(const propertyName: string);
begin
  fOnPropertyChanged.Invoke(Self, TPropertyChangedEventArgs.Create(propertyName) as IPropertyChangedEventArgs);
end;

procedure TMyInterfacedObject.SetName(const Value: string);
begin
  FName := Value;
  PropertyChanged('Name');
end;

function TMyInterfacedObject.GetName: string;
begin
  Result := FName;
end;

procedure TMain.ListOfMyInterfaceChanged(Sender: TObject; const item: IMyInterface; action: TCollectionChangedAction);
begin
  case action of
    caAdded: Writeln('item added: ', item.Name);
    caRemoved, caExtracted: Writeln('item removed: ', item.Name);
    caChanged: Writeln('item changed: ', item.Name);
  end;
end;

var
  main: TMain;
  iListOfMyInterface: IList<IMyInterface>;
  MyInterfacedObject: TMyInterfacedObject;
begin
  iListOfMyInterface := TCollections.CreateInterfaceList<IMyInterface>;
  iListOfMyInterface.OnChanged.Add(main.ListOfMyInterfaceChanged);
  MyInterfacedObject := TMyInterfacedObject.Create;
  MyInterfacedObject.Name := 'MyInterfacedObject';
  iListOfMyInterface.Add(MyInterfacedObject);
  iListOfMyInterface.first.Name := 'MyInterfacedObject hit the change event';
  Readln;
end.

1 Ответ

1 голос
/ 23 мая 2019

Поскольку вы никогда не создаете TObservableInterfaceList<T> в своем коде, но:

iListOfMyInterface := TCollections.CreateInterfaceList<IMyInterface>;

правильно:

iListOfMyInterface := TObservableInterfaceList<IMyInterface>.Create;

Также ваша реализация DoItemPropertyChanged является дефектом. Здесь вы ссылаетесь на специальный тип интерфейса, но на самом деле вы должны передать sender как T, потому что это тип элемента lists.

Я рассмотрю возможность добавления этого из коробки для 2.0.

...