Reputation: 639
I have a WPF Form, that has a text box and a button. I am validating the text box to have only characters. Validation works fine, But I need to disable the button if there are validation error and enable them if there are no validation errors. Below is my code:
<TextBox Name="tbProductName" Grid.Column="1" HorizontalAlignment="Left" Height="25" Margin="4,9,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top" Width="213"
Text="{Binding Path = ProductCode, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True,NotifyOnValidationError=True}">
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder x:Name="textBox"/>
<TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red"/>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
<Button Name ="btnDownload" Content="Download" Grid.Column="2" HorizontalAlignment="Left" Margin="10,10,0,0" Grid.Row="1" VerticalAlignment="Top" Width="87" Height="24" Click="btnDownload_Click"/>
public class ViewModel: System.ComponentModel.INotifyDataErrorInfo
{
private readonly Dictionary<string, ICollection<string>> _validationErrors = new Dictionary<string, ICollection<string>>();
private readonly Model _productCode = new Model();
public string ProductCode
{
get { return _productCode.ProductCode; }
set
{
_productCode.ProductCode = value;
ValidateModelProperty(value, "ProductCode");
}
}
protected void ValidateModelProperty(object value, string propertyName)
{
if (_validationErrors.ContainsKey(propertyName))
_validationErrors.Remove(propertyName);
PropertyInfo propertyInfo = _productCode.GetType().GetProperty(propertyName);
IList<string> validationErrors =
(from validationAttribute in propertyInfo.GetCustomAttributes(true).OfType<ValidationAttribute>()
where !validationAttribute.IsValid(value)
select validationAttribute.FormatErrorMessage(string.Empty))
.ToList();
_validationErrors.Add(propertyName, validationErrors);
RaiseErrorsChanged(propertyName);
}
/* Raise the ErrorsChanged for all properties explicitly */
protected void ValidateModel()
{
_validationErrors.Clear();
ICollection<ValidationResult> validationResults = new List<ValidationResult>();
ValidationContext validationContext = new ValidationContext(_productCode, null, null);
if (!Validator.TryValidateObject(_productCode, validationContext, validationResults, true))
{
foreach (ValidationResult validationResult in validationResults)
{
string property = validationResult.MemberNames.ElementAt(0);
if (_validationErrors.ContainsKey(property))
{
_validationErrors[property].Add(validationResult.ErrorMessage);
}
else
{
_validationErrors.Add(property, new List<string> { validationResult.ErrorMessage });
}
}
}
/* Raise the ErrorsChanged for all properties explicitly */
RaiseErrorsChanged("ProductCode");
}
#region INotifyDataErrorInfo members
public event EventHandler<System.ComponentModel.DataErrorsChangedEventArgs> ErrorsChanged;
private void RaiseErrorsChanged(string propertyName)
{
if (ErrorsChanged != null)
ErrorsChanged(this, new System.ComponentModel.DataErrorsChangedEventArgs(propertyName));
}
public System.Collections.IEnumerable GetErrors(string propertyName)
{
if (string.IsNullOrEmpty(propertyName)
|| !_validationErrors.ContainsKey(propertyName))
return null;
return _validationErrors[propertyName];
}
public bool HasErrors
{
get { return _validationErrors.Count > 0; }
}
#endregion
}
public class Model
{
[Required(ErrorMessage = "You must enter a product code to download.")]
[RegularExpression(@"^[a-zA-Z]+$", ErrorMessage = "The Product Code must only contain letters (a-z, A-Z).")]
public string ProductCode { get; set; }
}
How can I set the isEnabled property of the button to Validation.HasErrors?
Upvotes: 1
Views: 4806
Reputation: 6755
Ideally your button would bind it's "Command" property to a public ICommand viewmodel property.
The CanExecute method would be evaluated, returning true or false. The button would be enabled/disabled accordingly.
You can read more about ICommand, along with an implementation of the interface here.
Below are the changes required, assuming your using the RelayCommand (See Fig. 3) implementation described in the aforementioned article.
Register the command
private readonly ICommand _downloadCommand = new RelayCommand(OnDownload, CanDownload);
Used for binding:
public ICommand DownloadCommand { get { return _downloadCommand; } }
Methods to invoke when the command is executed:
private void OnDownload(object parameter) { ... Do your download code here ... }
private bool CanDownload(object parameter) { return HasErrors == false; }
Update your XAML binding:
<Button Content="Download" Grid.Column="2" HorizontalAlignment="Left" Margin="10,10,0,0" Grid.Row="1" VerticalAlignment="Top" Width="87" Height="24" Command="{Binding DownloadCommand}" />
Upvotes: 2
Reputation: 4203
I would say use the same technique for the Binding
you used for the rest of your code!!
In your xaml
<Button Name ="btnDownload" IsEnabled="{Binding Path= BtnIsEnabled} click="btnDownload_Click"/>
In your code
public bool BtnIsEnabled
{
get { return this._BtnIsEnabled; }
set
{
this._BtnIsEnabled = value;
base.OnPropertyChanged("BtnIsEnabled");
}
}
private bool _BtnIsEnabled;
and then where ever you want to disable your Button
go something like
BtnIsEnabled = Validation.HasErrors ? false : true ;
Obviously this is a pseudo scribble. Unless you're after something very specific that I can't seem to find out. Seems you used binding in your code already and I would guess you know how to.
Upvotes: 1