Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2005.08.07;
Скачать: [xml.tar.bz2];

Вниз

События динамически создаваемых объектов   Найти похожие ветки 

 
msguns ©   (2005-07-19 15:43) [0]

Извиняюсь за ламерство, но таких вещей просто не делал, а у Тексейры как-то вскользь об этом ;(
Библиотечный юнит создает форму и "кладет" на нее необходимые объекты. А вот как назначить обработку событий этих объектов - ума не приложу. В 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;


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


 
Юрий Зотов ©   (2005-07-19 15:53) [1]

http://www.delphikingdom.com/asp/viewitem.asp?catalogid=342

Снимет все вопросы по этой теме.


 
msguns ©   (2005-07-19 16:17) [2]

Эту статью перечитал еще раз (место про события). Однако в приведенном примере есть форма, у которой определяется соответствующее событие:

Procedure TForm1.OnClickButton( Sender : TObject );
Var Value : String;
Begin    
   MessageDlg("Нажата кнопка "+TControl(Sender).Name ,mtInformation,[mbOk],0);
End;


А затем указатель на это событие класса-формы присваивается событию OnClick батона.
У меня же юнит. Формы в дизайне нет. Получается, что мне надо ее прописать в интерфейсе модуля ?
Если не трудно, покажите, как это делается


 
Stakan ©   (2005-07-19 16:37) [3]

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


 
ZlDoc ©   (2005-07-19 16:50) [4]

А что если на событие повесить процедуру не относящуюся ни к одному классу. Сам не проверял, но советую попробовать.
А прописать ее в уните можно вставив описание до implementation.


 
evvcom ©   (2005-07-19 16:51) [5]


procedure EmulateMethodOnChange(Self: Pointer; Sender: TComboBox);
begin
 ...
end;

var
 l_pMyData: Pointer;
...
with TMethod(ColumnList.OnChange) do begin
 Code := @EmulateMethodOnChange;
 Data := l_pMyData;
end;

в качестве Self можешь передать любые дополнительные данные l_pMyData. Имена и типы в EmulateMethodOnChange можешь подставлять какие хочешь, главное, чтобы количество параметров соответствовало ColumnList.OnChange (вроде ты знаешь, что первым параметром, если не хакать, неявно Self передается). Но знай, что в качестве Sender туда в твоем случае ColumnList передастся.


 
evvcom ©   (2005-07-19 16:52) [6]


> А прописать ее в уните можно вставив описание до implementation

без разницы где, главное, чтобы она была видна в месте упоминания/присвоения.


 
Юрий Зотов ©   (2005-07-19 16:59) [7]

> msguns ©   (19.07.05 16:17) [2]

Обработчиком события OnChange может быть метод любого класса, имеющий единственный параметр Sender: TObject.

Любого класса. Хоть формы, хоть не формы, хоть главной, хоть не главной.

Поэтому, если у Вас есть любой собственный класс, то можно ввести такой метод в него. А если нет, то компилятор можно "обмануть" и использовать обычную процедуру:

procedure ColumnListChange(Self: TComboBox; Sender: TObject);
begin
 ShowMessage(Self.ClasName)
end;

var
 M: TMethod;
...
 M.Code := @ColumnListChange;
 M.Data := ColumnList;
 ColumnList.OnChange := TNotifyEvent(M);


 
msguns ©   (2005-07-19 18:25) [8]

>Юрий Зотов ©   (19.07.05 16:59) [7]

Спасибо. То, что нужно ;)


 
msguns ©   (2005-07-19 19:27) [9]

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

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

begin
 MColChange.Code := @OnChangeColumn;
 ..
 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;
end;


При изменении в комбобоксе AV в выделенном коде (не видит Grid)

Где грабли ?


 
AlexWlad ©   (2005-07-19 19:40) [10]

Юрий Зотов ©   (19.07.05 16:59) [7]
>Обработчиком события OnChange может быть метод любого класса, имеющий единственный параметр Sender: TObject.

Метод д.б. в секции PRIVATE.


 
jack128 ©   (2005-07-19 20:47) [11]

msguns ©   (19.07.05 19:27) [9]
Сравни
msguns ©   (19.07.05 19:27) [9]
procedure OnChangeColumn(Sender: TObject);

и
Юрий Зотов ©   (19.07.05 16:59) [7]
procedure ColumnListChange(Self: TComboBox; Sender: TObject);


ps Возможно из-за того чтоо функция локальная возникнут проблемы, лудше сделай её глобальной..


 
jack128 ©   (2005-07-19 20:50) [12]

И вообще, что вы привезались к этим хакам с приведением к Tmethod

type
TEventHandlers = class
  class procedure ChangeColumnHandler(Sender: TObject);
end;


class procedure TEventHandlers.ChangeColumnHandler(Sender: TObject);
begin
 ShowMessage("Test");
end;

...
ColumnList.OnChange := TEventHandlers.ChangeColumnHandler;
...


 
ЮЮ ©   (2005-07-20 03:28) [13]

