Delphi — ADOTable не удаляет записи должным образом и создает записи-призраки

#database #delphi #dbgrid

#База данных #delphi #dbgrid

Вопрос:

Мне нужна помощь в устранении проблемы с моей базой данных. У меня есть файл .mdb, который содержит записи автомобилей. Это связано с моей ADOTable, а затем с DBGrid в Delphi. Когда я удаляю записи с помощью кнопки в моей форме, кажется, что она не удаляет ее должным образом, потому что, когда я прокручиваю dbgrid, предполагается, что активная запись изменяется / обновляется (зависит от того, прокручиваю ли я вниз или вверх) и отображает значение каждого поля активной записи в edits нижеDBGrid.

После того, как мой код удалил запись, она не отображается ни в DBGrid, ни в mdb-файле, когда я просматриваю ее в MS Access, поэтому я предположил, что она была удалена правильно. Но, как я объяснил выше, когда событие OnMouseWheel завершается, оно отображает, как я полагаю, либо удаленную запись, либо данные предыдущей записи, когда указатель на активную запись DBGrid четко показывает, что она должна отображать данные следующей или предыдущей записей.

Интересно отметить, что кнопки OnCellClick и DBGridNavigator не оказывают такого влияния на DBGrid и отображаемую информацию о записи.

Изображения:

Перед OnMouseWheel-событием
Перед OnMouseWheel-событием

Каждое изображение после 1 исключения:
введите описание изображения здесь

введите описание изображения здесь
Изображение фактического MDB-файла:
введите описание изображения здесь


Код для используемых процедур и функций:

OnMouseWheel:

 procedure TCars.DBGrid1MouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; 
MousePos: TPoint; var Handled: Boolean);
begin
  Show_Car_Details;
end;
  

OnCellClick:

 procedure TCars.DBGrid1CellClick(Column: TColumn);
begin
  Show_Car_Details;
end;
  

Show_Car_Details:

 procedure TCars.Show_Car_Details;
begin
  with CarOwners.tbl_Cars do
  begin
    edt_Car_ID.text := inttostr(fieldbyname('ID').value);
    edt_Car_Type.text := fieldbyname('Make').value;
    edt_Car_Price.text := FloatToStr(fieldbyname('Price').value);
    edt_Car_Distance.text := inttostr(fieldbyname('Distance').value);
    edt_Owner_ID.text := inttostr(fieldbyname('OwnerID').value);

    if fieldbyname('Insurance').value = true then
    begin
      cbx_Insurance.ItemIndex := 0;
    end
    else
    begin
      cbx_Insurance.ItemIndex := 1;
    end;
  end;
end;
  

Процедура удаления:

 procedure TCars.bit_DeleteClick(Sender: TObject);
begin
  if messagedlg
    ('Are you sure you want to delete this record? It will permanently be removed.',
    mtConfirmation, [mbyes, mbno], 0) <> mryes then
    exit;

  CarOwners.tbl_Cars.Delete;
  DBGrid1.DataSource.DataSet.Refresh;
end;
  

Просто добавьте код для процедур добавления и обновления:
Add:

 procedure TCars.bit_AddClick(Sender: TObject);
var
  Make: string;
  OwnerID, Distance: Integer;
  Price: real;
  Insurance: Boolean;
begin
  Make := edt_Car_Type.text;
  OwnerID := strtoint(edt_Owner_ID.text);
  Distance := strtoint(edt_Car_Distance.text);
  Price := strtofloat(edt_Car_Price.text);
  if cbx_Insurance.ItemIndex = 0 then
  begin
    Insurance := true;
  end
  else
  begin
    Insurance := false;
  end;

  Add_Record(Make, OwnerID, Price, Distance, Insurance);
end;
//---------------------------------------------------------
procedure TCars.Add_Record(Make: string; OwnerID: Integer; Price: real;
  Distance: Integer; Insurance: Boolean);
