一次只检查一个CheckBox

Se7*_*7en 1 delphi vcl delphi-xe2

[编译: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;
Run Code Online (Sandbox Code Playgroud)

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

像 -

if cbOptions.ItemChecked[ItmIndex0] then
  cbOptions.ItemChecked[ItmIndex1] := False
else cbOptions.ItemChecked[ItmIndex1] := True;
Run Code Online (Sandbox Code Playgroud)

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

似乎事件停止工作,并且由于某种原因,我的一些尝试已经解雇了两次.

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

任何有关这方面的帮助将非常感激.

谢谢.

if cbOptions.ItemChecked[ItmIndex0] then
  cbOptions.ItemChecked[ItmIndex1] := False
else if cbOptions.ItemChecked[ItmIndex1] then
  cbOptions.ItemChecked[ItmIndex0] := False;
Run Code Online (Sandbox Code Playgroud)

如果第二个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;
Run Code Online (Sandbox Code Playgroud)

Ken*_*ite 6

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

TCheckBoxState 在Delphi文档中定义为

TCheckBoxState = (
  cbUnchecked,
  cbChecked,
  cbGrayed
);
Run Code Online (Sandbox Code Playgroud)

所以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;
Run Code Online (Sandbox Code Playgroud)

要反转两个复选框的状态(将一个复选框更改为备用状态而另一个复选框相反),您可以使用类似的东西(使用两个标准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;
Run Code Online (Sandbox Code Playgroud)

但是,如果您只是处理一个项目列表,其中应该检查一个项目而其他所有项目都未选中,那么您应该使用一个项目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;
Run Code Online (Sandbox Code Playgroud)

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