Установка свойства для нескольких объектов (RTTI#5)
Опубликовано 31.10.2011 г. 20:40
А у вас никогда не было желания написать что-нибудь вроде for c in [button1, button2, edit3] do c.enabled := false? Иногда, следуя логике поведения программы, нам необходимо, например, скрывать ряд компонентов, или делать их неактивными.
Такие ситуации могут встречаться достаточно часто, например, в режиме редактирования одни элементы активны, в противном случае нет. Либо какой-нибудь демо-режим программы, где вам нужно отключить некоторые функциональные возможности. Если элементы имеют одного родителя, то можно отключать его, но часто приходится писать код вида (не обращайте внимание на названия элементов):
button1.enabled := false; button2.enabled := false; edit1.enabled := false; ComboBox5.enabled := false;А затем, при наступлении какого то события, включать их обратно. Конечно, такие ситуации не так уж и часты, но согласитесь, встречаются. И каждый раз когда нужно одно временно отключить хотя бы два элемента, мне приходит в голову одна мысль: "ну почему же нельзя написать код вида.."
for c in [button1, button2, edit1, comboBox5] do c.enabled := false;Свойство enabled приведено для примера, хотя, наверное, все таки enabled & visible наиболее частые для подобных ситуаций. И пару дней назад, когда я ковырялся с утилитой настройки Outlook Social-провайдера для ВКонтакте, мне вдруг вспомнилась эта задача. Ведь на самом деле решить ее можно весьма просто, и механизм RTTI позволяет нам это сделать с наименьшими усилиями. Сразу родились 3 идеи решения, каждая из которых являлась улучшением предыдущей. Третья идея к сожалению не осуществилась, но и второй вариант на мой взгляд неплох. Следуя принципам ООП функционал для работы я заключил в запись TMultiProp, хотя можно было обойтись одной отдельной функцией (ибо класс имеет только 1 метод, да и в записи при вызове это было бы короче). Сразу напишу конечный результат. Вот пример обработчика нажатия кнопки, который отключает перечень других элементов формы, устанавливая в false значение свойства enabled.
procedure TForm1.Button1Click(Sender: TObject); begin TMultiProp.Objects([button1, button2, label1, comboBox1, RadioButton1] )['enabled'] := false; end;К минусам можно отнести то, что имя свойства указывается в виде строки, что может привести к ошибкам (правда легко выявляемым), но имя свойства остается регистро-независимо. Из представленного фрагмента кода можно сделать следующие выводы:
- Работа ведется с помощью структуры TMultiProp
- Данная структура имеет классовый метод Objects, название метода предполагает, что метод получает список объектов
- Параметром метода является открытый массив объектов
- Метод возвращает некоторую структуру, имеющую свойство по умолчанию в виде свойства-массива, с индексом строкой (именем устанавливаемого свойства объектов)
- значение свойства - TValue (раз уж мы упомянули о RTTI)
- Раз уж основной класс у нас только один, то возвращаемая структура является вложенным классом
TMultiProp = record strict private class var FData : array of TObject; public type TObjectProperty = record strict private procedure setValue(propName : string; value : TValue); public property Properties[propName :string ] :TValue write setValue; default; end; class function Objects(objects : array of TObject) : TObjectProperty; static; end;Метод Objects() копирует массив переданных указателей на объекты во внутреннюю переменную FData и возвращает структуру TObjectProperty. Дочерняя структура в свою очередь имеет доступ к private полям своего "родителя" (свойства и методы TObjectProperty тоже можно было определить как классовые).
class function TMultiProp.Objects(objects: array of TObject): TObjectProperty; var i : integer; begin setLength(FData, length(objects)); for i := 0 to high(objects) do FData[i] := objects[i]; end;Теперь дело за установкой значения свойств объектов с использованием RTTI. Сложного здесь ничего нет, для каждого объекта из переданного списка получить RTTI-описание его типа с помощью RTTI-контекста. Затем получить список свойств объекта, выбрать нужное, и установить его для выбранного экземпляра объекта.
procedure TMultiProp.TObjectProperty.setValue(propName: string; value: TValue); var ctx : TRttiContext; t : TRttiType; p : TRttiProperty; obj : TObject; begin ctx := TRttiContext.Create(); propName := LowerCase(propName); try for obj in FData do begin t := ctx.GetType(obj.ClassType); if t = nil then continue; for p in t.GetProperties() do begin if LowerCase(p.Name) <> propName then continue; p.SetValue(obj, value); end; end; finally ctx.Free(); FData := nil; end; end;В конце мы финализируем созданный ранее массив указателей на объекты. Конечно, в случае, если объекты (компоненты) не содержат информации об RTTI, такой подход работать не будет, а для использования такого класса вам понадобится Delphi 2010 и выше.
31.10.2011 в 22:53
пример вызова:
работает в д2007, а скорей всего и в более ранних.
02.11.2011 в 17:25
если мы захотим изменить свойство Caption для TButton & TLabel то уже не будет.
31.10.2011 в 22:31
02.11.2011 в 17:07
для данного свойства имхо тестирование весьма просто (:
01.11.2011 в 02:16
Наверное, можно также сделать сохранение/восстановление значений свойств контролов в массиве TValue:
02.11.2011 в 17:40
Create/Free освобождают все объекты созданные при работе с RTTI, например getPropertries() создает массив объектов TRttiProperties, и разрушаются они при вызове Free контекста.
01.11.2011 в 03:16
02.11.2011 в 18:52
02.11.2011 в 12:46
// for c in [button1, button2, edit1, comboBox5] do c.enabled := false;
а почему нельзя?? enabled/visible вроде в TControl объявлены?
Если да, то можно написать так:
for c in TArray.Create(button1, button2, edit1, comboBox5) do
c.Enabled := False;
02.11.2011 в 18:05
а тут можно любое свойство для любого наследника TObject.
зы: для массива еще в конец free нужен.
02.11.2011 в 16:28
Либо без объявления типа, но с дженериками:
02.11.2011 в 18:01
03.11.2011 в 10:40
Но хочется большего.
TMultiProp.Objects([button1, button2, label1, comboBox1, RadioButton1] )['enabled'] := false;
Каков принцип помещения компонента в список? А также механизм?
На уровне ощущений - в данном RTTI-подходе есть нечто большее, чем "компактирование" записи.
03.11.2011 в 10:30
ну можно подумать как можно что то подобное использовать для более практических целей, но чето хз (: