AbhiJeet
asked on
delphi - 2
in a panel i have two check boxes. User can only check one checkbox at a time. It should show a message and doesn't allow to check the check box if one is already checked.
? to implement?( don't tell me to take radio button)
? to implement?( don't tell me to take radio button)
take radiobutton :)
ASKER
i am searching a way to implement this taking tcheckbox only, any idea?
It's your choice at the end to use the component you prefer.
To have the TCheckBoxes do exactly what you mentioned then you have many options:
Or:
To have the TCheckBoxes do exactly what you mentioned then you have many options:
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if CheckBox1.Checked then
CheckBox2.Enabled := False
else
CheckBox2.Enabled := True;
end;
procedure TForm1.CheckBox2Click(Sender: TObject);
begin
if CheckBox1.Checked then
CheckBox1.Enabled := False
else
CheckBox1.Enabled := True;
end;
Or:
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if CheckBox2.Checked then
begin
CheckBox1.Checked := False;
CheckBox1.Hint := 'uncheck the other one first'; // or whatever hint you see better
CheckBox1.ShowHint := True;
end;
if not CheckBox1.Checked then
begin
CheckBox2.Hint := '';
CheckBox2.ShowHint := False;
end;
end;
procedure TForm1.CheckBox2Click(Sender: TObject);
begin
if CheckBox1.Checked then
begin
(Sender as TCheckBox).Checked := False;
(Sender as TCheckBox).Hint := 'uncheck the other one first';
(Sender as TCheckBox).ShowHint := True;
end;
if not CheckBox2.Checked then
begin
CheckBox1.Hint := '';
CheckBox1.ShowHint := False;
end;
end;
ASKER
checkboxes are getting created dynamically, so i may not able to use these events easily.
Any other nice workaround?
Any other nice workaround?
ASKER
and obviously there are lot other components in the panel.
create procedure as jimyX suggest and attach it to runtime created components in code:
chk1.OnClick := CheckBox1Click;
chk2.OnClick := CheckBox2Click;
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
What is wrong with my example?
Did you try it?
It can work with runtime created components also.
Did you try it?
It can work with runtime created components also.
jimmy, doesn't the onclick fire when setting the checked ?
i came up with this... somewhat similar
pasfile:
dfm:
basically, assign the onclick of all the checkboxes to this event
i see jimmy also hinted towards your next question
i came up with this... somewhat similar
pasfile:
unit uMain;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TfrmCheckboxMaster = class(TForm)
pnlCheckOptions: TPanel;
chkItem1: TCheckBox;
chkItem3: TCheckBox;
chkItem2: TCheckBox;
chkItem4: TCheckBox;
procedure chkSingleMasterCheckboxClick(Sender: TObject);
private
public
end;
var
frmCheckboxMaster: TfrmCheckboxMaster;
implementation
{$R *.dfm}
procedure TfrmCheckboxMaster.chkSingleMasterCheckboxClick(Sender: TObject);
var
MasterItem, SlaveItem: TCheckbox;
MasterContainer: TWinControl;
I: Integer;
begin
// The master is the checkbox clicked
// if checked, all other checkboxes in the same panel are disabled and unchecked
// if unchecked all other checkboxes in the same panel are enabled and unchecked
MasterItem := Sender as TCheckbox;
// Find the parent of the Master checkbox
MasterContainer := MasterItem.Parent;
// Traverse all items in same panel
for I := 0 to MasterContainer.ControlCount - 1 do
begin
// it the item is of the same class in this container, but not the same object
if (MasterContainer.Controls[I].ClassType = MasterItem.ClassType) and
(MasterContainer.Controls[I] <> MasterItem) then
begin
// Placeholder for the slave item
SlaveItem := MasterContainer.Controls[I] as TCheckBox;
// detach the onclick event
SlaveItem.OnClick := Nil;
try
// If master is checked
if MasterItem.Checked then
begin
// Uncheck the slave item
if SlaveItem.Checked then
SlaveItem.Checked := False;
// Disable the slave item
SlaveItem.Enabled := False;
end
else // master is not checked
begin
if not SlaveItem.Enabled then
SlaveItem.Enabled := True;
end;
finally
// Attach the onclick event again
SlaveItem.OnClick := MasterItem.OnClick;
end;
end;
end;
end;
end.
dfm:
object frmCheckboxMaster: TfrmCheckboxMaster
Left = 689
Top = 164
Caption = 'frmCheckboxMaster'
ClientHeight = 346
ClientWidth = 637
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object pnlCheckOptions: TPanel
Left = 24
Top = 40
Width = 489
Height = 249
TabOrder = 0
object chkItem1: TCheckBox
Left = 48
Top = 16
Width = 97
Height = 17
Caption = 'chkItem1'
TabOrder = 0
OnClick = chkSingleMasterCheckboxClick
end
object chkItem3: TCheckBox
Left = 120
Top = 48
Width = 97
Height = 17
Caption = 'chkItem3'
TabOrder = 1
OnClick = chkSingleMasterCheckboxClick
end
object chkItem2: TCheckBox
Left = 72
Top = 72
Width = 97
Height = 17
Caption = 'chkItem2'
TabOrder = 2
OnClick = chkSingleMasterCheckboxClick
end
object chkItem4: TCheckBox
Left = 112
Top = 112
Width = 97
Height = 17
Caption = 'chkItem4'
TabOrder = 3
OnClick = chkSingleMasterCheckboxClick
end
end
end
basically, assign the onclick of all the checkboxes to this event
i see jimmy also hinted towards your next question
> onclick fire when setting the checked
Correct Geert, that's why I set it back to false.
It was obvious that there are two ways to accomplish what the asker is after, either disable the other checkboxes or notify the user that there is a checkbox already checked and undo the checking.
The difference is not that much since you have to iterate through the TCheckBoxes on the panel.
For disabling the other checkboxes same code will be used but the procedure 'ChkBxsTicked' would be:
Correct Geert, that's why I set it back to false.
It was obvious that there are two ways to accomplish what the asker is after, either disable the other checkboxes or notify the user that there is a checkbox already checked and undo the checking.
The difference is not that much since you have to iterate through the TCheckBoxes on the panel.
For disabling the other checkboxes same code will be used but the procedure 'ChkBxsTicked' would be:
procedure ChkBxsTicked(Pnl:TPanel; Sndr: TObject);
var
I: Integer;
OthrChkBx: TControl;
begin
if Pnl <> nil then
begin
for I := 0 to Pnl.ControlCount - 1 do
begin
if Pnl.Controls[I] is TCheckBox then
begin
OthrChkBx := Pnl.Controls[I];
if (OthrChkBx is TCheckBox) and (OthrChkBx <> Sndr) then
begin
if (Sndr AS TCheckbox).Checked then
(OthrChkBx as TCheckBox).Enabled := False
else
(OthrChkBx as TCheckBox).Enabled := True;
end;
end;
end;
end;
end;
fwiw ... pnl is the same as Sender.Parent
Missed that. You are absolutely right.
Here is an example that registers any check box created dynamically and handles the click independent of the form
and the form
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TNotifyList = class(TComponent)
private
FList: TList;
FOnClick: TNotifyEvent;
procedure DoClickCheck(Sender: TObject);
function GetCheckedItem(ASkip: TCheckBox; out AItem: TCheckBox): Boolean;
procedure SetOnClick(const Value: TNotifyEvent);
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
public
constructor Create(Aowner: TComponent); override;
destructor Destroy; override;
procedure RegisterItem(AItem: TCheckBox);
property OnClick: TNotifyEvent read FOnClick write SetOnClick;
end;
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
FNotifyList: TNotifyList;
procedure OtherItemCheckedMessage(Sender: TObject);
procedure TestCreateCheckBox(Top: Integer);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TNotifyList }
constructor TNotifyList.Create(AOwner: TComponent);
begin
inherited;
FList := TList.Create;
end;
destructor TNotifyList.Destroy;
begin
FreeAndNil(FList);
inherited;
end;
procedure TNotifyList.DoClickCheck(Sender: TObject);
var
Chk, Chk2: TCheckBox;
Index: Integer;
begin
Index := FList.IndexOf(Sender);
if Index >= 0 then
begin
Chk := TCheckBox(FList[Index]);
if Chk.Checked and GetCheckedItem(Chk, Chk2) then
begin
Chk.OnClick := nil;
Chk.Checked := False;
Chk.OnClick := DoClickCheck;
if Assigned(FOnClick) then
FOnClick(Chk2);
end;
end;
end;
function TNotifyList.GetCheckedItem(ASkip: TCheckBox; out AItem: TCheckBox): Boolean;
var
I: Integer;
begin
for I := 0 to FList.Count -1 do
if TCheckBox(FList[I]).Checked then
begin
AItem := TCheckBox(FList[I]);
if AItem.Checked and (ASkip <> AItem) then
begin
Result := True;
Exit;
end;
end;
Result := False;
end;
procedure TNotifyList.Notification(AComponent: TComponent; Operation: TOperation);
var
Index: Integer;
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) and (AComponent is TCheckBox) then
begin
Index := FList.IndexOf(AComponent);
if Index >= 0 then
FList.Delete(Index);
end;
end;
procedure TNotifyList.RegisterItem(AItem: TCheckBox);
begin
if FList.IndexOf(AItem) < 0 then
begin
FList.Add(AItem);
AItem.OnClick := DoClickCheck;
end;
end;
procedure TNotifyList.SetOnClick(const Value: TNotifyEvent);
begin
FOnClick := Value;
end;
//Test
procedure TForm1.FormCreate(Sender: TObject);
var
I: Integer;
begin
FNotifyList := TNotifyList.Create(Self);
FNotifyList.OnClick := OtherItemCheckedMessage;
//Create five check boxes
for I := 1 to 5 do
TestCreateCheckBox(I * 20);
end;
procedure TForm1.OtherItemCheckedMessage(Sender: TObject);
begin
if Sender is TCheckBox then
ShowMessage(TCheckBox(Sender).Caption + ' is already checked');
end;
procedure TForm1.TestCreateCheckBox(Top: Integer);
var
Chk: TCheckBox;
begin
Chk := TCheckBox.Create(Self);
Chk.Parent := Self;
Chk.Top := Top;
Chk.Left := 8;
Chk.Caption := 'CheckBox' + IntToStr(Top);
FNotifyList.RegisterItem(Chk);
end;
end.
and the form
object Form1: TForm1
Left = 192
Top = 124
Width = 870
Height = 640
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
end
In attached example, I'm using it application wide.
Regards,
Steve
Unit1.pas
Unit1.dfm