Конференция "Компоненты" » Свойство типа TStrings [D7]
 
  • 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]
    Удалено модератором
  • кгшзх © (20.08.15 11:36) [20]
    раз:
    property DropDownList : TTSTDropDownList read FDropDownList write SetDropDownList;

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


    какая магия при чтении панели из дфм должна вызвать SetItemList, если ты сам этого не сделал?
  • кгшзх © (20.08.15 11:37) [21]
    Удалено модератором
  • r00xus © (20.08.15 11:42) [22]
    а какая магия тогда вызывает SetActive????? который тоже метод TTSTDropDownList скопируй код поставь breakpoint в SetActive и программа там остановиться. а в SetItemList нет. объясни это гений блин :)
  • кгшзх © (20.08.15 11:46) [23]
    Удалено модератором
  • r00xus © (20.08.15 11:52) [24]
    TTSTDropDownList = class(TPersistent)
    ...
    published
      property Active : Boolean read FActive write SetActive;
      property ItemList : TStrings read FItemList write SetItemList;
    end;

    гений, обрати пожалуйста внимание, что и Active и ItemList это published свойства TTSTDropDownList а не панели :)))))
  • кгшзх © (20.08.15 13:03) [25]
    Ты наконец уже обрати внимание, умник, что в дфм лежат компоненты формы.
    а на форме лежит панель, а не твой драный дропдаунлист.
    он всего лишь свойство у панели.

    вот если бы на форме  лежал сам TTSTDropDownList тогда и был бы тебе вызов SetItemList при загрузке из дфм.

    хотя кому я это говорю.....
  • r00xus © (20.08.15 13:20) [26]
    Удалено модератором
  • кгшзх © (20.08.15 13:54) [27]
    procedure TTSTDropDownList.SetActive(const Value: Boolean);
    begin
    ShowMessage('вызван TTSTDropDownList.SetActive(' + VarToStr(Value) + ')'#13#10'Список итемов:' + ItemList.Text);

    ShowMessage('А появится он только после того, как отработает Loaded панели. Но дятлам  это пофик. Они из чайлд-класса хотят рулить заполнением списков парента. Не имея представления ни о чем');

    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;

  • кгшзх © (20.08.15 13:57) [28]
    причем в примере выше все паблишед уже переставлены в "правильном" порядке

    published
      property ItemList : TStrings read FItemList write SetItemList;
      property Active : Boolean read FActive write SetActive default False;
  • кгшзх © (20.08.15 14:08) [29]
    TTSTPanel = class(TCustomPanel)
    .....
    procedure Loaded; override;
    ...

    procedure TTSTPanel.Loaded;
    begin
    inherited;
    ShowMessage('Вызван TTSTPanel.Loaded'#13#10'Списочек из дизайнтайм-редактора' + FDropDownList.ItemList.Text + #13#10'А теперь, осёл, запусти еще раз и на бумажке запиши последовательность появления всех мессаджбоксов');

    end;
  • DimaBr © (20.08.15 15:29) [30]

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

    Потому что вы не читали то что я вам написал, особенно пункт № 4. Методы SET для классов присваиваются при НАЗНАЧЕНИИ свойствам новых ВНЕШНИХ классов.
    Если я в коде напишу так, то сработает SetItemList
    var SL: TStringList;
    begin
     SL := TStringList.Create;
     TSTPanel.DropDownList.ItemList := SL;




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

    Загрузки из DFM происходит в том порядке, в котором в DFM хранятся данные. А вот сохраняются они туда в том порядке, в котором расположены в описании.
    TTest = class(TComponent)
     published
        property AAA: integer;
        property BBB: string;
    end;
    // в DFM сохранится свойство ААА, а за ним свойство BBB

    TTest = class(TComponent)
     published
        property BBB: string;
        property AAA: integer;
    end;
    // в DFM сохранится свойство BBB, а за ним свойство AAA

  • имя (20.10.15 17:50) [31]
    Удалено модератором
  • имя (20.10.15 19:16) [32]
    Удалено модератором
  • имя (20.10.15 20:14) [33]
    Удалено модератором
 
Конференция "Компоненты" » Свойство типа TStrings [D7]
Есть новые Нет новых   [134427   +34][b:0][p:0.004]