如何在 TDBCheckBox 后代中将 NULL 值显示为未选中状态?
How to display NULL value as unchecked state in a TDBCheckBox descendant?
具有布尔类型的可空数据集字段,如何在链接到该字段的 TDBCheckBox
控件后代中将其 NULL
值显示为未选中状态。默认情况下,TDBCheckBox
将字段的 NULL
值显示为灰色复选框:
但我需要它在我的 TDBCheckBox
控件后代中显示为未选中状态:
修改原始 TDBCheckBox
源代码对我来说不是一个选项,我也不能覆盖 TDBCheckBox.GetFieldState
因为它是一个私有方法。
那么,如何在我的 TDBCheckBox
后代中将 NULL
值显示为未选中状态?
如果您的项目是闭源项目,我建议复制一份 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
继承的技巧,并完全相同地调用您的 class。然后,您可以将其放入某个单元,并在您的使用中将 添加到 和 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;
感谢大家的帮助!
我是这样实现的:
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;
具有布尔类型的可空数据集字段,如何在链接到该字段的 TDBCheckBox
控件后代中将其 NULL
值显示为未选中状态。默认情况下,TDBCheckBox
将字段的 NULL
值显示为灰色复选框:
但我需要它在我的 TDBCheckBox
控件后代中显示为未选中状态:
修改原始 TDBCheckBox
源代码对我来说不是一个选项,我也不能覆盖 TDBCheckBox.GetFieldState
因为它是一个私有方法。
那么,如何在我的 TDBCheckBox
后代中将 NULL
值显示为未选中状态?
如果您的项目是闭源项目,我建议复制一份 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
继承的技巧,并完全相同地调用您的 class。然后,您可以将其放入某个单元,并在您的使用中将 添加到 和 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;
感谢大家的帮助!
我是这样实现的:
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;