How to Apply Only the Latest Grid Refresh in Pascal

A grid that loads its pages on a background thread has a race to manage. If the user changes the page or the query while a fetch is running, the earlier fetch can finish afterwards and overwrite what the user asked for most recently.

TVisualGrid in Sphere 10 Pascal Framework owns that request lifecycle. It keeps only the latest pending request, runs one fetch at a time, and discards the result of a fetch that was cancelled. This article describes that pipeline and the three members an application uses to control it, which are RefreshSuspended, CancelActiveFetch and RefreshThrottle.

The latest refresh result accepted while an older result remains separate.
The latest refresh result accepted while an older result remains separate.
💡 In short. How TVisualGrid admits one fetch at a time, keeps only the latest request and discards cancelled background results, using RefreshSuspended, CancelActiveFetch and RefreshThrottle.

1. Apply several changes as one request

When several settings change together, each assignment would normally be a request of its own. Suspending refreshes turns the group into one change, because nothing reaches the data source until the suspension ends.

Pascal
    Grid.RefreshSuspended := True;
    try
      Grid.CancelActiveFetch;
      Source.Revision := 1;
      Grid.PageIndex := 2;
      Grid.RefreshGrid;
      Grid.PageIndex := 3;
      Grid.RefreshGrid;

The sample starts this block while a worker is still inside the data source. The call to CancelActiveFetch is shown for clarity, since suspending already cancels the active fetch. The requests for page indexes 2 and 3 are recorded, and the second replaces the first. The data source is not called for either.

If the earlier worker finishes during the suspension, its result is discarded and the previously accepted rows remain visible.

2. Resume with the latest request

Clearing RefreshSuspended admits the retained request, which here is page index 3.

Pascal
    finally
      Source.ReleaseFetch.SetEvent;
      Grid.RefreshThrottle.Cancel;
      Grid.RefreshSuspended := False;
    end;

If the throttle is in a quiet-period mode and is still waiting, the resumed request respects the remaining interval. Calling RefreshThrottle.Cancel first, as the sample does, makes the resume immediate. An application that wants the quiet period, for example after typing in a search box, can leave that call out.

The sample confirms that exactly one fetch follows the resume.

Plain text
Cancelled fetch finished; rows on screen unchanged
Resume published page index 3 from revision 1
Fetches after resume: 1

3. How the grid admits and accepts a refresh

The suspend and resume sequence relies on the way the grid handles every request. Every client action that needs data, such as assigning PageIndex, calling RefreshGrid or changing a filter, records a pending request. A newer request replaces an older pending one, so the grid never queues a backlog.

A pending request is admitted to the data source only when no fetch is active and refreshes are not suspended. With FetchDataInThread set to True, the admitted request runs IDataSource.FetchPage on a worker thread. When the worker completes, the grid decides on the UI thread whether to publish its rows.

Completion and acceptance are therefore separate events. A cancelled worker is allowed to finish, but its rows never reach the screen. The grid restores the page it was showing and then admits whatever request is pending.

4. The members that control the pipeline

Member Effect
FetchDataInThread Runs each fetch on a worker thread. When False, the fetch runs synchronously inside the request.
CancelActiveFetch Marks the active fetch as cancelled and asks its thread to terminate cooperatively. It does not wait for the thread, and the rows on screen stay as they are.
RefreshSuspended While True, requests and layout continue to be recorded but nothing is admitted to the data source. Setting it also cancels the active fetch. Setting it back to False admits the one latest request.
RefreshThrottle A TThrottledEvent owned by the grid that can delay admission until a burst of requests has gone quiet. Its default mode, temNone, admits immediately.
IsUpdating True while a background fetch is running or waiting to be completed on the UI thread.

All of these are UI-thread members. Changing PageMode cancels the active fetch in the same way.

5. What the data source must do

Cancellation is cooperative. The grid never kills or joins a worker from CancelActiveFetch or from the RefreshSuspended setter, so a data source that ignores the request simply runs to completion and has its result dropped. That places three obligations on the provider.

  • Stay alive. The data source and anything it captured must outlive the worker, including a cancelled one. Destroying the grid waits for an outstanding worker, so release anything the worker is blocked on first.
  • Read inputs once. Capture the values a fetch depends on at the start of FetchAll, as the sample does with its revision number, so one fetch works from one consistent input.
  • Stay off the UI. FetchAll runs on the worker thread and must not read or change controls.
Pascal
  InterlockedIncrement(FetchCount);
  CapturedRevision := Revision;
â„šī¸ The sample holds its data source at an event so that the cancelled worker is still running when the newer requests arrive. That gate exists to make one specific ordering repeatable. It demonstrates the discard rule for that ordering and is not a general race test.

The visual grid unit documents these members beside their declarations. The UI event coalescing article looks at turning a burst of events into one refresh.

6. Get the Framework and run the example

Get Sphere 10 Pascal Framework and follow its package instructions . Use the units directly, or build the optional Lazarus or Delphi runtime package.

The sample was compiled and run on Windows with Free Pascal 3.2.0 and the Lazarus Win32 widgetset, against source revision 2cc6558 . Its Build.ps1 accepts the Framework, compiler, Lazarus and output paths.


Download the complete runnable sample.

sample.zip
Download the complete worked example and build instructions (ZIP).

For the broader Sphere 10 development toolkit, see the Sphere10 Framework overview .

Author: Herman Schoenfeld