я тоже не вижу никакого объявления Grid, а только его использование :)


 
Юрий Зотов ©   (2005-07-20 04:43) [14]

> AlexWlad ©   (19.07.05 19:40) [10]
> Ю Метод д.б. в секции PRIVATE.
Извините, но даже спорить не стану.

> msguns ©   (19.07.05 19:27) [9]
Мое ИМХО совпадает с ИМХО Жени - для начала надо попробовать вынести внутреннюю процедуру наружу. Дальше будет видно. Скорее всего, ошибка просто исчезнет.

> ЮЮ ©   (20.07.05 03:28) [13]
Grid объявлен в параметрах. Это тоже объявление.


 
msguns ©   (2005-07-20 09:36) [15]

>Юрий Зотов ©   (20.07.05 04:43) [14]
>Мое ИМХО совпадает с ИМХО Жени - для начала надо попробовать вынести внутреннюю процедуру наружу. Дальше будет видно. Скорее всего, ошибка просто исчезнет.

Плохо. Во-первых, все это у меня в библиотечном модуле, реализующем набор сервисных функций для определенного класса датасетов, написанном с целью унификации кодинга и каждая функция как бы отдельная и независимая ни от других функций (процедур), ни от самого модуля  в целом. Т.е. как бы самодостаточа. Чтобы "передать" фунции-"обработчику"  ссылку на грид, необходимо воспользоваться глобальной переменной, чего мне делать не хочется (следуя стилю Тексейры).
Видимо, без объявления класса вроде TMessageDialog из модуля Dialogs для инкапсуляции всего, что нужно для этой формы, не обойтись.

Или я неправ ?


 
jack128 ©   (2005-07-20 10:13) [16]

Если чесно ничего не понял. Что конкретно мешает сделать процедуру глобальной??  И причем здесь глобальные переменные?

msguns ©   (20.07.05 9:36) [15]
как бы отдельная и независимая ни от других функций (процедур),


Гм.. Это как ?? Не, ну если у тебя в функциях используются только ОПЕРАТОРЫ... А вот например твоя функция DBServ_SearchRecordByUserDialog точно зависит от других функций и методов..


 
evvcom ©   (2005-07-20 10:17) [17]


> как бы отдельная и независимая ни от других функций (процедур),
> ни от самого модуля  в целом.

Это что, обязательное условие? Нельзя себя так ограничивать. Что мешает procedure OnChangeColumn вынести чуть выше procedure DBServ_SearchRecordByUserDialog? Даже в interface ее не нужно прописывать. И будут они в паре самодостаточны.

> Чтобы "передать" фунции-"обработчику"  ссылку на грид, необходимо
> воспользоваться глобальной переменной

Зачем? В Sender у тебя будет ComboBox, а в Self (см. [5], что похоже ты игнорировал) можешь положить свой Grid.


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


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


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


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


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

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


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

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

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;

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


 
msguns ©   (2005-07-20 10:54) [21]

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

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

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


 
msguns ©   (2005-07-20 11:02) [22]

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

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


 
evvcom ©   (2005-07-20 11:03) [23]


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

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

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

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


 
evvcom ©   (2005-07-20 11:05) [24]


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

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


 
Stakan ©   (2005-07-20 11:12) [25]

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


 
evvcom ©   (2005-07-20 11:19) [26]


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

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


 
Digitman ©   (2005-07-20 11:20) [27]


> 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;


 
jack128 ©   (2005-07-20 11:21) [28]

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


 
Stakan ©   (2005-07-20 11:25) [29]

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


 
Stakan ©   (2005-07-20 11:26) [30]

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


 
evvcom ©   (2005-07-20 11:27) [31]


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

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


 
Stakan ©   (2005-07-20 11:30) [32]

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


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

> 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;


 
evvcom ©   (2005-07-20 11:41) [34]


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

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


 
msguns ©   (2005-07-20 11:47) [35]

Сделал так, как советовали в [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 позорно исчез. Все работает


 
msguns ©   (2005-07-20 11:55) [36]

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

Ниче мне не лень. Если б я, блин, одним программингом занимался (см.http://delphimaster.net/view/15-1121844334/) ;((
Запоздалая реакция вызвана инетом и долбаным ксерокопированием, а не моим упрямством или ленью.

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

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


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


> with Self, Sender as TComboBox do

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



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

Форум: "Основная";
Текущий архив: 2005.08.07;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.63 MB
Время: 0.049 c
6-1114766778
Filin8
2005-04-29 13:26
2005.08.07
TcpServer


4-1118150125
СССР
2005-06-07 17:15
2005.08.07
как прочитать информацию ATIP ?


4-1118237156
KonstantinXaker(KX)
2005-06-08 17:25
2005.08.07
Найти значение в строке и заменить его


1-1121778080
Shooroop
2005-07-19 17:01
2005.08.07
Как определить из какого пункта меню запущена форма?


1-1121770761
td
2005-07-19 14:59
2005.08.07
как скопировать текст из нескольких текстовых файлов в один?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский