Are there any recommendations about performing asynchronous MVVM-ish validations in WPF? Have read about INotifyDataErrorInfo, but unfortunately is only available to Silverlight.
Thanks.
IDataErrorInfo is the data validation mechanism for WPF. Don't you just love Microsoft's consistency? ;)
Implement IDataErrorInfo on your ViewModel like this:
public class MyViewModel : IDataErrorInfo
{
public string Error
{
get {
return GetErrorStringForThisViewModelInGeneral();
}
}
public string this[string columnName]
{
get
{
string result = null;
switch (columnName)
{
case "Quantity":
if (Quantity <= 0)
result = "Quantity must be greater than 1.";
break;
}
return result;
}
}
Inside of the property (aka this[]) validation, you could use the validator in the EnterpriseLibrary, a custom validator using Attributes, or anything you like. I am just showing a basic implementation to get you started.
Related
I have a window in WPF. I need to have a validation mechanizm to check all elements at once. If I use IDataErrorInfo there is only ability to validate one object at once in the indexer.
public string this[string columnName]
{
get
{
if (columnName == "Country"))
{
if (string.IsNullOrEmpty(Country))
return "Country can't be empty";
}
return null;
}
}
How can I get all fields of my Window in the validation method?
I think the best way is to declare a custom attribute and decorate the properties you wanna validate with it, and then use Reflection to iterate through those properties based on whether they have that custom attribute.
I'm looking to create a custom version of UpdateSourceTrigger that I can use with my binding. I don't know if this is possible, or if instead, I'd need to just create my own binding class. What I'm looking for is, instead of LostFocus or PropertyChanged, have something where it will update the source after some specified time limit.
I found this, but I don't know if there's a better way (one of the comments mentioned some memory leaks with the implementation).
Any ideas?
I just noticed that WPF 4.5 has a Delay Property, see for more information this link
http://www.shujaat.net/2011/12/wpf-45-developers-preview-delay-binding.html
I wouldn't bother doing this at the binding level, but would instead manifest it in my view model. When the property changes, restart a DispatcherTimer. When the timer expires, kick off your logic. It's that simple.
This can be easily implemented using Reactive Extensions's Throttle() method in conjunction with an observable property.
public class ObservablePropertyBacking<T> : IObservable<T>
{
private readonly Subject<T> _innerObservable = new Subject<T>();
private T _value;
public T Value
{
get { return _value; }
set
{
_value = value;
_innerObservable.OnNext(value);
}
}
#region IObservable<T> Members
public IDisposable Subscribe(IObserver<T> observer)
{
return _innerObservable
.DistinctUntilChanged()
.AsObservable()
.Subscribe(observer);
}
#endregion
}
Used like this:
// wire query observable
var queryActual = new ObservablePropertyBacking<string>();
queryActual.Throttle(TimeSpan.FromMilliseconds(300)).Subscribe(DoSearch);
Implement property:
string query;
public string Query
{
get { return query; }
set
{
queryActual.Value = value;
}
}
I'm writing an application where I'm attempting to use an MVVM style architecture to handle my data binding (although I'm not using a MVVM specific library, such as MVVM Light). I've got a class which stores all of the information that my application requires, and then each of the screens is assigned a view model to its DataContext, which simply selects the values required for the specific screen, formatting the data if necessary.
As an example, the main data store looks something like this:
class DataStore {
int a, b, c;
string d;
DateTime e;
}
And then the view model allocated to a specific screen, which only uses several of the properties, is something like
class MainScreenViewModel {
public int data1 { get { return App.DataStore.a * App.DataStore.c } }
public int data2 { get { return App.DataStore.e.Day } }
}
This seems to work fine, when the page loads the data bindings are populated as they should be. However, they do not update automatically when the page loads. I've implemented INotifyPropertyChanged on the DataStore, but it seems that the change event doesn't bubble through to be reflected in the view model. I'm sure I'm going about this a really bad way, so if anyone could help point me in the right direction I'd be very grateful. I've read a stack of guides online, but I seem to be confusing myself more and more!
You have to implement INotifyPropertyChanged and raise PropertyChanged on your VM. In order to do this you will have to listen for DataStore.PropertyChanged. Sample:
class MainScreenViewModel {
public int data1 { get { return App.DataStore.a * App.DataStore.c } }
public int data2 { get { return App.DataStore.e.Day } }
public MainScreenViewModel()
{
App.DataStore.PropertyChanged += (sender, e) =>
{
if (e.PropertyName == "a" || e.PropertyName == "c")
RaisePropertyChanged("data1");
if (e.PropertyName == "e")
RaisePropertyChanged("data2");
};
}
private void RaisePropertyChanged(string propertyName)
{
// raise it
}
}
The only part not covered here is the scenario when e.Day will change in DataStore.
Your approach itself is not the bad and is definitely good enough to start with.
You're binding to the MainScreenViewModel class, so it is that class that needs to implement INotifyPropertyChanged for the UI to get updated when the underlying data gets updated.
You could either move the logic into MainScreenViewModel and raise property change notification there, or handle the PropertyChanged event on DataStore in MainScreenViewModel and raise property changed notification for the appropriate properties.
I have two viewmodel classes called ChangePwdViewModel.cs and ExpiringPwdViewModel.cs.
ChangPwd.xaml binds to ChangePwdViewModel and ExpiringPwd.xaml binds to ExpiringPwdViewModel.
Both have the property as below.
private string _message;
public string Message
{
get { return _message; }
set { _message = value; OnPropertyChanged("Message"); }
}
In each class, there's a function called ValidatePwd() to validate the new password.
In this function, Message property is updated.
Eg.
if (IsAlphaNumeric(this.NewPassword) == false || IsAlphaNumeric(this.CfmPassword) == false)
{
this.Message = "Invalid new password, only characters and numbers are accepted, password must contain at least one character and one number";
this.ResetPasswordFields();
return false;
}
I want to create a common class to write this function and used by two viewmodel. But, How can I update the Message Property of the viewmodels from this class?
How about putting it in a base class:
class ViewModelBase
{
private string _message;
public string Message
{
get { return _message; }
set { _message = value; OnPropertyChanged("Message"); }
}
public bool VerifyPassword(string newPassword)
{
....
}
}
class ChangePwdViewModel : ViewModelBase
{
}
class ExpiringPwdViewModel : ViewModelBase
{
}
Update:
If you can't use a base class because your view models already have a base class then you could use an interface as suggested by others. However this means that you will still have to implement the interface in all your view model classes so you don't gain that much in terms of avoiding multiple implementations (except that you have a contract for your view models then which is usually a good thing to have).
You can achieve some kind of "multiple inheritance" in C# by using a tool like Dynamic Proxy which allows you to create mixins. So you could implement the Message property and password verification in one class and then create a mixin proxy which merges the view model with that implementation. It's not as nice as you will have to create all your view model instances via the proxy generator but it can be made to work. Have a look at this tutorial if it sounds like an option for you.
You could have the two ViewModel classes implement a common interface, say IMessage that implemented a single property - Message.
Then your common class or a function would take a parameter of type IMessage that it could use to update the message.
I would suggest to avoid base classes (could cause potential design issues in future) in such cases, I would rather suggest to pass through constructor an algorithm of validation, smth like this:
public class MyViewModel
{
public MyViewModel(Func<bool> validationAlgorithm)
{
// ... save function to use later for a validation
}
}
The design I've come up with for filtering is awkward at best, and buggy at worst. The idea is to have a base class to support a pick list, and let subclasses add on additional filtering logic as needed.
What is particularly confusing to me is how to trigger the view to filter as various filtering criteria change (see _ApplyFiler(), below). Is setting the filter that way appropriate? Where should I unsubscribe / set it to null after it filters?
Cheers,
Berryl
ugly code:
public class SubjectPickerBase<T> : ViewModelBase, ISubjectPicker<T>
where T : class, IAvailableItem, INotifyPropertyChanged, IActivitySubject
{
public CollectionViewSource Subjects { get; private set; }
protected SubjectPickerBase() { }
protected void _Initialize(IEnumerable<T> subjects, string subjectName) {
...
Subjects = new CollectionViewSource { Source = subjects };
_ApplyFilter();
}
protected void _ApplyFilter() {
Subjects.View.Filter += Filter;
}
private bool Filter(object obj)
{
var subject = obj as T;
if (ReferenceEquals(subject, null)) return false;
NotifyPropertyChanged(() => Status);
var isIncludedBySubclass = OnFilter(subject);
var isIncludedByBase = subject.IsAvailable;
return isIncludedByBase & isIncludedBySubclass;
}
/// <summary>Hook to allow implementing subclass to provide it's own filter logic</summary>
protected virtual bool OnFilter(T subject) { return true; }
}
public class ProjectSelectionViewModel : SubjectPickerBase<ProjectViewModel>
{
public ProjectSelectionViewModel(IEnumerable<ProjectViewModel> projects)
{
...
_Initialize(projects, Strings.ActivitySubject__Project);
}
public string DescriptionMatchText {
get { return _descriptionMatchText; }
set {
ApplyPropertyChange<ProjectSelectionViewModel, string>(ref _descriptionMatchText, x => x.DescriptionMatchText, value);
_ApplyFilter();
}
}
private string _descriptionMatchText;
protected override bool OnFilter(ProjectViewModel subject)
{
...
var isDescriptionMatch = subject.IsMatch_Description(DescriptionMatchText);
return isPrefixMatch && isMidfixMatch && isSequenceNumberMatch && isDescriptionMatch;
}
}
There are several pieces to a non-trivial manipulation of the view that I was missing, all having to do with refreshing the CollectionView that is a property of the CollectionViewSource:
Refresh
DeferRefresh
NeedsRefresh
IsRefreshDeferred
The first part of my question was when to set the filter. For my use case, what worked best so far turned out to be registering for the CollectionViewSource.Filter event and then using the View.Refresh method each time a filter is changed. The initial registration of the filter event also triggers the event handler, and many of the msdn samples you see show this as a way of filtering a view, and nothing else. But if your scenario is not trivial & the user can change some filter criteria, you need to use one or more of the above refresh related methods & properties.
The second part of my question had to do with whether you needed to unsubscribe to the filter event, and if so, when. Well, it turns out that you don't need to unsubscribe, but if you do so it effectively clears any filtering of the view. And many of the msdn trivial samples do exactly that to clear the filter, which is certainly the way to go if you want to completely clear any filtering, but for my use case was not what I really wanted. What I wanted was to clear some criteria but not others, and so again using Refresh (at the right time) gave me the desired behavior.
HTH,
Berryl