├── .gitignore ├── License.md ├── README.md ├── UsingValidation.sln ├── UsingValidation ├── UsingValidation.Droid │ ├── Assets │ │ └── AboutAssets.txt │ ├── Effects │ │ └── BorderEffect.cs │ ├── MainActivity.cs │ ├── Properties │ │ ├── AndroidManifest.xml │ │ └── AssemblyInfo.cs │ ├── Resources │ │ ├── AboutResources.txt │ │ ├── Resource.Designer.cs │ │ ├── drawable-hdpi │ │ │ └── icon.png │ │ ├── drawable-xhdpi │ │ │ └── icon.png │ │ ├── drawable-xxhdpi │ │ │ └── icon.png │ │ ├── drawable │ │ │ ├── border.xml │ │ │ └── icon.png │ │ ├── layout │ │ │ ├── tabs.xml │ │ │ └── toolbar.xml │ │ ├── values-v21 │ │ │ └── styles.xml │ │ └── values │ │ │ ├── colors.xml │ │ │ └── styles.xml │ ├── UsingValidation.Droid.csproj │ └── packages.config ├── UsingValidation.UWP │ ├── App.xaml │ ├── App.xaml.cs │ ├── Assets │ │ ├── LockScreenLogo.scale-200.png │ │ ├── SplashScreen.scale-200.png │ │ ├── Square150x150Logo.scale-200.png │ │ ├── Square44x44Logo.scale-200.png │ │ ├── Square44x44Logo.targetsize-24_altform-unplated.png │ │ ├── StoreLogo.png │ │ └── Wide310x150Logo.scale-200.png │ ├── Effects │ │ └── BorderEffect.cs │ ├── MainPage.xaml │ ├── MainPage.xaml.cs │ ├── Package.appxmanifest │ ├── Properties │ │ ├── AssemblyInfo.cs │ │ └── Default.rd.xml │ ├── UsingValidation.UWP.csproj │ ├── UsingValidation.UWP.nuget.targets │ ├── UsingValidation.UWP_TemporaryKey.pfx │ ├── project.json │ └── project.lock.json ├── UsingValidation.iOS │ ├── AppDelegate.cs │ ├── Effects │ │ └── BorderEffect.cs │ ├── Entitlements.plist │ ├── Info.plist │ ├── Main.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Resources │ │ ├── Default-568h@2x.png │ │ ├── Default-Portrait.png │ │ ├── Default-Portrait@2x.png │ │ ├── Default.png │ │ ├── Default@2x.png │ │ ├── Icon-60@2x.png │ │ ├── Icon-60@3x.png │ │ ├── Icon-76.png │ │ ├── Icon-76@2x.png │ │ ├── Icon-Small-40.png │ │ ├── Icon-Small-40@2x.png │ │ ├── Icon-Small-40@3x.png │ │ ├── Icon-Small.png │ │ ├── Icon-Small@2x.png │ │ ├── Icon-Small@3x.png │ │ └── LaunchScreen.storyboard │ ├── UsingValidation.iOS.csproj │ ├── iTunesArtwork │ ├── iTunesArtwork@2x │ └── packages.config └── UsingValidation │ ├── App.xaml │ ├── App.xaml.cs │ ├── Effects │ └── BorderEffect.cs │ ├── Models │ └── Item.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── UsingValidation.csproj │ ├── UsingValidation.csproj.user │ ├── Validation │ ├── EntryValidationBehavior.cs │ └── ValidationBase.cs │ ├── ViewModels │ └── MainPageViewModel.cs │ ├── Views │ ├── MainPage.xaml │ └── MainPage.xaml.cs │ └── packages.config └── packages ├── CommonServiceLocator.1.3 ├── CommonServiceLocator.1.3.nupkg └── lib │ └── portable-net4+sl5+netcore45+wpa81+wp8 │ ├── Microsoft.Practices.ServiceLocation.XML │ ├── Microsoft.Practices.ServiceLocation.dll │ ├── Microsoft.Practices.ServiceLocation.dll.mdb │ └── Microsoft.Practices.ServiceLocation.pdb ├── Prism.Core.6.2.0 ├── Prism.Core.6.2.0.nupkg └── lib │ ├── MonoAndroid10 │ ├── Prism.dll │ └── Prism.xml │ ├── MonoTouch10 │ ├── Prism.dll │ └── Prism.xml │ ├── Xamarin.iOS10 │ ├── Prism.dll │ └── Prism.xml │ ├── net45 │ ├── Prism.dll │ └── Prism.xml │ ├── portable-win+net45+wp80+win81+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10 │ ├── Prism.dll │ └── Prism.xml │ ├── wp8 │ ├── Prism.dll │ └── Prism.xml │ ├── wp81 │ ├── Prism.dll │ └── Prism.xml │ └── wpa81 │ ├── Prism.dll │ └── Prism.xml ├── Prism.Forms.6.2.0 ├── Prism.Forms.6.2.0.nupkg └── lib │ ├── MonoAndroid1.0 │ ├── Prism.Forms.dll │ └── Prism.Forms.xml │ ├── MonoTouch1.0 │ ├── Prism.Forms.dll │ └── Prism.Forms.xml │ ├── UAP10.0 │ ├── Prism.Forms.dll │ └── Prism.Forms.xml │ ├── Windows8.1 │ ├── Prism.Forms.dll │ └── Prism.Forms.xml │ ├── WindowsPhone8.0 │ ├── Prism.Forms.dll │ └── Prism.Forms.xml │ ├── WindowsPhoneApp8.1 │ ├── Prism.Forms.dll │ └── Prism.Forms.xml │ ├── Xamarin.iOS1.0 │ ├── Prism.Forms.dll │ └── Prism.Forms.xml │ └── portable-win+net45+wp80+win81+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10 │ ├── Prism.Forms.dll │ └── Prism.Forms.xml ├── Prism.Unity.Forms.6.2.0 ├── Prism.Unity.Forms.6.2.0.nupkg └── lib │ ├── MonoAndroid1.0 │ ├── Prism.Unity.Forms.dll │ ├── Prism.Unity.Forms.dll.config │ └── Prism.Unity.Forms.xml │ ├── MonoTouch1.0 │ ├── Prism.Unity.Forms.dll │ ├── Prism.Unity.Forms.dll.config │ └── Prism.Unity.Forms.xml │ ├── UAP10.0 │ ├── Prism.Unity.Forms.dll │ ├── Prism.Unity.Forms.dll.config │ └── Prism.Unity.Forms.xml │ ├── Windows8.1 │ ├── Prism.Unity.Forms.dll │ ├── Prism.Unity.Forms.dll.config │ └── Prism.Unity.Forms.xml │ ├── WindowsPhone8.0 │ ├── Prism.Unity.Forms.dll │ ├── Prism.Unity.Forms.dll.config │ └── Prism.Unity.Forms.xml │ ├── WindowsPhoneApp8.1 │ ├── Prism.Unity.Forms.dll │ ├── Prism.Unity.Forms.dll.config │ └── Prism.Unity.Forms.xml │ ├── Xamarin.iOS1.0 │ ├── Prism.Unity.Forms.dll │ ├── Prism.Unity.Forms.dll.config │ └── Prism.Unity.Forms.xml │ └── portable-win+net45+wp80+win81+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10 │ ├── Prism.Unity.Forms.dll │ ├── Prism.Unity.Forms.dll.config │ └── Prism.Unity.Forms.xml ├── Unity.4.0.1 ├── Unity.4.0.1.nupkg ├── UnityConfiguration30.xsd └── lib │ ├── net45 │ ├── Microsoft.Practices.Unity.Configuration.XML │ ├── Microsoft.Practices.Unity.Configuration.dll │ ├── Microsoft.Practices.Unity.RegistrationByConvention.XML │ ├── Microsoft.Practices.Unity.RegistrationByConvention.dll │ ├── Microsoft.Practices.Unity.dll │ └── Microsoft.Practices.Unity.xml │ ├── portable-net45+wp80+win8+wpa81+MonoAndroid10+MonoTouch10 │ ├── Microsoft.Practices.Unity.dll │ └── Microsoft.Practices.Unity.xml │ ├── win8 │ ├── Microsoft.Practices.Unity.RegistrationByConvention.XML │ ├── Microsoft.Practices.Unity.RegistrationByConvention.dll │ ├── Microsoft.Practices.Unity.RegistrationByConvention.pri │ ├── Microsoft.Practices.Unity.dll │ └── Microsoft.Practices.Unity.xml │ └── wp80 │ ├── Microsoft.Practices.Unity.RegistrationByConvention.XML │ ├── Microsoft.Practices.Unity.RegistrationByConvention.dll │ ├── Microsoft.Practices.Unity.dll │ └── Microsoft.Practices.Unity.xml ├── Xamarin.Android.Support.Animated.Vector.Drawable.23.3.0 ├── Xamarin.Android.Support.Animated.Vector.Drawable.23.3.0.nupkg └── lib │ └── MonoAndroid403 │ └── Xamarin.Android.Support.Animated.Vector.Drawable.dll ├── Xamarin.Android.Support.Design.23.3.0 ├── Xamarin.Android.Support.Design.23.3.0.nupkg └── lib │ └── MonoAndroid43 │ └── Xamarin.Android.Support.Design.dll ├── Xamarin.Android.Support.Vector.Drawable.23.3.0 ├── Xamarin.Android.Support.Vector.Drawable.23.3.0.nupkg ├── build │ ├── Xamarin.Android.Support.Tasks.VectorDrawable.dll │ └── Xamarin.Android.Support.Vector.Drawable.targets └── lib │ └── MonoAndroid403 │ └── Xamarin.Android.Support.Vector.Drawable.dll ├── Xamarin.Android.Support.v4.23.3.0 ├── Xamarin.Android.Support.v4.23.3.0.nupkg └── lib │ └── MonoAndroid403 │ └── Xamarin.Android.Support.v4.dll ├── Xamarin.Android.Support.v7.AppCompat.23.3.0 ├── Xamarin.Android.Support.v7.AppCompat.23.3.0.nupkg └── lib │ └── MonoAndroid403 │ └── Xamarin.Android.Support.v7.AppCompat.dll ├── Xamarin.Android.Support.v7.CardView.23.3.0 ├── Xamarin.Android.Support.v7.CardView.23.3.0.nupkg └── lib │ └── MonoAndroid403 │ └── Xamarin.Android.Support.v7.CardView.dll ├── Xamarin.Android.Support.v7.MediaRouter.23.3.0 ├── Xamarin.Android.Support.v7.MediaRouter.23.3.0.nupkg └── lib │ └── MonoAndroid403 │ └── Xamarin.Android.Support.v7.MediaRouter.dll └── Xamarin.Android.Support.v7.RecyclerView.23.3.0 ├── Xamarin.Android.Support.v7.RecyclerView.23.3.0.nupkg └── lib └── MonoAndroid403 └── Xamarin.Android.Support.v7.RecyclerView.dll /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | 24 | # Visual Studio 2015 cache/options directory 25 | .vs/ 26 | # Uncomment if you have tasks that create the project's static files in wwwroot 27 | #wwwroot/ 28 | 29 | # MSTest test Results 30 | [Tt]est[Rr]esult*/ 31 | [Bb]uild[Ll]og.* 32 | 33 | # NUNIT 34 | *.VisualState.xml 35 | TestResult.xml 36 | 37 | # Build Results of an ATL Project 38 | [Dd]ebugPS/ 39 | [Rr]eleasePS/ 40 | dlldata.c 41 | 42 | # DNX 43 | project.lock.json 44 | artifacts/ 45 | 46 | *_i.c 47 | *_p.c 48 | *_i.h 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.tmp_proj 63 | *.log 64 | *.vspscc 65 | *.vssscc 66 | .builds 67 | *.pidb 68 | *.svclog 69 | *.scc 70 | 71 | # Chutzpah Test files 72 | _Chutzpah* 73 | 74 | # Visual C++ cache files 75 | ipch/ 76 | *.aps 77 | *.ncb 78 | *.opendb 79 | *.opensdf 80 | *.sdf 81 | *.cachefile 82 | 83 | # Visual Studio profiler 84 | *.psess 85 | *.vsp 86 | *.vspx 87 | *.sap 88 | 89 | # TFS 2012 Local Workspace 90 | $tf/ 91 | 92 | # Guidance Automation Toolkit 93 | *.gpState 94 | 95 | # ReSharper is a .NET coding add-in 96 | _ReSharper*/ 97 | *.[Rr]e[Ss]harper 98 | *.DotSettings.user 99 | 100 | # JustCode is a .NET coding add-in 101 | .JustCode 102 | 103 | # TeamCity is a build add-in 104 | _TeamCity* 105 | 106 | # DotCover is a Code Coverage Tool 107 | *.dotCover 108 | 109 | # NCrunch 110 | _NCrunch_* 111 | .*crunch*.local.xml 112 | nCrunchTemp_* 113 | 114 | # MightyMoose 115 | *.mm.* 116 | AutoTest.Net/ 117 | 118 | # Web workbench (sass) 119 | .sass-cache/ 120 | 121 | # Installshield output folder 122 | [Ee]xpress/ 123 | 124 | # DocProject is a documentation generator add-in 125 | DocProject/buildhelp/ 126 | DocProject/Help/*.HxT 127 | DocProject/Help/*.HxC 128 | DocProject/Help/*.hhc 129 | DocProject/Help/*.hhk 130 | DocProject/Help/*.hhp 131 | DocProject/Help/Html2 132 | DocProject/Help/html 133 | 134 | # Click-Once directory 135 | publish/ 136 | 137 | # Publish Web Output 138 | *.[Pp]ublish.xml 139 | *.azurePubxml 140 | # TODO: Comment the next line if you want to checkin your web deploy settings 141 | # but database connection strings (with potential passwords) will be unencrypted 142 | *.pubxml 143 | *.publishproj 144 | 145 | # NuGet Packages 146 | *.nupkg 147 | # The packages folder can be ignored because of Package Restore 148 | **/packages/* 149 | # except build/, which is used as an MSBuild target. 150 | !**/packages/build/ 151 | # Uncomment if necessary however generally it will be regenerated when needed 152 | #!**/packages/repositories.config 153 | # NuGet v3's project.json files produces more ignoreable files 154 | *.nuget.props 155 | *.nuget.targets 156 | 157 | # Microsoft Azure Build Output 158 | csx/ 159 | *.build.csdef 160 | 161 | # Microsoft Azure Emulator 162 | ecf/ 163 | rcf/ 164 | 165 | # Microsoft Azure ApplicationInsights config file 166 | ApplicationInsights.config 167 | 168 | # Windows Store app package directory 169 | AppPackages/ 170 | BundleArtifacts/ 171 | 172 | # Visual Studio cache files 173 | # files ending in .cache can be ignored 174 | *.[Cc]ache 175 | # but keep track of directories ending in .cache 176 | !*.[Cc]ache/ 177 | 178 | # Others 179 | ClientBin/ 180 | ~$* 181 | *~ 182 | *.dbmdl 183 | *.dbproj.schemaview 184 | *.publishsettings 185 | node_modules/ 186 | orleans.codegen.cs 187 | 188 | # RIA/Silverlight projects 189 | Generated_Code/ 190 | 191 | # Backup & report files from converting an old project file 192 | # to a newer Visual Studio version. Backup files are not needed, 193 | # because we have git ;-) 194 | _UpgradeReport_Files/ 195 | Backup*/ 196 | UpgradeLog*.XML 197 | UpgradeLog*.htm 198 | 199 | # SQL Server files 200 | *.mdf 201 | *.ldf 202 | 203 | # Business Intelligence projects 204 | *.rdl.data 205 | *.bim.layout 206 | *.bim_*.settings 207 | 208 | # Microsoft Fakes 209 | FakesAssemblies/ 210 | 211 | # GhostDoc plugin setting file 212 | *.GhostDoc.xml 213 | 214 | # Node.js Tools for Visual Studio 215 | .ntvs_analysis.dat 216 | 217 | # Visual Studio 6 build log 218 | *.plg 219 | 220 | # Visual Studio 6 workspace options file 221 | *.opt 222 | 223 | # Visual Studio LightSwitch build output 224 | **/*.HTMLClient/GeneratedArtifacts 225 | **/*.DesktopClient/GeneratedArtifacts 226 | **/*.DesktopClient/ModelManifest.xml 227 | **/*.Server/GeneratedArtifacts 228 | **/*.Server/ModelManifest.xml 229 | _Pvt_Extensions 230 | 231 | # Paket dependency manager 232 | .paket/paket.exe 233 | 234 | # FAKE - F# Make 235 | .fake/ 236 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. 2 | 3 | The MIT License (MIT) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UsingValidation 2 | Xamarin.Forms validation sample using INotifyDataErrorInfo 3 | 4 |

