StringGrid AutoAdvance Editor Display Issue

Hi,

C++Builder 10.1 Berlin Update 2
AdvStringGrid 8.5.2.1

I have an editable StringGrid with autoadvance enabled.

The user enters a part in the part number column and presses enter. The autoadvance method fetches the part details and populates the rest of the cells in the row with the part data. However, the part number that was typed might different when the part is looked up. In that case, the string in the part number column needs to be updated to the corrected part number. When that happens, the column that is moved to shows the new part number and not the value it should show.

Here is a test AutoAdvance method

void __fastcall TForm1::PartGridAutoAdvance(TObject *Sender, int OldRow, int OldCol, int &NewRow, int &NewCol, bool &Allow) {
  if (OldCol == 2) {
    PartGrid->Cells[2][OldRow] = "A new value";
    NewCol = 4;
  }

  PartGrid->Cells[3][OldRow] = "c2";
  PartGrid->Cells[4][OldRow] = "c3";
  PartGrid->Cells[5][OldRow] = "c4";
  PartGrid->Cells[6][OldRow] = "c5";
  PartGrid->Cells[7][OldRow] = "c6";
  PartGrid->Cells[8][OldRow] = "c7";
  PartGrid->Cells[9][OldRow] = "c8";
  Allow = true;

}

When the code runs, column 2 shows the string "A new value" and column 4 also shows the string "A new value" where it should be showing "c3".

If I press Esc after the cell gains focus the cell shows the correct value. So I think the cell has the correct value, it is the cell editor that is showing the wrong value.

I am not sure I have explained this well. I have a test project that shows the error if that would help.

Shawn

I have retested this here with the latest version v8.5.2.3 of TAdvStringGrid on the form and the 
following code and I cannot reproduce this:


Hi,

I upgraded the component set and I am still seeing the problem.

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
  PartGrid->Col = 2;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::PartGridAutoAdvance(TObject *Sender, int OldRow, int OldCol, int &NewRow,
          int &NewCol, bool &Allow)
{
  if (OldCol == 2) {
    PartGrid->Cells[2][OldRow] = "A new value";
    NewCol = 4;
  }

  PartGrid->Cells[3][OldRow] = "c2";
  PartGrid->Cells[4][OldRow] = "c3";
  PartGrid->Cells[5][OldRow] = "c4";
  PartGrid->Cells[6][OldRow] = "c5";
  PartGrid->Cells[7][OldRow] = "c6";
  PartGrid->Cells[8][OldRow] = "c7";
  PartGrid->Cells[9][OldRow] = "c8";
  Allow = true;

}
//---------------------------------------------------------------------------

and the component

object PartGrid: TAdvStringGrid
  Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goDrawFocusSelected, goRowSizing, goColSizing, goEditing, goTabs, goThumbTracking]
  OnAutoAdvance = PartGridAutoAdvance
  AutoThemeAdapt = True
  ColumnHeaders.Strings = (
    ''
    ''
    'Part Number'
    'Supplier'
    'Qty'
    'Price'
    'Tax'
    'Disc. %'
    'Stock'
    'Description'
    'Location'
    'Min'
    'Alt. Part')
  FixedColWidth = 11
  FixedRowHeight = 24
  Look = glOffice2007
  Navigation.AllowInsertRow = True
  Navigation.AllowDeleteRow = True
  Navigation.AdvanceOnEnter = True
  Navigation.AdvanceOnEnterLoop = False
  Navigation.AdvanceInsert = True
  Navigation.AdvanceAutoEdit = False
  Navigation.AllowClipboardShortCuts = True
  Navigation.AllowClipboardAlways = True
  Navigation.AdvanceAuto = True
  Navigation.AppendOnArrowDown = True
  Navigation.CursorWalkEditor = True
  Navigation.SkipReadOnly = True
  UIStyle = tsOffice2013LightGray
  Version = '8.5.2.2'
  ColWidths = (
    11
    23
    53
    31
    64
    40
    45
    127
    64
    38
    64
    64
    64)
  RowHeights = (
    24
    24)
end


Would there be a difference with Delphi vs. C++Builder?

The issue is with setting grid.Cells[] from OnAutoAdvance.
OnAutoAdvance is triggered while the inplace editor is still active and while this editor is active, you are updating the cell already in your event handler.

Change your event handler to:


