【问题标题】:How to display NULL value as unchecked state in a TDBCheckBox descendant?如何在 TDBCheckBox 后代中将 NULL 值显示为未选中状态?
【发布时间】:2015-01-23 21:58:31
【问题描述】:

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

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

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

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

【问题讨论】:

  • 对不起我的英语:-)
  • 现在手头只有 Delphi 2009... 这个控件的代码不多,所以你可以考虑创建一个TCustomCheckBox 控件后代。或者您可以覆盖TDBCheckBox 的构造函数,为FDataLinkOnDataChangeOnUpdateData 事件提供您自己的处理程序(关闭AllowGrayed 属性)。或者您可以在获取时COALESCE(Field, False)您的字段数据(如果这是一个选项)。或者,您可以为数据集创建一个计算字段。或者也许数据集提供者可以为你在全球范围内进行这种转换......
  • 非常感谢您的回答。我为 FDataLink 的 OnDataChange 和 OnUpdateData 事件覆盖了 TDBCheckBox 的构造函数,但它不起作用((。我有很多数据集,所以我无法添加计算字段。
  • Err,收回我为FDataLink 提供事件处理程序的想法,它只是一个私有字段,至少在 D2009 中是这样(不能说是否改变了,甚至你有什么版本)。那里几乎是封闭控制。
  • 也许我可以使用 WMPaint(var Message: TWMPaint);消息 WM_PAINT?

标签: delphi delphi-6


【解决方案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;

【讨论】:

  • 非常感谢您的回答
【解决方案2】:

感谢大家的帮助!

我按照以下方式实现它:

  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;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-30
    • 2015-11-28
    • 2021-12-22
    • 2016-06-29
    • 1970-01-01
    • 2019-06-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多