Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C++ Builder > Динамическое создание компонентов


Автор: fenix666 7.7.2010, 11:23
Есть xml файл с набором компонентов, с определенными для них свойствами. Необходимо создать эти компоненты на форме. Пробовал следующим образом, не получилось:
Код

RegisterClass(__classid(TButton));
TClass cls = GetClass("TButton");/ 
Tobject *obj = cls->InitInstance(SysGetMem(cls->InstanceSize(cls) ));
SetPropValue(obj,"Caption","Test");

Память выделяется, проверяю obj->ClassName() возвращает 'TButton'. Но при вызове функции SetPropValue выходит ошибка EAccessViolation.

Может кто знает как все-таки динамически создать объект нужного типа

Автор: UniBomb 7.7.2010, 11:44
Код

TButton *btn = new TButton(Form1);
btn->Caption = "test";

Автор: fenix666 7.7.2010, 13:08
Цитата(UniBomb @ 7.7.2010,  11:44)
Код

TButton *btn = new TButton(Form1);
btn->Caption = "test";

Так я и сам знаю, но этот метод не подходит, т.к. если на форме тока кнопки то да, а если там около 20 разных компонентов, для каждого описывать создание не удобно, хочется универсальную функцию написать

Автор: borisbn 7.7.2010, 17:35
есть http://www.cnpack.org, который позволяет набросать и настроить визуально компоненты на форме, а потом он генерит код.
Можно ещё собственный парсер *.dfm файлов написать ( в принципе, ничего сложного )

Автор: fenix666 8.7.2010, 06:54
Цитата(borisbn @ 7.7.2010,  17:35)
есть http://www.cnpack.org, который позволяет набросать и настроить визуально компоненты на форме, а потом он генерит код.
Можно ещё собственный парсер *.dfm файлов написать ( в принципе, ничего сложного )

Спасибо за совет, но парсер отпадает, т.к. в билдере тока основную форму в редакторе далаю, а все остальные генерирую. Генератор кода тоже не вариант, т.к. смысл хранить код для 4-8 форм, если они могу и не вызваться%) Сомневаюсь что все так извращаются и пишут сотни строк ненужного кода. Проще функцию написать, для создания формы по заранее написанному конфигу. 

Автор: mrbrooks 8.7.2010, 07:47
интересно, а как будет анализироваться конфиг, если парсер не годится? За счет белой магии?

Автор: borisbn 8.7.2010, 07:47
Редактор форм в билдере есть не что иное как генератор этих самых
> заранее написанных конфигов
Я ж тебе и предлагаю сделать парсер этих конфигов (dfm), а включать cpp, h  и dfm в проект необязательно

Добавлено через 13 минут и 36 секунд
Не хочу нарываться на hollywar, но в http://qt.nokia.com это уже сделано с помощью http://doc.qt.nokia.com/4.6/quiloader.html. Посмотри в эту сторону.

Автор: xvr 8.7.2010, 10:11
А почему бы не воспользоваться системой стриминга самого Builder'а. Создать поток с формой (.dfm или бинарной), а потом через TStream::ReadComponent(TForm*)

Автор: Alexeis 8.7.2010, 10:27
fenix666, приведенный способ чистой воды хак, поскольку не вызывается конструктор объекта жди сюрпризов.

Можно воспользоваться недокументированной особенностью  объекта TApplication. Дело в том, что класс TApplication довольно безопасный и при создании формы внимательно проверяет что созданный объект строго является формой. Однако до создания объекта он не может выяснить что за метакласс ему подсунули и в результате он может запросто создать любого наследника TComponent.

Например:

Код

    TControl *cntl;
    RegisterClass(__classid(TButton));
    Application->CreateForm(__classid(TButton), &cntl);
    cntl->Parent = this;
    SetPropValue(cntl, L"Left", 50);
    SetPropValue(cntl, L"Top", 50);
    SetStrProp(cntl, L"Caption", L"Test");


Есть еще правильный вариант. Написать Паскалевский модуль, в котором будет всего одна функция, которая честно реализует функцию CreateComponent по всем правилам. И добавить этот модуль в проект на билдере. 
  Не ясно почему авторы билдера до сих пор не написали такую необходимую в билдере функцию. Видимо, как обычно, все внимание на мэинстрим.

Автор: fenix666 8.7.2010, 10:36
Цитата(borisbn @ 8.7.2010,  07:47)
Я ж тебе и предлагаю сделать парсер этих конфигов (dfm), а включать cpp, h  и dfm в проект необязательно

Даже если парсер dfm написать, то возникает опятьже вопрос, как всетаки создать динамически компонент(контрол)? 
Конструкцию типа Tclass *cls = new Tclass() не подходит, нужно чтото универсально, чтобы для любого класса создавалось

Автор: borisbn 8.7.2010, 11:02
fenix666, почему именно 
Цитата(fenix666 @  8.7.2010,  10:36 Найти цитируемый пост)
Tclass *cls = new Tclass()

в dfm содержится указание типа класса
Цитата

object MainForm: TMainForm
...
  object Label1: TLabel
...
  end
end

ну и делай
Код

if ( className == "TMainForm" )
{
    form = new TMainForm;
}
else if ( className == "TLabel" )
{
    label = new TLabel;
}

хотя, предыдущие советы по TStream::ReadComponent, Application->CreateForm и CreateComponent проще реализовать

Автор: fenix666 8.7.2010, 11:22
Цитата(Alexeis @ 8.7.2010,  10:27)
Есть еще правильный вариант. Написать Паскалевский модуль, в котором будет всего одна функция, которая честно реализует функцию CreateComponent по всем правилам. 

Нельзя ли поподробнее?

Автор: Alexeis 8.7.2010, 12:02
Цитата(fenix666 @  8.7.2010,  10:22 Найти цитируемый пост)
Нельзя ли поподробнее? 

Код

unit VCLCreate;

interface

uses classes;

function CreateComponent(cl : TComponentClass; AOwner : TComponent) : TComponent;

implementation

function CreateComponent(cl : TComponentClass; AOwner : TComponent) : TComponent;
begin
  Result := cl.Create(AOwner);
end;

end.


Код

#include "VCLCreate.hpp"

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    TControl *cntl;
    RegisterClass(__classid(TButton));
  //    Application->CreateForm(__classid(TButton), &cntl);
    cntl = dynamic_cast<TControl*>(CreateComponent(__classid(TButton), this));
    cntl->Parent = this;
    SetPropValue(cntl, L"Left", 50);
    SetPropValue(cntl, L"Top", 50);
    SetStrProp(cntl, L"Caption", L"Test");
}

Автор: Vyacheslav 9.7.2010, 15:23
Код

////////////////////////////////////////////////////////
// Создание компонента по его метаклассу: 
//   (Из форума RSDN) 

TComponent *CreateComponent( TComponentClass CClass, TComponent *AOwner)
{
  typedef TComponent *( __fastcall *TConstructor)( TComponentClass, bool, TComponent *); 
  TConstructor constructor = ((TConstructor *) CClass)[ vmtCreateObject - 1]; 
 return constructor( CClass, true, AOwner);
};  



Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)