c# - Multiple validation errors with INotifyDataErrorInfo, old messages don't disappear -


in wpf project, i'm validating textbox input inotifydataerrorinfo implementation. multiple errors can occur.

when input causes multiple errors, validation errors displayed. however, when fix 1 error, validation message doesn't change, means false error messages shown. when fix errors message disappears.

is problem implementation, or wpf implementation re-fetch validation message if haserrors changed? stepping through debugger, however, can see geterrors , haserrors both called.

steps reproduce attached example:

  • enter 333333. 2 validation messages shown.
  • change leading 3 2. still, both validation messages shown, although first error has been fixed.
  • change second 3 0. both messages disappear
  • change second digit 3 again. second validation message shown.
  • change leading digit 3 again. second validation message shown, although should display both.

and yes, example doesn't make sense, since rid of first check, since it's included in second check.

the view:

<window x:class="weirdvalidationtest.validationtestview"         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:weirdvalidationtest"         height="60" width="400">     <window.datacontext>         <local:validationtestviewmodel />     </window.datacontext>     <window.resources>         <local:validationerrorstostringconverter x:key="valerrtostring" />         <controltemplate x:key="errortemplate">             <border borderbrush="red" borderthickness="1">                 <stackpanel orientation="horizontal">                     <adornedelementplaceholder />                     <textblock text="{binding converter={staticresource valerrtostring}}" background="white" />                 </stackpanel>             </border>         </controltemplate>     </window.resources>     <grid>         <textbox maxlength="6" validation.errortemplate="{staticresource errortemplate}"                  text="{binding inputvalue, updatesourcetrigger=propertychanged}" horizontalalignment="left"                  verticalalignment="top" width="100" />     </grid> </window> 

(code-behind simple initializecomponent call in constructor)

the viewmodel:

using system; using system.collections; using system.collections.generic; using system.componentmodel; using system.runtime.compilerservices;  namespace weirdvalidationtest {     internal class validationtestviewmodel : inotifypropertychanged, inotifydataerrorinfo     {         private readonly dictionary<string, list<string>> errors = new dictionary<string, list<string>>();         private uint inputvalue;          public event propertychangedeventhandler propertychanged;          public event eventhandler<dataerrorschangedeventargs> errorschanged;          public uint inputvalue         {                         {                 return inputvalue;             }             set             {                 if (inputvalue != value)                 {                     if (value / 100000 == 2)                     {                         removeerror("inputvalue",                                     string.format("must in range {0}...{1}", "200000", "299999"));                     }                     else                     {                         adderror("inputvalue",                                  string.format("must in range {0}...{1}", "200000", "299999"));                     }                      uint testnumber = (uint) ((value) / 1e4);                      {                         string msg = string.format("must start value {0}", "20....");                         if (testnumber != 20)                         {                             adderror("inputvalue", msg);                         }                         else                         {                             removeerror("inputvalue", msg);                         }                     }                      inputvalue = value;                     onpropertychanged();                 }             }         }          public bool haserrors         {                         {                 return errors.count != 0;             }         }          public ienumerable geterrors(string propertyname)         {             list<string> val;             errors.trygetvalue(propertyname, out val);             return val;         }          void adderror(string propertyname, string messagetext)         {             list<string> errlist;             if (errors.trygetvalue(propertyname, out errlist))             {                 if (!errlist.contains(messagetext))                 {                     errlist.add(messagetext);                 }             }             else             {                 errlist = new list<string> { messagetext };                 errors.add(propertyname, errlist);             }             onerrorschanged(propertyname);         }          void removeerror(string propertyname, string messagetext)         {             list<string> errlist;             if (errors.trygetvalue(propertyname, out errlist))             {                 errlist.remove(messagetext);                 if (errlist.count == 0)                 {                     errors.remove(propertyname);                 }             }             onerrorschanged(propertyname);         }          private void onerrorschanged(string propertyname)         {             var handler = errorschanged;             if (handler != null)             {                 handler(this, new dataerrorschangedeventargs(propertyname));             }         }          private void onpropertychanged([callermembername] string propertyname = "")         {             var handler = propertychanged;             if (handler != null)             {                 handler(this, new propertychangedeventargs(propertyname));             }         }     } } 

the converter:

using system; using system.collections.objectmodel; using system.globalization; using system.linq; using system.windows; using system.windows.controls; using system.windows.data;  namespace weirdvalidationtest {     [valueconversion(typeof(readonlyobservablecollection<validationerror>), typeof(string))]     internal class validationerrorstostringconverter : ivalueconverter     {         public object convert(object value, type targettype, object parameter, cultureinfo culture)         {            var errorcollection = value readonlyobservablecollection<validationerror>;              if (errorcollection == null)             {                 return dependencyproperty.unsetvalue;             }              return string.join(", ", errorcollection.select(e => e.errorcontent.tostring()));         }          public object convertback(object value, type targettype, object parameter,                                   cultureinfo culture)         {             throw new notimplementedexception();         }     } } 

target .net version 4.5

edit: hit similar problem idataerrorinfo, see question: validation rule not updating correctly 2 validation rules changing converter helps

nice explanation , code looks pretty too. resolved issue in way hope it. change adderror , removeerror methods this,

void adderror(string propertyname, string messagetext)         {             list<string> errlist;             if (errors.trygetvalue(propertyname, out errlist))             {                 if (!errlist.contains(messagetext))                 {                     errlist.add(messagetext);                     errors.remove(propertyname);                     onerrorschanged(propertyname);                     if (errlist != null)                         errors.add(propertyname, errlist);                 }             }             else             {                 errlist = new list<string> { messagetext };                 errors.add(propertyname, errlist);                 onerrorschanged(propertyname);             }         }         void removeerror(string propertyname, string messagetext)         {             list<string> errlist;             if (errors.trygetvalue(propertyname, out errlist))             {                 errlist.remove(messagetext);                 errors.remove(propertyname);             }             onerrorschanged(propertyname);             if (errlist != null)                 errors.add(propertyname, errlist);          } 

Comments

Popular posts from this blog

java - Run spring boot application error: Cannot instantiate interface org.springframework.context.ApplicationListener -

reactjs - React router and this.props.children - how to pass state to this.props.children -

Excel VBA "Microsoft Windows Common Controls 6.0 (SP6)" Location Changes -