Validating data on the UI with WCF RIA and the MVVM pattern - silverlight

Is there a best practice or widely accepted way of structuring and validating data using MVVM in conjunction with RIA services in Silverlight?
Here's the crux of my problem. Let's say I have an EmployeeView, EmployeeViewModel and some Employee entity. In regular RIA applications I will expose that Employee entity on the view and I get validation "for free", because Entities implement INotifyDataErrorInfo and IDataErrorInfo (correct?).
Now if I want to expose some Employee properties through a ViewModel instead of directly through an Entity then it becomes more complicated. I could expose the bits that I need directly and hook them into the entity on the backend, like this:
private Employee _employee;
public EmployeeViewModel()
{
_employee = new Employee();
}
public string Name
{
get { return _employee.Name; }
set
{
_employee.Name = value;
// fire property change, etc.
}
}
... but I lose the tasty "free" validation of entities. Otherwise, I could expose the entity directly in the view model, like so
private Employee _employee;
public Employee Employee
{
get { return _employee; }
}
public EmployeeViewModel()
{
_employee = new Employee();
}
In this case, the view will bind directly to the Employee entity and find its properties in there, like so:
<StackPanel DataContext="{Binding Employee}">
<TextBox Text="{Binding Name}" />
</StackPanel>
Using this method we get "free" validation, but it's not exactly a clean implementation of MVVM.
A third option would be to implement INotifyDataErrorInfo and IDataErrorInfo myself in the VMs, but this seems like an awful lot of plumbing code, considering how easy it would be for me to use the above solution and have something slightly less "clean" but a heck of a lot easier at the end of the day.
So I guess my question is, which of these approaches are appropriate in which situation? Is there a better approach I am missing?
In case it's relevant I'm looking at the Caliburn.Micro MVVM framework, but I would be keen to see answers that apply generically.

I am using RIA with Caliburn.Micro and am pretty happy with my solution for client side validation.
What I have done is put a ValidationBaseViewModel between Screen (provided by Caliburn.Micro) and my actual application VMs (EmployeeViewModel in your case). ValidationBaseViewModel implements INotifyDataErrorInfo so that plumbing code your talking about is only written once. I then add/remove/notify of errors via ValidationBaseViewModel from an override of the (Caliburn.Micro) PropertyChangedBase.NotifyOfPropertyChange with the following code:
public override void NotifyOfPropertyChange(string property)
{
if (_editing == null)
return;
if (HasErrors)
RemoveErrorFromPropertyAndNotifyErrorChanges(property, 100);
if (_editing.HasValidationErrors)
{
foreach (var validationError in
_editing.ValidationErrors
.Where(error => error.MemberNames.Contains(property)))
{
AddErrorToPropertyAndNotifyErrorChanges(property, new ValidationErrorInfo() { ErrorCode = 100, ErrorMessage = validationError.ErrorMessage });
}
}
base.NotifyOfPropertyChange(property);
}
This is actually in another VM (between ValidationBaseViewModel and EmployeeViewModel) with the following definition:
public abstract class BaseEditViewModel<TEdit> :
ValidationBaseViewModel where TEdit : Entity
where Entity is RIAs System.ServiceModel.DomainServices.Client.Entity and the _editing class member is an instance of this type TEdit which is being edited by the current VM.
In combination with Caliburn coroutines this allows me to do some cool stuff like the following:
[Rescue]
public IEnumerable<IResult> Save()
{
if (HasErrors)
{
yield return new GiveFocusByName(PropertyInError);
yield break;
}
...
}