begin

  // ----validation----
  //validation done here(removed for space, just basic if with exit.)

  // add new information
  with CarOwners do
  begin
    tbl_Cars.DisableControls;
    tbl_Cars.last;
    tbl_Cars.Insert;
    tbl_Cars['Make'] := Make;
    tbl_Cars['OwnerID'] := OwnerID;
    tbl_Cars['Price'] := Price;
    tbl_Cars['distance'] := Distance;
    tbl_Cars['Insurance'] := Insurance;
    tbl_Cars.post;
    tbl_Cars.EnableControls;
  end;
end;
  

Процедура обновления:

 procedure TCars.bit_UpdateClick(Sender: TObject);
var
  Brand: string;
  Price: real;
  Insurance: Boolean;
  OwnerID, Distance: Integer;
begin
  Brand := edt_Car_Type.text;
  Price := strtofloat(edt_Car_Price.text);
  OwnerID := strtoint(edt_Owner_ID.text);
  Distance := strtoint(edt_Car_Distance.text);

  if cbx_Insurance.ItemIndex = 0 then
  begin
    Insurance := true;
  end
  else
  begin
    Insurance := false;
  end;

  Update_Record(Brand, OwnerID, Price, Distance, Insurance);
end;
//------------------------------------------------------------
procedure TCars.Update_Record(Make: string; OwnerID: Integer; Price: real;
  Distance: Integer; Insurance: Boolean);
begin
  //validation done here(removed for space, just basic if with exit.)

  // ----Update Information ----
  with CarOwners do
  begin
    tbl_Cars.DisableControls;
    tbl_Cars.edit;
    tbl_Cars['Make'] := Make;
    tbl_Cars['OwnerID'] := OwnerID;
    tbl_Cars['Price'] := Price;
    tbl_Cars['Distance'] := Distance;

    if Insurance then
    begin
      tbl_Cars['Insurance'] := true;
    end
    else
    begin
      tbl_Cars['Insurance'] := false;
    end;
    // ShowMessage('Posting...');
    tbl_Cars.post;
    // ShowMessage('Done');
    tbl_Cars.EnableControls;
  end;
end;
  

Любые советы или помощь приветствуются!!!
С уважением

Комментарии:

1. Вы должны связать свои поля непосредственно с источником данных.

2. Вы также можете использовать событие onDataChange для обновления полей.

3. «Извините, я подумал, что это бесполезная информация относительно моего вопроса и проблемы». Совсем нет. Фактически это объясняет, почему ваш код кажется написанным без какого-либо учета того, как правильно писать приложения с поддержкой Delphi db. Скажите своему преподавателю, что вам не нужно использовать TEdits вместо TDBEdits только для проверки. Вы можете использовать событие OnValidate TField для проверки отдельных полей и событие TDataSet Before Post для проверки между полями…

4. «вы не могли бы объяснить, как это решит мою проблему» MouseWheel Событие не является подходящим местом для обновления элементов управления полями. Если метод вызывается до того, как событие обрабатывается сеткой, то в конечном итоге отображается предыдущая запись.

5. @Olivier: Отличное объяснение! Я думаю, что OP должен больше времени слушать вас и меньше своего инструктора.

Ответ №1:

Спасибо @MartynA и @Olivier за ответ. Проблема заключалась в использовании неправильного обработчика событий для обновления и отображения значений полей записи.

НЕ ИСПОЛЬЗУЙТЕ: OnMouseWheel

ИСПОЛЬЗОВАНИЕ:

 procedure TCarOwners.ds_CarsDataChange(Sender: TObject; Field: TField);
begin
  if Field = nil then
  begin
    Cars.show_car_details;
  end;
end;
  

Это правильно обновляет элементы управления, не связанные с данными. Обязательно добавьте Form1 или в моем случае Cars_frm в uses список непосредственно под implementation .

Комментарии:

1. В более общем случае, когда требуемое поведение связано с данными в наборе данных, используйте события TDataSet и его потомков, таких как TADODataSet и TField, а не события в графическом интерфейсе, чтобы получить желаемое поведение.