如何在TDBCheckBox后代中将NULL值显示为未检查状态?

时间:2015-01-23 21:58:32

标签: delphi delphi-6

具有布尔类型的可空数据集字段,如何在链接到此字段的NULL控件后代中将其TDBCheckBox值显示为未选中状态。默认情况下,TDBCheckBox会将字段的NULL值显示为灰色复选框:

enter image description here

但是我需要它在我的TDBCheckBox控件后代中显示为未选中状态:

enter image description here

修改原始TDBCheckBox源代码对我来说不是一个选项,也不能覆盖TDBCheckBox.GetFieldState,因为它是私有方法。

那么,如何在NULL后代中将TDBCheckBox值显示为未选中状态?

2 个答案:

答案 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;