Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

События динамически создаваемых объектов [D6]


Страницы:   1 2

msguns ©   (2005-07-19 15:43:00) 

Извиняюсь за ламерство, но таких вещей просто не делал, а у Тексейры как-то вскользь об этом ;(
Библиотечный юнит создает форму и "кладет" на нее необходимые объекты. А вот как назначить обработку событий этих объектов - ума не приложу. В Dialog.pas это делается через промежуточный класс TMessageForm, можно ли обойтись без него ?

Код:

procedure DBServ_SearchRecordByUserDialog(Grid: TDBGrid);
// Создание окна поискового диалога
Const
 MinHeight: integer = 200;
 MinWidth: integer = 360;
var
 i: integer;
 SearchForm: TForm;
 SamplePanel: TPanel;
 LablPromptCol: TLabel;
 ColumnList: TComboBox;
begin
 // Создать форму
 SearchForm := TForm.Create(Grid.Owner);
 with SearchForm do
   begin
    BorderIcons := [biSystemMenu];
    BorderStyle := bsSingle;
    Position := poOwnerFormCenter;
    Height := MinHeight;
    Width := MinWidth;
    Caption := 'Поиск записей по значению колонки';
   end;
 // Создать панель пользователя
 SamplePanel := TPanel.Create(SearchForm);
 with SamplePanel do
   begin
    Parent := SearchForm;
    Align := alTop;
    BevelOuter := bvLowered;
    Height := 40;
   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 := ????
   end;
 SearchForm.ShowModal;
end;


Огромное спасибо всем откликнувшимся


evvcom   (2005-07-20 12:00:00) [1]


> with Self, Sender as TComboBox do

можешь написать
procedure OnChangeColumn(DBGrid: TDBGrid; ComboBox: TComboBox);
так красивее будет, а результат тот же - рабочий код.


msguns   (2005-07-20 11:55:00) [2]

>jack128 ©   (20.07.05 11:21) [28]
>..а создовать объект и передовать параметр, используя поле, ганзу лень, как я понимаю..

Ниче мне не лень. Если б я, блин, одним программингом занимался (см. http://www.delphimaster.ru/cgi-bin/forum.pl?id=1121844334&n=3) ;((
Запоздалая реакция вызвана инетом и долбаным ксерокопированием, а не моим упрямством или ленью.

И вообще, обиделся я. Губы надул, живот выпучил, глаза налились кровью, уши стали торчком, скулы обозначились, кулаки сжались ;)

Всем огромное спасибо ! Некрасиво кого-то выделять, но все же ЮЗ отдельный реверанс ;))


msguns   (2005-07-20 11:47:00) [3]

Сделал так, как советовали в [5] и [7]

procedure DBServ_SearchRecordByUserDialog(Grid: TDBGrid);
var
 ..
 MColChange: TMethod;

 procedure OnChangeColumn(Self: TDBGrid; Sender: TObject);
 var
   i: integer;
 begin
   //  Определить имя поля датасета по титулу выбранной колонки
   with Self, Sender as TComboBox do
    for i := 0 to Columns.Count-1 do
      if (Columns[i].Field.FieldKind=fkData) and
         (Columns[i].Title.Caption=Items[Itemindex]) then
        begin
         // Сделать выбранную колонку текущей в гриде
         SelectedIndex := i;
         break;
        end;
    Self.Invalidate;
 end;

begin
 MColChange.Code := @OnChangeColumn;
 MColChange.Data := Grid;
 ...
 // Создать комбобокс для списка колонок
 ColumnList := TCombobox.Create(SearchForm);
 with ColumnList do
   begin
    Top := 2;
    Parent := SamplePanel;
    ...  
    OnChange := TNotifyEvent(MColChange);
   end;
 SearchForm.ShowModal;
end;


AV позорно исчез. Все работает


evvcom   (2005-07-20 11:41:00) [4]


> ИМХО к этому следует прибегать в крайних случаях

Если не понимаешь, что делаешь, то к этому лучше совсем не прибегать.
А компилятор никто и не обманывал. :) Все написано на чистом паскале! :-))


Юрий Зотов   (2005-07-20 11:35:00) [5]

> msguns ©   (20.07.05 11:02) [22]

> Кстати, вывеску
> procedure OnChangeColumn(Sender: TObject);
> сменил на
> procedure OnChangeColumn(Self: TComboBox; Sender: TObject);

И правильно сделали. Но одного этого мало - надо еще этот самый Self инициализировать. Делается это так:

begin
 MColChange.Code := @OnChangeColumn;
 ...
 ColumnList := TCombobox.Create(SearchForm);
 with ColumnList do
 begin
   ...
    MColChange.Data := ColumnList; // Вот оно!!!
   OnChange := TNotifyEvent(MColChange);
 end;
end;


Stakan   (2005-07-20 11:30:00) [6]

evvcom ©   (20.07.05 11:27) [31]
Я бы не назвал этот путь более элегантным.
Это уже "обман компилятора", ИМХО к этому следует прибегать в крайних случаях


evvcom   (2005-07-20 11:27:00) [7]


> Stakan ©   (20.07.05 11:25) [29]

Так извращаться, когда есть более элегантный путь. :)


Stakan   (2005-07-20 11:26:00) [8]

jack128 ©   (20.07.05 11:21) [28]
Читаешь мои мысли :)


Stakan   (2005-07-20 11:25:00) [9]

evvcom ©   (20.07.05 11:19) [26]
Я думаю это можно победить.
Примерно так: создавать экземпляр класса (в конструкторе передавать grid и хранить в поле),
в обработчике использовать это поле (сделать его обычным методом).
Правда придётся продумать механизм уничтожения этого объекта, но я думаю что задача решаемая.