If you don't want to use external resources or frameworks, then I you could have a ViewModelBase that implement INotifyDataErrorInfo.
That class will have ValidateProperty(string propertyName, object value) to validate a speciic property, and Validate() method to validate the entire object. Internally use the Validator class to return the ValidationResults.
If you use reflector, it can be pretty easy to achieve by mimicking the validation process in the Entity class itself to the ViewModelBase.
Although it's no "free", is still relatively cheap tho.
Here is a sample implementation of IDataErrorInfo. Although not tested, will give you the idea.
public class ViewModelBase : INotifyPropertyChanged, INotifyDataErrorInfo
{
/*
* InotifyPropertyChanged implementation
* Consider using Linq expressions instead of string names
*/
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public IEnumerable GetErrors(string propertyName)
{
if (implValidationErrors == null) return null;
return ImplValidationErros.Where(ve =>
ve.MemberNames.Any(mn => mn == propertyName));
}
public bool HasErrors
{
get
{
return implValidationErrors == null || ImplValidationErros.Any();
}
}
private List<ValidationResult> implValidationErrors;
private List<ValidationResult> ImplValidationErros
{
get
{
return implValidationErrors ??
(implValidationErrors = new List<ValidationResult>());
}
}
private ReadOnlyCollection<ValidationResult> validationErrors;
[Display(AutoGenerateField = false)]
protected ICollection<ValidationResult> ValidationErrors
{
get
{
return validationErrors ??
(validationErrors =
new ReadOnlyCollection<ValidationResult>(ImplValidationErros));
}
}
protected void ValidateProperty(string propertyName, object value)
{
ValidationContext validationContext =
new ValidationContext(this, null, null);
validationContext.MemberName = propertyName;
List<ValidationResult> validationResults =
new List<ValidationResult>();
Validator.TryValidateProperty(
value,
validationContext,
validationResults);
if (!validationResults.Any()) return;
validationResults
.AddRange(ValidationErrors
.Where(ve =>
!ve.MemberNames.All(mn =>
mn == propertyName)));
implValidationErrors = validationResults;
if (ErrorsChanged != null)
ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
}
}

you can use a partial class to extend your entitty and add data validation there via idataerrorinfo.

Related

BindableCollection changes when other ObservableCollection changes

Is there a way to update an ObservableCollection with the items which are added/deleted in other ObservableCollection?
How can I update my ViewModel's BindableCollection when items are added, removed in FullyObservableCollection?
It is important to note I am trying to use MVVM pattern with Caliburn.Micro.
VieModel
private BindableCollection<Employees> _employees = new BindableCollection(OracleConnector.GetEmployeeRepositorys());
public BindableCollection<Employees> Employees
{
get
{
return _employees;
}
set
{
OracleConnector.List.CollectionChanged += (sender, args) =>
{
_employees = OracleConnector.List;
};
NotifyOfPropertyChange(() => Employees);
}
}
OracleConnector
public class OracleConnector
{
public static FullyObservableCollection<Employees> List = new FullyObservableCollection<Employees>();
public static FullyObservableCollection<Employees> GetEmployeeRepositorys()
{
using (IDbConnection cnn = GetDBConnection("localhost", 1521, "ORCL", "hr", "hr"))
{
var dyParam = new OracleDynamicParameters();
try
{
var output = cnn.Query<Employees>(OracleDynamicParameters.sqlSelect, param: dyParam).AsList();
foreach (Employees employees in output)
{
List.Add(employees);
}
}
catch (OracleException ex)
{
MessageBox.Show("Connection to database is not available.\n" + ex.Message, "Database not available", MessageBoxButton.OK, MessageBoxImage.Error);
}
return List;
}
}
}
I am able to detect if changes are made in the FullyObservableCollection but I don't know how to pass them to the ViewModel.
Use the IEventAggregator in the OracleConnector class when you add a new employee. Publish a EmployeeAddedMessage which contains the new employee. Make sure you publish on the correct thread too. You are likely to need to use PublishOnUiThread method. The ShellViewModel is then able to implement the IHandle<EmployeeAddedMessage> as a method probably called Handle(EmployeeAddedMessage msg). Inside the Handle method you can then add the Employee to the appropriate Employee collection.
You may need to add the OracleConnector to your application bootstrapper as well as the EventAggregator class that Caliburn Micro provide. Your ShellViewModel will also need to call the Subscribe(this) method on the event aggregator. Both the OracleConnector and ShellViewModel need to be using the same instance of the event notifier. So make sure you register the event aggregator as a singleton.
See here for more details about using the event notification. Also, my application here uses event notification for application events.

UWP - Refreshing listview and CollectionViewSource

