Возможно перехватить чтение существующей опубликованной собственности - PullRequest
1 голос
/ 14 мая 2019

У меня есть компонент с опубликованным свойством. Вообразите это следующим образом:

  TSomething = (sOne, sTwo, sThree, ...);
  TSomethings = set of TSomething;

  TSomeComponent = TWinControl
  private
    fSomethings: TSomethings;
    function GetSomethings: string;
    procedure SetSomethings(const Value: TSomethings);
  published
    property Somethings: TSomethings read GetSomethings write SetSomethings;
  end;

Проблема в том, что TSomething теперь на максимальной мощности для опубликованного набора (ошибка компилятора E2187), поэтому мне нужно разделить набор следующим образом:

  TSomething = (sTwo, sThree, ...);
  TSomethings = set of TSomething;

  // Some items moved from TSomething and named with an "o" prefix
  TOtherThing = (oOne, oThirtyThree, oThirtyFour, ...);
  TOtherThings = set of TOtherThing;

  TSomeComponent = TWinControl
  private
    fSomethings: TSomethings;
    fOtherThings: TOtherThings;
    function GetSomethings: string;
    procedure SetSomethings(const Value: TSomethings);
    function GetOtherThings: string;
    procedure SetOtherThings(const Value: TOtherThings);
  published
    property Somethings: TSomethings read GetSomethings write SetSomethings;
    property OtherThings: TOtherThings read GetOtherThings write SetOtherThings;
  end;

Некоторые из существующих элементов в TSomethings были перемещены в TOtherThings (логически классифицировали их), поэтому некоторые пользователи откроют свою форму с ошибкой:

Ошибка чтения SomeComponent1.Somethings: Неверное значение свойства

Так что я хотел бы молча обрабатывать изменения при открытии формы. Другими словами, перехватите чтение свойства Somethings в DFM и заполните оба свойства Somethings и OtherThings.

Но я не могу найти способ сделать это. Переопределение DefineProperties должно сработать, если я захочу полностью переименовать старое свойство, но это излишне нарушит большую часть кода (лечение хуже, чем болезнь).

Так есть ли другой способ, который я упустил из виду?

Похожие вопросы:

Ответы [ 2 ]

4 голосов
/ 14 мая 2019

Вам не нужно переименовывать существующие свойства, просто сделайте их public вместо published, чтобы отключить для них встроенную потоковую передачу DFM, не нарушая никакого пользовательского кода, а затем переопределите DefineProperties() для обработки потоковой передачи DFM длясвойства по мере необходимости.

Если вам нужно поддерживать поддержку во время разработки, вы можете попробовать создать «поддельные» свойства для отображения Инспектором объектов, чтобы пользователи могли редактировать значения открытых свойствпо мере необходимости.Или, скорее всего, было бы проще просто написать собственный редактор компонентов, который отображал бы модальную форму для редактирования значений, используя TCheckListBox или TListView с флажками.

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

Итак, у меня было опубликованное свойство SomeThings, в котором было более 32 элементов, которые необходимо было разделить (или не опубликовать).Я решил логически разделить существующие элементы (классифицировать их), что усложнило процесс, если бы я хотел минимизировать испорченный код для существующих пользователей.

Итак, я создаю два новых свойства, например TheseThings, OtherThings.

Вместо того чтобы удалять старое свойство SomeThings, я изменил его с опубликованного на общедоступный, чтобы существующий пользовательский код не сломался, если бы он назначил (большинство) старых свойств ему во время выполнения.

Для того, чтобычтобы избежать ошибки при открытии формы, содержащей элементы SomeThings, я переопределил DefineProperties (), который корректно переместил элементы в свойства TheseThings или OtherThings.

procedure TMyUnit.ReadSomeThings(Reader: TReader);
  {}
  function ReadSet(): string;
  var
    EnumName: string;
  begin
    Result := '';
    try
      if Reader.ReadValue <> vaSet then
        raise Exception.Create('Not a set');
      while True do
      begin
        EnumName := Reader.ReadStr;
        if EnumName = '' then Break;
        Result := Result + EnumName + ',';
      end;
    except
      while Reader.ReadStr <> '' do begin end;
      raise;
    end;
  end;
  {}
  function NextWord(var P: PAnsiChar): AnsiString;
  var
    I: Integer;
  begin
    I := 0;
    while not (P[I] in [',', ' ', #0,']']) do
      Inc(I);
    SetString(Result, P, I);
    while P[I] in [',', ' ',']'] do
      Inc(I);
    Inc(P, I);
  end;
  {}
var
  s: AnsiString;
  P: PAnsiChar;
  enumName: AnsiString;
  ttSet: TTheseThings;
  otSet: TOtherThings;
begin
  s := AnsiString( ReadSet() );
  if s = '' then
    Exit;

  otSet := [];
  ttSet := [];

  P := PAnsiChar( s );

  enumName := NextWord(P);
  while enumName <> '' do
  begin
    // Convert item names of OtherThings property to newer names (changed prefix)
    if SameText( enumName, 'sTwo' ) then
      otSet := otSet + [oTwo ]
    else
    if SameText( enumName, 'sThree' ) then
      otSet := otSet + [ oThree ]
    else
...

   // Exiting items of TTheseThings have same name as old TSomeThings to minimize breakage
    else
    if SameText( enumName, 'sOne' ) then
      ttSet := ttSet + [ sOne ]
    else
    if SameText( enumName, 'sFour' ) then
      ttSet := ttSet + [ sFour ]
    else
...

    enumName := NextWord(P);
  end;

  if ttSet <> [] then
    SetTheseThings(ttSet);

  if otSet <> [] then
    SetOtherThings(otSet);
end;

procedure TMyUnit.DefineProperties(Filer: TFiler);
begin
  inherited;
  Filer.DefineProperty( 'SomeThings', ReadSomeThings, nil, False );
end;

Примечание. В исходном коде использовался GetEnumValue ();преобразовать enumName в перечислитель, но он постоянно давал мне искаженные заданные значения, поэтому я выбрал это более простое (но более многословное) решение

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...