Search code examples
delphifiredac

FireDac FDQuery commit changed DataFields when browsing through results


I have a FDQuery bound to a FDConnection.

I am displaying the data on my form with DB Data-Aware components.

Whenever i use the FPQuery.Next, .Prior, ... it browses between the results.

Everything is working fine.

Except when i change a value (e.g. John -> Jane) and then use FPQuery.Next to get the next result it saves commits the changed value to the db even tho i didn't FDQuery1.CommitUpdates.

Is there a way to only save changed DataFields when the user presses the nbPost-Button or uses FDQuery1.CommitUpdates and NOT when browsing between results?

enter image description here

Thanks!


Solution

  • Like I said in a comment, the standard TDataset behaviour is to call its .Post method to save changes to the current row before navigating to another one. This happens in the routine TDataSet.CheckBrowseMode in Data.DB.Pas, which is called before any navigation action. This can't be changed without deriving a custom TDataset descendant.

    (from Data.DB.Pas)

    procedure TDataSet.CheckBrowseMode;
    begin
      CheckActive;
      DataEvent(deCheckBrowseMode, 0);
      case State of
        dsEdit, dsInsert:
          begin
            UpdateRecord;
            if Modified then Post else Cancel;
          end;
        dsSetKey:
          Post;
      end;
    end;
    

    Of course, a TDataSet has a BeforePost event, so it might be tempting to try and use that to cancel changes; however, the problem with BeforePost is how to determine the context in which it is being called, so as to be able to tell whether its being called from CheckBrowseMode rather than as a result of the user clicking the Save button.

    The simple way around that is to catch the BeforeAction event of your DBNavigator, before it calls a navigation action on the dataset which will trigger the .Post:

    procedure TForm1.DBNavigator1BeforeAction(Sender: TObject; Button:
        TNavigateBtn);
    var
      Res : Integer;
      DataSet : TDataSet;
    begin
      DataSet := DBNavigator1.DataSource.DataSet;
      case Button of
        nbFirst,
        nbPrior,
        nbNext,
        nbLast: begin
          if DataSet.State in [dsEdit, dsInsert] then begin
            Res := MessageDlg('The current row has unsaved changes.  Abandon them?', mtWarning, [mbYes, mbNo], 0);
            if Res = mrYes then
              DataSet.Cancel
            else
              DataSet.Post;
          end;
        end;
      end;
    end;