1

[编译器:德尔福 XE2]

昨天我花了一整天的时间尝试各种方法来完成这个特定的任务,但它们都以相同的结果结束。

使用 TRZCheckGroup 和此示例查看已检查的内容等。

procedure TFrmMain.cbOptionsChange(Sender: TObject; Index: Integer; NewState: TCheckBoxState);
var
  ItmIndex0, ItmIndex1: Integer;
begin
  { Initialize ItemIndex's }
  ItmIndex0 := -1;
  ItmIndex1 := -1;

  { Return the position Index of the string's(0 and 1) }
  ItmIndex0 := cbOptions.Items.IndexOf('One');
  ItmIndex1 := cbOptions.Items.IndexOf('Two');

  { Which CheckBox has been Checked } 
  cbOptions.ItemChecked[ItmIndex0] := True;
  cbOptions.ItemChecked[ItmIndex1] := False;
end;

注意:^这不是我的最终代码,只是我如何处理复选框的示例。

像 -

if cbOptions.ItemChecked[ItmIndex0] then
  cbOptions.ItemChecked[ItmIndex1] := False
else cbOptions.ItemChecked[ItmIndex1] := True;

他们第一次工作,然后它总是评估为真,我明白为什么。只有当我取消选中第一个 CheckBox 时,else 位才会起作用,这显然不是我想要的结果。

似乎事件停止工作,并且在我的一些尝试中由于某种原因它已经触发了两次。

cbListOptionsChange 上的 NewState 参数,这是什么,无论如何它可以帮助我吗?

对此的任何帮助将不胜感激。

谢谢。

if cbOptions.ItemChecked[ItmIndex0] then
  cbOptions.ItemChecked[ItmIndex1] := False
else if cbOptions.ItemChecked[ItmIndex1] then
  cbOptions.ItemChecked[ItmIndex0] := False;

如果选中第二个 CheckBox,请查看类似内容,然后我检查第一个它是否按要求工作,但显然之后您无法再检查第二个 CheckBox。


Ken White - 片段(工作)。将组件的名称替换为默认,因为人们可能会感到困惑,有时有助于默认命名以节省未来的问题。

procedure TForm1.RzCheckGroup1Change(Sender: TObject; Index: Integer; NewState: TCheckBoxState);
var
  i: Integer;
begin
  // Keep this event from being fired again while we're here.
  // Your code isn't clear about what the actual name of the
  // component or this event, (the event is named `cbListOptionsChange`,
  // but your code references `cbOptions` - I don't know which is
  // correct, so change it if needed in the next line and
  // the one in the `finally` block below. I'm using `cbListOptions`
  // here.
  RzCheckGroup1.OnChange := nil;

  try
    // If we're getting notified of a new item being checked...
    if NewState = cbChecked then
    begin
      // Iterate through the items, unchecking all that aren't
      // at the index that just became checked.
      // I wouldn't use `for..in`, because the ordering works better here
      for i := 0 to RzCheckGroup1.Items.Count - 1 do
        if i <> Index then
          RzCheckGroup1.ItemChecked[i] := False; // Ryan - Just changed to this from this cbListOptions.Items[i].Checked := False;
    end;

    // Ryan - Uncomment these two lines if you want one of them to be Checked at all times, this will set the CheckBox you are trying to Uncheck to Checked. 
    //if not RzCheckGroup1.ItemChecked[Index] then
    //  RzCheckGroup1.ItemChecked[Index] := True;

  finally
    // Reconnect the event
    RzCheckGroup1.OnChange := RzCheckGroup1Change;
  end;
end;
4

2 回答 2

6

我不熟悉TRZCheckGroup,但您当前的代码将始终检查项目ItmIndex0并取消选中其他项目。

TCheckBoxState在 Delphi 文档中定义为

TCheckBoxState = (
  cbUnchecked,
  cbChecked,
  cbGrayed
);

所以NewState似乎告诉你新设置的状态CheckBox,并Index告诉你哪个复选框正在改变。大多数时候,cbGrayed不使用,因为它表示该值从未设置过;它通常仅在您读取数据库中的 BOOLEAN(或位)列并且它为 NULL 时才有用。

这个事件并不是真的要交替两个复选框的状态,它会出现;它旨在让您在单个项目(在项目组中)更改其状态时做出反应:

procedure TFrmMain.cbListOptionsChange(Sender: TObject; Index: Integer; 
  NewState: TCheckBoxState);
begin
  case NewState of
    cbUnchecked: // Do whatever when cbOptions.Items[Index] is unchecked
    cbChecked:   // Do whatever when cbOptions.Items[Index] is checked
    cbGrayed:    // Usually ignored unless NULL in db column is indicated
  end;
end;

要反转两个复选框的状态(将一个更改为备用状态,另一个更改为相反状态),您可以使用类似这样的内容(使用两个标准TCheckBox控件,为它们的两个事件定义相同的OnClick事件):

procedure TFrmMain.CheckBoxClick(Sender: Object);
var
  ChkBox: TCheckBox;
  BoxToToggle: TCheckBox;
begin
  // If you're sure the event is only for TCheckBox
  ChangingBox := TCheckBox(Sender);
  // If there's a chance it's used for something else
  // if (Sender is TCheckBox) then
  // begin
  //   ChangingBox := TCheckBox(Sender); 
  //   
  // or
  //   ChangingBox := Sender as TCheckBox


  if ChangingBox = CheckBox1 then
    BoxToToggle := CheckBox2
  else
    BoxToToggle := CheckBox1;

  // Disable this event for both checkboxes, so it doesn't
  // fire recursively
  ChangingBox.OnClick := nil;
  BoxToToggle.OnClick := nil;
  try
    BoxToToggle.Checked := not ChangingBox.Checked;
  finally
    // Reconnect event handlers
    ChangingBox.OnClick := CheckBoxClick;
    BoxToToggle.OnClick := CheckBoxClick; 
  end;

但是,如果您只是处理一个项目列表,其中一个应该被选中,而其他所有项目都未选中,那么您应该使用 aTRadioGroup来代替。它会自动为您提供此行为。使用复选框违反了正常的 Windows GUI 行为,并且会使您的用户感到困惑。

话虽如此(并且我强烈反对这样做!),并且因为我没有你正在使用的组件而未经测试,你可以试试这个(这违背了我的更好判断甚至写!):

procedure TFrmMain.cbListOptionsChange(Sender: TObject; Index: Integer; 
  NewState: TCheckBoxState);
var
  i: Integer;
begin
  // Keep this event from being fired again while we're here.
  // Your code isn't clear about what the actual name of the
  // component or this event, (the event is named `cbListOptionsChange`,
  // but your code references `cbOptions` - I don't know which is
  // correct, so change it if needed in the next line and
  // the one in the `finally` block below. I'm using `cbListOptions`
  // here.
  cbListOptions.OnChange := nil;

  try
    // If we're getting notified of a new item being checked...
    if NewState = cbChecked then
    begin
      // Iterate through the items, unchecking all that aren't
      // at the index that just became checked.
      // I wouldn't use `for..in`, because the ordering works better here
      for i := 0 to cbListOptions.Items.Count - 1 do
        if i <> Index then
          cbListOptions.Items[i].Checked := False;
    end;        
  finally
    // Reconnect the event
    cbListOptions.OnChange := cbListOptionsChange;
  end;
end;

为了确保清楚,我认为这是一个非常糟糕的主意,如果你为我工作,我不会允许的。当有适当的选项可用时,做一些与预期的 Windows 行为相反的事情是完全错误的,IMO。

于 2012-06-05T19:22:33.887 回答
1

此示例使用三个 TCheckbox 控件。

将三个 TCheckbox 控件拖放到窗体上。对于这个例子,我给它们命名

cbOpenorderscbClos​​edorderscbAllorders

向对象检查器中的cbOpenorders.Onclick属性添加一个事件。

然后,将 cbClos​​edorders 和 cbAllorders 的 OnClick 事件属性设置为此 cbOpenorders 事件。所有三个框都将调用相同的事件处理程序,从而减少所需的代码量。

procedure TFrmPreorderViewDialog.cbOpenOrdersClick(Sender: TObject);
begin
  if TCheckbox(Sender).Checked then
  begin
    cbOpenorders.Checked   := (TCheckbox(Sender) = cbOpenorders);
    cbClosedorders.checked := (TCheckbox(Sender) = cbClosedorders);
    cbAllorders.checked    := (TCheckbox(Sender) = cbAllorders);
  End;
end;

在此示例中,用户只能选择一个框或没有框。

于 2012-06-07T00:15:01.063 回答