-
r00xus © (19.08.15 11:24) [0]здравствуйте. пишу компонент 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]Удалено модератором