jack128   (2005-07-20 11:21:00) [10]

Stakan ©   (20.07.05 11:12) [25]
в классовый метод не передать дополнительного параметра, а создовать объект и передовать параметр, используя поле, ганзу лень, как я понимаю..


Digitman   (2005-07-20 11:20:00) [11]


> jack128 ©   (20.07.05 10:46) [20]
> в этом примере причина AV именно
> в том что функция локальная


нет, не в этом

procedure TForm1.Button2Click(Sender: TObject);
function CallBack(Wnd: THandle; Param: Integer): BOOL; stdcall;
var
  s: string;
begin
  SetLength(s, GetWindowTextLength(Wnd));
  SetLength(s, GetWindowText(Wnd, PChar(s), Length(s)));
  Form1.Memo1.Lines.Add(s);
  Result := True;
end;
begin
EnumWindows(@Callback, 0);
end;

как видишь, ф-ция как была локальной , так ей и осталось, однако теперь, когда я явно сослался на объект-форму (вместо неявного Self), все расчудесно работает


> msguns ©   (20.07.05 10:54) [21]


и у тебя все должно работать, когда self на место вернешь ..

procedure TForm1.Button1Click(Sender: TObject);
var
 M: TMethod;

 procedure DoClick( SelfEmulated,Sender: TObject);
 begin
   showmessage('Self is ' + SelfEmulated.ClassName + ', Sender is ' + Sender.ClassName);
 end;
begin
M.Code := @DoClick;
M.Data := Button2;
Button1.OnClick := TNotifyEvent(M);
end;


evvcom   (2005-07-20 11:19:00) [12]


> А чем не подходит вариант jack128 ©   (19.07.05 20:50) [12]

Туда Grid не передать.


Stakan   (2005-07-20 11:12:00) [13]

А чем не подходит вариант jack128 ©   (19.07.05 20:50) [12]
Никакого геморроя, всё по правилам.


evvcom   (2005-07-20 11:05:00) [14]


> Зачем нужен этот Self, если туда нельзя ничего запихать?

Можно, см. [5]. TMethod(ColumnList.OnChange).Data - это и есть тот Self


evvcom   (2005-07-20 11:03:00) [15]


> То, что ей нужен указатель на TDBGrid (переменная Grid),
> который я не могу передать параметром

Извини, но "чукча писатель, чукча не читатель" сейчас про тебя. :) Можешь передать, см. [5].

> сидит внутри DBServ_SearchRecordByUserDialog, где неявно
> (как параметр) объявлена переменная Grid, то можно юзать
> этот указатель и в ней

нельзя юзать этот указатель, см. [19]. ComboBox при вызове твоего OnChange и ведать не ведает, что в стек еще надо положить Grid, который ты ложишь в DBServ_SearchRecordByUserDialog. Отсюда и AV!


msguns   (2005-07-20 11:02:00) [16]

Кстати, вывеску
procedure OnChangeColumn(Sender: TObject);
сменил на
procedure OnChangeColumn(Self: TComboBox; Sender: TObject);

ничего не изменилось.
Попутно возникла еще одна неразрешимая (для таких вумных, как я)
трабла:
Зачем нужен этот Self, если туда нельзя ничего запихать ? Ведь эта процедура вызывается не мною, а классом TComboBox. Не перекрывать же событие ?


msguns   (2005-07-20 10:54:00) [17]

>jack128 ©   (20.07.05 10:13) [16]
>Если чесно ничего не понял. Что конкретно мешает сделать процедуру глобальной??
То, что ей нужен указатель на TDBGrid (переменная Grid), который я не могу передать параметром, т.к. обращение к ней по событию (NotifyEvent) с единственным параметром Sender: TObject
Если же эта процедура (я говорю о OnChangeColumn) сидит внутри DBServ_SearchRecordByUserDialog, где неявно (как параметр) объявлена переменная Grid, то можно юзать этот указатель и в ней.
Но вот это-то почему-то и не пашет ;((

>А вот например твоя функция DBServ_SearchRecordByUserDialog точно зависит от других функций и методов..

Нет. Не зависит. Она должна быть самодостаточной.


jack128   (2005-07-20 10:46:00) [18]

Хе, дошло причем тут глоб. переменная..

Digitman ©   (20.07.05 10:23) [18]
Не хочешь менять лок.ф-цию на глобальную - не меняй


Гм.. Ну покрайней мере в этом примере причина AV именно в том что функция локальная..

procedure TForm1.FormCreate(Sender: TObject);
 function CallBack(Wnd: THandle; Param: Integer): BOOL; stdcall;
 var
   s: string;
 begin
   SetLength(s, GetWindowTextLength(Wnd));
   SetLength(s, GetWindowText(Wnd, PChar(s), Length(s)));
   Memo1.Lines.Add(s);
   Result := True;
 end;
begin
 EnumWindows(@Callback, 0);
end;

В чем причина я разбираться не стал, но теперь не использую лок. функции в качестве калбеков..


evvcom   (2005-07-20 10:43:00) [19]


> Никаких проблем не возникнет.

Да, сейчас посмотрел CPU Window, код генерится нормальный для этого. Главное не использовать из этой локальной процедуры локальные переменные "родительской" процедуры.


Digitman   (2005-07-20 10:23:00) [20]


> msguns ©   (20.07.05 09:36) [15]


Не хочешь менять лок.ф-цию на глобальную - не меняй.
Никаких проблем не возникнет.
А проблема с AV именно потому что ты потерял Self


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо