一次只检查一个CheckBox

时间:2012-06-05 18:51:29

标签: delphi delphi-xe2 vcl

[编译:Delphi 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;

注意:^这不是我的最终代码,只是我如何处理CheckBoxes的一个例子。

之类的东西 -

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

他们第一次工作然后总是评估为真,我理解为什么。当我取消检查第一个CheckBox时,else位才会生效,这显然不是我想要的结果。

似乎事件停止工作,并且我的一些尝试因为某种原因而被解雇了两次。

关于cbListOptionsChange的NewState Param,这是什么,它能帮助我吗?

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

感谢。

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

如果第二个CheckBox被选中,请查看类似的内容,然后根据需要检查第一个CheckBox,但之后显然不能再检查第二个CheckBox。


Ken White - 片段(工作)。将组件的名称替换为Default,因为人们可能会混淆其他名称,有时会帮助默认命名以保存将来的问题。

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;

2 个答案:

答案 0 :(得分: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;

但是,如果您只是处理一个项目列表,其中一个项目应该被选中而所有其他项目都未选中,那么您应该使用TRadioGroup代替。它会自动为您提供此行为。使用复选框违反了正常的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;

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

答案 1 :(得分:1)

此示例使用三个TCheckbox控件。

将三个TCheckbox控件拖放到表单上。在这个例子中,我将它们命名为

cbOpenorders cbClos​​edorders cbAllorders

将事件添加到对象检查器中的 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;

在此示例中,用户检查一个框或没有框的能力有限。