Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Не срабатывает TEdit.OnChange


Автор: chikistbuch 19.10.2013, 15:52
Всем привет. Изменение данных в ячейках DBGrid'а делаю через обычный TEdit, чтобы контролировать что вводит пользователь. ChangeCell Вызывается при двойном нажатии на ячейку DBGrid'а. События описанные в Edit.OnKeyPress, Edit.OnExit хорошо работают, а вот Edit.OnChange не обрабатывается. Т.е. всё что написано в EditChange попросту не выполняется. Чё за нах??
Код

procedure TForm1.ChangeCell(Sender: TObject); //прорисовывает EDIT для измнения значений в DBGrid
var
  Edit: TEdit;
begin
  Edit:=TEdit.Create(TDBGrid(Sender));
  Edit.Name:='DBEdit';
  Edit.Top:=DBSelectedCellRect.Top-2;
  Edit.Left:=DBSelectedCellRect.Left;
  Edit.Width:=DBSelectedCellRect.Right - DBSelectedCellRect.Left;
  Edit.Text:=TDBGrid(Sender).SelectedField.AsString;//Заполняем edit
  Edit.OnChange:=EditChange;
  Edit.OnKeyPress:=EditKeyPress;
  Edit.OnExit:=EditExit;
  Edit.Parent:=TDBGrid(Sender);
  Edit.SetFocus;
end;


На всякий случай:
Код

procedure TForm1.EditChange(Sender: TObject);
var
  i: word;
  value, typ: string;
begin
  typ:=getAS(TDBGrid(TEdit(Sender).Parent).SelectedField.FieldName, 4);
  if typ='real' then begin

    value:=TEdit(Sender).Text;
    if value='' then TEdit(Sender).Color:=clWindow else begin
      for i:=1 to length(value) do begin
        if NOT (strtoint(value[i]) in [0..9]) OR NOT (value[i]=',') then begin
          TEdit(Sender).Color:=$00A3A7FA;
          exit;
        end;

      end;
    end;
  end;
end;

Код

procedure TForm1.EditKeyPress(Sender: TObject; var Key: Char); //edit для label'ов
var
  sql, NumberComponent: string;
begin
  if Key = #13 then begin //если нажат enter
    Key := #0;//чтобы не звенело
    if TEdit(Sender).Name='DBEdit' then begin //если это EDIT для изменения данных в ячейке таблице, то сохраняем изменения
      TDBGrid(TEdit(Sender).Parent).DataSource.DataSet.Edit;//переводим Dataset  в режим редактирования
      TDBGrid(TEdit(Sender).Parent).SelectedField.Value:=RoundEx(strtofloat(TEdit(Sender).Text), 3); //округляем и сохраняем
      TDBGrid(TEdit(Sender).Parent).DataSource.DataSet.Post;//сохраняет вставленную или отредактированную запись в БД
      TDBGrid(TEdit(Sender).Parent).SetFocus;
    end
    else begin // а если это edit для названия выходов (Label'ов)? nj
      if TEdit(Sender).Text<>'' then begin
        NumberComponent:=getNumberComponent(TEdit(Sender).Name);
        TLabel(getComponent('DBGridLabel'+NumberComponent, TEdit(Sender).Parent, 1)).Caption:=TEdit(Sender).Text; //присваиваем label значение edit
        sql:='UPDATE '+add_kav(G_PROJECT_NAME+'_exit', 1)+' SET exit_name='+add_kav(TEdit(Sender).Text, 2)+' WHERE id='+NumberComponent;
        form1.sql_execute(form1.UniQuery3, sql);
        TEdit(Sender).Parent.SetFocus;
        //PostMessage(Form1.Handle, WM_DeleteMyComponent, integer(Sender), 0);//удаляем компоент
       end;
    end;
  end;
  if Key = #27 then begin //если нажат escape
    Key := #0;//чтобы не звенело
    TDBGrid(TEdit(Sender).Parent).SetFocus;
  end;
end;

Код

procedure TForm1.EditExit(Sender: TObject);
begin
  PostMessage(Form1.Handle, WM_DeleteMyComponent, integer(Sender), 0);//удаляем компоент
end;

Автор: Poseidon 19.10.2013, 23:52
Тут только отладчик скажет в чем же дело. Есть подозрения, что typ все-такие не равен 'real', но это только догадки. Возможно событие не назначено конкретному компоненту, как вариант. 

Создай тестовый проект с воспроизводящейся этой ошибкой и выложи весь проект сюда, посмотрим.

Автор: chikistbuch 20.10.2013, 00:14
Цитата(Poseidon @  19.10.2013,  23:52 Найти цитируемый пост)
Есть подозрения, что typ все-такие не равен 'real'

До этого даже не доходит. Т.е. если я ставлю BREAK (BREAKPOINT), то при запуске проги на нём не происходит остановка выполнения кода:
user posted image
А события описанные:
Код

  Edit.OnKeyPress:=EditKeyPress;
  Edit.OnExit:=EditExit;

Выполняются. хз

Автор: БелАмор 20.10.2013, 09:15
OnChange не вызывается из-за назначения Parent'ом грида.
С причиной не разбирался.
Если назначить Parent'ом форму (Self), то OnChange нормально вызывается.
В данном случае не вижу препятствий сделать именно так,
нужно будет только пересчитать координаты.

Автор: chikistbuch 20.10.2013, 12:24
Цитата(БелАмор @ 20.10.2013,  09:15)
Если назначить Parent'ом форму (Self), то OnChange нормально вызывается.

Код

Edit:=TEdit.Create(self);

Нифига, всё равно не обрабатывается OnChange!

