【问题标题】:How to write live binding expression that control TEdit.PasswordChar based on TCheckBox.Checked?如何编写基于 TCheckBox.Checked 控制 TEdit.PasswordChar 的实时绑定表达式?
【发布时间】:2012-07-20 05:33:42
【问题描述】:

我在一个表单上有 2 个控件,TCheckBox 和 TEdit。

我想使用实时绑定来执行此操作:

  1. 当 TCheckBox.Checked = True 时,设置 TEdit.PasswordChar = *
  2. 当 TCheckBox.Checked = False 时,设置 TEdit.PasswordChar = #0

我该如何编写 ControlExpression 来实现这一点?如果我可以避免注册自定义方法,那就太好了。

【问题讨论】:

  • 更好地将复选框分配给操作并在执行时编写“本机”代码。也许在某些情况下使用实时绑定是有原因的,但它们绝对不适合懒惰的程序员;)

标签: delphi livebindings


【解决方案1】:

这是一个简单的例子。我找不到布尔表达式评估器,所以我注册了一个新的,还有一个字符串到字符的转换器(似乎也丢失了)。

形式:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 282
  ClientWidth = 418
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object CheckBox1: TCheckBox
    Left = 24
    Top = 24
    Width = 97
    Height = 17
    Caption = 'CheckBox1'
    TabOrder = 0
    OnClick = CheckBox1Click
  end
  object Edit1: TEdit
    Left = 24
    Top = 56
    Width = 121
    Height = 21
    TabOrder = 1
    Text = 'Edit1'
  end
  object BindingsList1: TBindingsList
    Methods = <>
    OutputConverters = <>
    UseAppManager = True
    Left = 212
    Top = 13
    object BindExpression1: TBindExpression
      Category = 'Binding Expressions'
      ControlComponent = Edit1
      SourceComponent = CheckBox1
      SourceExpression = 'iif(Checked, '#39'*'#39', '#39#39')'
      ControlExpression = 'PasswordChar'
      NotifyOutputs = True
      Direction = dirSourceToControl
    end
  end
end

和代码:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Data.Bind.EngExt, Vcl.Bind.DBEngExt, System.Rtti,
  Vcl.Bind.Editors, Data.Bind.Components, System.Bindings.Outputs;

type
  TForm1 = class(TForm)
    CheckBox1: TCheckBox;
    Edit1: TEdit;
    BindingsList1: TBindingsList;
    BindExpression1: TBindExpression;
    procedure CheckBox1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  System.TypInfo,
  System.Bindings.EvalProtocol,
  System.Bindings.Methods;

resourcestring
  sIifArgError = 'Expected three variables for Iif() call';
  sIifExpectedBoolean = 'First argument to Iif() must be a boolean';

function MakeIif: IInvokable;
begin
  Result := MakeInvokable(
    function(Args: TArray<IValue>): IValue
    var
      V: IValue;
      B: Boolean;
    begin
      if Length(Args) <> 3 then
        raise EEvaluatorError.Create(sIifArgError);
      V := Args[0];
      if (V.GetType^.Kind <> tkEnumeration) or (V.GetType^.Name <> 'Boolean') then
        raise EEvaluatorError.Create(sIifExpectedBoolean);

      B := V.GetValue.AsBoolean;
      if B then
        Result := TValueWrapper.Create(Args[1].GetValue)
      else
        Result := TValueWrapper.Create(Args[2].Getvalue);
    end
  );
end;

procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  BindingsList1.Notify(CheckBox1, 'Checked');
end;

initialization
  TBindingMethodsFactory.RegisterMethod(TMethodDescription.Create(MakeIif, 'iif', 'iif', '', True, '', nil));
  TValueRefConverterFactory.RegisterConversion(TypeInfo(string), TypeInfo(Char),
    TConverterDescription.Create(
      procedure(const I: TValue; var O: TValue)
      var
        S: string;
      begin
        S := I.AsString;
        if Length(S) = 1 then
          O := S[1]
        else
          O := #0;
      end,
      'StringToChar', 'StringToChar', '', True, '', nil));

finalization
  TValueRefConverterFactory.UnRegisterConversion('StringToChar');
  TBindingMethodsFactory.UnRegisterMethod('iif');

end.

【讨论】:

  • 这个例子显示了活绑定设计是多么的荒谬。编写大量代码,仍然必须实现 OnClick 事件。
  • 您不必必须实现OnClick 事件处理程序,如此处所示。还有其他可能性(例如,Application.OnIdle 使用通用代码自动重新评估所有活动绑定,类似于 Actions)。
  • 更糟。组件应该具有绑定意识,理论上它们是因为它们支持在处理数据集 afaik 时使用的 bindlink。但它不适用于组件到组件或组件到简单数据对象的绑定。
  • 同意,更好的方法是在源组件更改时自动评估所有绑定。也许这可以通过某种 RTTI 钩子来完成。
  • 编写 OnChange 事件处理程序很好,因为它可能有很大的机会被许多控件共享。绑定方式促进了代码复用,可以大大减少.pas文件中的代码,专注于UI控件交互以外的逻辑。
猜你喜欢
  • 2016-10-11
  • 1970-01-01
  • 2016-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多