c# - How to validate view controls against data in the viewmodel -
i'm new @ wpf, , i'm getting head around validators, seems need inherit validationrule , override validate function, totally separated view model, if want validate against list/collection/set/dictionary in viewmodel, check whether new input not in list, example creating validation see whether or not username not taken.
there several different ways validation in wpf. there's 2 main ways can think of off top of head
- create validation rules apply them in xaml
- implement idataerrorinfo in viewmodel
validation rules specified in xaml (gui), while implementing idataerrorinfo moves validation logic viewmodel (business logic). while, validationrules nice because can create own , reuse them, fail provide validation in business logic required.
the concept of client vs. server side validation interesting, perhaps pertains silverlight, since tagged wpf, i'm assuming difference whether validation occurs in views or viewmodels (ui or business logic). seem me if ui validated inputs, viewmodels still need proper validation.
therefore, suggest implementing idataerrorinfo. way, reason idataerrorinfo works, because validationrule exists checks idataerrorinfo interface! here's example of how in viewmodelbase class:
note: following examples ignore fact need inotifypropertychanged notifications update bindings , instead focuses on validation.
public class viewmodelbase : idataerrorinfo { private dictionary<string, string> errors = new dictionary<string, string>(); // required idataerrorinfo public virtual string error { { return string.join(environment.newline, errors); } } // required idataerrorinfo public string this[string propertyname] { { string result; errors.trygetvalue(propertyname, out result); return result; } } // useful property check if have errors public bool haserrors { { return errors.count > 0; } } protected void seterror<t>(string propertyname, string error) { if (error == null) errors.remove(propertyname); else errors[propertyname] = error; onhaserrorschanged(); } protected string geterror<t>(string propertyname, string error) { string s; errors.trygetvalue(propertyname, out s); return s; } protected virtual void onhaserrorschanged() { } }
then viewmodels can implement this:
public class myviewmodel : viewmodelbase { private string someproperty; public string someproperty { { return someproperty; } set { if(someproperty != null) { someproperty = value; seterror("someproperty", validatesomeproperty()); } } } private string validatesomeproperty() { if(string.isnullorempty(someproperty)) return "value required"; return null; } }
in ui, you'll need add validatesondataerrors , notifyonvalidationerror this:
text="{binding someproperty, updatesourcetrigger=propertychanged, validatesondataerrors=true, notifyonvalidationerror=true}"
note: passing in strings represent properties kinda ugly (it's not refactor safe if rename property forget rename string). inotifypropertychanged same way when want notify of property changes databindings. prism's notificationobject has refactor safe solution , looks instead:
replace geterror / seterror in previous example this:
protected void seterror<t>(expression<func<t>> prop, string error) { string propertyname = propertysupport.extractpropertyname(prop); if (error == null) errors.remove(propertyname); else errors[propertyname] = error; onhaserrorschanged(); } protected string geterror<t>(expression<func<t>> prop, string error) { string propertyname = propertysupport.extractpropertyname(prop); string s; errors.trygetvalue(propertyname, out s); return s; }
and viewmodelbase this:
public class viewmodelbase : notificationobject, idataerrorinfo
then implement properties:
public class myviewmodel : viewmodelbase { private string someproperty; public string someproperty { { return someproperty; } set { if(someproperty != null) { someproperty = value; seterror( () => someproperty, validatesomeproperty()); // update validation property raisepropertychanged( () => someproperty); // notify data bindings } } }
i didn't show implementation raisepropertychanged in prism's notificationobject open source , free download. can instead implement inotifypropertychanged , either raise event strings (not refactor safe) or implement similar above implemention seterror (extract property name , fire event it).
you'll need helper method:
public static class propertysupport { public static string extractpropertyname<t>(expression<func<t>> propertyexpresssion) { if (propertyexpresssion == null) { throw new argumentnullexception("propertyexpression"); } var memberexpression = propertyexpresssion.body memberexpression; if (memberexpression == null) { throw new argumentexception("the expression not member access expression.", "propertyexpression"); } var property = memberexpression.member propertyinfo; if (property == null) { throw new argumentexception("the member access expression not access property.", "propertyexpression"); } var getmethod = property.getgetmethod(true); if (getmethod.isstatic) { throw new argumentexception("the referenced property static property.", "propertyexpression"); } return memberexpression.member.name; } }
edit: simplified refactor safe alternative
if using .net 4.5 or later, can use callermemberattribute this example shows inotifypropertychanged , seterror implementation. wouldn't need extract property name via reflection above (it simplifies quite bit).
sorry off track talking property change notifications, go hand in hand if want databindings , validation work!
Comments
Post a Comment