Мастера DELPHI, Delphi programming community Рейтинг@Mail.ru Титульная страница Поиск, карта сайта Написать письмо 
| Новости |
Новости сайта
Поиск |
Поиск по лучшим сайтам о Delphi
FAQ |
Огромная база часто задаваемых вопросов и, конечно же, ответы к ним ;)
Статьи |
Подборка статей на самые разные темы. Все о DELPHI
Книги |
Новинки книжного рынка
Новости VCL
Обзор свежих компонент со всего мира, по-русски!
|
| Форумы
Здесь вы можете задать свой вопрос и наверняка получите ответ
| ЧАТ |
Место для общения :)
Орешник |
Коллекция курьезных вопросов из форумов
KOL и MCK |
KOL и MCK - Компактные программы на Delphi
Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / rsdn.org

 
Чтобы не потерять эту дискуссию, сделайте закладку « предыдущая ветвь | форум | следующая ветвь »
Страницы: 1 2

Свойство типа TStrings [D7]


r00xus ©   (19.08.15 11:24

здравствуйте. пишу компонент TTSTPanel наследник TCustomPanel. он имеет свойство DropDownList для управление выпадающим списком. это свойство - отдельный класс с возможностью активировать или деактивировать выпадающим список через свойство Active, при установке в True на компоненте появляется TComboBox, и свойство для элементов самого TComboBox списка ItemList типа TStrings.

проблема заключается в том что если в design-time установить свойство DropDownList.Active = True и заполнить DropDownList.ItemList, то в FComboBox не попадают элементы списка из DropDownList.ItemList. Я посмотрел в отладчике при запуске приложения даже не срабатывает точка останова в методе TTSTDropDownList.SetItemList, но список как-то заполняется. Если установить в run-time DropDownList.Active = False, а потом DropDownList.Active = True, то список заполняется. как корректно заполнить TComboBox?

вот код компонента:

unit TSTPanel;

interface

uses
 Controls, Classes, ExtCtrls, StdCtrls;

type

 TTSTPanel = class;
 TTSTDropDownList = class;

 TTSTDropDownList = class(TPersistent)
 private
   FOwner : TTSTPanel;
   FActive: Boolean;
   FItemList: TStrings;
   procedure SetActive(const Value: Boolean);
   procedure SetItemList(const Value: TStrings);
   procedure UpdateItems;
 protected
   function GetOwner: TPersistent; override;
 public
   constructor Create(AOwner : TTSTPanel);
   destructor Destroy; override;
 published
   property Active : Boolean read FActive write SetActive;
   property ItemList : TStrings read FItemList write SetItemList;
 end;

 TTSTPanel = class(TCustomPanel)
 private
   FDropDownList: TTSTDropDownList;
   FComboBox : TComboBox;
   procedure SetDropDownList(const Value: TTSTDropDownList);
 public
   constructor Create(AOwner: TComponent); override;
 published
   property DropDownList : TTSTDropDownList read FDropDownList write SetDropDownList;
 end;

implementation

uses SysUtils;

{ TTSTPanel }

constructor TTSTPanel.Create(AOwner: TComponent);
begin
 inherited;
 FDropDownList := TTSTDropDownList.Create(Self);
end;

procedure TTSTPanel.SetDropDownList(const Value: TTSTDropDownList);
begin
 FDropDownList := Value;
end;

{ TTSTDropDownList }

constructor TTSTDropDownList.Create(AOwner: TTSTPanel);
begin
 FOwner := AOwner;
 FItemList := TStringList.Create;
end;

destructor TTSTDropDownList.Destroy;
begin
 FItemList.Free;
 inherited;
end;

function TTSTDropDownList.GetOwner: TPersistent;
begin
 Result := FOwner;
end;

procedure TTSTDropDownList.SetActive(const Value: Boolean);
begin
 FActive := Value;
 if FActive then
 begin
   FOwner.FComboBox := TComboBox.Create(FOwner);
   FOwner.FComboBox.Parent := FOwner;
   UpdateItems;
 end
 else
 begin
   if Assigned(FOwner.FComboBox) then
     FreeAndNil(FOwner.FComboBox);
 end;
end;

procedure TTSTDropDownList.SetItemList(const Value: TStrings);
begin
 FItemList.Assign(Value);
 if FActive then
   UpdateItems;
end;

procedure TTSTDropDownList.UpdateItems;
begin
 FOwner.FComboBox.Items.Assign(FItemList);
end;

end.


кгшзх ©   (19.08.15 11:41[1]

что если в design-time установить свойство DropDownList.Active = True и заполнить DropDownList.ItemList, то в FComboBox не попадают элементы списка из DropDownList.ItemList.

constructor TTSTDropDownList.Create(AOwner: TTSTPanel);
begin
FOwner := AOwner;
FItemList := TStringList.Create;
end;


Ну установили оунера, ну создали список.
Больше ничего не делали.
Зачем удивляешься что список пуст?


r00xus ©   (19.08.15 12:00[2]

это же всего лишь конструктор. если я устанавливаю свойство TTSTPanel.DropDownList.Active = True, то после выполнения конструктора сработает метод TTSTDropDownList.SetActive с параметром Value из DFM-файла формы и там должен заполниться мой список, но для этого должен сработать метод TTSTDropDownList.SetItemList для заполнения TStrings значениями из DFM-файла формы, но этого не происходит. Я не могу понять почему SetActive срабатывает, а SetItemList нет...


кгшзх ©   (19.08.15 12:03[3]

это же всего лишь конструктор.

Ну и чего?
Где лоадед?
Где дефолт для паблишед свойства?
Где спецификатор хранения в дфм для него же?

Сейчас у тебя выполняется конструктор.
В нем то делается то, что в нем делается.
Больше ничего не делается.
Вот и пустой твой список.


кгшзх ©   (19.08.15 12:06[4]

Я не могу понять почему SetActive срабатывает, а SetItemList нет...


а ты его вызвал? нет не вызвал.
а сам он как бы не умеет вызываться.


r00xus ©   (19.08.15 12:20[5]

ну я SetActive тоже не вызывал, но он же срабатывает автоматически, а почему тогда SetItemList не срабатывает?


r00xus ©   (19.08.15 12:22[6]

и что нужно сделать чтобы SetItemList срабатывал?


кгшзх ©   (19.08.15 12:31[7]

нужно читать доку по созданию классов с паблишед свойствами хранимыми в dfm [3]

а почему тогда SetItemList не срабатывает?
еще раз по-русски:
где оно у тебя вызывается, чтобы ты мог ожидать его "срабатывания"?

посмотри на оба свои конструктора.
в них все выполнилось что у тебя там накорябано?
все.

constructor TTSTPanel.Create(AOwner: TComponent);
begin
inherited;
FDropDownList := TTSTDropDownList.Create(Self);
end;

constructor TTSTDropDownList.Create(AOwner: TTSTPanel);
begin
FOwner := AOwner;
FItemList := TStringList.Create;
end;

списки создались? создались.
оунер присвоился? присвоился.
что-нибудь еще делалось? ничего не делалось.

лоадед был переопределен? не был.
список заполненный в дизайне куда-нибудь перекачивался? никем и никуда не перкачивался.


кгшзх ©   (19.08.15 12:34[8]

ну я SetActive тоже не вызывал,

а это кто делал:
Если установить в run-time DropDownList.Active = False, а потом DropDownList.Active = True,


r00xus ©   (19.08.15 12:49[9]

если в design-time установить DropDownList.Active = True, а в run-time ничего не делать, то метод SetActive вес равно срабатывает и записывает значение из DFM. это и навело на мысль что и SetItemList тоже должен сработать аналогично.

что нужно сделать чтобы он сработал? вы что-то пишите про "лоадед" где его нужно переопределить?


кгшзх ©   (19.08.15 12:50[10]

где его нужно переопределить?

а что, есть много вариантов где?
скажи сколько ты знаешь, я скажу в каком надо.


r00xus ©   (19.08.15 12:56[11]

Удалено модератором


кгшзх ©   (19.08.15 12:57[12]

это и навело на мысль что и SetItemList тоже должен сработать аналогично.

Он и срабатывает аналогично.
посмотри в свой дфм и увидишь, что на рантайме в нем ровно столько строк, сколько строк в дфм у TTSTPanel


кгшзх ©   (19.08.15 12:59[13]

я конечно понимаю что

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


DimaBr ©   (20.08.15 08:14[14]

По коду:
1. Свойство Active стоит раньше свойства ItemList, следовательно в DFM так и пишется, сначала Active затем ItemList. Что из этого следует ? А то, что при чтении из DFM сначала читается свойство Active, присваевается ему значение true, далее в методе SetActive идёт создание TComboBox и обновление списка UpdateItems. Но увы, в это момент времени ItemList пустой, так как ещё не считался из DFM.
Вывод: чтобы было что обновлять, нужно сначала считать список, а затем применять UpdateItems, то есть поменять Active и ItemList местами.
2. Принудительно обновление списка.
Как уже было замечено ранее можно использовать Loaded. Этот метод вызывается после загрузки всех свойств. Если в нём "дёрнуть" UpdateItems, то тоже список должен заполниться
procedure TTSTPanel.Loaded;
begin
 inherited Loaded;
 if DropDownList.Active then DropDownList.UpdateItems;
end;
end;


3. В коде FComboBox находится в TTSTPanel и везде по коду фигурирует такая конструкция FOwner.FComboBox. Может стоит переместить его в TTSTDropDownList ?

4. Метод TTSTPanel.SetDropDownList написан неверно. Он сработает тогда, когда вы присвоите значение DropDownList, например
begin
 ADropDown := TTSTDropDownList.Create;
 TSTPanel.DropDownList := ADropDown;
end;

И что в итоге получится? Указатель на внутренний DropDownList сотрётся и присвоится новый указатель. Нужно чтобы было так
procedure TTSTPanel.SetDropDownList(const Value: TTSTDropDownList);
begin
FDropDownList.Assign(Value);
end;

Вот в SetItemList сделано правильно, но вдобавок нужно повесить обработку на изменение списка
constructor TTSTDropDownList.Create(AOwner: TTSTPanel);
begin
FOwner := AOwner;
FItemList := TStringList.Create;
FItemList.OnChange := DoChange;
end;

procedure TTSTDropDownList.DoChange(Sender: TObject);
begin
 UpdateItems;
end;


5. Метод procedure UpdateItems лучше написать так
procedure TTSTDropDownList.UpdateItems;
begin
 if Assigned(FOwner.FComboBox) and Active then
   FOwner.FComboBox.Items.Assign(FItemList);
end;

тогда не нужно будет везде по коду реализовывать такие конструкции
if FActive then UpdateItems;


r00xus ©   (20.08.15 10:03[15]

Огромное спасибо за советы.

У меня есть еще вопросы:

1. Почему если я ставлю breakpoint-ы в методе SetActive и SetItemList то при запуске программы breakpoint срабатывает только в SetActive. Ведь оба значения этих свойств по идее грузятся из DFM если они не установлены в значения по умолчанию.
Насколько я понимаю так происходит всегда со свойствами тип которых не элементарный тип данных, а объект или не всегда?

2. Насколько я понял порядок загрузки свойств из DFM аналогичен алфавитному порядку их имен (я сделал свойство AA и оно загрузилось раньше Active). Возможно ли как-то изменить этот порядок? При определении свойства можно указать index, но это вроде не для этого. Как поменять порядок?

Заранее спасибо за ответ...


r00xus ©   (20.08.15 10:08[16]

По пункту 2 написал глупость. Свойства загружаются в порядке их следования в коде компонента. Остался вопрос 1.


кгшзх ©   (20.08.15 11:20[17]

SetItemList - метод TTSTDropDownList, а не панели.

Сам TTSTDropDownList создан на рантайме и никакого чтения из дфм для него не происходит и не должно происходить. Хотя бы потому, что его самого в дфм нету.


r00xus ©   (20.08.15 11:30[18]

Ну как это его нет в DFM???

object Form1: TForm1
 Left = 722
 Top = 317
 Width = 586
 Height = 388
 Caption = 'Form1'
 Color = clBtnFace
 Font.Charset = DEFAULT_CHARSET
 Font.Color = clWindowText
 Font.Height = -11
 Font.Name = 'MS Sans Serif'
 Font.Style = []
 OldCreateOrder = False
 PixelsPerInch = 96
 TextHeight = 13
 object TSTPanel1: TTSTPanel
   Left = 40
   Top = 24
   Width = 337
   Height = 129
   DropDownList.ItemList.Strings = (
     '1'
     '2'
     '3')
   DropDownList.Active = True
 end
end


вы код вообще читать умеете или только грубить?


кгшзх ©   (20.08.15 11:33[19]

Удалено модератором


Страницы: 1 2 версия для печати

Написать ответ

Ваше имя (регистрация  E-mail 







Разрешается использование тегов форматирования текста:
<b>жирный</b> <i>наклонный</i> <u>подчеркнутый</u>,
а для выделения текста программ, используйте <code> ... </code>
и не забывайте закрывать теги! </b></i></u></code> :)


Наверх

  Рейтинг@Mail.ru     Титульная страница Поиск, карта сайта Написать письмо