This post is also available in the Premier Developer blog.

5 |

I have recently been investigating the support available in Xamarin.Forms for validation and, in particular, researched the possibility of using INotifyDataErrorInfo to complement the traditional approach of using Behaviors.

6 |

In simple scenarios, it's possible to perform validation by simply attaching a Behavior to the required view, as shown by the following sample code:

7 | 8 |
public class SampleValidationBehavior:Behavior<Entry>
  9 | {
 10 |     protected override void OnAttachedTo(Entry entry)
 11 |     {
 12 |         entry.TextChanged += OnEntryTextChanged;
 13 |         base.OnAttachedTo(entry);
 14 |     }
 15 | 
 16 |     protected override void OnDetachingFrom(Entry entry)
 17 |     {
 18 |         entry.TextChanged -= OnEntryTextChanged;
 19 |         base.OnDetachingFrom(entry);
 20 |     }
 21 | 
 22 |     void OnEntryTextChanged(object sender, TextChangedEventArgs args)
 23 |     {
 24 | 	 var textValue = args.NewTextValue;
 25 |         bool isValid = !string.IsNullOrEmpty(textValue) && textValue.Length >= 5;
 26 |         ((Entry)sender).TextColor = isValid ? Color.Default : Color.Red;
 27 |     }
 28 | }
 29 | 
