Commit 512d9d7

mo <email@solidware.ca>
2011-04-05 03:02:00
make Observable implement IDataErrorInfo, and move validation registration down.
1 parent a1005c4
product/desktop.ui/bootstrappers/Bootstrapper.cs
@@ -108,6 +108,7 @@ namespace solidware.financials.windows.ui.bootstrappers
 
             builder.RegisterType<AddNewStockSymbolPresenter>();
             builder.RegisterType<AddNewStockSymbolPresenter.AddCommand>();
+            builder.RegisterType<AddNewStockSymbolPresenter.IsValid>();
 
             builder.RegisterType<StockViewModel.MoreCommand>();
 
product/desktop.ui/presenters/AddNewStockSymbolPresenter.cs
@@ -1,47 +1,33 @@
 using System;
-using System.Linq.Expressions;
 using gorilla.utility;
 using solidware.financials.infrastructure;
 using solidware.financials.messages;
 using solidware.financials.windows.ui.presenters.validation;
+using solidware.financials.windows.ui.views.controls;
+using utility;
 
 namespace solidware.financials.windows.ui.presenters
 {
-    public class AddNewStockSymbolPresenter : DialogPresenter, INotification<AddNewStockSymbolPresenter>
+    public class AddNewStockSymbolPresenter : DialogPresenter
     {
         public AddNewStockSymbolPresenter(UICommandBuilder builder)
         {
             this.builder = builder;
-            Notification = new Notification<AddNewStockSymbolPresenter>();
+            Symbol = new ObservableProperty<string>();
         }
 
         public ObservableCommand Add { get; set; }
         public ObservableCommand Cancel { get; set; }
-        public virtual string Symbol { get; set; }
+        public virtual Observable<string> Symbol { get; set; }
         public virtual Action close { get; set; }
-        public Notification<AddNewStockSymbolPresenter> Notification { get; set; }
 
         public void present()
         {
-            Add = builder.build<AddCommand>(this);
+            Add = builder.build<AddCommand, IsValid>(this);
             Cancel = builder.build<CancelCommand>(this);
 
-            Notification.Register<Error>(x => x.Symbol, () => Symbol.is_blank(), () => "Please specify a symbol.");
-        }
-
-        public string this[string property]
-        {
-            get { return Notification[property]; }
-        }
-
-        public string this[Expression<Func<AddNewStockSymbolPresenter, object>> property]
-        {
-            get { return Notification[property]; }
-        }
-
-        public string Error
-        {
-            get { return Notification.Error; }
+            Symbol.Notify(Add);
+            Symbol.Register<Error>(x => x.is_blank(), () => "Please specify a symbol.");
         }
 
         UICommandBuilder builder;
@@ -57,9 +43,17 @@ namespace solidware.financials.windows.ui.presenters
 
             public override void run(AddNewStockSymbolPresenter presenter)
             {
-                bus.publish(new StartWatchingSymbol {Symbol = presenter.Symbol.ToUpperInvariant()});
+                bus.publish(new StartWatchingSymbol {Symbol = presenter.Symbol.Value.ToUpperInvariant()});
                 presenter.close();
             }
         }
+
+        public class IsValid : UISpecification<AddNewStockSymbolPresenter>
+        {
+            public override bool is_satisfied_by(AddNewStockSymbolPresenter presenter)
+            {
+                return presenter.Symbol.not(x => x.Value.is_blank());
+            }
+        }
     }
 }
\ No newline at end of file
product/desktop.ui/views/controls/Observable.cs
@@ -3,14 +3,23 @@ using System.ComponentModel;
 
 namespace solidware.financials.windows.ui.views.controls
 {
-    public interface Observable<T> : Observable
+    public interface Observable<T> : Observable, IDataErrorInfo
     {
         new T Value { get; set; }
         void WhenChanged(Action observer);
+        void Register<Severity>(Func<T, bool> failCondition, Func<string> errorMessage) where Severity : presenters.validation.Severity, new();
     }
 
     public interface Observable : INotifyPropertyChanged
     {
         object Value { get; }
     }
+
+    static public class ObservableExtensions
+    {
+        static public void Notify<T>(this Observable<T> observable, ObservableCommand command)
+        {
+            observable.WhenChanged(() => command.notify_observers());
+        }
+    }
 }
\ No newline at end of file
product/desktop.ui/views/controls/ObservableProperty.cs
@@ -1,9 +1,10 @@
 using System;
 using System.ComponentModel;
