diff --git a/SimpleMVVM.Binding.Components.pas b/SimpleMVVM.Binding.Components.pas index d747ca3..c120603 100644 --- a/SimpleMVVM.Binding.Components.pas +++ b/SimpleMVVM.Binding.Components.pas @@ -18,25 +18,13 @@ interface SimpleMVVM.Observable; type - ICommand = interface - procedure Execute; - end; - - TCommand = class(TInterfacedObject, ICommand) - private - fMethod: TRttiMethod; - fInstance: TObject; - procedure Execute; - public - constructor Create(const method: TRttiMethod; const instance: TObject); - end; - TBinding = class(TComponent) private fObservable: IObservable; protected procedure InitComponent; virtual; - function Initialize(const observable: IObservable): TFunc; virtual; + procedure InitObservable(const observable: IObservable); virtual; + function InitGetValue(const observable: IObservable): TFunc; virtual; procedure SetComponent(const component: TComponent); virtual; abstract; public constructor Create(const component: TComponent; @@ -56,40 +44,36 @@ TBinding = class(TBinding) TComponentBinding = class(TBinding) protected fProperty: TRttiProperty; - function Initialize(const observable: IObservable): TFunc; override; + function InitGetValue(const observable: IObservable): TFunc; override; public constructor Create(const component: TComponent; const observable: IObservable; const propertyName: string); reintroduce; end; - TButtonBinding = class(TComponent) - private - fAction: ICommand; + TButtonBinding = class(TBinding) protected - fComponent: TButton; - procedure Initialize; procedure HandleClick(Sender: TObject); - public - constructor Create(const component: TButton; const action: ICommand); reintroduce; + procedure InitComponent; override; + procedure InitObservable(const observable: IObservable); override; end; TEditBinding = class(TBinding) protected procedure HandleChange(Sender: TObject); procedure InitComponent; override; - function Initialize(const observable: IObservable): TFunc; override; + function InitGetValue(const observable: IObservable): TFunc; override; end; TComboBoxBinding = class(TBinding) protected procedure HandleChange(Sender: TObject); procedure InitComponent; override; - function Initialize(const observable: IObservable): TFunc; override; + function InitGetValue(const observable: IObservable): TFunc; override; end; TLabelBinding = class(TBinding) protected - function Initialize(const observable: IObservable): TFunc; override; + function InitGetValue(const observable: IObservable): TFunc; override; end; function GetBindingClass(const target: TObject; const expression: string): TBindingClass; @@ -108,6 +92,8 @@ function GetBindingClass(const target: TObject; const expression: string): TBind Result := TComboBoxBinding else if (target is TLabel) and SameText(expression, 'Text') then Result := TLabelBinding + else if (target is TButton) and SameText(expression, 'Click') then + Result := TButtonBinding else Result := nil; end; @@ -140,23 +126,6 @@ procedure TControlHelper.SetDisabled(const value: Boolean); {$ENDREGION} -{$REGION 'TCommand'} - -constructor TCommand.Create(const method: TRttiMethod; const instance: TObject); -begin - inherited Create; - fMethod := method; - fInstance := instance; -end; - -procedure TCommand.Execute; -begin - fMethod.Invoke(fInstance, []); -end; - -{$ENDREGION} - - {$REGION 'TBinding'} constructor TBinding.Create(const component: TComponent; @@ -164,12 +133,7 @@ constructor TBinding.Create(const component: TComponent; begin inherited Create(component); SetComponent(component); - fObservable := TDependentObservable.Create( - Initialize(observable), - procedure(const value: TValue) - begin - observable.Value := value; - end); + InitObservable(observable); InitComponent; end; @@ -177,7 +141,7 @@ procedure TBinding.InitComponent; begin end; -function TBinding.Initialize(const observable: IObservable): TFunc; +function TBinding.InitGetValue(const observable: IObservable): TFunc; begin Result := function: TValue @@ -186,6 +150,16 @@ function TBinding.Initialize(const observable: IObservable): TFunc; end; end; +procedure TBinding.InitObservable(const observable: IObservable); +begin + fObservable := TDependentObservable.Create( + InitGetValue(observable), + procedure(const value: TValue) + begin + observable.Value := value; + end); +end; + {$ENDREGION} @@ -218,7 +192,7 @@ constructor TComponentBinding.Create(const component: TComponent; inherited Create(component, observable); end; -function TComponentBinding.Initialize( +function TComponentBinding.InitGetValue( const observable: IObservable): TFunc; begin Result := @@ -240,23 +214,19 @@ function TComponentBinding.Initialize( {$REGION 'TButtonBinding'} -constructor TButtonBinding.Create(const component: TButton; - const action: ICommand); +procedure TButtonBinding.HandleClick(Sender: TObject); begin - inherited Create(component); - fComponent := component; - fAction := action; - Initialize; + fObservable.Value; end; -procedure TButtonBinding.HandleClick(Sender: TObject); +procedure TButtonBinding.InitComponent; begin - fAction.Execute; + fComponent.OnClick := HandleClick; end; -procedure TButtonBinding.Initialize; +procedure TButtonBinding.InitObservable(const observable: IObservable); begin - fComponent.OnClick := HandleClick; + fObservable := observable; end; {$ENDREGION} @@ -274,7 +244,7 @@ procedure TEditBinding.InitComponent; fComponent.OnChange := HandleChange; end; -function TEditBinding.Initialize(const observable: IObservable): TFunc; +function TEditBinding.InitGetValue(const observable: IObservable): TFunc; begin Result := function: TValue @@ -309,7 +279,7 @@ procedure TComboBoxBinding.InitComponent; fComponent.OnChange := HandleChange; end; -function TComboBoxBinding.Initialize(const observable: IObservable): TFunc; +function TComboBoxBinding.InitGetValue(const observable: IObservable): TFunc; begin Result := function: TValue @@ -329,7 +299,7 @@ function TComboBoxBinding.Initialize(const observable: IObservable): TFunc; +function TLabelBinding.InitGetValue(const observable: IObservable): TFunc; begin Result := function: TValue diff --git a/SimpleMVVM.Binding.pas b/SimpleMVVM.Binding.pas index 0657b1b..a38a5a4 100644 --- a/SimpleMVVM.Binding.pas +++ b/SimpleMVVM.Binding.pas @@ -175,7 +175,7 @@ function CreateObservable(instance: TObject; typ := prop.PropertyType; end; if i < High(expressions) then - instance := prop.GetValue(instance).AsObject; + instance := Result.Value.AsObject; end; end; end; @@ -186,7 +186,6 @@ procedure Bind(const target: TComponent; const targetExpression: string; observable: IObservable; typ: TRttiType; method: TRttiMethod; - command: ICommand; bindingClass: TBindingClass; begin observable := CreateObservable(source, sourceExpression); @@ -196,16 +195,18 @@ procedure Bind(const target: TComponent; const targetExpression: string; typ := ctx.GetType(source.ClassInfo); method := typ.GetMethod(sourceExpression); if Assigned(method) then - command := TCommand.Create(method, source); + observable := TObservable.Create( + function: TValue + begin + Result := method.Invoke(source, []); + end); end; - Assert(Assigned(observable) or Assigned(command), 'expression not found: ' + sourceExpression); + Assert(Assigned(observable), 'expression not found: ' + sourceExpression); bindingClass := GetBindingClass(target, targetExpression); if Assigned(bindingClass) then bindingClass.Create(target, observable) - else if (target is TButton) and SameText(targetExpression, 'Click') then - TButtonBinding.Create(TButton(target), command) else TComponentBinding.Create(target, observable, targetExpression); end; diff --git a/SimpleMVVM.Observable.pas b/SimpleMVVM.Observable.pas index 1051acf..d5017b7 100644 --- a/SimpleMVVM.Observable.pas +++ b/SimpleMVVM.Observable.pas @@ -17,7 +17,7 @@ interface SysUtils; type - IObservable = interface + IObservable = interface(IInvokable) ['{3F78EF38-FA16-4E08-AD8D-3FD9A5E44BEF}'] function GetValue: TValue; procedure SetValue(const value: TValue); @@ -51,6 +51,18 @@ TObservableBase = class(TInterfacedObject, IObservable) destructor Destroy; override; end; + TObservable = class(TObservableBase) + private + fGetter: TFunc; + fSetter: TAction; + protected + function GetValueNonGeneric: TValue; override; final; + procedure SetValueNonGeneric(const value: TValue); override; final; + public + constructor Create(const getter: TFunc); overload; + constructor Create(const getter: TFunc; const setter: TAction); overload; + end; + TDependentObservable = class(TObservableBase) protected fValue: TValue; @@ -80,7 +92,6 @@ TObservable = class(TObservableBase, IObservable) public constructor Create; overload; constructor Create(const value: T); overload; - property Value: T read GetValue write SetValue; end; TDependentObservable = class(TObservableBase, IObservable) @@ -115,6 +126,7 @@ implementation function TValueHelper.ToType: T; begin + Result := Default(T); if not TryAsType(Result) then // hardcode some simple conversions for demo purpose - use Spring4D value converter later case Kind of @@ -173,6 +185,36 @@ procedure TObservableBase.RegisterDependency; {$ENDREGION} +{$REGION 'TObservable'} + +constructor TObservable.Create(const getter: TFunc); +begin + Create(getter, nil); +end; + +constructor TObservable.Create(const getter: TFunc; + const setter: TAction); +begin + inherited Create; + fGetter := getter; + fSetter := setter; +end; + +function TObservable.GetValueNonGeneric: TValue; +begin + RegisterDependency; + Result := fGetter; +end; + +procedure TObservable.SetValueNonGeneric(const value: TValue); +begin + fSetter(value); + Changed; +end; + +{$ENDREGION} + + {$REGION 'TDependentObservable'} constructor TDependentObservable.Create(const getter: TFunc);