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 @@ + + + + + + + + + + + + + + + + + + + +