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

Мне нужна помощь в устранении проблемы с моей базой данных. У меня есть файл .mdb, содержащий записи автомобилей. Это связано с моим ADOTable, а затем с DBGrid в Delphi. Когда я удаляю записи с помощью кнопки в моей форме, кажется, что они не удаляются должным образом, потому что, когда я прокручиваю dbgrid, предполагается, что активная запись изменяется/обновляется (зависит от того, прокручиваю ли я вниз или up) и отображать значение каждого поля активной записи в редактировании под 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;

Показать_детали_автомобиля:

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;

На всякий случай, код для процедур добавления и обновления: Добавить:

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;

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


person Romans    schedule 18.10.2020    source источник
comment
Итак, в чем именно заключается ваш вопрос? Вы должны отредактировать свой пост и добавить МИНИМАЛЬНУЮ программу, которая воспроизводит проблему без использования какой-либо базы данных, которую нам не нужно тестировать.   -  person fpiette    schedule 18.10.2020
comment
Вы должны напрямую связать свои поля с источником данных.   -  person Olivier    schedule 18.10.2020
comment
@ Оливье, если я это сделаю, у меня не будет возможности их проверить. Это также является частью школьной практики или PAT, в которой говорится, что это должно быть сделано с помощью правок. Извините, я думал, что это бесполезная информация относительно моего вопроса и проблемы.   -  person Romans    schedule 18.10.2020
comment
Вы также можете использовать событие OnDataChange для обновления поля.   -  person Olivier    schedule 18.10.2020
comment
@ Оливье, не могли бы вы объяснить, как это решит мою проблему? Я еще немного нуб :).   -  person Romans    schedule 18.10.2020
comment
Извините, я думал, что это бесполезная информация относительно моего вопроса и проблемы. Нисколько. На самом деле это объясняет, почему ваш код кажется написанным без учета того, как правильно писать приложения, поддерживающие базу данных Delphi. Скажите своему наставнику, что вам не нужно использовать TEdits вместо TDBEdits только для проверки. Вы можете использовать событие TField OnValidate для проверки отдельных полей и событие TDataSet Before Post для проверки между полями...   -  person MartynA    schedule 18.10.2020
comment
не могли бы вы объяснить, как это решило бы мою проблему. Событие MouseWheel не является подходящим местом для обновления элементов управления полем. Если метод вызывается до, когда событие обрабатывается сеткой, вы в конечном итоге отображаете предыдущую запись.   -  person Olivier    schedule 18.10.2020
comment
@Olivier: Отличное объяснение! Я думаю, что ОП должен больше времени слушать вас и меньше своего инструктора.   -  person MartynA    schedule 18.10.2020
comment
Я предлагаю вам сэкономить много времени и нервов, погуглив себе достойный учебник по приложениям master-detail в Delphi. Погуглите учебник по delphi master-detail db-aware, это было первым хитом, и выглядит неплохо.   -  person MartynA    schedule 18.10.2020
comment
Может ли кто-нибудь из вас привести пример использования OnDataChange? Большое спасибо, ребята, за помощь до сих пор. Да, я знаю, что мой репетитор отстой, я учусь в государственном образовании, поэтому я не могу ожидать ничего другого.   -  person Romans    schedule 18.10.2020
comment
@MartynA, @Olivier, большое спасибо за помощь! OnDataChange было правильным событием для использования! Вы, ребята, спасли мне жизнь.   -  person Romans    schedule 18.10.2020


Ответы (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.

person Romans    schedule 18.10.2020
comment
В более общем смысле, когда желаемое поведение связано с данными в наборе данных, используйте события TDataSet и его потомков, таких как TAdoDataSet и TField, а не события в графическом интерфейсе, чтобы получить желаемое поведение. - person MartynA; 18.10.2020