Получить возможные и текущие значения из типа tkSet, используя RTTI - PullRequest
1 голос
/ 10 апреля 2019

Кто-то, возможно, уже задавал этот вопрос, но не нашел его, поэтому проблема в следующем:

Я хочу проанализировать свойства компонента tkSet (в нашем случае Panel1), но я не знаю, как это сделать правильно. Мне удалось найти базовый перечислимый тип набора с помощью rContext.FindType (), но я почти уверен, что есть более элегантный / простой способ сделать это. С этого момента я полностью потерян. Я должен разобраться со значениями этого перечислимого типа и проверить каждое значение по сравнению с текущим значением свойства компонента.

procedure TForm12.GetProperties2;
var
  rContext: TRttiContext;
  rType: TRttiType;
  rProp: TRttiProperty;
begin
  rType := rContext.GetType(Panel1.ClassType);

  for rProp in rType.GetProperties do
  begin
    if (rProp.Visibility in [mvPublished]) and (rProp.PropertyType.TypeKind in [tkSet]) and (rProp.Name = 'Anchors') then
    begin
      Memo1.Lines.Add('Name: ' + rProp.Name);
      Memo1.Lines.Add('PropertyType: ' + rProp.PropertyType.ToString);
      Memo1.Lines.Add('Value: ' + rProp.GetValue(Panel1).ToString);
      Memo1.Lines.Add('QualifiedName: ' + rProp.PropertyType.QualifiedName);
      Memo1.Lines.Add('ElementType: ' + rContext.FindType(rProp.PropertyType.QualifiedName).AsSet.ElementType.ToString);
      // here comes the desired results
      Memo1.Lines.Add('Possible values:');
      Memo1.Lines.Add(' 0 > akLeft');
      Memo1.Lines.Add(' 1 > akTop');
      Memo1.Lines.Add(' 2 > akRight');
      Memo1.Lines.Add(' 3 > akBottom');
      Memo1.Lines.Add('Present values:');
      Memo1.Lines.Add(' 0 > akLeft');
      Memo1.Lines.Add(' 1 > akTop');
      Memo1.Lines.Add('');
    end;
  end;
end;

Другая возможная проблема - это заданные свойства без базовых типов перечислений, например, если вы посмотрите на свойство TPanel.StyleElements, вы увидите, что объявление TStyleElements выглядит следующим образом:

TStyleElements = set of (seFont, seClient, seBorder);

В этом случае ElementType не работает.

Итак, вопрос в том, как я могу проанализировать свойства типа tkSet, чтобы получить желаемые результаты, используя контекст RTTI?

1 Ответ

3 голосов
/ 12 апреля 2019

Это довольно легко, используя базовый TypInfo.

procedure PrintSet(const v: TValue); // v contains a value from a set type
var
  enumType: PTypeInfo;
  enumData: PTypeData;
  buffer: set of Byte; // biggest possible set type
  i: Integer;
begin
  buffer := [];
  v.ExtractRawData(@buffer);
  enumType := v.TypeInfo.TypeData.CompType^;
  enumData := enumType.TypeData;
  for i := enumData.MinValue to enumData.MaxValue do
    Writeln(GetEnumName(enumType, i) + ' = ' + (i in buffer).ToString(TUseBoolStrs.True));
end;

A set of Byte - это самый большой возможный тип набора, поэтому мы можем использовать его в качестве буфера, где все поместится, а затем использовать TValue.ExtractRawDataспособ записать в него столько данных, сколько имеет фактический тип набора.Все остальное было обнулено, если установить его пустым.

Затем мы можем использовать данные типа enum для получения значений min и max.Поскольку несмежные перечислимые типы не имеют typeinfo, нам не нужно заботиться об этом, и фактически они обрабатывают только те из них, которые двоично совместимы с классическими битовыми масками.

...