Автор: Akella 20.10.2013, 14:51
Для начала почитай про owner и parent.  что это и с чем едят, различия и т.д.

Автор: chikistbuch 20.10.2013, 17:40
Цитата(Akella @ 20.10.2013,  14:51)
Для начала почитай про owner и parent.  что это и с чем едят, различия и т.д.

Akella, спасибо! Почитал вот эту http://www.delphi-manual.ru/dynamically-created-components.php и вроде бы как понял, что "компонент-родитель должен уметь принимать и обрабатывать сообщения от системы Windows". Походу DBGrid не отлавливает OnChange вот и ничего не получалось.
Вот работающий код:
Код

procedure TForm1.ChangeCell(Sender: TObject); //прорисовывает EDIT для измнения значений в DBGrid
var
  Edit: TEdit;
begin
  Edit:=TEdit.Create(Form1);
  Edit.Name:='DBEdit';
  Edit.Top:=DBSelectedCellRect.Top+TDBGrid(Sender).Top;
  Edit.Left:=DBSelectedCellRect.Left++TDBGrid(Sender).Left+2;
  Edit.Width:=DBSelectedCellRect.Right - DBSelectedCellRect.Left;
  Edit.Text:=TDBGrid(Sender).SelectedField.AsString;//Заполняем edit
  Edit.OnChange:=EditChange;
  Edit.OnKeyPress:=EditKeyPress;
  Edit.OnExit:=EditExit;
  Edit.Parent:=TPanel(Sender).Parent;
  Edit.SetFocus;
end;


В принципе БелАмор говорил это же, только я его не понял )))

Автор: Akella 20.10.2013, 18:01
Цитата(chikistbuch @  20.10.2013,  12:24 Найти цитируемый пост)
Edit:=TEdit.Create(self);

Это означает, что ты указал владельца, а не родителя, т.е. именно владелец (self - форма) будет уничтожать компоненту "Edit", когда сам будет помирать.

Автор: Poseidon 20.10.2013, 19:23
Цитата(chikistbuch @  20.10.2013,  17:40 Найти цитируемый пост)
Edit.Parent:=TPanel(Sender).Parent;
Очень интересный подход, особенно если учесть, что Sender - это TDBGrid. Почему бы просто не указать там Form1 или Self, тем более что вся эта индийская конструкция все-равно вернет именно это?

Автор: БелАмор 21.10.2013, 00:50
Цитата(Poseidon @  20.10.2013,  20:23 Найти цитируемый пост)
Цитата(chikistbuch @  20.10.2013,  17:40 )
Edit.Parent:=TPanel(Sender).Parent;

Очень интересный подход, особенно если учесть, что Sender - это TDBGrid. Почему бы просто не указать там Form1 или Self, тем более что вся эта индийская конструкция все-равно вернет именно это?

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

1. Как уже сказали, Sender здесь ссылается на грид (объект типа TDBGrid).
2. Ошибки компиляции в этом месте не возникает потому, что приведение типа производится жёсткое, без проверки реального типа объекта, а компилятору в случае жёсткого приведения по-барабану что к чему приводить и каковы будут последствия.
3. Во время выполнения программы ошибки в этом месте не возникает потому, что и TPanel, и TDBGrid наследуют свойство Parent от их дальнего общего предка TControl. Обращение к необщему свойству привело бы к ошибке. Приведение с проверкой Edit.Parent:=(Sender as TPanel).Parent также привело бы к ошибке. Правильным вариантом было бы приведение либо к TDBGrid, либо к любому его предку вплоть до TControl.
4. Если Sender в процедуре используется многократно, то логично завести локальную переменную нужного типа (здесь - TDBGrid или предка), один раз присвоить ей значение (причём лучше с проверкой типа, через as) и вместо TDBGrid(Sender).Parent спокойно писать g.Parent
5. Edit:=TEdit.Create(Form1); - это опять-же, грубейшая логическая ошибка, в частном случае не приводящая к фактической.
  TForm1 - класс. Экземпляров класса (объектов) в общем случае может быть много. Form1 - это переменная-указатель (4 или 8 байт), которая может указывать только на один экземпляр формы. А может и не указывать вообще никуда. А все формы класса TForm1 должы работать, сколько бы их ни было создано. И работать со своими данными. Короче, правильным вариантом записи было бы Edit:=TEdit.Create(Self); Разберитесь что такое Self.
6. Посмотрите статью: http://delphikingdom.com/asp/viewitem.asp?catalogid=342

Ваша процедура с указанными исправлениями могла бы выгледеть примено так:

Код

procedure TForm1.ChangeCell(Sender: TObject);
var
  Edit: TEdit;
  g: TDBGrid;  // Добавлено
begin
  g := Sender as TDBGrid;  // Добавлено
  Edit:=TEdit.Create(Self);  // Form1 ---> Self
  Edit.Name:='DBEdit';
  Edit.Top:=DBSelectedCellRect.Top+g.Top;
  Edit.Left:=DBSelectedCellRect.Left+g.Left+2;
  Edit.Width:=DBSelectedCellRect.Right - DBSelectedCellRect.Left;
  Edit.Text:=g.SelectedField.AsString;
  Edit.OnChange:=EditChange;
  Edit.OnKeyPress:=EditKeyPress;
  Edit.OnExit:=EditExit;
  Edit.Parent:=g.Parent;  // TPanel(Sender) ---> g
  Edit.SetFocus;
end;


Автор: Zadnica 21.10.2013, 09:03
Модератор: Сообщение скрыто.

Автор: chikistbuch 21.10.2013, 19:22
БелАмор, вот это разжевал! И не поленился столько писать!! Зачёт и спасибо  smile 

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