diff --git a/TournamentDemo.dpr b/TournamentDemo.dpr
new file mode 100644
index 0000000..76d9778
--- /dev/null
+++ b/TournamentDemo.dpr
@@ -0,0 +1,15 @@
+program TournamentDemo;
+
+uses
+ Forms,
+ TournamentView in 'TournamentView.pas' {TournamentViewForm},
+ TournamentViewModel in 'TournamentViewModel.pas';
+
+{$R *.res}
+
+begin
+ Application.Initialize;
+ Application.MainFormOnTaskbar := True;
+ Application.CreateForm(TTournamentViewForm, TournamentViewForm);
+ Application.Run;
+end.
diff --git a/TournamentDemo.dproj b/TournamentDemo.dproj
new file mode 100644
index 0000000..1fed2f6
--- /dev/null
+++ b/TournamentDemo.dproj
@@ -0,0 +1,109 @@
+
+
+ {47EB8284-96E4-4E1C-9C4D-8F0BAD61081B}
+ 12.3
+ TournamentDemo.dpr
+ True
+ Debug
+ Win32
+ Application
+ VCL
+ DCC32
+
+
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Base
+ true
+
+
+ error
+ .\$(Config)\$(Platform)
+ .\$(Config)\$(Platform)
+ WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;$(DCC_UnitAlias)
+ 00400000
+
+
+ true
+ DEBUG;$(DCC_Define)
+ true
+ false
+
+
+ RELEASE;$(DCC_Define)
+ 0
+ false
+ false
+
+
+
+ MainSource
+
+
+
+
+
+
+ Cfg_2
+ Base
+
+
+ Base
+
+
+ Cfg_1
+ Base
+
+
+
+
+
+ Delphi.Personality.12
+
+
+
+
+ False
+ False
+ 1
+ 0
+ 0
+ 0
+ False
+ False
+ False
+ False
+ False
+ 1031
+ 1252
+
+
+
+
+ 1.0.0.0
+
+
+
+
+
+ 1.0.0.0
+
+
+
+
+
+ True
+
+
+ 12
+
+
diff --git a/TournamentView.dfm b/TournamentView.dfm
new file mode 100644
index 0000000..25b8b70
--- /dev/null
+++ b/TournamentView.dfm
@@ -0,0 +1,138 @@
+object TournamentViewForm: TTournamentViewForm
+ Left = 0
+ Top = 0
+ Caption = 'TournamentViewForm'
+ ClientHeight = 289
+ ClientWidth = 554
+ Color = clBtnFace
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -11
+ Font.Name = 'Tahoma'
+ Font.Style = []
+ OldCreateOrder = False
+ OnCreate = FormCreate
+ PixelsPerInch = 96
+ TextHeight = 13
+ object Label1: TLabel
+ Left = 16
+ Top = 32
+ Width = 31
+ Height = 13
+ Caption = 'Label1'
+ end
+ object Label2: TLabel
+ Left = 16
+ Top = 75
+ Width = 31
+ Height = 13
+ Caption = 'Label1'
+ end
+ object Label3: TLabel
+ Left = 16
+ Top = 144
+ Width = 31
+ Height = 13
+ Caption = 'Label1'
+ end
+ object Label4: TLabel
+ Left = 16
+ Top = 187
+ Width = 31
+ Height = 13
+ Caption = 'Label1'
+ end
+ object Label5: TLabel
+ Left = 240
+ Top = 56
+ Width = 31
+ Height = 13
+ Caption = 'Label1'
+ end
+ object Label6: TLabel
+ Left = 240
+ Top = 168
+ Width = 31
+ Height = 13
+ Caption = 'Label1'
+ end
+ object Label7: TLabel
+ Left = 464
+ Top = 128
+ Width = 31
+ Height = 13
+ Caption = 'Label1'
+ end
+ object Edit1: TEdit
+ Left = 16
+ Top = 48
+ Width = 121
+ Height = 21
+ TabOrder = 0
+ Text = 'Edit1'
+ end
+ object Edit2: TEdit
+ Left = 16
+ Top = 91
+ Width = 121
+ Height = 21
+ TabOrder = 1
+ Text = 'Edit2'
+ end
+ object Edit3: TEdit
+ Left = 16
+ Top = 160
+ Width = 121
+ Height = 21
+ TabOrder = 2
+ Text = 'Edit1'
+ end
+ object Edit4: TEdit
+ Left = 16
+ Top = 203
+ Width = 121
+ Height = 21
+ TabOrder = 3
+ Text = 'Edit2'
+ end
+ object Edit5: TEdit
+ Left = 240
+ Top = 72
+ Width = 121
+ Height = 21
+ TabOrder = 4
+ Text = 'Edit1'
+ end
+ object Edit6: TEdit
+ Left = 240
+ Top = 184
+ Width = 121
+ Height = 21
+ TabOrder = 5
+ Text = 'Edit2'
+ end
+ object Button1: TButton
+ Left = 143
+ Top = 70
+ Width = 75
+ Height = 25
+ Caption = 'Save score'
+ TabOrder = 6
+ end
+ object Button2: TButton
+ Left = 143
+ Top = 172
+ Width = 75
+ Height = 25
+ Caption = 'Save score'
+ TabOrder = 7
+ end
+ object Button3: TButton
+ Left = 368
+ Top = 123
+ Width = 75
+ Height = 25
+ Caption = 'Save score'
+ TabOrder = 8
+ end
+end
diff --git a/TournamentView.pas b/TournamentView.pas
new file mode 100644
index 0000000..d68496e
--- /dev/null
+++ b/TournamentView.pas
@@ -0,0 +1,70 @@
+unit TournamentView;
+
+interface
+
+uses
+ Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
+ Dialogs, StdCtrls, SimpleMVVM.Binding;
+
+type
+ TTournamentViewForm = class(TForm)
+
+ [Bind('Value', 'SemiFinalOne.HomeScore')]
+ Edit1: TEdit;
+ [Bind('Value', 'SemiFinalOne.AwayScore')]
+ Edit2: TEdit;
+ [Bind('Value', 'SemiFinalTwo.HomeScore')]
+ Edit3: TEdit;
+ [Bind('Value', 'SemiFinalTwo.AwayScore')]
+ Edit4: TEdit;
+ [Bind('Enabled', 'Finale.CanPlay')]
+ [Bind('Value', 'Finale.HomeScore')]
+ Edit5: TEdit;
+ [Bind('Enabled', 'Finale.CanPlay')]
+ [Bind('Value', 'Finale.AwayScore')]
+ Edit6: TEdit;
+
+ [Bind('Text', 'SemiFinalOne.HomeTeam')]
+ Label1: TLabel;
+ [Bind('Text', 'SemiFinalOne.AwayTeam')]
+ Label2: TLabel;
+ [Bind('Text', 'SemiFinalTwo.HomeTeam')]
+ Label3: TLabel;
+ [Bind('Text', 'SemiFinalTwo.AwayTeam')]
+ Label4: TLabel;
+ [Bind('Text', 'Finale.HomeTeam')]
+ Label5: TLabel;
+ [Bind('Text', 'Finale.AwayTeam')]
+ Label6: TLabel;
+ [Bind('Text', 'Finale.Winner')]
+ Label7: TLabel;
+
+ [Bind('Click', 'SemiFinalOne_SaveScore')]
+ Button1: TButton;
+ [Bind('Click', 'SemiFinalTwo_SaveScore')]
+ Button2: TButton;
+ [Bind('Click', 'Finale_SaveScore')]
+ Button3: TButton;
+ procedure FormCreate(Sender: TObject);
+ private
+ { Private declarations }
+ public
+ { Public declarations }
+ end;
+
+var
+ TournamentViewForm: TTournamentViewForm;
+
+implementation
+
+{$R *.dfm}
+
+uses
+ TournamentViewModel;
+
+procedure TTournamentViewForm.FormCreate(Sender: TObject);
+begin
+ ApplyBindings(Self, TTournamentViewModel.Create);
+end;
+
+end.
diff --git a/TournamentViewModel.pas b/TournamentViewModel.pas
new file mode 100644
index 0000000..f76ca96
--- /dev/null
+++ b/TournamentViewModel.pas
@@ -0,0 +1,120 @@
+unit TournamentViewModel;
+
+interface
+
+uses
+ Classes,
+ SimpleMVVM.Observable;
+
+type
+ TMatch = class
+ private
+ fHomeTeam: IObservable;
+ fAwayTeam: IObservable;
+ fHomeScore: IObservable;
+ fAwayScore: IObservable;
+ fWinner: IObservable;
+ public
+ constructor Create(const matchName: string;
+ const homeTeam: IObservable;
+ const awayTeam: IObservable);
+
+ procedure SaveScore;
+
+ property HomeTeam: IObservable read fHomeTeam;
+ property AwayTeam: IObservable read fAwayTeam;
+ property HomeScore: IObservable read fHomeScore;
+ property AwayScore: IObservable read fAwayScore;
+
+ property Winner: IObservable read fWinner;
+ end;
+
+ TTournamentViewModel = class(TComponent)
+ private
+ fSemiFinalOne: TMatch;
+ fSemiFinalTwo: TMatch;
+ fFinale: TMatch;
+ public
+ constructor Create; reintroduce;
+ destructor Destroy; override;
+
+ // temporary until methods also work in chained expressions
+ procedure SemiFinalOne_SaveScore;
+ procedure SemiFinalTwo_SaveScore;
+ procedure Finale_SaveScore;
+
+ property SemiFinalOne: TMatch read fSemiFinalOne;
+ property SemiFinalTwo: TMatch read fSemiFinalTwo;
+ property Finale: TMatch read fFinale;
+ end;
+
+implementation
+
+{ TTournamentViewModel }
+
+constructor TTournamentViewModel.Create;
+begin
+ inherited Create(nil);
+
+ fSemiFinalOne := TMatch.Create(
+ 'semifinal one',
+ TObservable.Create('Team 1'),
+ TObservable.Create('Team 2'));
+ fSemiFinalTwo := TMatch.Create(
+ 'semifinal two',
+ TObservable.Create('Team 3'),
+ TObservable.Create('Team 4'));
+ fFinale := TMatch.Create(
+ 'final',
+ fSemiFinalOne.Winner,
+ fSemiFinalTwo.Winner);
+end;
+
+destructor TTournamentViewModel.Destroy;
+begin
+ fFinale.Free;
+ fSemiFinalTwo.Free;
+ fSemiFinalOne.Free;
+ inherited;
+end;
+
+procedure TTournamentViewModel.Finale_SaveScore;
+begin
+ fFinale.SaveScore;
+end;
+
+procedure TTournamentViewModel.SemiFinalOne_SaveScore;
+begin
+ fSemiFinalOne.SaveScore;
+end;
+
+procedure TTournamentViewModel.SemiFinalTwo_SaveScore;
+begin
+ fSemiFinalTwo.SaveScore;
+end;
+
+{ TMatch }
+
+constructor TMatch.Create(const matchName: string;
+ const homeTeam: IObservable; const awayTeam: IObservable);
+begin
+ fHomeTeam := homeTeam;
+ fAwayTeam := awayTeam;
+ fHomeScore := TObservable.Create;
+ fAwayScore := TObservable.Create;
+ fWinner := TObservable.Create;
+end;
+
+procedure TMatch.SaveScore;
+var
+ homeScore, awayScore: Integer;
+begin
+ homeScore := fHomeScore.Value;
+ awayScore := fAwayScore.Value;
+ if homeScore > awayScore then
+ fWinner.Value := fHomeTeam.Value
+ else if homeScore < awayScore then
+ fWinner.Value := fAwayTeam.Value;
+end;
+
+end.