So I have as simple Custom Control that validates input from the User against a Regular Expression. The control has a HasError Property that is default to true.
Custom Control....
public class InputValidation : Control
{
public string RegEX
{
get { return (string)GetValue(RegEXProperty); }
set { SetValue(RegEXProperty, value); }
}
public static readonly DependencyProperty RegEXProperty =
DependencyProperty.Register("RegEX", typeof(string), typeof(InputValidation), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnRegEXBindingChanged));
private static void OnRegEXBindingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ValidateInput(d);
}
public bool HasError
{
get { return (bool)GetValue(HasErrorProperty); }
set { SetValue(HasErrorProperty, value); }
}
public static readonly DependencyProperty HasErrorProperty =
DependencyProperty.Register("HasError", typeof(bool), typeof(InputValidation), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnHasErrorBindingChanged));
private static void OnHasErrorBindingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public string ErrorMessage
{
get { return (string)GetValue(ErrorMessageProperty); }
set { SetValue(ErrorMessageProperty, value); }
}
public static readonly DependencyProperty ErrorMessageProperty =
DependencyProperty.Register("ErrorMessage", typeof(string), typeof(InputValidation), new FrameworkPropertyMetadata("Incorrect", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnErrorMessageBindingChanged));
private static void OnErrorMessageBindingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(InputValidation), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnTextBindingChanged));
private static void OnTextBindingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ValidateInput(d);
}
private static void ValidateInput(DependencyObject d)
{
var uc = d as InputValidation;
if (IsInputValidValid(uc.Text, uc.RegEX))
{
uc.HasError = false;
}
else
{
uc.HasError = true;
}
}
public static bool IsInputValidValid(string text, string regEX)
{
if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(regEX))
return false;
var regex = new Regex(regEX, RegexOptions.IgnoreCase);
return regex.IsMatch(text);
}
static InputValidation()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(InputValidation), new FrameworkPropertyMetadata(typeof(InputValidation)));
}
}
Control Style
<Window.Resources>
<ControlTemplate x:Key="ValidationTemplate" TargetType="{x:Type Controls:InputValidation}">
<Border x:Name="border" BorderBrush="Transparent" BorderThickness="1" Margin="1 ">
<TextBox ToolTip="{Binding ErrorMessage, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource TemplatedParent}}" Text="{Binding Text, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}" Margin="1"></TextBox>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="HasError" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="EmailValidation" TargetType="{x:Type Controls:InputValidation}">
<Setter Property="RegEX" Value="^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,6}|[0-9]{1,3})(\]?)$"/>
<Setter Property="ErrorMessage" Value="Please Enter a Valid Email Address"/>
<Setter Property="Template" Value="{StaticResource ValidationTemplate}"/>
</Style>
</Window.Resources>
Control XAML
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Email Address" Width="100"/>
<Controls:InputValidation HasError="{Binding HasEmailError, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" Style="{StaticResource EmailValidation}" x:Name="EmailAddress" Width="200" Height="25" BorderBrush="Black" BorderThickness="1"
Text="{Binding EmailAddress, UpdateSourceTrigger=PropertyChanged}" d:Text="[email protected]"/>
</StackPanel>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<Button Width="100" HorizontalAlignment="Center" Content="OK"
Command="{Binding Path=OKCommand}">
</Button>
</StackPanel>
</StackPanel>
View Model
public class MainViewModel : BaseViewModel
{
public RelayCommand OKCommand { get; set; }
private string _EmailAddress;
public string EmailAddress
{
get { return _EmailAddress; }
set { _EmailAddress = value; OnPropertyChanged(); }
}
private bool _HasEmailError;
public bool HasEmailError
{
get { return _HasEmailError; }
set { _HasEmailError = value; OnPropertyChanged(); }
}
public MainViewModel()
{
//HasEmailError = true;
OKCommand = new RelayCommand(OnOKCommand, CanOKCommand);
this.PropertyChanged += MainViewModel_PropertyChanged;
}
private bool CanOKCommand()
{
return !HasEmailError;
}
private void OnOKCommand()
{
}
private void MainViewModel_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OKCommand.RaiseCanExecuteChanged();
}
}
The issue that I have is that, despite setting the Default Value of the HasError DP to true, it's not reflecting that state in the UI or the VM.
What I am expecting when the application first runs is the OK button to be disabled and a Red Border around the Text Box. But that's not happening (Button is enabled and no Red Border).
If I uncomment the HasEmailError = true; line, it works fine. Am I missing something really simple?