Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2005.08.14;
Скачать: CL | DM;

Вниз

Динамическая форма в библиотечном модуле   Найти похожие ветки 

 
msguns ©   (2005-07-21 13:53) [0]

В продолжение ветки
http://delphimaster.net/view/1-1121773434/

Все же я пришел к выводу, что в библиотечном модуле целесообразно объявить класс формы, которую надо создавать и показывать при обращении к соответстующим процедурам/функциям библиотечного модуля. Там же прописывать все визуальные объекты и, главное, методы и евенты контролов. В этом случае я могу обращаться из этих методов друг к другу как к методам класса.
Например, в событии OnChange эдита легко можно активировать/деактивировать соотв.кнопки, по OnClick в листбоксе запросто вызвать метод поиска записи в датасете внешнего модуля, продеклариованный и описанный в этом же классе. Кроме того, становятся отовсюду доступны переменные как самой формы, так и всех "подотчетных" и продекларированных в ней контролов.

При реализации подобных действий способом из приведенной ссылки ("надув" компиллятора по рецепту ЮЗ) чрезвычайно геморный. Текст получается громоздкий и плохочитаемый.

Подчеркну, что имеется в виду не форма типа MessageBox, а достаточно "нагруженное" окно, предназначенное для универсального поиска юзером записей в произвольных датасетах (6 контролов типа чекбокс, едит, комбобокс, листбокс, несколько кнопок, куча лабелей. И все это лежит на "наполеоне" из панелей.

Прошу высказаться за либо против принятого мною решения.
С благодарностью.


 
Stakan ©   (2005-07-21 14:41) [1]

Я уже высказывался за такой подход в той ветке.
Всё получается красиво и никаких недокументированных приёмов.


 
msguns ©   (2005-07-21 15:00) [2]

>Stakan ©   (21.07.05 14:41) [1]

Т.е. "ЗА" ?

ЗЫ. Извиняюсь, что ветка попала в "Базы". ИМХО, было бы неплохо пересадить ее в "Основную"


 
evvcom ©   (2005-07-21 15:06) [3]

Возможно, что проще действительно создать класс формы. Я прибегаю к способу [5] из той ветки действительно в крайних случаях. Например, в последнем проекте:
В конструктор динамически создаваемой формы передается массив записей с типами и свойствами создаваемых на форме контролов. Для некоторых контролов важны дополнительные параметры из "его" записи. Вот ссылку на эту запись я и передаю через параметр Data в обработчик-процедуру. Получается в процедуре все очень наглядно, так как и имена и типы такие, какие мне удобны. Можно, конечно, то же самое сделать и через метод класса, а параметр через Tag передать, хотя тоже не в каждом объекте этот Tag имеется.


 
msguns ©   (2005-07-21 16:18) [4]

Прошу сильно не смеяться, я такого не делал еще, а Тексейры под рукой нет.
Код:

unit DBServ; // Библиотечный модуль

interface

Uses Forms, Classes, Controls, StdCtrls, ComCtrls, ExtCtrls, Graphics,
    Buttons, DB, Grids, Types, DBGrids, Dialogs;

type
 TDBServ_SearchDlgForm = class(TForm)
 // Контролы, которые должны быть созданы формой
 pnUserTop: TPanel;       // Панель пользователя
  lbColumnName: TLabel;
  cmbColumnList: TComboBox;
  lbPromptEnter: TLabel;
  edSample: TEdit;
 pnSearchOptions: TPanel; // Панель опций поиска
  lbSearchMode: TLabel;
  rbtSearchImdl: TRadioButton;
  rbtSearchFirst: TRadioButton;
  rbtSearchList: TRadioButton;
  lbSearchOptions: TLabel;
  chbSearchCaseIns: TCheckBox;
  lbSearchResume: TLabel;
  spbStartSearch: TSpeedButton;
  spbCloseForm: TSpeedButton;
 pnFindResult: TPanel;    // Панель результатов спискового поиска
  lbFindResult: TLabel;
  lsbFindList: TListBox;
  pnListStatus: TPanel;
 private
 public
//   constructor Create(AOwner: TComponent);
//   procedure FormShow(Sender: TObject);
 end;
 ...

type
 procedure DBServ_SearchRecordByUserDialog(Grid: TDBGrid);
 ...

implementation
...

procedure DBServ_SearchRecordByUserDialog(Grid: TDBGrid);
begin
 with TDBServ_SearchDlgForm.Create(Application) do
   begin
    ShowMessage("Форма создана") ;
//     ShowModal;
   end;
end;


При обращении к этой процедуре дохнет с сообщением о невозможности найти файл ресурсов.

Ради Бога, побейте ногами !


 
msguns ©   (2005-07-21 16:23) [5]

Теоретически я понимаю, что дельфя пытается создать на форме контролы, свойства которых (парент, местоположение и т.д.) должны находиться в .res. Но у меня и вся фишка в том, что нет и не может быть файла ресурсов ! Закомментарив все эти контролы и оставив только саму форму, получил тот же эффект.


 
alex_***   (2005-07-21 16:29) [6]

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


 
evvcom ©   (2005-07-21 16:34) [7]


> При обращении к этой процедуре дохнет с сообщением о невозможности
> найти файл ресурсов.

Правильно дохнет. У тебя исходники VCL есть? Если бы туда заглянул, то увидел бы, что в TCustomForm.Create есть такое:
 if not InitInheritedComponent(Self, TForm) then
   raise EResNotFound.CreateFmt(SResNotFound, [ClassName]);
в InitInheritedComponent вызывается InternalReadComponentRes до тех пор пока не дойдет до RootAncestor = TForm.

Либо создавай форму с ресурсами, либо наследуйся от TObject, например, и заведи поле класса TForm.


 
msguns ©   (2005-07-21 16:35) [8]

>alex_***   (21.07.05 16:29) [6]

Да я так и делаю сейчас. Только вот код страшный:

procedure DBServ_SearchRecordByUserDialog(Grid: TDBGrid);
// Создание окна поискового диалога
Const
 MinHeight: integer = 320;
 MinWidth: integer = 360;
var
 i: integer;
 SearchForm: TForm;
 SamplePanel,SearchPanel,SearchOptionPanel,FindListPanel: TPanel;
 LablPromptCol,SampleLabel,OptTitle1Label,OptTitle2Label,ListTitleLabel: TLabel;
 ColumnList: TComboBox;
 ListFind: TListBox;
 ListStatus: TPanel;
 OptRadioImdl, OptRadioFirst, OptRadioList: TRadioButton;
 OptCheckCase, OptCheckPartial: TCheckBox;
 SampleEdit: TEdit;
 GridActiveCell: TRect;
 MColChange, MEditChange: TMethod;
 MRadioButtonClick: TMethod;
 //---------------------------------------------------------------
 // Обработчики событий динамически созданных визуальных контролов
 //---------------------------------------------------------------
 procedure OnChangeColumn(Self: TDBGrid; Sender: TObject);
 var
   i: integer;
 begin
   //  Определить имя поля датасета по титулу выбранной колонки
   with Self, Sender as TComboBox do
   for i := 0 to Self.Columns.Count-1 do
     if (Self.Columns[i].Field.FieldKind=fkData) and
        (Self.Columns[i].Title.Caption=Items[Itemindex]) then
       begin
        // Сделать выбранную колонку текущей в гриде
        SelectedIndex := i;
        break;
       end;
 end;
 //---------------------------------------------------------------
 procedure OnChangeSample(Self: TDBGrid; Sender: TObject);
 begin
   if TEdit(Sender).Tag>0 then exit;
   with Self do
     DBServ_LocateRecord(DataSource.DataSet,SelectedField.FieldName,TEdit(Sender).Text);
 end;
 //---------------------------------------------------------------
 procedure OnClickRadioButton(Self: TEdit; Sender: TObject);
 begin
   if TRadioButton(Sender).Caption="Немедленный поиск" then Self.Tag := 0 else Self.Tag := 1;
 end;

begin
//  with TDBServ_SearchDlgForm.Create(Application) do
//    begin
//     ShowMessage("Форма создана") ;
//     ShowModal;
//    end;
//  exit;
 MColChange.Code := @OnChangeColumn;
 MColChange.Data := Grid;
 MEditChange.Code := @OnChangeSample;
 MEditChange.Data := Grid;
 MRadioButtonClick.Code := @OnClickRadioButton;
 // ******************* СОЗДАНИЕ ВИЗУАЛЬНЫХ КОНРОЛОВ ******************
 // Получить координаты ячейки
 GridActiveCell := TDBGridCrack(Grid).CellRect(TDBGridCrack(Grid).Col,TDBGridCrack(Grid).Row);
 // Создать форму
 SearchForm := TForm.Create(Grid.Owner);
 with SearchForm do
   begin
    BorderIcons := [biSystemMenu];
    BorderStyle := bsSingle;
    Height := MinHeight;    // 200
    Width := MinWidth;      // 360
    // Позиционировать форму
    if (Grid.ClientOrigin.X+GridActiveCell.Right+MinWidth)>(TForm(Grid.Owner).Left+TForm(Grid.Owner).Width) then
       // Слева от ячейки
       Left := Grid.ClientOrigin.X+GridActiveCell.Left-MinWidth
    else
       Left := Grid.ClientOrigin.X+GridActiveCell.Right;
    Top := TForm(Grid.Owner).Top+TForm(Grid.Owner).Height div 2 - Height div 2;
    Caption := "Поиск записей по значению колонки";
   end;
 // Создать панель пользователя
 SamplePanel := TPanel.Create(SearchForm);
 with SamplePanel do
   begin
    Parent := SearchForm; Align := alTop; BevelOuter := bvLowered; Height := 48;
   end;
 LablPromptCol := TLabel.Create(SearchForm);
 with LablPromptCol do
   begin
    Caption := "Поиск по колонке ";
    Left := 4;
    Top := 6;
    Parent := SamplePanel;
   end;
 // Создать комбобокс для списка колонок
 ColumnList := TCombobox.Create(SearchForm);
 with ColumnList do
   begin
    Top := 2;
    Parent := SamplePanel;
    Left := LablPromptCol.Left+LablPromptCol.Width+4;
    Width := MinWidth-Left-10;
    Sorted := false;
    // Заполнить список названиями колонок
    for i := 0 to Grid.Columns.Count-1 do
      if Grid.Columns[i].Field.FieldKind=fkData then
        begin
         Items.Add(Grid.Columns[i].Title.Caption);
         if Grid.Columns[i].Field=Grid.SelectedField then
         ItemIndex := i;   // Текущее поле
        end;
    OnChange := TNotifyEvent(MColChange);
   end;


 
msguns ©   (2005-07-21 16:37) [9]

 // Создать надпись перед шаблоном
 SampleLabel := TLabel.Create(SearchForm);
 with SampleLabel do
   begin
    Caption := "Маска поиска "; Left := 4; Parent := SamplePanel;
   end;
 // Создать эдит для ввода шаблона поиска
 SampleEdit := TEdit.Create(SearchForm);
 with SampleEdit do
   begin
    Top := ColumnList.Top+ColumnList.Height+1;
    Left := SampleLabel.Left+SampleLabel.Width+4;
    Width := MinWidth-Left-10;
    Text := Grid.SelectedField.AsString;
    OnChange := TNotifyEvent(MEditChange);
    Parent := SamplePanel;
   end;
 MRadioButtonClick.Data := SampleEdit;
 // Выровнять метку по высоте
 SampleLabel.Top := SampleEdit.Top+3; //(ColumnList.Top-LablPromptCol.Top);
 // Создать панель поиска
 SearchPanel := TPanel.Create(SearchForm);
 with SearchPanel do
   begin
    Parent := SearchForm; Align := alClient; BevelOuter := bvLowered;
   end;
 // Создать панель опций поиска
 SearchOptionPanel := TPanel.Create(SearchForm);
 with SearchOptionPanel do
   begin
    Parent := SearchPanel; Align := alLeft; BevelOuter := bvRaised; Width := 160;
   end;
 // Создать заголовок режимов
 OptTitle1Label := TLabel.Create(SearchForm);
 with OptTitle1Label do
   begin
    Caption := "Режимы поиска"; Parent := SearchOptionPanel;
    Font.Style := [fsbold]; Height := 20; //Layout := tlBottom;
    Align := alTop; Alignment := taCenter; Layout := tlCenter;
   end;
 // Создать радиобатоны переключения режимов поиска
 OptRadioImdl := TRadioButton.Create(SearchForm);
 OptRadioFirst := TRadioButton.Create(SearchForm);
 OptRadioList := TRadioButton.Create(SearchForm);
 with OptRadioImdl do
   begin
    Caption := "Немедленный поиск"; Parent := SearchOptionPanel;
    Left := 4; Width := TPanel(Parent).Width-Left-4;
    Top := OptTitle1Label.Top+OptTitle1Label.Height+2; Checked := true;
    OnClick := TNotifyEvent(MRadioButtonClick);
   end;
 with OptRadioFirst do
   begin
    Caption := "Поиск первой похожей"; Parent := SearchOptionPanel;
    Left := OptRadioImdl.Left; Width := OptRadioImdl.Width;
    Top := OptRadioImdl.Top+OptRadioImdl.Height+2; // Checked := false;
    OnClick := TNotifyEvent(MRadioButtonClick);
   end;
 with OptRadioList do
   begin
    Caption := "Поиск всех похожих"; Parent := SearchOptionPanel;
    Left := OptRadioImdl.Left; Width := OptRadioImdl.Width;
    Top := OptRadioFirst.Top+OptRadioFirst.Height+2; // Checked := false;
    OnClick := TNotifyEvent(MRadioButtonClick);
   end;
 // Создать заголовок опций
 OptTitle2Label := TLabel.Create(SearchForm);
 with OptTitle2Label do
   begin
    Caption := "Опции поиска"; Parent := SearchOptionPanel;
    Font.Style := [fsbold];
    Left := 2; Width := TPanel(Parent).Width-4;
    Top := OptRadioList.Top+OptRadioList.Height+8;
    Alignment := taCenter;
   end;


 
msguns ©   (2005-07-21 16:38) [10]

 // Создать чекбоксы опций
 OptCheckCase := TCheckBox.Create(SearchForm);
 OptCheckPartial := TCheckBox.Create(SearchForm);
 with OptCheckCase do
   begin
    Caption := "Игнорировать регистр"; Parent := SearchOptionPanel;
    Top := OptTitle2Label.Top+OptTitle2Label.Height+2;
    Left := 6; Width := TPanel(Parent).Width-8;
    Checked := true;
   end;
 with OptCheckPartial do
   begin
    Caption := "Полное совпадение"; Parent := SearchOptionPanel;
    Top := OptCheckCase.Top+OptCheckCase.Height+2;
    Left := 6; Width := TPanel(Parent).Width-8;
    Checked := true;
   end;
 // Создать панель со списком найденных
 FindListPanel := TPanel.Create(SearchForm);
 with FindListPanel do
   begin
    Parent := SearchPanel; Align := alClient; BevelOuter := bvRaised;
   end;
 // Создать надпись
 ListTitleLabel := TLabel.Create(SearchForm);
 with ListTitleLabel do
   begin
    Caption := "Результат поиска"; Parent := FindListPanel;
    Font := OptTitle1Label.Font; Align := alTop; Height := OptTitle1Label.Height;
    Alignment := OptTitle1Label.Alignment;
    Layout := OptTitle1Label.Layout;
   end;
 // Создать панель статуса
 ListStatus := TPanel.Create(SearchForm);
 with ListStatus do
   begin
    Parent := FindListPanel; Align := alBottom;
    Height := 18; BevelOuter := bvLowered;
    Alignment := taCenter; Caption := "  Запись 2 из 9999";
   end;
 // Создать список найденных
 ListFind := TListBox.Create(SearchForm);
 with ListFind do
   begin
    Parent := FindListPanel; Align := alClient;
    Color := TPanel(Parent).Color;
   end;

 SearchForm.ShowModal;
end;

function DBServ_LocateRecord(DataSet: TDataSet; FieldName,Sample: string;
        CaseIns: boolean = true; PartKey: boolean = true): boolean;
begin
 result := DataSet.Locate(FieldName,Sample,[loCaseInsensitive,loPartialKey]);
end;


 
msguns ©   (2005-07-21 16:40) [11]

Кроме того, непонятки с тем, как мне в процедурах - обработчиках событий обращаться к другим (не Sender`у) контролам и, главное, как выполнить процедуры, определенные внутри DBServ_SearchRecordByUserDialog


 
evvcom ©   (2005-07-21 16:47) [12]


> procedure OnChangeColumn(Self: TDBGrid; Sender: TObject);
>  var
>    i: integer;
>  begin
>    //  Определить имя поля датасета по титулу выбранной
> колонки
>    with Self, Sender as TComboBox do
>    for i := 0 to Self.Columns.Count-1 do
...
> =Items[Itemindex])

Ты опять "не читатель", ты опять "писатель"? Я ж писал тебе в той еще ветке. Замени, ведь нагляднее будет так:

procedure OnChangeColumn(DBGrid: TDBGrid; ComboBox: TComboBox);
...
for i := 0 to DBGrid.Columns.Count-1 do
...
= ComboBox.Items[ComboBox.Itemindex])

и мой совет, прекращай with использовать, код с ним менее читабелен, а отладка затруднительна. Вместо with Object1.Object2.Object3 лучше завести локальную переменную и явно ей присвоить эту байду, а потом к свойствам/методам обращаться уже через нее.


 
evvcom ©   (2005-07-21 16:52) [13]

Во, напостил! Одного я не пойму, чего тебе так обычная форма с ресурсом не нравится? Политические убеждения или вера не позволяет?


 
msguns ©   (2005-07-21 17:00) [14]

>evvcom ©   (21.07.05 16:52) [13]
>Одного я не пойму, чего тебе так обычная форма с ресурсом не нравится? Политические убеждения или вера не позволяет?

Это фрагмент библиотечного модуля, содержащего универсальные фичи работы с произвольным НД. К нему я буду обращаться из конкретных приложений с помощью uses.
Какие в нем могут быть ресурсы и формы ?
Работает же OpenDialog безо всяких визуальных компонент, самодостаточно.


 
stakan ©   (2005-07-21 17:03) [15]

unit FormUnit;

interface

procedure ShowUnitForm;

implementation

uses
 Forms, StdCtrls, Dialogs, Classes, Windows;

type
 TUnitForm = class(TForm)
 private
   Button: TButton;
   procedure ButtonClick(Sender: TObject);
 public
   constructor CreateNew(AOwner: TComponent); reintroduce;
 end;

procedure ShowUnitForm;
var
 Form: TUnitForm;
begin
 Form := TUnitForm.CreateNew(Application);
 try
   Form.Button := TButton.Create(Form);
   Form.Button.Parent := Form;
   Form.Button.OnClick := Form.ButtonClick;
   Form.ShowModal;
 finally
   Form.Free;
 end;
end;

{ TUnitForm }

procedure TUnitForm.ButtonClick(Sender: TObject);
begin
 ShowMessage("Banzai");
end;

constructor TUnitForm.CreateNew(AOwner: TComponent);
var
 NonClientMetrics: TNonClientMetrics;
begin
 inherited CreateNew(AOwner);
 NonClientMetrics.cbSize := sizeof(NonClientMetrics);
 if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NonClientMetrics, 0) then
   Font.Handle := CreateFontIndirect(NonClientMetrics.lfMessageFont);
end;

end.

Подсмотрено в Dialogs.pas


 
Stakan ©   (2005-07-21 17:07) [16]


>public
>    constructor CreateNew(AOwner: TComponent);
> reintroduce;

Лишнее.
Главное - это Form := TUnitForm.CreateNew(Application);


 
evvcom ©   (2005-07-22 08:23) [17]


> Какие в нем могут быть ресурсы и формы ?

Обычные. Чем тебе ресурсы-то не нравятся? Ну будет у тебя не один юнит, а dfm-ка еще с ним, чего плохого?

> Работает же OpenDialog безо всяких визуальных компонент,
> самодостаточно.

OpenDialog, насколько я помню, просто надстройка над API-шной функцией, чтобы параметры передать в стиле Delphi.



Страницы: 1 вся ветка

Текущий архив: 2005.08.14;
Скачать: CL | DM;

Наверх




Память: 0.54 MB
Время: 0.024 c
14-1121068350
msguns
2005-07-11 11:52
2005.08.14
Руслана в Запорожье


1-1121859509
pasha L
2005-07-20 15:38
2005.08.14
искусственно создать RXMemoryData


9-1114019137
Green_Templar
2005-04-20 21:45
2005.08.14
TDxInput в ДэлфиХ


4-1119458310
XCept.ion
2005-06-22 20:38
2005.08.14
Kейлоггинг


1-1122376796
MiSt
2005-07-26 15:19
2005.08.14
отображать eml файл при помощи Outlook