Since you don't own the timer, you must guard you code against being run again before finishing actual run.
Declare a global variable that persist between calls to the routine.
then change your code this way.
void UpdateBayGridview(System.Collections.Generic.List<bay> bay)
{
if (updating) return;
updating = 1;
int vscroll = dgBinContent.FirstDisplayedScrollingRowIndex;
int hscroll = dgBinContent.FirstDisplayedScrollingColumnIndex;
this.BayTable.BeginLoadData();
DataTable dtupdates = bay.ToDataTable<bay>();
DataColumn[] pkUPdateColumn = { dtupdates.Columns["ID"] };
dtupdates.PrimaryKey = pkUPdateColumn;
DataColumn[] pkColumn = { this.BayTable.Columns["ID"] };
this.BayTable.PrimaryKey = pkColumn;
this.BayTable.Merge(dtupdates);
this.BayTable.EndLoadData();
schemachange = false;
bsBays.ResetBindings(schemachange);
if (vscroll < 0)
vscroll = 0;
if (hscroll < 0)
hscroll = 0;
dgBinContent.FirstDisplayedScrollingRowIndex = vscroll;
dgBinContent.FirstDisplayedScrollingColumnIndex = hscroll;
updating = 0;
}
This can solve the problem of moving cursor while updating.
You also have potential conflict if you move the cursor while the update routine is active.