В процессе разработки приложений на Delphi иногда возникает необходимость использовать свойства типа Variant. Однако, как выяснилось, стандартный инспектор свойств (Object Inspector) в Embarcadero RAD Studio 12 не всегда корректно работает с этим типом данных. В этой статье мы рассмотрим проблему и предложим несколько решений для её устранения.
Описание проблемы
Вот пример определения компонента с свойством CheckedValue типа Variant:
TTestComponent = class(TComponent)
private
FCheckedValue: Variant;
function GetCheckedValue: Variant;
function IsCheckedValueStored: Boolean;
procedure SetCheckedValue(const Value: Variant);
procedure CheckedValueChanged();
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property CheckedValue: Variant read GetCheckedValue write SetCheckedValue stored IsCheckedValueStored;
end;
Когда вы пытаетесь установить новое значение для этого свойства в режиме Design-Time, вы сталкиваетесь с ошибкой: «Could not convert variant». В выпадающем списке типов система не позволяет выбрать новый тип, и при попытке ввести новое значение появляется ошибка «Could not convert variant».
Причина проблемы
Проблема заключается в том, что стандартные редакторы свойств в режиме Design-Time не полностью поддерживают тип Variant. Это связано с тем, что Variant является динамическим типом данных, который может хранить значения различных типов, и стандартные редакторы не могут корректно обрабатывать такие динамические типы.
Решение проблемы
Для устранения этой проблемы можно внести небольшие изменения в метод SetCheckedValue и функцию .IsCheckedValueStored, чтобы убедиться, что типы Variant совпадают перед сравнением их значений. Также можно добавить обработку для строковых значений, чтобы убедиться, что они имеют правильный тип.
Вот исправленный код:
procedure TTestComponent.SetCheckedValue(const Value: Variant);
begin
if (VarType(FCheckedValue) <> VarType(Value)) or // Добавляем проверку на совпадение типов
(not VarSameValue(FCheckedValue, Value)) then
begin
FCheckedValue := Value;
if VarType(FCheckedValue) = varString then
FCheckedValue := VarToStr(Value); // Изменяем тип на varUString для строковых значений
CheckedValueChanged();
end;
end;
function TTestComponent.IsCheckedValueStored: Boolean;
begin
Result := (VarType(FCheckedValue) <> varBoolean) or // Добавляем проверку на тип
(not VarSameValue(FCheckedValue, True));
end;
Дополнительное решение: создание кастомного редактора свойств
Если стандартные редакторы свойств всё ещё не корректно обрабатывают свойство Variant, можно создать кастомный редактор свойств. Это позволит вам полностью контролировать процесс установки и отображения значений свойств.
Вот пример создания кастомного редактора свойств для типа Variant:
uses
Variants, DesignIntf, DesignEditors, DesignConst;
{ TMyVariantTypeProperty }
const
VarTypeNames: array[varEmpty..varInt64] of string = (
'Unassigned', // varEmpty
'Null', // varNull
'Smallint', // varSmallint
'Integer', // varInteger
'Single', // varSingle
'Double', // varDouble
'Currency', // varCurrency
'Date', // varDate
'OleStr', // varOleStr
'', // varDispatch
'', // varError
'Boolean', // varBoolean
'', // varVariant
'', // varUnknown
'', // [varDecimal]
'', // [undefined]
'Shortint', // varShortInt
'Byte', // varByte
'Word', // varWord
'LongWord', // varLongWord
'Int64'); // varInt64
type
TMyVariantTypeProperty = class(TNestedProperty)
public
function AllEqual: Boolean; override;
function GetAttributes: TPropertyAttributes; override;
function GetName: string; override;
function GetValue: string; override;
procedure GetValues(Proc: TGetStrProc); override;
procedure SetValue(const Value: string); override;
end;
function TMyVariantTypeProperty.AllEqual: Boolean;
var
i: Integer;
V1, V2: Variant;
begin
Result := False;
if PropCount > 1 then
begin
V1 := GetVarValue;
for i := 1 to PropCount - 1 do
begin
V2 := GetVarValueAt(i);
if VarType(V1) <> VarType(V2) then Exit;
end;
end;
Result := True;
end;
function TMyVariantTypeProperty.GetAttributes: TPropertyAttributes;
begin
Result := [paMultiSelect, paValueList, paSortList];
end;
function TMyVariantTypeProperty.GetName: string;
begin
Result := 'Type';
end;
function TMyVariantTypeProperty.GetValue: string;
begin
case VarType(GetVarValue) and varTypeMask of
Low(VarTypeNames)..High(VarTypeNames):
Result := VarTypeNames[VarType(GetVarValue) and varTypeMask];
varString, varUString: // Исправляем для строковых значений
Result := SString;
else
Result := SUnknown;
end;
end;
procedure TMyVariantTypeProperty.GetValues(Proc: TGetStrProc);
var
i: Integer;
begin
for i := 0 to High(VarTypeNames) do
if VarTypeNames[i] <> '' then
Proc(VarTypeNames[i]);
Proc(SString);
end;
procedure TMyVariantTypeProperty.SetValue(const Value: string);
function GetSelectedType: Integer;
var
i: Integer;
begin
Result := -1;
for i := 0 to High(VarTypeNames) do
if VarTypeNames[i] = Value then
begin
Result := i;
break;
end;
if (Result = -1) and (Value = SString) then
Result := varUString; // Исправляем для строковых значений
end;
var
NewType: Integer;
V: Variant;
begin
NewType := GetSelectedType;
case NewType of
varEmpty: VarClear(V);
varNull: V := NULL;
-1: raise EDesignPropertyError.CreateRes(@SUnknownType);
else
V := GetVarValue; // Перемещаем здесь для надежности
try
VarCast(V, V, NewType);
except
{ Если не удалось привести к типу, очищаем и пытаемся снова }
VarClear(V);
VarCast(V, V, NewType);
end;
end;
SetVarValue(V);
end;
{ TMyVariantProperty }
type
TMyVariantProperty = class(TVariantProperty)
procedure GetProperties(Proc: TGetPropProc); override;
end;
procedure TMyVariantProperty.GetProperties(Proc: TGetPropProc);
begin
Proc(TMyVariantTypeProperty.Create(Self));
end;
procedure Register;
begin
// Изменяем 2-й и 3-й параметры, если хотите использовать этот редактор для всех свойств типа Variant
RegisterPropertyEditor(TypeInfo(Variant), TTestComponent, 'CheckedValue', TMyVariantProperty);
end;
Вывод
Использование свойств типа Variant в Delphi может быть полезным, но требует внимательного подхода, особенно в режиме Design-Time. Стандартные редакторы свойств не всегда корректно работают с этим типом данных, поэтому рекомендуется вносить необходимые изменения в методы SetCheckedValue и IsCheckedValueStored, а также создавать кастомные редакторы свойств, если это необходимо. Эти решения помогут вам избежать ошибок и обеспечат корректную работу с типом Variant в вашем проекте.
Статья описывает проблему некорректной работы стандартного инспектора свойств в Delphi с типом данных Variant и предлагает решения, включая исправление кода и создание кастомного редактора свойств.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.