3
votes

I have a grid of entries the user will click on to multi-select for a process. Some of the entries will be invalid based upon values of the first selected row.

I know about DBGrid.SelectedRows.CurrentRowSelected, but I cannot find a suitable place for checking my conditions to set it to True or False.

Something like this:

var
  bm: TBookmark;
  CachedIdentity: String;
  CanSelect: Boolean;
begin
  with dgbSkypeConversations do
  begin
    if SelectedRows.Count > 0 then
    begin
      DataSource.DataSet.DisableControls;
      bm := DataSource.DataSet.GetBookmark;
      CachedIdentity := DataSource.DataSet.FieldByName('identity').AsString;
      DataSource.DataSet.GotoBookmark(SelectedRows[0]);
      CanSelect := DataSource.DataSet.FieldByName('identity').AsString <> CachedIdentity;
      DataSource.DataSet.GotoBookmark(bm);
      DataSource.DataSet.FreeBookmark(bm);
      SelectedRows.CurrentRowSelected := CanSelect; 
      DataSource.DataSet.EnableControls;
    end;
  end
end;

I have tried the OnMouseDown events in Application.OnMessage and of the DBGrid and the Form, but they do not work, and there is no TBookmarkList.BeforeInsertItem event. What can I do or must I change?

2
Your q's title doesn't seem to match what you describe in the text of the q. Are you trying to prevent the user selecting some rows in the grid? If so, why no just prevent them being displayed in the first place, e.g. by using a filter on the grid's dataset?Alex James
First part: yes. Second because the user needs to see the data and then decide what to select, not before... I´ve done all the filter thing you can imagine, but i cant change the user requirementJosé Eduardo
Once the user has made the first selection (which is the point you want to disable rows), you can filter to remove the non-eligible rows, which is much cleaner from a UI standpoint. While you might hack in a way to prevent the user from selecting certain rows, at that point you can't draw them differently to indicate they're disabled (not selectable).Ken White

2 Answers

1
votes

I would not make it so complicated.
If the comparison always takes place on the first selection.
The code I use here has hardcoded Value.
No jump around to bookmarks.

// first selection----------------------------- 
if DBGrid1.SelectedRows.Count = 1 then begin
   CachedIdentity := 'Sonnenbrille'; // Sunglasses
 //CachedIdentity := DataSource.DataSet.FieldByName('identity').AsString;
   Exit;
end;

Do the comparison

if DBGrid1.SelectedRows.CurrentRowSelected then begin
 //if CachedIdentity <> DataSource.DataSet.FieldByName('identity').AsString
   if Pos(LookingFor,DBGrid1.DataSource.DataSet.FieldByName('haupttxt').AsString)=0
   then  DBGrid1.SelectedRows.CurrentRowSelected := False;
   ShowMessage(IntToStr(DBGrid1.SelectedRows.Count));
end;

We can see two are selected so
ShowMessage(IntToStr(DBGrid1.SelectedRows.Count));

shows 2

enter image description here

Now we want to select the line

SPORTLICH, ELEGANTE GUCCI SONNENBRILLE

We know a simple Pos() Sonnenbrille to find SONNENBRILLE Pos() will be = 0, so a selection-will not take place.

enter image description here

ShowMessage(IntToStr(DBGrid1.SelectedRows.Count));

shows 2 too

CODE:

var
CachedIdentity : string;

procedure TForm2.canSelectedV1;
begin
    // first selection----------------------------- 
if DBGrid1.SelectedRows.Count = 1 then begin
   CachedIdentity := 'Sonnenbrille'; // Sunglasses
 //CachedIdentity := DataSource.DataSet.FieldByName('identity').AsString;
   Exit;
end;
if DBGrid1.SelectedRows.CurrentRowSelected then
 begin
 //if CachedIdentity <> DataSource.DataSet.FieldByName('identity').AsString
   if Pos(LookingFor,DBGrid1.DataSource.DataSet.FieldByName('haupttxt').AsString)=0
   then  DBGrid1.SelectedRows.CurrentRowSelected := False;
   ShowMessage(IntToStr(DBGrid1.SelectedRows.Count));
 end;
end;

procedure TForm2.DBGrid1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
canSelectedV1;
end;

procedure TForm2.DBGrid1KeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
canSelectedV1;
end;

end.
2
votes

If you look at the source of TCustomDBGrid.MouseDown you'll see how it works out which dataset row (if any) the Mousedown event occurred on. You'll also see the line which leads to the Selection state of the current row being toggled:

  if ssCtrl in Shift then
    CurrentRowSelected := not CurrentRowSelected

With that in mind, set up an OnMouseUp event for your grid and put a breakpoint in it.

You should then notice that because of what goes on in the grid's MouseDown event, by the time your OnMouseUp event is called, the current row of the grid's dataset has moved to the clicked datarow (see note below). So, at that point you can check whether the current row meets the criteria by which you want to allow the user to select it, and deselect it if it doesn't. I think that answers your specific "how to prevent a row from being selected?"

As a user that deselecting behaviour would annoy me somewhat, so you should probably give the user some indication of why the row has been deselected.

Note: Obviously, the fact that the grid's Mousedown will result in a call to the dataset's MoveBy means that the dataset's OnScroll event has been fired. Depending on exactly what you want to do, the OnScroll event might be the place to check whether the current datarow meets your selection criteria and, if it doesn't, begin the process of de-selecting it there. In any case, the fact that the dataset should have already be on the datarow where the DBGrid.MouseDown event was called should save you the trouble of identifying it in your MouseUp.

Hopefully that will be enough to get you going ...