Added basic validation project
This commit is contained in:
parent
6053dedaf8
commit
6f429cf5e9
|
@ -17,6 +17,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BasicNavigation", "BasicNav
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StyletBasicNavigation", "..\StyletBasicNavigation\StyletBasicNavigation.csproj", "{F3F7DC59-238B-42D0-BC91-BDACECB4C2C0}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StyletBasicValidation", "StyletBasicValidation", "{D09B63C8-7726-4864-99BC-9B4EC78574E0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StyletFirstValidation", "..\StyletFirstValidation\StyletFirstValidation.csproj", "{489F6CD9-E492-4AC8-ACE7-B9B9513C1ED1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -43,6 +47,10 @@ Global
|
|||
{F3F7DC59-238B-42D0-BC91-BDACECB4C2C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F3F7DC59-238B-42D0-BC91-BDACECB4C2C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F3F7DC59-238B-42D0-BC91-BDACECB4C2C0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{489F6CD9-E492-4AC8-ACE7-B9B9513C1ED1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{489F6CD9-E492-4AC8-ACE7-B9B9513C1ED1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{489F6CD9-E492-4AC8-ACE7-B9B9513C1ED1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{489F6CD9-E492-4AC8-ACE7-B9B9513C1ED1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -53,6 +61,7 @@ Global
|
|||
{D778EC71-2747-4605-9DEE-FD2E399CB772} = {B0182D30-4833-48A2-9236-7390C9DE4323}
|
||||
{F33DF940-E1FF-477B-81EC-28719EEF6FAE} = {B0182D30-4833-48A2-9236-7390C9DE4323}
|
||||
{F3F7DC59-238B-42D0-BC91-BDACECB4C2C0} = {093E93E7-1E20-48DD-90EC-186C134ED377}
|
||||
{489F6CD9-E492-4AC8-ACE7-B9B9513C1ED1} = {D09B63C8-7726-4864-99BC-9B4EC78574E0}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {81976B23-619E-40C9-AD7F-F6C825119E38}
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
# StyletWpfExamples
|
||||
- MyFirstStyletProject
|
||||
Basic MVVM Navigation example heavily inspired by by https://github.com/MeshackMusundi/StaffStuff-Stylet.
|
||||
<br>
|
||||
- StyletBasicNavigation
|
||||
Basic MVVM Navigation including Modal.
|
||||
![basic_navigation](./Showcase/basic_navigation.png)
|
||||
<br>
|
||||
- StyletBasicNavigation
|
||||
Basic MVVM Validation.
|
||||
![basic_validation](./Showcase/basic_validation.png)
|
||||
<br>
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
|
@ -37,7 +37,7 @@
|
|||
<ListBox ItemsSource="{Binding ViewsCollection}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBox Text="{Binding Path=.}" Background="{Binding Converter={StaticResource StringToBrushConverter}}" Foreground="White" />
|
||||
<TextBlock Text="{Binding Path=.}" Background="{Binding Converter={StaticResource StringToBrushConverter}}" Foreground="White" />
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<Application x:Class="StyletFirstValidation.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:local="clr-namespace:StyletFirstValidation">
|
||||
<Application.Resources>
|
||||
<s:ApplicationLoader>
|
||||
<s:ApplicationLoader.Bootstrapper>
|
||||
<local:Bootstrapper/>
|
||||
</s:ApplicationLoader.Bootstrapper>
|
||||
</s:ApplicationLoader>
|
||||
</Application.Resources>
|
||||
</Application>
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace StyletFirstValidation
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using FluentValidation;
|
||||
using Stylet;
|
||||
using StyletFirstValidation.ViewModels;
|
||||
using StyletIoC;
|
||||
|
||||
namespace StyletFirstValidation
|
||||
{
|
||||
public class Bootstrapper : Bootstrapper<ShellViewModel>
|
||||
{
|
||||
protected override void ConfigureIoC(IStyletIoCBuilder builder)
|
||||
{
|
||||
builder.Bind(typeof(IModelValidator<>)).To(typeof(FluentModelValidator<>));
|
||||
//builder.Bind<IValidator<UserViewModel>>().To<UserViewModelValidator>();
|
||||
builder.Bind(typeof(IValidator<>)).ToAllImplementations();
|
||||
}
|
||||
|
||||
protected override void Configure()
|
||||
{
|
||||
// Perform any other configuration before the application starts
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StyletFirstValidation.Converters
|
||||
{
|
||||
[TypeConverter(typeof(StringableConverter))]
|
||||
public struct Stringable<T> : IEquatable<Stringable<T>>
|
||||
{
|
||||
private readonly string _stringValue;
|
||||
|
||||
/// <summary>
|
||||
/// String representation of the value
|
||||
/// </summary>
|
||||
public string StringValue
|
||||
{
|
||||
get { return this._stringValue; }
|
||||
}
|
||||
|
||||
private readonly T _value;
|
||||
/// <summary>
|
||||
/// Actual value, or default(T) if IsValid is false
|
||||
/// </summary>
|
||||
public T Value
|
||||
{
|
||||
get { return this._value; }
|
||||
}
|
||||
|
||||
private readonly bool _isValid;
|
||||
/// <summary>
|
||||
/// True if Value ias a proper value (i.e. we were constructed from a T, or we were constructed from a string which could be converted to a T)
|
||||
/// </summary>
|
||||
public bool IsValid
|
||||
{
|
||||
get { return this._isValid; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance, representing the given value
|
||||
/// </summary>
|
||||
/// <param name="value">Value to represent</param>
|
||||
public Stringable(T value) : this(value, value.ToString(), true) { }
|
||||
|
||||
private Stringable(T value, string stringValue, bool isValid)
|
||||
{
|
||||
this._value = value;
|
||||
this._stringValue = stringValue;
|
||||
this._isValid = isValid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance from the given string. If the string can be converted to a T, then IsValue is true and Value contains the converted value.
|
||||
/// If not, IsValid is false and Value is default(T)
|
||||
/// </summary>
|
||||
/// <param name="stringValue"></param>
|
||||
/// <returns></returns>
|
||||
public static Stringable<T> FromString(string stringValue)
|
||||
{
|
||||
T dest = default(T);
|
||||
bool isValid = false;
|
||||
|
||||
// The TypeConverter for String can't convert it into anything else, so don't bother getting that
|
||||
var fromConverter = TypeDescriptor.GetConverter(typeof(T));
|
||||
if (fromConverter.CanConvertFrom(typeof(string)) && fromConverter.IsValid(stringValue))
|
||||
{
|
||||
dest = (T)fromConverter.ConvertFrom(stringValue);
|
||||
isValid = true;
|
||||
}
|
||||
|
||||
return new Stringable<T>(dest, stringValue, isValid);
|
||||
}
|
||||
|
||||
public static implicit operator T(Stringable<T> stringable)
|
||||
{
|
||||
return stringable.Value;
|
||||
}
|
||||
|
||||
public static implicit operator Stringable<T>(T value)
|
||||
{
|
||||
return new Stringable<T>(value);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.StringValue;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is Stringable<T>))
|
||||
return false;
|
||||
return base.Equals((Stringable<T>)obj);
|
||||
}
|
||||
|
||||
public bool Equals(Stringable<T> other)
|
||||
{
|
||||
return EqualityComparer<T>.Default.Equals(this.Value, other.Value) && this.StringValue == other.StringValue;
|
||||
}
|
||||
|
||||
public static bool operator ==(Stringable<T> o1, Stringable<T> o2)
|
||||
{
|
||||
return o1.Equals(o2);
|
||||
}
|
||||
|
||||
public static bool operator !=(Stringable<T> o1, Stringable<T> o2)
|
||||
{
|
||||
return !(o1 == o2);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 27 + this.Value.GetHashCode();
|
||||
hash = hash * 27 + this.StringValue.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TypeConverter for Stringable{T}
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is used by WPF. This means that if a Stringable{T} property is bound to a string control (e.g. TextBox), then this TypeConverter
|
||||
/// is used to convert from that string back to a Stringable{T}
|
||||
/// </remarks>
|
||||
public class StringableConverter : TypeConverter
|
||||
{
|
||||
private readonly Type valueType;
|
||||
private readonly Func<string, object> generator;
|
||||
|
||||
public StringableConverter(Type type)
|
||||
{
|
||||
if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(Stringable<>) || type.GetGenericArguments().Length != 1)
|
||||
throw new ArgumentException("Incompatible type", "type");
|
||||
|
||||
this.valueType = type;
|
||||
|
||||
// Generate a Func<string, object> which gives us a Stringable<T>, given a string
|
||||
// WPF instantiates us once, then uses us lots, so the overhead of doing this here is worth it
|
||||
var fromMethod = type.GetMethod("FromString", BindingFlags.Static | BindingFlags.Public);
|
||||
var param = Expression.Parameter(typeof(string));
|
||||
this.generator = Expression.Lambda<Func<string, object>>(Expression.TypeAs(Expression.Call(fromMethod, param), typeof(object)), param).Compile();
|
||||
}
|
||||
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
|
||||
{
|
||||
return this.generator(value as string);
|
||||
}
|
||||
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
return destinationType == typeof(string) || destinationType == this.valueType || base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (value == null)
|
||||
return String.Empty;
|
||||
// Common case = just call the overloaded ToString - no need for reflection
|
||||
if (destinationType == typeof(string))
|
||||
return value.ToString();
|
||||
var valueType = value.GetType();
|
||||
if (destinationType.IsAssignableFrom(this.valueType) && typeof(Stringable<>).IsAssignableFrom(valueType))
|
||||
{
|
||||
var valueProperty = valueType.GetProperty("Value", BindingFlags.Public | BindingFlags.Instance);
|
||||
return valueProperty.GetValue(value);
|
||||
}
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
using FluentValidation;
|
||||
using Stylet;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StyletFirstValidation
|
||||
{
|
||||
public class FluentModelValidator<T> : IModelValidator<T>
|
||||
{
|
||||
private readonly IValidator<T> validator;
|
||||
private T subject;
|
||||
|
||||
public FluentModelValidator(IValidator<T> validator)
|
||||
{
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
public void Initialize(object subject)
|
||||
{
|
||||
this.subject = (T)subject;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<string>> ValidatePropertyAsync(string propertyName)
|
||||
{
|
||||
// If someone's calling us synchronously, and ValidationAsync does not complete synchronously,
|
||||
// we'll deadlock unless we continue on another thread.
|
||||
return (await this.validator.ValidateAsync(this.subject, o=>o.IncludeProperties(propertyName), CancellationToken.None).ConfigureAwait(false))
|
||||
.Errors.Select(x => x.ErrorMessage);
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, IEnumerable<string>>> ValidateAllPropertiesAsync()
|
||||
{
|
||||
// If someone's calling us synchronously, and ValidationAsync does not complete synchronously,
|
||||
// we'll deadlock unless we continue on another thread.
|
||||
return (await this.validator.ValidateAsync(this.subject).ConfigureAwait(false))
|
||||
.Errors.GroupBy(x => x.PropertyName)
|
||||
.ToDictionary(x => x.Key, x => x.Select(failure => failure.ErrorMessage));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
|
||||
<RootNamespace>StyletFirstValidation</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentValidation" Version="9.3.0" />
|
||||
<PackageReference Include="Stylet" Version="1.3.5.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,27 @@
|
|||
using FluentValidation;
|
||||
using StyletFirstValidation.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StyletFirstValidation.Validators
|
||||
{
|
||||
public class UserViewModelValidator : AbstractValidator<UserViewModel>
|
||||
{
|
||||
public UserViewModelValidator()
|
||||
{
|
||||
ValidatorOptions.Global.LanguageManager.Enabled = false;
|
||||
RuleFor(x => x.UserName).NotEmpty().Length(1, 20);
|
||||
RuleFor(x => x.Email).NotEmpty().EmailAddress();
|
||||
RuleFor(x => x.Password).NotEmpty().Matches("[0-9]").WithMessage("{PropertyName} must contain a number");
|
||||
RuleFor(x => x.PasswordConfirmation).Equal(s => s.Password).WithMessage("{PropertyName} should match Password");
|
||||
RuleFor(x => x.Age).Must(x => x.IsValid).WithMessage("{PropertyName} must be a valid number");
|
||||
When(x => x.Age.IsValid, () =>
|
||||
{
|
||||
RuleFor(x => x.Age.Value).GreaterThan(0).WithName("Age");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using Stylet;
|
||||
|
||||
namespace StyletFirstValidation.ViewModels
|
||||
{
|
||||
public class ShellViewModel : Conductor<IScreen>
|
||||
{
|
||||
public ShellViewModel(UserViewModel userViewModel)
|
||||
{
|
||||
this.DisplayName = "Stylet.Samples.ModelValidation";
|
||||
|
||||
this.ActiveItem = userViewModel;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
using Stylet;
|
||||
using StyletFirstValidation.Converters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StyletFirstValidation.ViewModels
|
||||
{
|
||||
public class UserViewModel : Screen
|
||||
{
|
||||
private IWindowManager windowManager;
|
||||
public ObservableCollection<string> ErrorsCollection { get; set; } = new ObservableCollection<string>();
|
||||
#region Properties
|
||||
private string _userName;
|
||||
public string UserName
|
||||
{
|
||||
get => _userName;
|
||||
set
|
||||
{
|
||||
SetAndNotify(ref _userName, value);
|
||||
ValidateProperty();
|
||||
this.NotifyOfPropertyChange(() => this.CanSubmit);
|
||||
}
|
||||
}
|
||||
|
||||
private string _email;
|
||||
public string Email
|
||||
{
|
||||
get => _email;
|
||||
set
|
||||
{
|
||||
SetAndNotify(ref _email, value);
|
||||
ValidateProperty();
|
||||
this.NotifyOfPropertyChange(() => this.CanSubmit);
|
||||
}
|
||||
}
|
||||
|
||||
private string _password;
|
||||
public string Password
|
||||
{
|
||||
get => _password;
|
||||
set
|
||||
{
|
||||
SetAndNotify(ref _password, value);
|
||||
ValidateProperty();
|
||||
this.NotifyOfPropertyChange(() => this.CanSubmit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private string _passwordConfirmation;
|
||||
public string PasswordConfirmation
|
||||
{
|
||||
get => _passwordConfirmation;
|
||||
set
|
||||
{
|
||||
SetAndNotify(ref _passwordConfirmation, value);
|
||||
ValidateProperty();
|
||||
this.NotifyOfPropertyChange(() => this.CanSubmit);
|
||||
}
|
||||
}
|
||||
|
||||
private Stringable<int> _age;
|
||||
public Stringable<int> Age
|
||||
{
|
||||
get => _age;
|
||||
set
|
||||
{
|
||||
SetAndNotify(ref _age, value);
|
||||
ValidateProperty();
|
||||
this.NotifyOfPropertyChange(() => this.CanSubmit);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
public UserViewModel(IWindowManager windowManager, IModelValidator<UserViewModel> validator) : base(validator)
|
||||
{
|
||||
this.windowManager = windowManager;
|
||||
// Force initial validation
|
||||
this.Validate();
|
||||
// Whenever password changes, we need to re-validate PasswordConfirmation
|
||||
this.Bind(x => x.Password, (o, e) => this.ValidateProperty(() => this.PasswordConfirmation));
|
||||
}
|
||||
|
||||
public void Submit()
|
||||
{
|
||||
if (this.Validate())
|
||||
this.windowManager.ShowMessageBox("Successfully submitted", "success");
|
||||
}
|
||||
|
||||
public void SubmitAlways()
|
||||
{
|
||||
ErrorsCollection.Clear();
|
||||
if (this.Validate())
|
||||
{
|
||||
this.windowManager.ShowMessageBox("Successfully submitted", "success");
|
||||
}
|
||||
else
|
||||
{
|
||||
var errors = this.Validator.ValidateAllPropertiesAsync().Result;
|
||||
foreach (var error in errors.Values.ToList().SelectMany(d=>d))
|
||||
{
|
||||
ErrorsCollection.Add(error.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public bool CanSubmit
|
||||
{
|
||||
get { return !this.HasErrors; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<Window x:Class="StyletFirstValidation.Views.ShellView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:local="clr-namespace:StyletFirstValidation.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance local:ShellViewModel}"
|
||||
Title="Stylet Project" Height="450" Width="800">
|
||||
<Grid>
|
||||
<ContentControl s:View.Model="{Binding ActiveItem}"/>
|
||||
</Grid>
|
||||
</Window>
|
|
@ -0,0 +1,15 @@
|
|||
using System.Windows;
|
||||
|
||||
namespace StyletFirstValidation.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ShellView.xaml
|
||||
/// </summary>
|
||||
public partial class ShellView : Window
|
||||
{
|
||||
public ShellView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
<UserControl x:Class="StyletFirstValidation.Views.UserView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:local="clr-namespace:StyletFirstValidation.Views"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<Style x:Key="CommonStyle" TargetType="{x:Type Control}">
|
||||
<Setter Property="Margin" Value="10,5,0,5"/>
|
||||
<Setter Property="FontSize" Value="12" />
|
||||
</Style>
|
||||
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource CommonStyle}"/>
|
||||
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource CommonStyle}"/>
|
||||
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource CommonStyle}"/>
|
||||
</UserControl.Resources>
|
||||
<Grid Margin="20">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" Grid.Row="0" Grid.Column="2" Grid.RowSpan="5">
|
||||
<ListBox ItemsSource="{Binding ErrorsCollection}" Background="Red">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Path=.}" Foreground="White"/>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</ScrollViewer>
|
||||
|
||||
<Label Grid.Row="0" Grid.Column="0" Target="{Binding ElementName=txtUserName}">UserName:</Label>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" x:Name="txtUserName" Text="{Binding UserName, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
|
||||
<Label Grid.Row="1" Grid.Column="0" Target="{Binding ElementName=txtEmail}">Age:</Label>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" x:Name="txtAge" Text="{Binding Age, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" Target="{Binding ElementName=txtEmail}">Email:</Label>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" x:Name="txtEmail" Text="{Binding Email, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" Target="{Binding ElementName=txtPassword}">Password:</Label>
|
||||
<TextBox Grid.Row="3" Grid.Column="1" x:Name="txtPassword" Text="{Binding Password, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
|
||||
<Label Grid.Row="4" Grid.Column="0" Target="{Binding ElementName=txtPasswordConfirmation}">Confirm Password:</Label>
|
||||
<TextBox Grid.Row="4" Grid.Column="1" x:Name="txtPasswordConfirmation" Text="{Binding PasswordConfirmation, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
|
||||
<Button Grid.Row="5" Grid.Column="0" Command="{s:Action SubmitAlways}" HorizontalAlignment="Left">Submit Always</Button>
|
||||
<Button Grid.Row="5" Grid.Column="1" Command="{s:Action Submit}" HorizontalAlignment="Left">Submit</Button>
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace StyletFirstValidation.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for UserView.xaml
|
||||
/// </summary>
|
||||
public partial class UserView : UserControl
|
||||
{
|
||||
public UserView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue