我有一个Checkbox1,我想自定义,当用户点击复选框的标题(文本)时,它不会改变它的状态(选中/取消选中),但只有在点击时才会这样做实际的复选框方块。
这是当前代码,包含2个全局变量,一个表示何时跳过更改状态,另一个表示当前状态并确保它在OnClick之后保持不变 - 因为当流程处于OnClick状态时状态已经更改:
var
gSkipClick:boolean = false;
gPrevState:boolean;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
// Make sure previous state is assigned when Skip
if gSkipClick then
Checkbox1.Checked := gPrevState;
end;
procedure TForm1.CheckBox1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if (x > 12) then
begin
// Click outside checkbox square
gSkipClick := True; // skip Click
gPrevState := CheckBox1.Checked; // save current state
end
else
gSkipClick := False; // enable Click
end;
现在我想用TCheckBox中的2个新属性来替换全局变量:
TCheckBox = class(Vcl.StdCtrls.TCustomCheckBox)
private
FSkipStateChange:Boolean;
FPrevState:Boolean;
protected
published
property SkipStateChange:Boolean read FSkipStateChange write FSkipStateChange;
property PrevState:Boolean read FPrevState write FPrevState;
end;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
// Click outside checkbox square
If TCheckBox(Checkbox1).SkipStateChange Then
Checkbox1.Checked := TCheckBox(Checkbox1).PrevState;
end;
procedure TForm1.CheckBox1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if (x > 12) then
begin
// Click outside checkbox square
TCheckBox(Checkbox1).SkipStateChange := True;
TCheckBox(Checkbox1).PrevState := Checkbox1.Checked;
end
else
TCheckBox(Checkbox1).SkipStateChange := False;
end;
它可以工作,但是如果我点击Caption然后关闭表单,就会出现这个错误:
Project Project1.exe引发了带有消息的异常类$ C0000005 '访问冲突0x004080c5:读取地址0x0000000d'。
错误发生在系统部件的procedure TMonitor.Destroy;
中:
procedure TMonitor.Destroy;
begin
if (MonitorSupport <> nil) and (FLockEvent <> nil) then { <-- ERROR ON THIS LINE}
MonitorSupport.FreeSyncObject(FLockEvent);
FreeMem(@Self);
end;
我做错了什么,为什么会出现错误?
答案 0 :(得分:5)
首先回答问题,询问运行时错误。您的插入器类需要在表单类之前声明。你还没有这样做,因为你需要投射。你写了
TCheckBox(Checkbox1)
但如果插入器被正确声明,你可以写
CheckBox1
没有演员。
再次查看插入器模式的每个代码示例。内插器类在使用它的任何类之前声明。
因为在代码中的表单之后声明了插入器,所以流机制实例化原始类而不是插入的类。因此,您的强制转换无效并导致运行时内存损坏。
定义一个类型并强制转换它是不够的。您必须确保实例化此类型。对象的类型在实例化时确定。
通常,您应该始终使用is
或as
检查您的演员。如果你这样做了,系统可以告诉你出了什么问题。虽然你本来应该问自己为什么要在第一时间施展。这应该是警告信号。请记住,您始终可以通过强制转换来抑制编译器错误,但这样做并不能解决问题。将某些东西变成某种东西实际上并不是这样。它只是告诉编译器一个很大的谎言,最终它得到报复!
对于插入者,您应该从Vcl.StdCtrls.TCheckBox
而不是Vcl.StdCtrls.TCustomCheckBox
派生。
在派生的插入器类和表单之间混合代码是非常糟糕的设计。如果你想在不同的表格上做类似的事情,你真的要再次复制代码吗?像这样的代码需要在控件中自包含。
另一方面,逻辑可能特定于表格。在这种情况下,它应该住在那里,你会使用标准复选框。它让我充满了你提到使用全局变量的祸患。如果需要与表单关联的变量,请将它们添加到表单类。
批评您的UI设计的评论者在我看来是正确的。用户喜欢可预测性。当控件以他们期望的方式运行时,任何人都不会感到沮丧,就像系统中其他类似控件的行为一样。停止修改标准控件,使用它们,让用户能够预测程序的行为。