{"id":66,"date":"2011-11-28T19:03:00","date_gmt":"2011-11-28T19:03:00","guid":{"rendered":"http:\/\/tpodolak.com.hostingasp.pl\/blog\/2011\/11\/28\/walidacja\/"},"modified":"2016-01-31T00:37:45","modified_gmt":"2016-01-31T00:37:45","slug":"walidacja","status":"publish","type":"post","link":"https:\/\/tpodolak.com\/blog\/2011\/11\/28\/walidacja\/","title":{"rendered":"Walidacja"},"content":{"rendered":"<p>Walidacja jest to technika sprawdzaj\u0105ca, czy dany obiekt spe\u0142nia pewne za\u0142o\u017cenia poprawno\u015bci danych. W WPF-ie oraz Silverlighcie istniej\u0105 trzy sposoby walidacji obiekt\u00f3w:<\/p>\n<ul>\n<li>walidacja poprzez rzucanie wyj\u0105tk\u00f3w,<\/li>\n<li>walidacja z u\u017cyciem interfejsu <span style=\"font-style: italic;\">IDataErrorInfo<\/span>,<\/li>\n<li>walidacja z u\u017cyciem interfejsu <span style=\"font-style: italic;\">INotifyDataErrorInfo<\/span><\/li>\n<\/ul>\n<h3>1. Walidacja poprzez rzucanie wyj\u0105tk\u00f3w<\/h3>\n<p>Walidacja poprzez rzucanie wyj\u0105tk\u00f3w odbywa si\u0119 w nast\u0119puj\u0105cy spos\u00f3b. W seterze danej w\u0142a\u015bciwo\u015bci dodajemy warunek sprawdzaj\u0105cy czy wpisane dane s\u0105 poprawne. Je\u017celi nie to najzwyczajniej w \u015bwiecie rzucamy wyj\u0105tek, w kt\u00f3rym podajemy komunikat b\u0142\u0119du. Przyk\u0142adowy properties z walidacj\u0105 mo\u017ce wygl\u0105da\u0107 w ten spos\u00f3b:<\/p>\n<pre lang=\"csharp\">\r\nprivate string _name;\r\npublic string Name\r\n{\r\n    get { return _name; }\r\n    set\r\n    {\r\n        _name = value;\r\n        if (string.IsNullOrEmpty(_name))\r\n            throw new Exception(\"Nazwa nei mo\u017ce by\u0107 pusta\");\r\n    }\r\n}\r\n<\/pre>\n<p>W celu &#8220;wy\u0142apania&#8221; tego wyj\u0105tku i pokazania odpowiedniego komunikatu,w bindingu musimy ustawi\u0107 w\u0142a\u015bciwo\u015b\u0107 <span style=\"font-style: italic;\">ValidatesOnExceptions <\/span>na warto\u015b\u0107 true.<\/p>\n<pre lang=\"xml\">\r\n<TextBox Text=\"{Binding Name, ValidatesOnExceptions=True, Mode=TwoWay}\" \/><\/pre>\n<p>Taki spos\u00f3b walidowania jest jednak rzadko stosowany i wielu programist\u00f3w twierdzi, \u017ce rzucanie wyj\u0105tk\u00f3w powinno si\u0119 odbywa\u0107 tylko w przypadku nieprawid\u0142owego dzia\u0142ania aplikacji. Ponadto walidowane propertisy nie mog\u0105 by\u0107 autopropertisami, co dodatkowo wyd\u0142u\u017ca czas tworzenia klas.<\/p>\n<h3>2. Walidacja z u\u017cyciem interfejsu <span style=\"font-style: italic;\">IDataErrorInfo<\/span><\/h3>\n<pre lang=\"csharp\">\r\npublic interface IDataErrorInfo\r\n{\r\n    string this[string columnName] { get; }\r\n    string Error { get; }\r\n}\r\n<\/pre>\n<p>W celu wy\u0142apywania b\u0142\u0119d\u00f3w w widoku, nale\u017cy w bindingu ustawi\u0107 <span style=\"font-style: italic;\">ValidatesOnDataError = true <\/span><\/p>\n<pre lang=\"xml\">\r\n<TextBox Text=\"{Binding Name, ValidatesOnDataError=True, Mode=TwoWay}\" \/>\r\n<\/pre>\n<p>Przyk\u0142adowa klasa implementuj\u0105ca interfejs <span style=\"font-style: italic;\">IDataErrorInfo <\/span>mo\u017ce wygl\u0105da\u0107 nast\u0119puj\u0105co:<\/p>\n<pre lang=\"csharp\">\r\npublic class Customer : IDataErrorInfo\r\n{\r\n    public string Name { get; set; }\r\n    \r\n    public string Error\r\n    {\r\n        get { return string.Empty; }\r\n    }\r\n    \r\n    public string this[string propertyName]\r\n    {\r\n        get \r\n        {\r\n            string result = string.Empty;\r\n            if (propertyName == \"Name\")\r\n            {\r\n                if (string.IsNullOrEmpty(Name))\r\n                    result = \"Warto\u015b\u0107 nie mo\u017c by\u0107 pusta\";\r\n            }\r\n            \r\n            return result;\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>Najwa\u017cniejsz\u0105 metod\u0105 w powy\u017cszej klasie jest indekser<\/p>\n<pre lang=\"csharp\">\r\nstring this[string propertyName]\r\n<\/pre>\n<p>to w\u0142a\u015bnie tutaj mog\u0105 zosta\u0107 sprawdzone wszystkie w\u0142a\u015bciwo\u015bci danego obiektu &#8211; <span style=\"font-style: italic;\">propertyName <\/span>oznacza nazw\u0119 propertisu, kt\u00f3ry walidujemy. W przypadku, gdy warto\u015b\u0107 jakiej\u015b w\u0142a\u015bciwo\u015bci jest nieprawid\u0142owa, w pole result wpisujemy komunikat b\u0142\u0119du. Komunikaty te &#8220;wy\u0142apywane&#8221; s\u0105 przez widok, a nast\u0119pnie wy\u015bwietlane w postaci komunikat\u00f3w przy odpowiednich kontrolkach. Je\u017celi wszystko jest OK zwracamy <span style=\"font-style: italic;\">string.Empty<\/span>.Walidacje przy pomocy interfejsu <span style=\"font-style: italic;\">IDataErrorInfo <\/span>idealnie nadaj\u0105 si\u0119 do walidowania modelu.<\/p>\n<h3>3. Walidacja z u\u017cyciem interfejsu <span style=\"font-style: italic;\">INotifyDataErrorInfo<\/span><\/h3>\n<p>Interfejs <span style=\"font-style: italic;\">INotifyDataErrorInfo <\/span>prezentuje si\u0119 w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n<pre lang=\"csharp\">\r\npublic interface INotifyDataErrorInfo\r\n{\r\n     bool HasErrors { get; }\r\n     event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;\r\n     IEnumerable GetErrors(string propertyName);\r\n}\r\n<\/pre>\n<ul>\n<li><span style=\"font-style: italic;\">bool HasErrors<\/span> &#8211; okre\u015bla czy dany obiekt zawiera b\u0142\u0119dy<\/li>\n<li><span style=\"font-style: italic;\">event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged<\/span>&#8211; zdarzenie informuj\u0105ce o zmienie ilo\u015bci b\u0142\u0119d\u00f3w w obiekcie<\/li>\n<li><span style=\"font-style: italic;\">IEnumerable GetErrors(string propertyName)<\/span> &#8211; funkcja pobieraj\u0105ca kolekcj\u0119 b\u0142\u0119d\u00f3w dla danego propertisa<\/li>\n<\/ul>\n<p>W celu &#8220;wy\u0142apania&#8221; b\u0142\u0119d\u00f3w przez widok nale\u017cy w bindingu ustawi\u0107 properties<br \/>\n<span style=\"font-style: italic;\">NotifyOnValidationError = true<\/span><\/p>\n<pre lang=\"xml\">\r\n<TextBox Text=\"{Binding Name, NotifyOnValidationError=True, Mode=TwoWay}\" \/>\r\n<\/pre>\n<p>Jak ju\u017c wcze\u015bniej wspomniano funkcja <span style=\"font-style: italic;\">GetErrors(string propertyName)<\/span> zwraca kolekcj\u0119 b\u0142\u0119d\u00f3w dla danej w\u0142a\u015bciwo\u015bci. Zatem do klasy, kt\u00f3ra b\u0119dzie implementowa\u0142a interfejs <span style=\"font-style: italic;\">INotifyDataErrorInfo <\/span>nale\u017cy doda\u0107 kolekcj\u0119 przechowuj\u0105c\u0105 obiekty typu <span style=\"font-style: italic;\">ValidationResult<\/span>. Przyk\u0142adowa implementacja interfejsu mo\u017ce wygl\u0105da\u0107 w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n<pre lang=\"csharp\">\r\npublic class BaseViewModel : INotifyDataErrorInfo\r\n{\r\n    private ICollection<ValidationResult> _validationResults;\r\n    public IEnumerable GetErrors(string propertyName)\r\n    {\r\n        return _validationResults.Where(result => result.MemberNames.Contains(propertyName));\r\n    }\r\n \r\n    public bool HasErrors\r\n    {\r\n        get { return _validationResults.Count > 0; }\r\n    }\r\n \r\n    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;\r\n    \r\n    private void NotifyErrorsChanged(string propertyName)\r\n    {\r\n        if(ErrorsChanged!=null)\r\n            ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));\r\n    }\r\n\r\n    protected void Validate()\r\n    {\r\n        _validationResults.Clear();\r\n        Validator.TryValidateObject(this, new ValidationContext(this, null, null), _validationResults, true);\r\n     \r\n        foreach (var result in _validationResults)\r\n            NotifyErrorsChanged(result.MemberNames.First());\r\n    }\r\n    \r\n    protected void Validate(string propertyName)\r\n    {\r\n        var validationResults = _validationResults.Where(result => result.MemberNames.Contains(propertyName)).ToList();\r\n        foreach (var result in validationResults)\r\n               _validationResults.Remove(result);\r\n        \r\n        Validator.TryValidateProperty(value, new ValidationContext(this, null, null) { MemberName = propertyName }, _validationResults);\r\n           NotifyErrorsChanged(propertyName);\r\n     }\r\n}\r\n<\/pre>\n<p>Funkcja <span style=\"font-style: italic;\">Validate()<\/span> najpierw czy\u015bci wszystkie poprzednie wyniki walidacji, a nast\u0119pnie przy pomocy klasy <span style=\"font-style: italic;\">Validator <\/span>oraz funkcji <span style=\"font-style: italic;\">TryValidateObject <\/span>waliduje wszystkie w\u0142a\u015bciwo\u015bci, kt\u00f3re zosta\u0142y oznaczone atrybutem dziedzicz\u0105cym po klasie <span style=\"font-style: italic;\">ValidationAttribute<\/span>.Z kolei funkcja <span style=\"font-style: italic;\">Validate(string propertyName)<\/span> waliduje tylko konkretn\u0105 w\u0142a\u015bciwo\u015b\u0107.<br \/>\nPrzyk\u0142adowe walidowanie w\u0142a\u015bciwo\u015bci przy pomocy atrybut\u00f3w mo\u017ce wygl\u0105da\u0107 w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n<pre lang=\"csharp\">\r\n[Required(ErrorMessage = \"Pole nie mo\u017ce by\u0107 puste\")]\r\npublic string Subject\r\n{\r\n    get { return _subject; }\r\n    set\r\n    {\r\n        _subject = value;\r\n         Validate(\u201cSubject\u201d)\r\n    }\r\n}\r\n<\/pre>\n<p>W celu stworzenia w\u0142asnych reg\u00f3\u0142 walidacji, nie uwzgl\u0119dnionych w zapewnionych przez framework atrybutach nale\u017cy stworzy\u0107 w\u0142asn\u0105 klas\u0119 dziedzicz\u0105c\u0105 po klasie <span style=\"font-style: italic;\">ValidationAttribute<\/span>, a nast\u0119pnie przeci\u0105\u017cy\u0107 metod\u0119 <span style=\"font-style: italic;\">IsValid<\/span>. Przyk\u0142adowa klasa mo\u017ce wygl\u0105da\u0107 w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n<pre lang=\"csharp\">\r\npublic class IntValidation : ValidationAttribute\r\n{\r\n    private bool allowNull;\r\n    public IntValidation(bool allowNull)\r\n    {\r\n        this.allowNull = allowNull;\r\n    }\r\n    \r\n    protected override ValidationResult IsValid(object value, ValidationContext validationContext)\r\n    {\r\n        if (value != null)\r\n        {\r\n            int resul;\r\n            if (int.TryParse(value.ToString(), out resul))\r\n                return ValidationResult.Success;\r\n            else\r\n                return new ValidationResult(ErrorMessage, new List{validationContext.MemberName });\r\n        }\r\n        \r\n        return allowNull ? ValidationResult.Success : new ValidationResult(ErrorMessage);\r\n    }\r\n}\r\n<\/pre>\n<p>Walidacja przy u\u017cyciu interfejsu <span style=\"font-style: italic;\">INotifyDataErrorInfo <\/span>idealnie nadaje si\u0119 (wg mnie) do walidowaniu ca\u0142ych ViewModeli. Przy zamykaniu okna wystarczy wywo\u0142a\u0107 funkcj\u0119 Validate() z bazowego ViewModelu, a w przypadku gdy zwr\u00f3ci ona false zatrzyma\u0107 zamykanie okna. Jako, \u017ce walidacja zostanie przeprowadzona na wszystkich wybranych przez nas propertisach, widok automatycznie si\u0119 zaktualizuje i poka\u017ce komunikaty b\u0142\u0119d\u00f3w na odpowiednich kontrolkach okna.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Walidacja jest to technika sprawdzaj\u0105ca, czy dany obiekt spe\u0142nia pewne za\u0142o\u017cenia poprawno\u015bci danych. W WPF-ie oraz Silverlighcie istniej\u0105 trzy sposoby walidacji obiekt\u00f3w: walidacja poprzez rzucanie wyj\u0105tk\u00f3w, walidacja z u\u017cyciem interfejsu IDataErrorInfo, walidacja z u\u017cyciem interfejsu INotifyDataErrorInfo 1. Walidacja poprzez rzucanie wyj\u0105tk\u00f3w Walidacja poprzez rzucanie wyj\u0105tk\u00f3w odbywa si\u0119 w nast\u0119puj\u0105cy spos\u00f3b. W seterze danej w\u0142a\u015bciwo\u015bci dodajemy [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[141,142,44,108,45],"tags":[268,269,190,243,191],"class_list":["post-66","post","type-post","status-publish","format-standard","hentry","category-idataerrorinfo","category-inotifyerrorinfo","category-silverlight","category-walidacja","category-wpf","tag-idataerrorinfo","tag-inotifyerrorinfo","tag-silverlight","tag-walidacja","tag-wpf"],"_links":{"self":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/66","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/comments?post=66"}],"version-history":[{"count":9,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/66\/revisions"}],"predecessor-version":[{"id":571,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/66\/revisions\/571"}],"wp:attachment":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/media?parent=66"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/categories?post=66"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/tags?post=66"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}