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.
Related
I am using IDataErrorInfo inherited business objects for validation.
public string UserId { get; set; }
public string this[string columnName]
{
get
{
string result = null;
if (columnName == "UserId")
{
if (string.IsNullOrEmpty(UserId))
result = "Please enter User Id";
}
}
}
I want to clear all the validation errors when I click a button on the menu - like - LogOut.
Window is making the Login panel visible but the previous panel's validation error marks are still appearing in the current login panel.
I tried all the options to assign NULL datacontext, fresh entity object...but no luck
I appreciate your help.
Using the IDataErrorInfo interface is an error-first type approach. This means that you will see the error(s) until they are cleared. You can see that there is no setter on the indexer.
The original IDataErrorInfo interface is not overly useful by itself as it only deals with one error at a time. I added the following field into my BaseDataType class:
protected ObservableCollection<string> errors = new ObservableCollection<string>();
In my actual data classes, I have the following property:
public override ObservableCollection<string> Errors
{
get
{
errors = new ObservableCollection<string>();
errors.AddUniqueIfNotEmpty(this["Property1"]);
errors.AddUniqueIfNotEmpty(this["Property2"]);
errors.AddUniqueIfNotEmpty(this["PropertyN"]);
return errors;
}
}
The AddUniqueIfNotEmpty method is an extension method which I believe is self-explanatory. This property calls the indexer any number of times and compiles all of the results into a ObservableCollection<string> collection ready to be displayed in the UI. You'll need to call the INotifyPropertyChanged.PropertyChanged event with the name Errors when Property1, Property2 and PropertyN are updated to make this work.
You could do something like this, but add a setter for you to pass in an empty collection or string when you want to clear the errors.
I'm writing a WPF application and I'm currently refactoring some reused code to a base ViewModel Class which my other viewmodels can inherit from.
One Property field on this base class is
public class MessageParentBase
{
MessageParentBase() {}
public string Name;
}
internal ObservableCollection<MessageParentBase> _GridData = new ObservableCollection<MessageParentBase>();
I have a subsequent property declaration
public ObservableCollection<MessageParentBase> GridData
{
get { return _GridData; }
set { _GridData = value; }
}
This works great and everything my issue is that the inerited classes actually use the follow class
Public class ChatMessage : MessageParentBase
{
public string Message;
}
and the view contains a grid of data which is bound to this GridData property but the column which should be bound to the Message field from the ChatMessage class is blank and the fields found in the MessageParentBase class are populated.
So I presume there is an issue with the view not knowing to cast up to the ChatMessage from the MessageParentBase class.
Can I inform the view that the objects will be of the type "ChatMessage".
I did try moving the property declaration up to the inherited viewmodel as
public ObservableCollection<ChatMessage> GridData
{
get { return _GridData; }
set { _GridData = value; }
}
but this gives me the following error:-
Cannot implicitly convert type 'System.Collections.ObjectModel.ObservableCollection' to 'System.Collections.ObjectModel.ObservableCollection'
Do I need to cast at the view level or can I change the viewmodels to implement this better?
Any suggestions would be greatly appreciated.
Emlyn
Change the collection to this:
public ObservableCollection<MessageParentBase> GridData { get; set; }
then add into your constructor
this.GridData = new ObservableCollection<MessageParentBase>();
Since WPF uses reflection to retrieve bound data from the data context it should be able to get the values of the derived classes stored in that collection.
Also when you run your application check the output window with Debug selected, the XAML engine will output any binding errors there.
Your ViewModel should contain a list with the type that your grid will show (in this case, the ChatMessage type). You can still use the inheritance to call common methods, but the binded list must be of the ChatMessage type
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.
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
}
}
When creating a custom IValueConverter for a user-editable field, the Convert implementation is usually fairly straightforward.
The ConvertBack implementation is not, since (in the absence of an explicit validation rule) it has to cope with bad user input (eg. typing "hi" in a numeric input field).
If an error occurs during conversion, there doesn't seem to be any way to communicate the specific error:
ConvertBack isn't allowed to throw exceptions (if it does, the program terminates).
Returning a ValidationError doesn't do anything.
Returning DependencyProperty.UnsetValue (which the docs suggest) results in silent failure (no error UI is shown to the user, even if you've set up an error template).
Returning the original unconverted value does cause error UI to be shown, but with misleading error text.
Does anyone know of any better way to handle this sort of thing?
(Note: while defining a custom ValidationRule would work, I don't think that's a good answer, since it would basically have to duplicate the conversion logic to discover the error anyway.)
You can avoid duplicating logic by having the validation rule's first step being to call the value converter to find out if the value it's validating is even usable.
Of course, this couples your validation rules to your value converters, unless you make a validation rule that searches the binding to find out what value converter is in use. But if you start going down that road, sooner later it will probably occur to you, as it has to many, "wait, if I'm using MVVM, what am I doing screwing around with value converters?"
Edit:
If your ViewModel implements IDataErrorInfo, which is really the only way to live, it's relatively straightforward to hook a value converter into a property setter without writing a lot of property-specific validation logic.
In your ViewModel class, create two private fields:
Dictionary<string, string> Errors;
Dictionary<string, IValueConverter>;
Create them (and populate the second) in the constructor. Also, for IDataErrorInfo:
public string this[string columnName]
{
return Errors.ContainsKey(columnName)
? Errors[columnName]
: null;
}
Now implement a method like this:
private bool ValidateProperty(string propertyName, Type targetType, object value)
{
Errors[propertyName] = null;
if (!Converters.ContainsKey(propertyName))
{
return true;
}
try
{
object result = Converters[propertyName].ConvertBack(value, targetType, null, null)
return true;
}
catch (Exception e)
{
Errors[propertyName] = e.Message;
return false;
}
}
Now your property setter looks like this:
public SomeType SomeProperty
{
set
{
if (ValidateProperty("SomeProperty", typeof(SomeType), value))
{
_SomeProperty = value;
}
}
}