I have created ChatMessageGroup and ChatMessageGroupCollection and a ListView with ItemsSource set to CollectionViewSource:
<ListView x:Name="ChatMessageLv" ItemsSource="{Binding SelectedChat.ChatMessageGroupCollection.Cvs.View}" ItemTemplateSelector="{StaticResource ChatMessageDataTemplateSelector}">
public class ChatMessageGroup : IGrouping<DateTime, ChatMessage>, INotifyCollectionChanged
{
private ObservableCollection<ChatMessage> _chatMessages;
public DateTime Key { get; set; }
public ObservableCollection<ChatMessage> ChatMessages
{
get { return _chatMessages; }
set
{
if (_chatMessages != null)
_chatMessages.CollectionChanged -= CollectionChanged;
_chatMessages = value;
_chatMessages.CollectionChanged += CollectionChanged;
}
}
public ChatMessageGroup()
{
ChatMessages = new ObservableCollection<ChatMessage>();
}
public IEnumerator<ChatMessage> GetEnumerator()
{
return ChatMessages.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
}
public class ChatMessageGroupCollection : IEnumerable<ChatMessageGroup>
{
private readonly ObservableCollection<ChatMessageGroup> _groups;
public ObservableCollection<ChatMessage> Source { get; set; }
public CollectionViewSource Cvs { get; set; }
public ChatMessageGroupCollection(ObservableCollection<ChatMessage> messages)
{
Source = messages;
messages.CollectionChanged += Messages_CollectionChanged;
var groups = messages
.GroupBy(GetGroupKey)
.Select(x => new ChatMessageGroup()
{
Key = x.Key,
ChatMessages = x.OrderBy(GetGroupKey).ToObservableOrDefault()
})
.OrderBy(x => x.Key);
_groups = new ObservableCollection<ChatMessageGroup>(groups);
Cvs = new CollectionViewSource() { IsSourceGrouped = true, Source = _groups };
}
...
Everything works fine in here except the changes inside group collection:
_groups.Add(new ChatMessageGroup()); -> this line reflect changes in ListView
but if I do like this: _groups[0].ChatMessages.Add(new ChatMessage()) it doesn't work even though ChatMessageGroup is implementing INotifyCollectionChanged and is raised every time ChatMessages ObservableCollection is changed.
The workaround is to update ChatMessages and remove group from _groups and then add it again but it's not a solution. Refresh() on CollectionViewSource is not available in UWP. Are there any other solutions?
Whilst this doesn't technically qualify as an answer to the question I do think it might qualify as an architectural answer.
The way to make a grouped list view update based on the underlying collection changing in WPF is as simple as setting IsLiveGroupingRequested to true on the CollectionViewSource instance declared in XAML.
As I've been working my way through UWP (after nearly a decade of WPF) I've come to the conclusion that Microsoft are suggesting through omission that this isn't the right approach to the problem for UWP. So going to the lengths of implementing the feature yourself could be construed as missing the point somewhat.
In my particular situation I've decided to change my approach entirely and implement the rendering as multiple instance of ListViews as opposed to forcing an old paradigm onto a new platform.
The result of this actually made me arrive at a solution that improved my UX to boot.
Food for thought ...

making async calls using mvvm in silverlight

I am trying to make a call to a wcf service with my silverlight application and I am having some trouble understanding how the model returns the result back to the view model. Within my view model I have the following command:
public DelegateCommand GetSearchResultCommand
{
get
{
if (this._getSearchResultCommand == null)
this._getSearchResultCommand = new DelegateCommand(GetSearchResultCommandExecute, CanGetSearchResultsCommandExecute);
return this._getSearchResultCommand;
}
}
private void GetSearchResultCommandExecute(object parameter)
{
this.SearchResults = this._DataModel.GetSearchResults(this.SearchTerm);
}
/// <summary>
/// Bindable property for SearchResults
/// </summary>
public ObservableCollection<QueryResponse> SearchResults
{
get
{
return this._SearchResults;
}
private set
{
if (this._SearchResults == value)
return;
// Set the new value and notify
this._SearchResults = value;
this.NotifyPropertyChanged("SearchResults");
}
}
then within my model I have the following code
public ObservableCollection<QueryResponse> GetSearchResults(string searchQuery)
{
//return type cannot be void needs to be a collection
SearchClient sc = new SearchClient();
//******
//TODO: stubbed in placeholder for Endpoint Address used to retreive proxy address at runtime
// sc.Endpoint.Address = (clientProxy);
//******
sc.QueryCompleted += new EventHandler<QueryCompletedEventArgs>(sc_QueryCompleted);
sc.QueryAsync(new Query { QueryText = searchQuery });
return LastSearchResults;
}
void sc_QueryCompleted(object sender, QueryCompletedEventArgs e)
{
ObservableCollection<QueryResponse> results = new ObservableCollection<QueryResponse>();
results.Add(e.Result);
this.LastSearchResults = results;
}
When I insert breakpoints within the model I see where the query is being executed and a result is returned within the model (this.LastSearchResults = results) however I cannot seem to get this collection to update/ notify the view model of the result. I've generated and run a similar test using just a method and dummy class and it seems to work so I suspect the issue is due to the async call /threading. I have INotifyPropertyChanged within the ViewModel to sync the View and ViewModel. Do I need to also implement INotifyPropChng within the model as well? I'm new to mvvm so any help / example of how I should approach this would be appreciated.
Thank you,
UPDATE
In further testing I added INotifyPropertyChanged to the model and changed the Completed event as follows:
void sc_QueryCompleted(object sender, QueryCompletedEventArgs e)
{
ObservableCollection<QueryResponse> results = new ObservableCollection<QueryResponse>();
results.Add(e.Result);
//this.LastSearchResults = results;
SearchResults = results;
}
Placing a watch on Search Results I now see it is updated with results from teh WCF. My question is still around is this teh correct approach? It seems to work right now however I am curious if I am missing something else or if I should not be placing INotify within the Model.
Thank you,
I've found that it's best to encapsulate my WCF services in an additional layer of Service classes. This allows me to more easily Unit Test my ViewModels. There are several patterns when doing this, though this is the simplest I've used. The pattern is to create a method that matches the definition of the service call, though also contains an Action that can be invoked after the service call completes.
public class Service : IService
{
public void GetSearchResults(string searchQuery, Action<ObservableCollection<QueryResponse>> reply)
{
//return type cannot be void needs to be a collection
SearchClient sc = new SearchClient();
//******
//TODO: stubbed in placeholder for Endpoint Address used to retreive proxy address at runtime
// sc.Endpoint.Address = (clientProxy);
//******
sc.QueryCompleted += (s,e) =>
{
ObservableCollection<QueryResponse> results = new ObservableCollection<QueryResponse>();
results.Add(e.Result);
reply(results);
};
sc.QueryAsync(new Query { QueryText = searchQuery });
}
}
You can also provide an interface that your ViewModel can use. This makes Unit Testing even easier, though is optional.
public interface IService
{
void GetSearchResults(string searchQuery, Action<ObservableCollection<QueryResponse>> reply);
}
Your ViewModel would then look something like this:
public class MyViewModel : INotifyPropertyChanged
{
private IService _service;
public MyViewModel()
: this(new Service())
{ }
public MyViewModel(IService service)
{
_service = service;
SearchResults = new ObservableCollection<QueryResponse>();
}
private ObservableCollection<QueryResponse> _searchResults
public ObservableCollection<QueryResponse> SearchResults
{
get { return _searchResults; }
set
{
_searchResults = value;
NotifyPropertyChanged("SearchResults");
}
}
public void Search()
{
_service.GetSearchResults("abcd", results =>
{
SearchResults.AddRange(results);
});
}
protected void NotifyPropertyChanged(string property)
{
var handler = this.PropertyChanged;
if(handler != null)
handler(new PropertyChangedEventArgs(property));
}
}
An additional reason for encapsulating your service calls into another class like this is that it can provide a single place for such things as logging and error handling. That way your ViewModel itself doesn't need to take care of those things specifically related to the Service.
I would likely use something along the lines of:
public class ViewModel : INotifyPropertyChanged
{
private readonly IModel model;
private readonly DelegateCommand getSearchResultsCommand;
public DelegateCommand GetSearchResultsCommand
{
get { return getSearchResultsCommand; }
}
public ObservableCollection<QueryResponse> SearchResults
{
get { return model.SearchResults; }
}
public ViewModel(IModel model)
{
this.model = model;
this.model.SearchResultsRetrieved += new EventHandler(model_SearchResultsRetrieved);
this.getSearchResultsCommand = new DelegateCommand(model.GetSearchResultCommandExecute, model.CanGetSearchResultsCommandExecute);
}
private void model_SearchResultsRetrieved(object sender, EventArgs e)
{
this.NotifyPropertyChanged("SearchResults");
}
}
public interface IModel
{
event EventHandler SearchResultsRetrieved;
void GetSearchResultCommandExecute(object parameter);
bool CanGetSearchResultsCommandExecute(object parameter);
ObservableCollection<QueryResponse> SearchResults { get; }
}
With the SearchResultsRetrieved event being fired by the Model when its SearchResults collection has been filled with the appropriate data. I prefer to have custom events rather than implement INotifyPropertyChanged on my models, particularly if there are only one, or a few, events that need to be communicated to the viewmodel.

Validation Error messages in silverlight MVVM

I'm attempting to implement the technique for data validation from Josh Smith's example here: Using a viewmodel to provide meaningful validation...
My code is remarkably similar to the example, except for a few difference, namely I'm using the MVVM-Light toolkit, and my model person class is a partial class that comes from a WCF backend.
Here's an example of the code in question:
First is the Automatically generated version of the class which comes from the WCF:
public partial class Person : BaseObject
{
private string FooField;
public string Foo {
get {
return this.FooField;
}
set {
if ((object.ReferenceEquals(this.FooField, value) != true)) {
this.FooField = value;
this.RaisePropertyChanged("Foo");
}
}
}
I then extend the partial class to implement IDataErrorInfo:
public partial class Person : IDataErrorInfo
{
public string Error
{
get { return null;}
}
public string this[string propertyName]
{
if (propertyName == "Foo")
{
//Do some backend Validation
}
}
}
And lastly I have a viewmodel:
public class PersonViewModel : INotifyProperyChanged, IDataErrorInfo
{
private string _fooString;
private Person _person;
...
public string Foo {
get { return _fooString; }
set
{
if (value == _fooString;)
return;
_fooString = value;
RaisePropertyChanged("Foo");
}
public string this[string propertyName]
{
if (propertyName == "Foo")
{
string msg = Validate(Foo); //Frontend Validation, range, format, etc.
if(msg ! = null)
return msg;
_person.Foo = Foo;
}
}
}
So when I bind to the property in the viewmodel the validation code defined in IDataErrorInfo's indexer gets executed on the view model and my textbox or whatever gets highlighted if my validation fails, as expected. However in my code the Indexer on the MODEL side never gets executed, at all. I can honestly say that I don't see or understand the mechanism that is supposed to invoke it. I have run the example code from Josh Smith's example, and it does work, calling the MV's this[], then if validation passes the Model's this[] hits for additional validation, but I for the life of me can't see how it happens.
I really hope this is something simple I'm overlooking. Thanks for looking at it.
You are missing
return _person[propertyName];
from the the indexer in the ViewModel.

WPF Binding : Use DataAnnotations for ValidationRules

I have read a lot of Blog post on WPF Validation and on DataAnnotations. I was wondering if there is a clean way to use DataAnnotations as ValidationRules for my entity.
So instead of having this (Source) :
<Binding Path="Age" Source="{StaticResource ods}" ... >
<Binding.ValidationRules>
<c:AgeRangeRule Min="21" Max="130"/>
</Binding.ValidationRules>
</Binding>
Where you must have your
public class AgeRangeRule : ValidationRule
{...}
I want the WPF Binding to go see the Age property and look for DataAnnotation a bit like this:
[Range(1, 120)]
public int Age
{
get { return _age; }
set
{
_age = value;
RaisePropertyChanged<...>(x => x.Age);
}
}
Any ideas if this is possible ?
The closest approach I found is :
// This loop into all DataAnnotations and return all errors strings
protected string ValidateProperty(object value, string propertyName)
{
var info = this.GetType().GetProperty(propertyName);
IEnumerable<string> errorInfos =
(from va in info.GetCustomAttributes(true).OfType<ValidationAttribute>()
where !va.IsValid(value)
select va.FormatErrorMessage(string.Empty)).ToList();
if (errorInfos.Count() > 0)
{
return errorInfos.FirstOrDefault<string>();
}
return null;
Source
public class PersonEntity : IDataErrorInfo
{
[StringLength(50, MinimumLength = 1, ErrorMessage = "Error Msg.")]
public string Name
{
get { return _name; }
set
{
_name = value;
PropertyChanged("Name");
}
}
public string this[string propertyName]
{
get
{
if (porpertyName == "Name")
return ValidateProperty(this.Name, propertyName);
}
}
}
Source and Source
That way, the DataAnnotation works fine, I got a minimum to do on the XAML ValidatesOnDataErrors="True" and it's a fine workaround of Aaron post with the DataAnnotation.
In your model you could implement IDataErrorInfo and do something like this...
string IDataErrorInfo.this[string columnName]
{
get
{
if (columnName == "Age")
{
if (Age < 0 ||
Age > 120)
{
return "You must be between 1 - 120";
}
}
return null;
}
}
You will also need to notify the binding target of the newly defined behavior.
<TextBox Text="{Binding Age, ValidatesOnDataErrors=True}" />
EDIT:
If you only want to use Data Annotations you can follow this blog post which outlines how to accomplish the task.
UPDATE:
Historical representation of the aforementioned link.
Sounds good Aaron. I'm just into WPF and will study databindings next week at work ;) So cannot completely judge your answer...
But with winforms I have used Validation Application Block from the Entlib and implemented IDataErrorInfo (actually IDXDataErrorInfo because we work with DevExpress controls) on a base entity (business object) and that works pretty fine!
It's a bit more sophisticated than the solution you sketched in this way that you place your validation logic on the object and not in the interface implementation. Making it more OOP and maintainable. At the ID(XD)ataErrorInfo I just call Validation.Validate(this), or even better get the validator for the property that the interface is called for and validate the specific validator. Don't forget to call the [SelfValidation] as well because of validation for combinations of properties ;)
You might be interested in the BookLibrary sample application of the WPF Application Framework (WAF). It uses the DataAnnotations Validation attributes together with WPF Binding.
Recently I've had the same idea using the Data Annotation API to validate EF Code First POCO classes in WPF. Like Philippe's post my solution uses reflection, but all necessary code is included in a generic validator.
internal class ClientValidationRule : GenericValidationRule<Client> { }
internal class GenericValidationRule<T> : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
string result = "";
BindingGroup bindingGroup = (BindingGroup)value;
foreach (var item in bindingGroup.Items.OfType<T>()) {
Type type = typeof(T);
foreach (var pi in type.GetProperties()) {
foreach (var attrib in pi.GetCustomAttributes(false)) {
if (attrib is System.ComponentModel.DataAnnotations.ValidationAttribute) {
var validationAttribute = attrib as System.ComponentModel.DataAnnotations.ValidationAttribute;
var val = bindingGroup.GetValue(item, pi.Name);
if (!validationAttribute.IsValid(val)) {
if (result != "")
result += Environment.NewLine;
if (string.IsNullOrEmpty(validationAttribute.ErrorMessage))
result += string.Format("Validation on {0} failed!", pi.Name);
else
result += validationAttribute.ErrorMessage;
}
}
}
}
}
if (result != "")
return new ValidationResult(false, result);
else
return ValidationResult.ValidResult;
}
}
The code above shows a ClientValidatorRule which is derived from the generic GenericValidationRule class. The Client class is my POCO class which will be validated.
public class Client {
public Client() {
this.ID = Guid.NewGuid();
}
[Key, ScaffoldColumn(false)]
public Guid ID { get; set; }
[Display(Name = "Name")]
[Required(ErrorMessage = "You have to provide a name.")]
public string Name { get; set; }
}

Resources