![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
События динамически создаваемых объектов [D6]
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