具有布尔类型的可空数据集字段,如何在链接到此字段的NULL
控件后代中将其TDBCheckBox
值显示为未选中状态。默认情况下,TDBCheckBox
会将字段的NULL
值显示为灰色复选框:
但是我需要它在我的TDBCheckBox
控件后代中显示为未选中状态:
修改原始TDBCheckBox
源代码对我来说不是一个选项,也不能覆盖TDBCheckBox.GetFieldState
,因为它是私有方法。
那么,如何在NULL
后代中将TDBCheckBox
值显示为未选中状态?
答案 0 :(得分:1)
如果您的项目已关闭,我建议您复制一份DBCtrls
,将其中的一行更改为Result := cbGrayed
并将其明确添加到您的项目中。更改将在您的整个应用程序中,但不会更改原始代码。
然而还有另一种方法 - 实际上是一个hack所以小心我建议放一个编译器指令,防止在不同的Delphi版本中编译它,要求再次查看该代码并确保它的工作原理。
这是在Delphi XE中运行的代码 - 在Delphi 6中可能看起来不同但你会明白这一点。
type
TDBCheckBoxHack = class(TCustomCheckBox)
private
FDataLink: TFieldDataLink;
FValueCheck: string;
FValueUncheck: string;
procedure DataChange(Sender: TObject);
function GetFieldState: TCheckBoxState;
function ValueMatch(const ValueList, Value: string): Boolean;
end;
我将这些方法的实施保留下来,因为它们只是原始受版权保护的代码的副本。您必须在GetFieldState
方法中更改一行或两行。
诀窍是创建与原始TDBCheckBox相同的内存布局,以便您可以访问私有字段 - 这就是为什么应该谨慎使用此代码!
然后将固定的DataChange
方法分配给数据链接:
TDBCheckBoxHack(DBCheckBox1).FDataLink.OnDataChange :=
TDBCheckBoxHack(DBCheckBox1).DataChange;
为了使这更容易使用,您可以使用从原始TDBCheckBox
继承的另一种技巧,并使您的类完全相同。然后,您可以将其放入某个单元,并在 DBCtrls
之后添加此。这会导致您在表单上放置的每个TDBCheckBox
调用构造函数,而无需使用自己的构造函数并将其注册到IDE:
type
TDBCheckBox = class(DBCtrls.TDBCheckBox)
public
constructor Create(AOwner: TComponent); override;
end;
constructor TDBCheckBox.Create(AOwner: TComponent);
begin
inherited;
TDBCheckBoxHack(Self).FDataLink.OnDataChange := TDBCheckBoxHack(Self).DataChange;
end;
答案 1 :(得分:0)
谢谢大家的帮助!
我按照以下方式实施:
type
TDBCheckBoxHack = class(TDBCheckBox)
FDataLink: TFieldDataLink;
procedure DataChange(Sender: TObject); virtual;
function GetFieldState: TCheckBoxState; virtual;
public
constructor Create(AOwner: TComponent); override;
end;
在构造函数中,我使用消息CM_GETDATALINK将DataLink检索到我的组件
constructor TDBCheckBoxHack.Create(AOwner: TComponent);
var
AMessage: TMessage;
begin
inherited;
FillChar(AMessage, 0, sizeof(AMessage));
AMessage.Msg := CM_GETDATALINK;
Dispatch(AMessage);
FDataLink := TFieldDataLink(Pointer(AMessage.Result));
FDataLink.OnDataChange := DataChange;
end;
和GetFieldState实现:
function TFsDBCheckBox.GetFieldState: TCheckBoxState;
var
Text: string;
begin
if FDatalink.Field <> nil then
if FDataLink.Field.IsNull then
Result := cbUnchecked
else if FDataLink.Field.DataType = ftBoolean then
if FDataLink.Field.AsBoolean then
Result := cbChecked
else
Result := cbUnchecked
else
begin
Result := cbGrayed;
Text := FDataLink.Field.Text;
if ValueMatch(ValueChecked, Text) then Result := cbChecked else
if ValueMatch(ValueUnchecked, Text) then Result := cbUnchecked;
end
else
Result := cbUnchecked;
end;