+using solidware.financials.windows.ui.presenters.validation;
 
 namespace solidware.financials.windows.ui.views.controls
 {
-    public class ObservableProperty<T> : Observable<T>
+    public class ObservableProperty<T> : Observable<T>, IDataErrorInfo
     {
         T value;
 
@@ -12,8 +13,11 @@ namespace solidware.financials.windows.ui.views.controls
         public ObservableProperty(T value)
         {
             this.value = value;
+            Notification = new Notification<Observable<T>>();
         }
 
+        public Notification<Observable<T>> Notification { get; private set; }
+
         public T Value
         {
             get { return value; }
@@ -29,6 +33,11 @@ namespace solidware.financials.windows.ui.views.controls
             PropertyChanged += (o, e) => observer();
         }
 
+        public void Register<TSeverity>(Func<T, bool> failCondition, Func<string> errorMessage) where TSeverity : Severity, new()
+        {
+            Notification.Register<TSeverity>(x => x.Value, () => failCondition(Value), errorMessage);
+        }
+
         object Observable.Value
         {
             get { return value; }
@@ -45,5 +54,15 @@ namespace solidware.financials.windows.ui.views.controls
         {
             return value.ToString();
         }
+
+        public string this[string property]
+        {
+            get { return Notification[property]; }
+        }
+
+        public string Error
+        {
+            get { throw new NotImplementedException(); }
+        }
     }
 }
\ No newline at end of file
product/desktop.ui/views/dialogs/AddNewStockSymbolDialog.xaml
@@ -13,7 +13,7 @@
 		</StackPanel.Resources>
 		<DockPanel>
 			<Label Width="120px">Symbol:</Label>
-			<TextBox Text="{Binding Path=Symbol, UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}" Style="{StaticResource Error}"></TextBox>
+			<TextBox Text="{Binding Path=Symbol.Value, UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}" Style="{StaticResource Error}"></TextBox>
 		</DockPanel>
 		<DockPanel LastChildFill="False" HorizontalAlignment="Right">
 			<Button IsDefault="True" Command="{Binding Path=Add}">_Add</Button>
product/desktop.ui/UISpecification.cs
@@ -7,8 +7,7 @@ namespace solidware.financials.windows.ui
         bool is_satisfied_by<T>(T presenter) where T : Presenter;
     }
 
-    public abstract class UISpecification<TPresenter> : UISpecification, Specification<TPresenter>
-        where TPresenter : Presenter
+    public abstract class UISpecification<TPresenter> : UISpecification, Specification<TPresenter> where TPresenter : Presenter
     {
         bool UISpecification.is_satisfied_by<T>(T presenter)
         {
product/specs/unit/ui/presenters/AddNewStockSymbolPresenterSpecs.cs
@@ -27,7 +27,7 @@ namespace specs.unit.ui.presenters
             {
                 add_command = Create.an<ObservableCommand>();
                 cancel_command = Create.an<ObservableCommand>();
-                builder.is_told_to(x => x.build<AddNewStockSymbolPresenter.AddCommand>(sut)).it_will_return(add_command);
+                builder.is_told_to(x => x.build<AddNewStockSymbolPresenter.AddCommand, AddNewStockSymbolPresenter.IsValid>(sut)).it_will_return(add_command);
                 builder.is_told_to(x => x.build<CancelCommand>(sut)).it_will_return(cancel_command);
             };
 
@@ -55,7 +55,7 @@ namespace specs.unit.ui.presenters
             Because of = () =>
             {
                 sut.present();
-                result = sut[x => x.Symbol];
+                result = sut.Symbol["Value"];
             };
 
             It should_display_an_error = () =>
@@ -85,7 +85,7 @@ namespace specs.unit.ui.presenters
                 Establish context = () =>
                 {
                     presenter = Create.an<AddNewStockSymbolPresenter>();
-                    presenter.is_told_to(x => x.Symbol).it_will_return("TD.TO");
+                    presenter.is_told_to(x => x.Symbol).it_will_return("TD.TO".ToObservable());
                     presenter.Stub(x => x.close).Return(() =>
                     {
                         closed = true;
@@ -111,5 +111,62 @@ namespace specs.unit.ui.presenters
                 static bool closed;
             }
         }
+
+        public class IsValidSpecs
+        {
+            public class concern
+            {
+                Establish context = () =>
+                {
+                    sut = new AddNewStockSymbolPresenter.IsValid();
+                };
+
+                static protected AddNewStockSymbolPresenter.IsValid sut;
+            }
+
+            public class when_a_valid_symbol_is_entered : concern
+            {
+                Establish context = () =>
+                {
+                    presenter = Create.an<AddNewStockSymbolPresenter>();
+                    presenter.is_told_to(x => x.Symbol).it_will_return("ARX.TO".ToObservable());
+                };
+
+                Because of = () =>
+                {
+                    result = sut.is_satisfied_by(presenter);
+                };
+
+                It should_be_valid = () =>
+                {
+                    result.should_be_true();
+                };
+
+                static bool result;
+                static AddNewStockSymbolPresenter presenter;
+            }
+
+            public class when_a_invalid_symbol_is_entered : concern
+            {
+                Establish context = () =>
+                {
+                    presenter = Create.an<AddNewStockSymbolPresenter>();
+                    presenter.is_told_to(x => x.Symbol).it_will_return("".ToObservable());
+                };
+
+                Because of = () =>
+                {
+                    result = sut.is_satisfied_by(presenter);
+                };
+
+                It should_be_invalid = () =>
+                {
+                    result.should_be_false();
+                };
+
+                static bool result;
+                static AddNewStockSymbolPresenter presenter;
+            }
+        }
     }
 }
\ No newline at end of file
product/utility/BooleanLogic.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace utility
+{
+    static public class BooleanLogic
+    {
+        static public bool not<T>(this T item, Func<T, bool> condition)
+        {
+            return !condition(item);
+        }
+    }
+}
\ No newline at end of file
product/utility/utility.csproj
@@ -43,6 +43,7 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="BooleanLogic.cs" />
     <Compile Include="MapKey.cs" />
     <Compile Include="MapperRegistery.cs" />
     <Compile Include="Mathematics.cs" />