DBGrid:如何防止选中某一行?

3

我有一个条目网格,用户将单击以进行多选处理。根据第一行选择的值,其中一些条目将无效。

我知道 DBGrid.SelectedRows.CurrentRowSelected,但我找不到适当的地方来检查我的条件并将其设置为 True 或 False。

类似这样:

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;

我已经尝试了在Application.OnMessageDBGrid和表单中使用OnMouseDown事件,但是它们都无法工作,并且没有TBookmarkList.BeforeInsertItem事件。我该怎么做或者必须进行哪些更改?


你的问题标题似乎与你在问题文本中描述的不符。你是想防止用户在网格中选择某些行吗?如果是这样,为什么不直接在第一时间阻止它们被显示出来,例如通过在网格数据集上使用过滤器? - Alex James
第一部分:是的。第二部分:因为用户需要先看数据,然后再决定选择什么,而不是在之前就决定好了... 我已经尝试了所有可能的筛选方式,但我不能改变用户的要求。 - José Eduardo
一旦用户进行了第一次选择(即您想要禁用行的点),您可以过滤以删除不合格的行,从UI角度来看更加清晰。虽然您可能会以某种方式入侵以防止用户选择某些行,但此时您无法以不可选方式绘制它们以指示其已禁用。 - Ken White
2个回答

2
如果您查看TCustomDBGrid.MouseDown的源代码,您会看到它是如何确定鼠标按下事件发生在哪个数据集行(如果有)上的。您还将看到导致当前行的选择状态切换的行:
  if ssCtrl in Shift then
    CurrentRowSelected := not CurrentRowSelected

考虑到这一点,请为您的网格设置一个 OnMouseUp 事件,并在其中设置断点。由于在网格的 MouseDown 事件中发生了什么,因此当调用您的 OnMouseUp 事件时,数据集的当前行已经移动到单击的数据行(请参见下面的注释)。所以,在那时,您可以检查当前行是否符合您希望允许用户选择的条件,并在不符合的情况下取消选择它。我想这就回答了你特定的“如何防止选中某一行?”的问题。作为一个用户,取消选择的行为会让我有些烦恼,所以您应该给用户一些指示,说明为什么取消选择该行。
注:显然,网格的 Mousedown 会导致数据集的 MoveBy 调用,这意味着数据集的 OnScroll 事件已被触发。根据您想要执行的确切操作,OnScroll 事件可能是检查当前数据行是否符合您的选择条件并在不符合的情况下开始取消选择的地方。无论如何,数据集应该已经在调用 DBGrid.MouseDown 的数据行上,这样您就不必在 MouseUp 中再次确认它。希望这足以让您开始...

1
我不会让它变得太复杂。
如果比较总是在第一次选择时进行。
我在这里使用的代码具有硬编码值。
不要跳到书签。
// 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;

我们可以看到有两个被选中,因此
ShowMessage(IntToStr(DBGrid1.SelectedRows.Count));

显示2

enter image description here

现在我们想要选择这行。 时尚优雅的Gucci太阳镜 我们知道可以使用简单的Pos()函数来查找SONNENBRILLE,其位置为= 0,因此不会进行选择。

enter image description here

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

显示 2 也是如此

代码:

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.

我已经减少了书签的宽度。但是DBGrid的KeyUP和MouseUP事件是我想要的关键。非常感谢你。 - José Eduardo
@JoséEduardo:很高兴我能帮到你 :-) - moskito-x

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接