diff --git a/CQRSSample.Commands/CQRSSample.Commands.csproj b/CQRSSample.Commands/CQRSSample.Commands.csproj
index d8c1e7f..852b82c 100644
--- a/CQRSSample.Commands/CQRSSample.Commands.csproj
+++ b/CQRSSample.Commands/CQRSSample.Commands.csproj
@@ -34,11 +34,13 @@
..\External Libs\FluentValidation\FluentValidation.dll
+
+
diff --git a/CQRSSample.Commands/DynamicCommand.cs b/CQRSSample.Commands/DynamicCommand.cs
index 3dcd432..b0cb0ae 100644
--- a/CQRSSample.Commands/DynamicCommand.cs
+++ b/CQRSSample.Commands/DynamicCommand.cs
@@ -1,7 +1,16 @@
-namespace CQRSSample.Commands
+using System.Dynamic;
+
+namespace CQRSSample.Commands
{
- public class DynamicCommand
+ public class DynamicCommand : DynamicObject where T : Command
{
- private readonly dynamic _innerCommand;
+ private readonly dynamic _innerCommand;
+
+ public DynamicCommand(T command)
+ {
+ _innerCommand = command;
+ }
+
+ public T InnerCommand { get { return _innerCommand; } }
}
}
\ No newline at end of file
diff --git a/CQRSSample.Commands/RelocatingCustomerCommand.cs b/CQRSSample.Commands/RelocatingCustomerCommand.cs
index f2992b6..b694cd5 100644
--- a/CQRSSample.Commands/RelocatingCustomerCommand.cs
+++ b/CQRSSample.Commands/RelocatingCustomerCommand.cs
@@ -1,4 +1,5 @@
using System;
+using FluentValidation;
namespace CQRSSample.Commands
{
@@ -20,4 +21,12 @@ public RelocatingCustomerCommand(Guid id, string street, string streetNumber, st
City = city;
}
}
+
+ public class RelocatingCustomerValidator : AbstractValidator
+ {
+ public RelocatingCustomerValidator()
+ {
+ RuleFor(command => command.City).NotEmpty().NotNull();
+ }
+ }
}
diff --git a/CQRSSample.WpfClient/App.xaml b/CQRSSample.WpfClient/App.xaml
index ed9a04b..6f0a924 100644
--- a/CQRSSample.WpfClient/App.xaml
+++ b/CQRSSample.WpfClient/App.xaml
@@ -11,7 +11,6 @@
-
diff --git a/CQRSSample.WpfClient/ApplicationFramework/Icons/customer.png b/CQRSSample.WpfClient/ApplicationFramework/Icons/customer.png
new file mode 100644
index 0000000..bb30814
Binary files /dev/null and b/CQRSSample.WpfClient/ApplicationFramework/Icons/customer.png differ
diff --git a/CQRSSample.WpfClient/ApplicationFramework/Icons/no.png b/CQRSSample.WpfClient/ApplicationFramework/Icons/no.png
new file mode 100644
index 0000000..93e171f
Binary files /dev/null and b/CQRSSample.WpfClient/ApplicationFramework/Icons/no.png differ
diff --git a/CQRSSample.WpfClient/ApplicationFramework/Resources/Style.xaml b/CQRSSample.WpfClient/ApplicationFramework/Resources/Style.xaml
deleted file mode 100644
index 5490324..0000000
--- a/CQRSSample.WpfClient/ApplicationFramework/Resources/Style.xaml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CQRSSample.WpfClient/ApplicationFramework/ScreenWithValidatingCommand.cs b/CQRSSample.WpfClient/ApplicationFramework/ScreenWithValidatingCommand.cs
new file mode 100644
index 0000000..6f084ad
--- /dev/null
+++ b/CQRSSample.WpfClient/ApplicationFramework/ScreenWithValidatingCommand.cs
@@ -0,0 +1,22 @@
+using Caliburn.Micro;
+using CQRSSample.Commands;
+using FluentValidation;
+using FluentValidation.Results;
+
+namespace CQRSSample.WpfClient.ApplicationFramework
+{
+ public class ScreenWithValidatingCommand : Screen where T : Command
+ {
+ public ValidatingCommand Command { get; protected set; }
+
+ public IValidator Validator { get; set; }
+
+ ///
+ /// Validates the command
+ ///
+ protected virtual ValidationResult Validate()
+ {
+ return Command.Validate();
+ }
+ }
+}
\ No newline at end of file
diff --git a/CQRSSample.WpfClient/ApplicationFramework/ValidatingCommand.cs b/CQRSSample.WpfClient/ApplicationFramework/ValidatingCommand.cs
new file mode 100644
index 0000000..ee1d95e
--- /dev/null
+++ b/CQRSSample.WpfClient/ApplicationFramework/ValidatingCommand.cs
@@ -0,0 +1,76 @@
+using System;
+using System.ComponentModel;
+using System.Dynamic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+using CQRSSample.Commands;
+using FluentValidation;
+using FluentValidation.Results;
+
+namespace CQRSSample.WpfClient.ApplicationFramework
+{
+ public class ValidatingCommand : DynamicCommand, IDataErrorInfo where T : Command
+ {
+ private readonly IValidator _validator;
+
+ public ValidatingCommand(T command, IValidator validator)
+ : base(command)
+ {
+ _validator = validator;
+ }
+
+ public override bool TryGetMember(GetMemberBinder binder, out object result)
+ {
+ var ex = Expression.Property(Expression.Constant(InnerCommand), binder.Name);
+ result = Expression.Lambda(ex).Compile().DynamicInvoke();
+ return true;
+ }
+
+ public override bool TrySetMember(SetMemberBinder binder, object value)
+ {
+ var method = InnerCommand.GetType().GetProperty(binder.Name).GetSetMethod();
+ method.Invoke(InnerCommand, new[] { value });
+
+ return true;
+ }
+
+ ///
+ /// Validates the command
+ ///
+ public virtual ValidationResult Validate()
+ {
+ return _validator.Validate(InnerCommand);
+ }
+
+ public string this[string columnName]
+ {
+ get
+ {
+ var validationResults = Validate();
+
+ if (validationResults == null) return string.Empty;
+
+ var columnResults = validationResults.Errors.FirstOrDefault(x => string.Compare(x.PropertyName, columnName, true) == 0);
+
+ return columnResults != null ? columnResults.ErrorMessage : string.Empty;
+ }
+ }
+
+ public string Error
+ {
+ get
+ {
+ var message = new StringBuilder();
+
+ foreach (var validationFailure in Validate().Errors)
+ {
+ message.Append(validationFailure.ErrorMessage);
+ message.Append(Environment.NewLine);
+ }
+
+ return message.ToString();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CQRSSample.WpfClient/CQRSSample.WpfClient.csproj b/CQRSSample.WpfClient/CQRSSample.WpfClient.csproj
index cc3f850..53b3b6d 100644
--- a/CQRSSample.WpfClient/CQRSSample.WpfClient.csproj
+++ b/CQRSSample.WpfClient/CQRSSample.WpfClient.csproj
@@ -84,6 +84,8 @@
Code
+
+
CreateCustomerView.xaml
@@ -222,6 +224,10 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+