void __fastcall TForm1::PartGridAutoAdvance(TObject *Sender, int OldRow, int OldCol, int &NewRow,
          int &NewCol, bool &Allow)
{
  if (OldCol == 2) {
    PartGrid->HideInplaceEdit();
    PartGrid->Cells[2][OldRow] = "A new value";
    NewCol = 4;
  }
Great.

Thanks for the help.

Is there a better place I should be doing the check? Maybe the AutoAdvance handler is not the right place?

Shawn

OnCellValidate is the place where entered value in the inplace editor can be checked and modified when needed through the event parameter. OnAutoAdvance is used for adapting the auto-advance mechanism.

Great. I will update my code to do that. Thanks.

I made the change to use CellValidate and that seems to work ok in most instances.

However, there are times that the program needs to display a dialog to the user and have them pick something. If that happens, then the AutoAdvance does not fire, the user stays in the cell editor. For example, the following code

void __fastcall TForm1::PartGridCellValidate(TObject *Sender, int ACol, int ARow, UnicodeString &Value, bool &Valid) {
  if (ACol == 2) {
    HelperDialog->ShowModal();
    Value = "A new value";
    PartGrid->Cells[3][ARow] = "c2";
    PartGrid->Cells[4][ARow] = "c3";
  }

  Valid = true;
}

results in the AutoAdvance handler not being called. If I remove the ShowModal call then AutoAdvance fires. Is there a way to get auto advance to fire if the grid loses focus?

Thanks.

You should not show a modal dialog from OnCellValidate.
OnCellValidate is triggered when the inplace editor is still active & focused.
A modal dialog takes the focus away from the inplace editor and the grid and a focus leave triggers the end of the editing process. So, this conflicts with the end of the editing already initiated by OnCellValidate. If you need to show a modal dialog, this should be done after editing ended, for example from the OnCellEditDone event

My use case is


User types a part number in column 2. One of four things can happen
  1. The part number is in the system: other columns are populated from the part (description, price) and the column is moved to column 4 so the user can enter the quantity
  2. The part number has changed, e.g. ABC becomes ABC-A. In this instance, column two should show ABC-A, all the other columns should get populated, and the user is moved to column 4.
  3. The user types AB and hits enter. In this instance, a dialog is displayed with all the parts that start with AB and the user selects one. The row is populated with the details from the selected part and the user is advanced to column 4.
  4. The user enters a part number with no match. In this case, the user is advanced to column 3.
As I understand it, the order of fire is:
  • CellValidate
  • AutoAdvance
  • EditCellDone
I need to know the "final" part before I know which column to autoadvance to. So I don't think I can do the lookup/dialog display in EditCellDone.

Would it be best to do the lookup in AutoAdvance, so I know which column to advance the user, but then set the values in EditCellDone?

class::AutoAdvance(TObject *Sender, ...) {
  if (OldCol == PART_COLUMN) {
    partOk = verifyPart(PartGrid->Cells[OldCol][OldRow]);
    NewCol = partOk ? QTY_COLUMN : SUPPLIER_COLUMN;
  }
}

class::EditCellDone(TObject *Sender, int ACol, int ARow) {
  if (ACol == PART_COLUMN) {
    if (PartGrid->Cells[ACol][ARow] != FCurrentPart.PNum) {
      PartGrid->Cells[ACol][ARow] = FCurrentPart.PNum;
    }
    PartGrid->Cells[3][ARow] = FCurrentPart.Supplier;
    PartGrid->Cells[4][ARow] = 1;
    PartGrid->Cells[5][ARow] = FCurrentPart.Description;
   ...
  }
}

Does that approach make sense?

Thanks.

If this needs to be handled with a modal dialog:


- OnCellValidate: set a flag entry is invalid
- OnAutoAdvance : when flag is set, skip, when flag is not set, set new column
- OnEditCellDone : when flag is set, show modal dialog and programmatically move to the new cell for entry by setting grid.Col
I almost have it working. Just one issue now.

If PartGrid->Navigation.AdvanceAutoEdit is enabled, then after I advance from column 2 to column 4, the edit in column 4 does not stick.

With the code below and AdvanceAutoEdit is false, then if I type into column 2 and press enter I end up in column 4. I type in a new value, press enter, I end up in column 5 and the value in column 4 is correct. If AdvanceAutoEdit is true, when I change the value in column 4 and press enter the value reverts to 1 and I move to column 5.

Anything I can do so the user does not need to hit enter twice to advance to the next cell?

Thanks.


__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
  PartGrid->Col = 2;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::PartGridCellValidate(TObject *Sender, int ACol, int ARow, UnicodeString &Value, bool &Valid) {
  if (ACol == 2) {
    if (Value != "A new value")
      Value = "A new value";
  }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::PartGridAutoAdvance(TObject *Sender, int OldRow, int OldCol, int &NewRow,
          int &NewCol, bool &Allow)
{
  if (OldCol == 2) {
    NewCol = 4;
  }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::PartGridEditCellDone(TObject *Sender, int ACol, int ARow) {
  if (ACol == 2) {
    PartGrid->Cells[3][ARow] = "S2";
    PartGrid->Cells[4][ARow] = 1;
    PartGrid->Cells[5][ARow] = "c4";
    PartGrid->Cells[6][ARow] = "c5";
    PartGrid->Cells[7][ARow] = "c6";
    PartGrid->Cells[8][ARow] = "c7";
    PartGrid->Cells[9][ARow] = "c8";
  }
}
//---------------------------------------------------------------------------



I figured it out. If I call PartGrid->HideInplaceEdit(); before I set the cell values and then call after I set them it works ok.

Thanks again for all your help.

Thanks for confirming a solution was found!