30 | In this case, we are validating the input and modifying the UI if the number of characters entered is less than 5. 31 | 32 | What about more articulated scenarios when multiple business rules are required to be checked for the input values? 33 | 34 | In these cases, we could take advantage of other types available to make our code more structured and extensible: 35 | 40 | To start exploring this approach, I created a new Xamarin.Forms Prism solution using the Prism Template Pack which generated the following project structure: 41 | 42 | 43 | 44 | Then, I added the following new model to be validated using DataAnnotations and INotifyDataErrorInfo: 45 |
using System.ComponentModel.DataAnnotations;
 46 | using System.Runtime.CompilerServices;
 47 | using UsingValidation.Validation;
 48 | 
 49 | namespace UsingValidation.Models
 50 | {
 51 |     public class Item : ValidationBase
 52 |     {
 53 |         public Item()
 54 |         {
 55 |             Name = string.Empty;
 56 |             Description = string.Empty;
 57 |         }
 58 | 
 59 |         private string _name;
 60 |         private string _description;
 61 | 
 62 |         [Required(ErrorMessage = "Name cannot be empty!")]
 63 |         public string Name
 64 |         {
 65 |             get { return _name; }
 66 |             set
 67 |             {
 68 |                 ValidateProperty(value);
 69 |                 SetProperty(ref _name, value);
 70 |             }
 71 |         }
 72 | 
 73 |         [Required(ErrorMessage = "Description cannot be empty!")]
 74 |         [RegularExpression(@"\w{5,}", ErrorMessage = "Description: more than 4 letters/numbers required")]
 75 |         public string Description
 76 |         {
 77 |             get { return _description; }
 78 |             set
 79 |             {
 80 |                 ValidateProperty(value);
 81 |                 SetProperty(ref _description, value);
 82 |             }
 83 |         }
 84 | 
 85 |         protected override void ValidateProperty(object value, [CallerMemberName] string propertyName = null)
 86 |         {
 87 |             base.ValidateProperty(value, propertyName);
 88 | 
 89 |             OnPropertyChanged("IsSubmitEnabled");
 90 |         }
 91 | 
 92 |         public bool IsSubmitEnabled
 93 |         {
 94 |             get
 95 |             {
 96 |                 return !HasErrors;
 97 |             }
 98 |         }
 99 |     }
100 | }
101 | The model uses attributes declared in the SystemComponentModel.DataAnnotations namespace which can be referenced in the solution modifying the Portable Class Library profile of the UsingValidation common project: 102 | 103 | 104 | 105 | Quick tip: to be able to change the PCL profile I had to remove all the NuGet packages used by the common project, remove the Windows Phone 8 profile and then add back all the removed NuGet packages to the UsingValidation PCL. 106 | 107 | To use the capability offered by INotifyDataErrorInfo, the model needs to implements 3 members defined in the interface: 108 | 113 | This interface is quite flexible and is designed to be customised depending on the different scenarios needed: I took as a starting point this implementation available on GitHub and modified it accordingly: I decided to separate the implementation of INotifyDataErrorInfo in a different base class called ValidationBase which contains the following code using a Dictionary<string, List<string>> needed for storing the generated validation errors: 114 |
public class ValidationBase : BindableBase, INotifyDataErrorInfo
115 | {
116 |     private Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();
117 | 
118 |     public ValidationBase()
119 |     {
120 |         ErrorsChanged += ValidationBase_ErrorsChanged;
121 |     }
122 | 
123 |     private void ValidationBase_ErrorsChanged(object sender, DataErrorsChangedEventArgs e)
124 |     {
125 |         OnPropertyChanged("HasErrors");
126 |         OnPropertyChanged("ErrorsList");
127 |     }
128 | 
129 |     #region INotifyDataErrorInfo Members
130 | 
131 |     public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
132 | 
133 |     public IEnumerable GetErrors(string propertyName)
134 |     {
135 | 		if (!string.IsNullOrEmpty(propertyName))
136 | 		{
137 | 			if (_errors.ContainsKey(propertyName) && (_errors[propertyName].Any()))
138 | 			{
139 | 			   return _errors[propertyName].ToList();
140 | 			}
141 | 			else
142 | 			{
143 | 			   return new List<string>();
144 | 			}
145 | 		}
146 | 		else
147 | 		{
148 | 		   return _errors.SelectMany(err => err.Value.ToList()).ToList();
149 | 		}
150 |     }
151 | 
152 |     public bool HasErrors
153 |     {
154 |         get
155 |         {
156 | 	    return _errors.Any(propErrors => propErrors.Value.Any());
157 |         }
158 |     }
159 | 
160 |     #endregion
161 | 
162 | The validation is performed by this method which evaluates the DataAnnotations decorating the model using the Validator available in the System.ComponentModel.DataAnnotations namespace and then stores the error messages in the dictionary: 163 |
protected virtual void ValidateProperty(object value, [CallerMemberName] string propertyName = null)
164 | {
165 |     var validationContext = new ValidationContext(this, null)
166 |     {
167 |         MemberName = propertyName
168 |     };
169 | 
170 |     var validationResults = new List<ValidationResult>();
171 |     Validator.TryValidateProperty(value, validationContext, validationResults);
172 |     RemoveErrorsByPropertyName(propertyName);
173 | 
174 |     HandleValidationResults(validationResults);
175 | }
176 | At this stage, I needed a solution for linking the UI to the model, and modifying the visuals depending on the presence or not of validation errors. 177 | 178 | The ViewModel for the sample page, contains only a property storing an instance of the item defined in the model: 179 |
public class MainPageViewModel : BindableBase, INavigationAware
180 | {
181 |     public MainPageViewModel()
182 |     {
183 |         DemoItem = new Item();
184 |     }
185 | 
186 |     private Item _item;
187 |     public Item DemoItem
188 |     {
189 |         get { return _item; }
190 |         set { SetProperty(ref _item, value); }
191 |     }
192 | 
193 |     public void OnNavigatedFrom(NavigationParameters parameters)
194 |     {
195 |     }
196 | 
197 |     public void OnNavigatedTo(NavigationParameters parameters)
198 |     {
199 |         DemoItem.Name = string.Empty;
200 |         DemoItem.Description = string.Empty;
201 |     }
202 | }
203 | Then, the corresponding XAML contains two Entry views used for input and a ListView used for showing the validation errors: 204 |
<?xml version="1.0" encoding="utf-8" ?>
205 | <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
206 |              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
207 |              xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
208 |              xmlns:validation="clr-namespace:UsingValidation.Validation"
209 |              xmlns:effects="clr-namespace:UsingValidation.Effects"             
210 |              prism:ViewModelLocator.AutowireViewModel="True"
211 |              x:Class="UsingValidation.Views.MainPage"
212 |              Title="MainPage">  
213 |   <Grid VerticalOptions="Center">
214 | ……   
215 |     <Label Text ="Name: " VerticalTextAlignment="Center" Grid.Column="1" />   
216 |     <Entry Text="{Binding Name, Mode=TwoWay}" BindingContext="{Binding DemoItem}"
217 | 	 	   Grid.Column="2" HorizontalOptions="FillAndExpand">
218 | 		<Entry.Behaviors>
219 | 			<validation:EntryValidationBehavior PropertyName="Name" />	
220 | 		</Entry.Behaviors>
221 |     </Entry>
222 | 
223 |     <Label Text ="Description: " VerticalTextAlignment="Center" Grid.Column="1" Grid.Row="2" />
224 |     <Entry Text="{Binding Description, Mode=TwoWay}" BindingContext="{Binding DemoItem}"
225 | 	 	   Grid.Column="2" HorizontalOptions="FillAndExpand" Grid.Row="2">
226 | 	<Entry.Behaviors>
227 | 		<validation:EntryValidationBehavior PropertyName="Description" />
228 | 	</Entry.Behaviors>
229 |     </Entry>
230 | 
231 |     <ListView ItemsSource="{Binding DemoItem.ErrorsList}" 
232 |               Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="2" />
233 | 
234 |     <Button Text="Submit" IsEnabled="{Binding DemoItem.IsSubmitEnabled}" 
235 |             Grid.Row="6" Grid.Column="1" Grid.ColumnSpan="2" />
236 |   </Grid>
237 | </ContentPage>
238 | The sample page uses a Behavior called EntryValidationBehavior which take care of changing the colour of the Entry background views in the case validation errors are present: 239 |
using System.Linq;
240 | using UsingValidation.Effects;
241 | using Xamarin.Forms;
242 | 
243 | namespace UsingValidation.Validation
244 | {
245 |     public class EntryValidationBehavior : Behavior<Entry>
246 |     {
247 | 	private Entry _associatedObject;
248 | 
249 |         protected override void OnAttachedTo(Entry bindable)
250 |         {
251 |             base.OnAttachedTo(bindable);
252 | 
253 |             _associatedObject = bindable;
254 | 
255 | 	    _associatedObject.TextChanged += _associatedObject_TextChanged;
256 |         }
257 | 
258 | 	void _associatedObject_TextChanged(object sender, TextChangedEventArgs e)
259 | 	{
260 | 	     var source = _associatedObject.BindingContext as ValidationBase;
261 | 	     if (source != null && !string.IsNullOrEmpty(PropertyName))
262 | 	     {
263 | 		var errors = source.GetErrors(PropertyName).Cast<string>();
264 | 		if (errors != null && errors.Any())
265 | 		{
266 |                    var borderEffect = _associatedObject.Effects.FirstOrDefault(eff => eff is BorderEffect);
267 |                     if (borderEffect == null)
268 |                     {
269 |                         _associatedObject.Effects.Add(new BorderEffect());
270 |                     }
271 | 
272 |                     if (Device.OS != TargetPlatform.Windows)
273 |                     {
274 |                         _associatedObject.BackgroundColor = Color.Red;
275 |                     }
276 |                 }
277 |                 else
278 | 		{
279 |                     var borderEffect = _associatedObject.Effects.FirstOrDefault(eff => eff is BorderEffect);
280 |                     if (borderEffect != null)
281 |                     {
282 |                         _associatedObject.Effects.Remove(borderEffect);
283 |                     }
284 | 
285 |                     if (Device.OS != TargetPlatform.Windows)
286 |                     {
287 |                         _associatedObject.BackgroundColor = Color.Default;
288 |                     }
289 |                 }
290 | 	   }
291 | 	}
292 | 
293 | 	protected override void OnDetachingFrom(Entry bindable)
294 |         {
295 |             base.OnDetachingFrom(bindable);
296 |             // Perform clean up
297 | 
298 | 	    _associatedObject.TextChanged += _associatedObject_TextChanged;
299 | 
300 |             _associatedObject = null;
301 |         }
302 | 
303 | 	public string PropertyName { get; set; }
304 |     }
305 | }
306 | The UI is also fine-tuned using a Xamarin.Forms effect applied only to the UWP platform, in order to change the colour of the Entry border when validation errors occur: 307 |
using UsingValidation.UWP.Effects;
308 | using Windows.UI;
309 | using Windows.UI.Xaml.Media;
310 | using Xamarin.Forms;
311 | using Xamarin.Forms.Platform.UWP;
312 | 
313 | [assembly: ResolutionGroupName("UsingValidationSample")]
314 | [assembly: ExportEffect(typeof(BorderEffect), "BorderEffect")]
315 | namespace UsingValidation.UWP.Effects
316 | {
317 |     public class BorderEffect : PlatformEffect
318 |     {
319 |         Brush _previousBrush;
320 |         Brush _previousBorderBrush;
321 |         Brush _previousFocusBrush;
322 |         FormsTextBox _control;
323 | 
324 |         protected override void OnAttached()
325 |         {
326 |             _control = Control as FormsTextBox;           
327 |             if (_control != null)
328 |             {
329 |                 _previousBrush = _control.Background;
330 |                 _previousFocusBrush = _control.BackgroundFocusBrush;
331 |                 _previousBorderBrush = _control.BorderBrush;
332 |                 _control.Background = new SolidColorBrush(Colors.Red);
333 |                 _control.BackgroundFocusBrush = new SolidColorBrush(Colors.Red);
334 |                 _control.BorderBrush = new SolidColorBrush(Colors.Red);                 
335 |             }
336 |         }
337 | 
338 |         protected override void OnDetached()
339 |         {
340 |             if (_control != null)
341 |             {
342 |                 _control.Background = _previousBrush;
343 |                 _control.BackgroundFocusBrush = _previousFocusBrush;
344 |                 _control.BorderBrush = _previousBorderBrush;
345 |             }
346 |         }
347 |     }
348 | }
349 | And this is the result when the application is executed on Android and UWP: 350 | 351 | 352 | 353 | 354 | 355 | Conclusions 356 | 357 | Xamarin.Forms provides a rich set of features for implementing validation: the usage of INotifyDataErrorInfo, Data Annotations, Behaviors and Effects permit the handling of complex scenarios including multiple conditions per field, cross-property validation, entity-level validation and custom UI depending on the platform. 358 | 359 | Happy coding! 360 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/Assets/AboutAssets.txt: -------------------------------------------------------------------------------- 1 | Any raw assets you want to be deployed with your application can be placed in 2 | this directory (and child directories) and given a Build Action of "AndroidAsset". 3 | 4 | These files will be deployed with you package and will be accessible using Android's 5 | AssetManager, like this: 6 | 7 | public class ReadAsset : Activity 8 | { 9 | protected override void OnCreate (Bundle bundle) 10 | { 11 | base.OnCreate (bundle); 12 | 13 | InputStream input = Assets.Open ("my_asset.txt"); 14 | } 15 | } 16 | 17 | Additionally, some Android functions will automatically load asset files: 18 | 19 | Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); 20 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/Effects/BorderEffect.cs: -------------------------------------------------------------------------------- 1 | using Android.Support.V4.Content; 2 | using System; 3 | using UsingValidation.Droid.Effects; 4 | using Xamarin.Forms; 5 | using Xamarin.Forms.Platform.Android; 6 | 7 | [assembly: ResolutionGroupName("UsingValidationSample")] 8 | [assembly: ExportEffect(typeof(BorderEffect), "BorderEffect")] 9 | namespace UsingValidation.Droid.Effects 10 | { 11 | public class BorderEffect : PlatformEffect 12 | { 13 | protected override void OnAttached() 14 | { 15 | try 16 | { 17 | var drawable = ContextCompat.GetDrawable(Control.Context, Resource.Drawable.border); 18 | Control.SetBackground(drawable); 19 | } 20 | catch (Exception) 21 | { 22 | } 23 | } 24 | 25 | protected override void OnDetached() 26 | { 27 | try 28 | { 29 | Control.SetBackground(null); 30 | } 31 | catch (Exception) 32 | { 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/MainActivity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Android.App; 4 | using Android.Content.PM; 5 | using Android.Runtime; 6 | using Android.Views; 7 | using Android.Widget; 8 | using Android.OS; 9 | using Prism.Unity; 10 | using Microsoft.Practices.Unity; 11 | 12 | namespace UsingValidation.Droid 13 | { 14 | [Activity(Label = "UsingValidation", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] 15 | public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity 16 | { 17 | protected override void OnCreate(Bundle bundle) 18 | { 19 | TabLayoutResource = Resource.Layout.tabs; 20 | ToolbarResource = Resource.Layout.toolbar; 21 | 22 | base.OnCreate(bundle); 23 | 24 | global::Xamarin.Forms.Forms.Init(this, bundle); 25 | LoadApplication(new App(new AndroidInitializer())); 26 | } 27 | } 28 | 29 | public class AndroidInitializer : IPlatformInitializer 30 | { 31 | public void RegisterTypes(IUnityContainer container) 32 | { 33 | 34 | } 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/Properties/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | using Android.App; 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyTitle("UsingValidation.Droid")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("")] 13 | [assembly: AssemblyProduct("UsingValidation.Droid")] 14 | [assembly: AssemblyCopyright("Copyright © 2017")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | [assembly: ComVisible(false)] 18 | 19 | // Version information for an assembly consists of the following four values: 20 | // 21 | // Major Version 22 | // Minor Version 23 | // Build Number 24 | // Revision 25 | // 26 | // You can specify all the values or you can default the Build and Revision Numbers 27 | // by using the '*' as shown below: 28 | // [assembly: AssemblyVersion("1.0.*")] 29 | [assembly: AssemblyVersion("1.0.0.0")] 30 | [assembly: AssemblyFileVersion("1.0.0.0")] 31 | 32 | // Add some common permissions, these can be removed if not needed 33 | [assembly: UsesPermission(Android.Manifest.Permission.Internet)] 34 | [assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)] 35 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/Resources/AboutResources.txt: -------------------------------------------------------------------------------- 1 | Images, layout descriptions, binary blobs and string dictionaries can be included 2 | in your application as resource files. Various Android APIs are designed to 3 | operate on the resource IDs instead of dealing with images, strings or binary blobs 4 | directly. 5 | 6 | For example, a sample Android app that contains a user interface layout (main.xml), 7 | an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) 8 | would keep its resources in the "Resources" directory of the application: 9 | 10 | Resources/ 11 | drawable-hdpi/ 12 | icon.png 13 | 14 | drawable-ldpi/ 15 | icon.png 16 | 17 | drawable-mdpi/ 18 | icon.png 19 | 20 | layout/ 21 | main.xml 22 | 23 | values/ 24 | strings.xml 25 | 26 | In order to get the build system to recognize Android resources, set the build action to 27 | "AndroidResource". The native Android APIs do not operate directly with filenames, but 28 | instead operate on resource IDs. When you compile an Android application that uses resources, 29 | the build system will package the resources for distribution and generate a class called 30 | "Resource" that contains the tokens for each one of the resources included. For example, 31 | for the above Resources layout, this is what the Resource class would expose: 32 | 33 | public class Resource { 34 | public class drawable { 35 | public const int icon = 0x123; 36 | } 37 | 38 | public class layout { 39 | public const int main = 0x456; 40 | } 41 | 42 | public class strings { 43 | public const int first_string = 0xabc; 44 | public const int second_string = 0xbcd; 45 | } 46 | } 47 | 48 | You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main 49 | to reference the layout/main.xml file, or Resource.strings.first_string to reference the first 50 | string in the dictionary file values/strings.xml. 51 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/Resources/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.Droid/Resources/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/Resources/drawable-xhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.Droid/Resources/drawable-xhdpi/icon.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/Resources/drawable-xxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.Droid/Resources/drawable-xxhdpi/icon.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/Resources/drawable/border.xml: -------------------------------------------------------------------------------- 1 |  2 | 4 | 5 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/Resources/drawable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.Droid/Resources/drawable/icon.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/Resources/layout/tabs.xml: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/Resources/layout/toolbar.xml: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/Resources/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 |  2 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/Resources/values/colors.xml: -------------------------------------------------------------------------------- 1 |  2 | #2196F3 3 | #1976D2 4 | #FFC107 5 | #F5F5F5 6 | #FF0000 7 | 8 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/Resources/values/styles.xml: -------------------------------------------------------------------------------- 1 |  2 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/UsingValidation.Droid.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {FAA6365F-E99F-482D-BA4C-1CBDC4B8442C} 9 | {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10 | Library 11 | Properties 12 | UsingValidation.Droid 13 | UsingValidation.Droid 14 | 512 15 | true 16 | Resources\Resource.Designer.cs 17 | Off 18 | Properties\AndroidManifest.xml 19 | true 20 | v6.0 21 | armeabi,armeabi-v7a,x86 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | true 31 | full 32 | false 33 | bin\Debug\ 34 | DEBUG;TRACE 35 | prompt 36 | 4 37 | True 38 | None 39 | 40 | 41 | pdbonly 42 | true 43 | bin\Release\ 44 | TRACE 45 | prompt 46 | 4 47 | False 48 | SdkOnly 49 | 50 | 51 | 52 | ..\..\packages\Xamarin.Forms.2.3.4.192-pre2\lib\MonoAndroid10\FormsViewGroup.dll 53 | 54 | 55 | ..\..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll 56 | True 57 | 58 | 59 | ..\..\packages\Unity.4.0.1\lib\portable-net45+wp80+win8+wpa81+MonoAndroid10+MonoTouch10\Microsoft.Practices.Unity.dll 60 | True 61 | 62 | 63 | 64 | 65 | ..\..\packages\Prism.Core.6.2.0\lib\MonoAndroid10\Prism.dll 66 | True 67 | 68 | 69 | ..\..\packages\Prism.Forms.6.2.0\lib\MonoAndroid1.0\Prism.Forms.dll 70 | True 71 | 72 | 73 | ..\..\packages\Prism.Unity.Forms.6.2.0\lib\MonoAndroid1.0\Prism.Unity.Forms.dll 74 | True 75 | 76 | 77 | 78 | 79 | 80 | 81 | ..\..\packages\Xamarin.Android.Support.Animated.Vector.Drawable.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.Animated.Vector.Drawable.dll 82 | True 83 | 84 | 85 | ..\..\packages\Xamarin.Android.Support.Design.23.3.0\lib\MonoAndroid43\Xamarin.Android.Support.Design.dll 86 | True 87 | 88 | 89 | ..\..\packages\Xamarin.Android.Support.v4.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.v4.dll 90 | True 91 | 92 | 93 | ..\..\packages\Xamarin.Android.Support.v7.AppCompat.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.v7.AppCompat.dll 94 | True 95 | 96 | 97 | ..\..\packages\Xamarin.Android.Support.v7.CardView.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.v7.CardView.dll 98 | True 99 | 100 | 101 | ..\..\packages\Xamarin.Android.Support.v7.MediaRouter.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.v7.MediaRouter.dll 102 | True 103 | 104 | 105 | ..\..\packages\Xamarin.Android.Support.v7.RecyclerView.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.v7.RecyclerView.dll 106 | True 107 | 108 | 109 | ..\..\packages\Xamarin.Android.Support.Vector.Drawable.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.Vector.Drawable.dll 110 | True 111 | 112 | 113 | ..\..\packages\Xamarin.Forms.2.3.4.192-pre2\lib\MonoAndroid10\Xamarin.Forms.Core.dll 114 | 115 | 116 | ..\..\packages\Xamarin.Forms.2.3.4.192-pre2\lib\MonoAndroid10\Xamarin.Forms.Platform.dll 117 | 118 | 119 | ..\..\packages\Xamarin.Forms.2.3.4.192-pre2\lib\MonoAndroid10\Xamarin.Forms.Platform.Android.dll 120 | 121 | 122 | ..\..\packages\Xamarin.Forms.2.3.4.192-pre2\lib\MonoAndroid10\Xamarin.Forms.Xaml.dll 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | Designer 153 | 154 | 155 | 156 | 157 | UsingValidation 158 | 159 | 160 | 161 | 162 | 163 | 164 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 165 | 166 | 167 | 168 | 169 | 170 | 171 | 178 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.Droid/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/App.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Runtime.InteropServices.WindowsRuntime; 6 | using Windows.ApplicationModel; 7 | using Windows.ApplicationModel.Activation; 8 | using Windows.Foundation; 9 | using Windows.Foundation.Collections; 10 | using Windows.UI.Xaml; 11 | using Windows.UI.Xaml.Controls; 12 | using Windows.UI.Xaml.Controls.Primitives; 13 | using Windows.UI.Xaml.Data; 14 | using Windows.UI.Xaml.Input; 15 | using Windows.UI.Xaml.Media; 16 | using Windows.UI.Xaml.Navigation; 17 | 18 | namespace UsingValidation.UWP 19 | { 20 | /// 21 | /// Provides application-specific behavior to supplement the default Application class. 22 | /// 23 | sealed partial class App : Application 24 | { 25 | /// 26 | /// Initializes the singleton application object. This is the first line of authored code 27 | /// executed, and as such is the logical equivalent of main() or WinMain(). 28 | /// 29 | public App() 30 | { 31 | this.InitializeComponent(); 32 | this.Suspending += OnSuspending; 33 | } 34 | 35 | /// 36 | /// Invoked when the application is launched normally by the end user. Other entry points 37 | /// will be used such as when the application is launched to open a specific file. 38 | /// 39 | /// Details about the launch request and process. 40 | protected override void OnLaunched(LaunchActivatedEventArgs e) 41 | { 42 | 43 | #if DEBUG 44 | if (System.Diagnostics.Debugger.IsAttached) 45 | { 46 | this.DebugSettings.EnableFrameRateCounter = true; 47 | } 48 | #endif 49 | 50 | Frame rootFrame = Window.Current.Content as Frame; 51 | 52 | // Do not repeat app initialization when the Window already has content, 53 | // just ensure that the window is active 54 | if (rootFrame == null) 55 | { 56 | // Create a Frame to act as the navigation context and navigate to the first page 57 | rootFrame = new Frame(); 58 | 59 | rootFrame.NavigationFailed += OnNavigationFailed; 60 | 61 | Xamarin.Forms.Forms.Init(e); 62 | 63 | if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) 64 | { 65 | //TODO: Load state from previously suspended application 66 | } 67 | 68 | // Place the frame in the current Window 69 | Window.Current.Content = rootFrame; 70 | } 71 | 72 | if (rootFrame.Content == null) 73 | { 74 | // When the navigation stack isn't restored navigate to the first page, 75 | // configuring the new page by passing required information as a navigation 76 | // parameter 77 | rootFrame.Navigate(typeof(MainPage), e.Arguments); 78 | } 79 | // Ensure the current window is active 80 | Window.Current.Activate(); 81 | } 82 | 83 | /// 84 | /// Invoked when Navigation to a certain page fails 85 | /// 86 | /// The Frame which failed navigation 87 | /// Details about the navigation failure 88 | void OnNavigationFailed(object sender, NavigationFailedEventArgs e) 89 | { 90 | throw new Exception("Failed to load Page " + e.SourcePageType.FullName); 91 | } 92 | 93 | /// 94 | /// Invoked when application execution is being suspended. Application state is saved 95 | /// without knowing whether the application will be terminated or resumed with the contents 96 | /// of memory still intact. 97 | /// 98 | /// The source of the suspend request. 99 | /// Details about the suspend request. 100 | private void OnSuspending(object sender, SuspendingEventArgs e) 101 | { 102 | var deferral = e.SuspendingOperation.GetDeferral(); 103 | //TODO: Save application state and stop any background activity 104 | deferral.Complete(); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/Assets/LockScreenLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.UWP/Assets/LockScreenLogo.scale-200.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/Assets/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.UWP/Assets/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/Assets/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.UWP/Assets/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/Assets/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.UWP/Assets/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/Assets/Square44x44Logo.targetsize-24_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.UWP/Assets/Square44x44Logo.targetsize-24_altform-unplated.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/Assets/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.UWP/Assets/StoreLogo.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/Assets/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.UWP/Assets/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/Effects/BorderEffect.cs: -------------------------------------------------------------------------------- 1 | using UsingValidation.UWP.Effects; 2 | using Windows.UI; 3 | using Windows.UI.Xaml.Media; 4 | using Xamarin.Forms; 5 | using Xamarin.Forms.Platform.UWP; 6 | 7 | [assembly: ResolutionGroupName("UsingValidationSample")] 8 | [assembly: ExportEffect(typeof(BorderEffect), "BorderEffect")] 9 | namespace UsingValidation.UWP.Effects 10 | { 11 | public class BorderEffect : PlatformEffect 12 | { 13 | Brush _previousBrush; 14 | Brush _previousBorderBrush; 15 | Brush _previousFocusBrush; 16 | FormsTextBox _control; 17 | 18 | protected override void OnAttached() 19 | { 20 | _control = Control as FormsTextBox; 21 | if (_control != null) 22 | { 23 | _previousBrush = _control.Background; 24 | _previousFocusBrush = _control.BackgroundFocusBrush; 25 | _previousBorderBrush = _control.BorderBrush; 26 | _control.Background = new SolidColorBrush(Colors.Red); 27 | _control.BackgroundFocusBrush = new SolidColorBrush(Colors.Red); 28 | _control.BorderBrush = new SolidColorBrush(Colors.Red); 29 | } 30 | } 31 | 32 | protected override void OnDetached() 33 | { 34 | if (_control != null) 35 | { 36 | _control.Background = _previousBrush; 37 | _control.BackgroundFocusBrush = _previousFocusBrush; 38 | _control.BorderBrush = _previousBorderBrush; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/MainPage.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/MainPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using Prism.Unity; 2 | using Microsoft.Practices.Unity; 3 | 4 | namespace UsingValidation.UWP 5 | { 6 | public sealed partial class MainPage 7 | { 8 | public MainPage() 9 | { 10 | this.InitializeComponent(); 11 | 12 | LoadApplication(new UsingValidation.App(new UwpInitializer())); 13 | } 14 | } 15 | 16 | public class UwpInitializer : IPlatformInitializer 17 | { 18 | public void RegisterTypes(IUnityContainer container) 19 | { 20 | 21 | } 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/Package.appxmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | UsingValidation.UWP 18 | dazo 19 | Assets\StoreLogo.png 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("UsingValidation.UWP")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("UsingValidation.UWP")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Build and Revision Numbers 25 | // by using the '*' as shown below: 26 | // [assembly: AssemblyVersion("1.0.*")] 27 | [assembly: AssemblyVersion("1.0.0.0")] 28 | [assembly: AssemblyFileVersion("1.0.0.0")] 29 | [assembly: ComVisible(false)] -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/Properties/Default.rd.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/UsingValidation.UWP.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x86 7 | {5F0A88CA-CD17-413E-88C9-07741C36C671} 8 | AppContainerExe 9 | Properties 10 | UsingValidation.UWP 11 | UsingValidation.UWP 12 | en-US 13 | UAP 14 | 10.0.14393.0 15 | 10.0.10586.0 16 | 14 17 | true 18 | 512 19 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 20 | UsingValidation.UWP_TemporaryKey.pfx 21 | 22 | 23 | true 24 | bin\ARM\Debug\ 25 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 26 | ;2008 27 | full 28 | ARM 29 | false 30 | prompt 31 | true 32 | 33 | 34 | bin\ARM\Release\ 35 | TRACE;NETFX_CORE;WINDOWS_UWP 36 | true 37 | ;2008 38 | pdbonly 39 | ARM 40 | false 41 | prompt 42 | true 43 | true 44 | 45 | 46 | true 47 | bin\x64\Debug\ 48 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 49 | ;2008 50 | full 51 | x64 52 | false 53 | prompt 54 | true 55 | 56 | 57 | bin\x64\Release\ 58 | TRACE;NETFX_CORE;WINDOWS_UWP 59 | true 60 | ;2008 61 | pdbonly 62 | x64 63 | false 64 | prompt 65 | true 66 | true 67 | 68 | 69 | true 70 | bin\x86\Debug\ 71 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 72 | ;2008 73 | full 74 | x86 75 | false 76 | prompt 77 | true 78 | 79 | 80 | bin\x86\Release\ 81 | TRACE;NETFX_CORE;WINDOWS_UWP 82 | true 83 | ;2008 84 | pdbonly 85 | x86 86 | false 87 | prompt 88 | true 89 | true 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | App.xaml 98 | 99 | 100 | 101 | MainPage.xaml 102 | 103 | 104 | 105 | 106 | 107 | Designer 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | MSBuild:Compile 124 | Designer 125 | 126 | 127 | MSBuild:Compile 128 | Designer 129 | 130 | 131 | 132 | 133 | UsingValidation 134 | 135 | 136 | 137 | 14.0 138 | 139 | 140 | 147 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/UsingValidation.UWP.nuget.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | True 5 | NuGet 6 | C:\Source\UsingValidation\UsingValidation\UsingValidation.UWP\project.lock.json 7 | $(UserProfile)\.nuget\packages\ 8 | C:\Users\dazo\.nuget\packages\ 9 | ProjectJson 10 | 4.0.0 11 | 12 | 13 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/UsingValidation.UWP_TemporaryKey.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.UWP/UsingValidation.UWP_TemporaryKey.pfx -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.UWP/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "CommonServiceLocator": "1.3.0", 4 | "Microsoft.NETCore.UniversalWindowsPlatform": "5.1.0", 5 | "Prism.Core": "6.2.0", 6 | "Prism.Forms": "6.2.0", 7 | "Prism.Unity.Forms": "6.2.0", 8 | "Unity": "4.0.1", 9 | "Xamarin.Forms": "2.3.4.192-pre2" 10 | }, 11 | "frameworks": { 12 | "uap10.0": {} 13 | }, 14 | "runtimes": { 15 | "win10-arm": {}, 16 | "win10-arm-aot": {}, 17 | "win10-x86": {}, 18 | "win10-x86-aot": {}, 19 | "win10-x64": {}, 20 | "win10-x64-aot": {} 21 | } 22 | } -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/AppDelegate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Foundation; 6 | using UIKit; 7 | using Prism.Unity; 8 | using Microsoft.Practices.Unity; 9 | 10 | namespace UsingValidation.iOS 11 | { 12 | // The UIApplicationDelegate for the application. This class is responsible for launching the 13 | // User Interface of the application, as well as listening (and optionally responding) to 14 | // application events from iOS. 15 | [Register("AppDelegate")] 16 | public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate 17 | { 18 | // 19 | // This method is invoked when the application has loaded and is ready to run. In this 20 | // method you should instantiate the window, load the UI into it and then make the window 21 | // visible. 22 | // 23 | // You have 17 seconds to return from this method, or iOS will terminate your application. 24 | // 25 | public override bool FinishedLaunching(UIApplication app, NSDictionary options) 26 | { 27 | global::Xamarin.Forms.Forms.Init(); 28 | LoadApplication(new App(new iOSInitializer())); 29 | 30 | return base.FinishedLaunching(app, options); 31 | } 32 | } 33 | 34 | public class iOSInitializer : IPlatformInitializer 35 | { 36 | public void RegisterTypes(IUnityContainer container) 37 | { 38 | 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Effects/BorderEffect.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UIKit; 3 | using UsingValidation.iOS.Effects; 4 | using Xamarin.Forms; 5 | using Xamarin.Forms.Platform.iOS; 6 | 7 | [assembly: ResolutionGroupName("UsingValidationSample")] 8 | [assembly: ExportEffect(typeof(BorderEffect), "BorderEffect")] 9 | namespace UsingValidation.iOS.Effects 10 | { 11 | public class BorderEffect : PlatformEffect 12 | { 13 | protected override void OnAttached() 14 | { 15 | try 16 | { 17 | Control.Layer.BorderColor = UIColor.Red.CGColor; 18 | Control.Layer.BorderWidth = 1; 19 | } 20 | catch (Exception) 21 | { 22 | } 23 | } 24 | 25 | protected override void OnDetached() 26 | { 27 | try 28 | { 29 | Control.Layer.BorderWidth = 0; 30 | } 31 | catch (Exception) 32 | { 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Entitlements.plist: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Info.plist: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | UIDeviceFamily 6 | 7 | 1 8 | 2 9 | 10 | UISupportedInterfaceOrientations 11 | 12 | UIInterfaceOrientationPortrait 13 | UIInterfaceOrientationLandscapeLeft 14 | UIInterfaceOrientationLandscapeRight 15 | 16 | UISupportedInterfaceOrientations~ipad 17 | 18 | UIInterfaceOrientationPortrait 19 | UIInterfaceOrientationPortraitUpsideDown 20 | UIInterfaceOrientationLandscapeLeft 21 | UIInterfaceOrientationLandscapeRight 22 | 23 | MinimumOSVersion 24 | 6.0 25 | CFBundleDisplayName 26 | UsingValidation 27 | CFBundleIdentifier 28 | com.yourcompany.UsingValidation 29 | CFBundleVersion 30 | 1.0 31 | CFBundleIconFiles 32 | 33 | Icon-60@2x 34 | Icon-60@3x 35 | Icon-76 36 | Icon-76@2x 37 | Default 38 | Default@2x 39 | Default-568h@2x 40 | Default-Portrait 41 | Default-Portrait@2x 42 | Icon-Small-40 43 | Icon-Small-40@2x 44 | Icon-Small-40@3x 45 | Icon-Small 46 | Icon-Small@2x 47 | Icon-Small@3x 48 | 49 | UILaunchStoryboardName 50 | LaunchScreen 51 | 52 | 53 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Main.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Foundation; 6 | using UIKit; 7 | 8 | namespace UsingValidation.iOS 9 | { 10 | public class Application 11 | { 12 | // This is the main entry point of the application. 13 | static void Main(string[] args) 14 | { 15 | // if you want to use a different Application Delegate class from "AppDelegate" 16 | // you can specify it here. 17 | UIApplication.Main(args, null, "AppDelegate"); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("UsingValidation.iOS")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("UsingValidation.iOS")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("72bdc44f-c588-44f3-b6df-9aace7daafdd")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Resources/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.iOS/Resources/Default-568h@2x.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Resources/Default-Portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.iOS/Resources/Default-Portrait.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Resources/Default-Portrait@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.iOS/Resources/Default-Portrait@2x.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Resources/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.iOS/Resources/Default.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Resources/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.iOS/Resources/Default@2x.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Resources/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.iOS/Resources/Icon-60@2x.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Resources/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.iOS/Resources/Icon-60@3x.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Resources/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.iOS/Resources/Icon-76.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Resources/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.iOS/Resources/Icon-76@2x.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Resources/Icon-Small-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.iOS/Resources/Icon-Small-40.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Resources/Icon-Small-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.iOS/Resources/Icon-Small-40@2x.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Resources/Icon-Small-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.iOS/Resources/Icon-Small-40@3x.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Resources/Icon-Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.iOS/Resources/Icon-Small.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Resources/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.iOS/Resources/Icon-Small@2x.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Resources/Icon-Small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.iOS/Resources/Icon-Small@3x.png -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/Resources/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/UsingValidation.iOS.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | iPhoneSimulator 6 | 8.0.30703 7 | 2.0 8 | {5B636BFD-BFB6-43FC-8F60-6AAB03973898} 9 | {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10 | Exe 11 | UsingValidation.iOS 12 | Resources 13 | UsingValidation.iOS 14 | 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\iPhoneSimulator\Debug 22 | DEBUG 23 | prompt 24 | 4 25 | false 26 | i386, x86_64 27 | None 28 | true 29 | 30 | 31 | none 32 | true 33 | bin\iPhoneSimulator\Release 34 | prompt 35 | 4 36 | None 37 | i386, x86_64 38 | false 39 | 40 | 41 | true 42 | full 43 | false 44 | bin\iPhone\Debug 45 | DEBUG 46 | prompt 47 | 4 48 | false 49 | ARMv7, ARM64 50 | iPhone Developer 51 | true 52 | Entitlements.plist 53 | 54 | 55 | none 56 | true 57 | bin\iPhone\Release 58 | prompt 59 | 4 60 | ARMv7, ARM64 61 | false 62 | iPhone Developer 63 | Entitlements.plist 64 | 65 | 66 | none 67 | True 68 | bin\iPhone\Ad-Hoc 69 | prompt 70 | 4 71 | False 72 | ARMv7, ARM64 73 | True 74 | Automatic:AdHoc 75 | iPhone Distribution 76 | Entitlements.plist 77 | 78 | 79 | none 80 | True 81 | bin\iPhone\AppStore 82 | prompt 83 | 4 84 | False 85 | ARMv7, ARM64 86 | Automatic:AppStore 87 | iPhone Distribution 88 | Entitlements.plist 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | UsingValidation 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | ..\..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll 127 | True 128 | 129 | 130 | ..\..\packages\Unity.4.0.1\lib\portable-net45+wp80+win8+wpa81+MonoAndroid10+MonoTouch10\Microsoft.Practices.Unity.dll 131 | True 132 | 133 | 134 | ..\..\packages\Prism.Core.6.2.0\lib\Xamarin.iOS10\Prism.dll 135 | True 136 | 137 | 138 | ..\..\packages\Prism.Forms.6.2.0\lib\Xamarin.iOS1.0\Prism.Forms.dll 139 | True 140 | 141 | 142 | ..\..\packages\Prism.Unity.Forms.6.2.0\lib\Xamarin.iOS1.0\Prism.Unity.Forms.dll 143 | True 144 | 145 | 146 | 147 | 148 | 149 | ..\..\packages\Xamarin.Forms.2.3.4.192-pre2\lib\Xamarin.iOS10\Xamarin.Forms.Core.dll 150 | 151 | 152 | ..\..\packages\Xamarin.Forms.2.3.4.192-pre2\lib\Xamarin.iOS10\Xamarin.Forms.Platform.dll 153 | 154 | 155 | ..\..\packages\Xamarin.Forms.2.3.4.192-pre2\lib\Xamarin.iOS10\Xamarin.Forms.Platform.iOS.dll 156 | 157 | 158 | ..\..\packages\Xamarin.Forms.2.3.4.192-pre2\lib\Xamarin.iOS10\Xamarin.Forms.Xaml.dll 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/iTunesArtwork: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.iOS/iTunesArtwork -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/iTunesArtwork@2x: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidezordan/UsingValidation/23f19708eba368c34dcb0b075d11a973a0b273f6/UsingValidation/UsingValidation.iOS/iTunesArtwork@2x -------------------------------------------------------------------------------- /UsingValidation/UsingValidation.iOS/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation/App.xaml: -------------------------------------------------------------------------------- 1 |  2 | 6 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using Prism.Unity; 2 | using UsingValidation.Views; 3 | 4 | namespace UsingValidation 5 | { 6 | public partial class App : PrismApplication 7 | { 8 | public App(IPlatformInitializer initializer = null) : base(initializer) { } 9 | 10 | protected override void OnInitialized() 11 | { 12 | InitializeComponent(); 13 | 14 | NavigationService.NavigateAsync("MainPage?title=Hello%20from%20Xamarin.Forms"); 15 | } 16 | 17 | protected override void RegisterTypes() 18 | { 19 | Container.RegisterTypeForNavigation(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation/Effects/BorderEffect.cs: -------------------------------------------------------------------------------- 1 | using Xamarin.Forms; 2 | 3 | namespace UsingValidation.Effects 4 | { 5 | public class BorderEffect : RoutingEffect 6 | { 7 | public BorderEffect() : base("UsingValidationSample.BorderEffect") 8 | { 9 | 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation/Models/Item.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.Runtime.CompilerServices; 3 | using UsingValidation.Validation; 4 | 5 | namespace UsingValidation.Models 6 | { 7 | public class Item : ValidationBase 8 | { 9 | public Item() 10 | { 11 | Name = string.Empty; 12 | Description = string.Empty; 13 | } 14 | 15 | private string _name; 16 | private string _description; 17 | 18 | [Required(ErrorMessage = "Name cannot be empty!")] 19 | public string Name 20 | { 21 | get { return _name; } 22 | set 23 | { 24 | ValidateProperty(value); 25 | SetProperty(ref _name, value); 26 | } 27 | } 28 | 29 | [Required(ErrorMessage = "Description cannot be empty!")] 30 | [RegularExpression(@"\w{5,}", ErrorMessage = "Description: more than 4 letters/numbers required")] 31 | public string Description 32 | { 33 | get { return _description; } 34 | set 35 | { 36 | ValidateProperty(value); 37 | SetProperty(ref _description, value); 38 | } 39 | } 40 | 41 | protected override void ValidateProperty(object value, [CallerMemberName] string propertyName = null) 42 | { 43 | base.ValidateProperty(value, propertyName); 44 | 45 | OnPropertyChanged("IsSubmitEnabled"); 46 | } 47 | 48 | public bool IsSubmitEnabled 49 | { 50 | get 51 | { 52 | return !HasErrors; 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Xamarin.Forms.Xaml; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("UsingValidation")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("UsingValidation")] 15 | [assembly: AssemblyCopyright("Copyright © 2017")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | [assembly: NeutralResourcesLanguage("en")] 19 | [assembly: XamlCompilation(XamlCompilationOptions.Compile)] 20 | 21 | // Version information for an assembly consists of the following four values: 22 | // 23 | // Major Version 24 | // Minor Version 25 | // Build Number 26 | // Revision 27 | // 28 | // You can specify all the values or you can default the Build and Revision Numbers 29 | // by using the '*' as shown below: 30 | // [assembly: AssemblyVersion("1.0.*")] 31 | [assembly: AssemblyVersion("1.0.0.0")] 32 | [assembly: AssemblyFileVersion("1.0.0.0")] 33 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation/UsingValidation.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 10.0 6 | Debug 7 | AnyCPU 8 | {E05D5699-9971-4C37-8898-A60D9C902AB4} 9 | Library 10 | Properties 11 | UsingValidation 12 | UsingValidation 13 | v4.5 14 | Profile7 15 | 512 16 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 17 | 18 | 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | App.xaml 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | MainPage.xaml 49 | 50 | 51 | 52 | 53 | ..\..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll 54 | True 55 | 56 | 57 | ..\..\packages\Unity.4.0.1\lib\portable-net45+wp80+win8+wpa81+MonoAndroid10+MonoTouch10\Microsoft.Practices.Unity.dll 58 | True 59 | 60 | 61 | ..\..\packages\Prism.Core.6.2.0\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\Prism.dll 62 | True 63 | 64 | 65 | ..\..\packages\Prism.Forms.6.2.0\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\Prism.Forms.dll 66 | True 67 | 68 | 69 | ..\..\packages\Prism.Unity.Forms.6.2.0\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\Prism.Unity.Forms.dll 70 | True 71 | 72 | 73 | ..\..\packages\Xamarin.Forms.2.3.4.192-pre2\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.Core.dll 74 | 75 | 76 | ..\..\packages\Xamarin.Forms.2.3.4.192-pre2\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.Platform.dll 77 | 78 | 79 | ..\..\packages\Xamarin.Forms.2.3.4.192-pre2\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.Xaml.dll 80 | 81 | 82 | 83 | 84 | MSBuild:UpdateDesignTimeXaml 85 | Designer 86 | 87 | 88 | MSBuild:UpdateDesignTimeXaml 89 | Designer 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 101 | 102 | 103 | 104 | 105 | 112 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation/UsingValidation.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | ShowAllFiles 5 | 6 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation/Validation/EntryValidationBehavior.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using UsingValidation.Effects; 3 | using Xamarin.Forms; 4 | 5 | namespace UsingValidation.Validation 6 | { 7 | public class EntryValidationBehavior : Behavior 8 | { 9 | private Entry _associatedObject; 10 | 11 | protected override void OnAttachedTo(Entry bindable) 12 | { 13 | base.OnAttachedTo(bindable); 14 | // Perform setup 15 | 16 | _associatedObject = bindable; 17 | 18 | _associatedObject.TextChanged += _associatedObject_TextChanged; 19 | } 20 | 21 | void _associatedObject_TextChanged(object sender, TextChangedEventArgs e) 22 | { 23 | var source = _associatedObject.BindingContext as ValidationBase; 24 | if (source != null && !string.IsNullOrEmpty(PropertyName)) 25 | { 26 | var errors = source.GetErrors(PropertyName).Cast(); 27 | if (errors != null && errors.Any()) 28 | { 29 | var borderEffect = _associatedObject.Effects.FirstOrDefault(eff => eff is BorderEffect); 30 | if (borderEffect == null) 31 | { 32 | _associatedObject.Effects.Add(new BorderEffect()); 33 | } 34 | } 35 | else 36 | { 37 | var borderEffect = _associatedObject.Effects.FirstOrDefault(eff => eff is BorderEffect); 38 | if (borderEffect != null) 39 | { 40 | _associatedObject.Effects.Remove(borderEffect); 41 | } 42 | } 43 | } 44 | } 45 | 46 | protected override void OnDetachingFrom(Entry bindable) 47 | { 48 | base.OnDetachingFrom(bindable); 49 | // Perform clean up 50 | 51 | _associatedObject.TextChanged -= _associatedObject_TextChanged; 52 | 53 | _associatedObject = null; 54 | } 55 | 56 | public string PropertyName { get; set; } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation/Validation/ValidationBase.cs: -------------------------------------------------------------------------------- 1 | using Prism.Mvvm; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.ComponentModel.DataAnnotations; 7 | using System.Linq; 8 | using System.Runtime.CompilerServices; 9 | 10 | namespace UsingValidation.Validation 11 | { 12 | public class ValidationBase : BindableBase, INotifyDataErrorInfo 13 | { 14 | private Dictionary> _errors = new Dictionary>(); 15 | 16 | public ValidationBase() 17 | { 18 | ErrorsChanged += ValidationBase_ErrorsChanged; 19 | } 20 | 21 | private void ValidationBase_ErrorsChanged(object sender, DataErrorsChangedEventArgs e) 22 | { 23 | OnPropertyChanged("HasErrors"); 24 | OnPropertyChanged("ErrorsList"); 25 | } 26 | 27 | #region INotifyDataErrorInfo Members 28 | 29 | public event EventHandler ErrorsChanged; 30 | 31 | public IEnumerable GetErrors(string propertyName) 32 | { 33 | if (!string.IsNullOrEmpty(propertyName)) 34 | { 35 | if (_errors.ContainsKey(propertyName) && (_errors[propertyName].Any())) 36 | { 37 | return _errors[propertyName].ToList(); 38 | } 39 | else 40 | { 41 | return new List(); 42 | } 43 | } 44 | else 45 | { 46 | return _errors.SelectMany(err => err.Value.ToList()).ToList(); 47 | } 48 | } 49 | 50 | public bool HasErrors 51 | { 52 | get 53 | { 54 | return _errors.Any(propErrors => propErrors.Value.Any()); 55 | } 56 | } 57 | 58 | #endregion 59 | 60 | protected virtual void ValidateProperty(object value, [CallerMemberName] string propertyName = null) 61 | { 62 | var validationContext = new ValidationContext(this, null) 63 | { 64 | MemberName = propertyName 65 | }; 66 | 67 | var validationResults = new List(); 68 | Validator.TryValidateProperty(value, validationContext, validationResults); 69 | 70 | RemoveErrorsByPropertyName(propertyName); 71 | 72 | HandleValidationResults(validationResults); 73 | } 74 | 75 | private void RemoveErrorsByPropertyName(string propertyName) 76 | { 77 | if (_errors.ContainsKey(propertyName)) 78 | { 79 | _errors.Remove(propertyName); 80 | } 81 | 82 | RaiseErrorsChanged(propertyName); 83 | } 84 | 85 | private void HandleValidationResults(List validationResults) 86 | { 87 | var resultsByPropertyName = from results in validationResults 88 | from memberNames in results.MemberNames 89 | group results by memberNames into groups 90 | select groups; 91 | 92 | foreach (var property in resultsByPropertyName) 93 | { 94 | _errors.Add(property.Key, property.Select(r => r.ErrorMessage).ToList()); 95 | RaiseErrorsChanged(property.Key); 96 | } 97 | } 98 | 99 | private void RaiseErrorsChanged(string propertyName) 100 | { 101 | ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); 102 | } 103 | 104 | public IList ErrorsList 105 | { 106 | get 107 | { 108 | return GetErrors(string.Empty).Cast().ToList(); 109 | } 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation/ViewModels/MainPageViewModel.cs: -------------------------------------------------------------------------------- 1 | using Prism.Mvvm; 2 | using Prism.Navigation; 3 | using UsingValidation.Models; 4 | 5 | namespace UsingValidation.ViewModels 6 | { 7 | public class MainPageViewModel : BindableBase, INavigationAware 8 | { 9 | public MainPageViewModel() 10 | { 11 | DemoItem = new Item(); 12 | } 13 | 14 | private Item _item; 15 | public Item DemoItem 16 | { 17 | get { return _item; } 18 | set { SetProperty(ref _item, value); } 19 | } 20 | 21 | public void OnNavigatedFrom(NavigationParameters parameters) 22 | { 23 | } 24 | 25 | public void OnNavigatedTo(NavigationParameters parameters) 26 | { 27 | DemoItem.Name = string.Empty; 28 | DemoItem.Description = string.Empty; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /UsingValidation/UsingValidation/Views/MainPage.xaml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |