Reputation: 11121
I have a page where a few textboxes cannot be empty before clicking a Save button.
<TextBox...
<TextBox.Text>
<Binding Path ="LastName" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:StringRequiredValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
My rule works, I have a red border around my textbox until I enter a value. I now want to add this validation rule to my other text boxes.
How do I disable the Save button until the page has no validation errors? I'm not sure what to check.
Upvotes: 30
Views: 56819
Reputation: 145
This website has the code you're looking for: https://www.wpfsharp.com/2012/02/03/how-to-disable-a-button-on-textbox-validationerrors-in-wpf/
For posterity the button code should look like this if you are using a ValidationRule override on the input fields:
<Button Content="<NameThisButton>" Click="<MethodToCallOnClick>" >
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="IsEnabled" Value="false" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=<TextBoxName>, Path=(Validation.HasError)}" Value="false" />
<Condition Binding="{Binding ElementName=<TextBoxName>, Path=(Validation.HasError)}" Value="false" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="true" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Upvotes: 0
Reputation: 472
Because it's still missing, here is an adaption of Developer's answer in case the link ever goes away:
XAML:
<TextBox.Text Validation.Error="handleValidationError">
<Binding Path ="LastName"
UpdateSourceTrigger="PropertyChanged"
NotifyOnValidationError="True">
<Binding.ValidationRules>
<local:StringRequiredValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
<Button IsEnabled="{Binding HasNoValidationErrors}"/>
CodeBehind/C#:
private int _numberOfValidationErrors;
public bool HasNoValidationErrors => _numberOfValidationErrors = 0;
private void handleValidationError(object sender, ValidationErrorEventArgs e)
{
if (e.Action == ValidationErrorEventAction.Added)
_numberOfValidationErrors++;
else
_numberOfValidationErrors--;
}
Upvotes: 2
Reputation: 1019
I've tried several of the solutions stated above; however, none of them worked for me.
I have a simple input window that request a URI from the user, if the TextBox
value isn't a valid Uri
then the Okay
button should be disabled.
Here is what worked for me:
CommandBindings.Add(new CommandBinding(AppCommands.Okay,
(sender, args) => DialogResult = true,
(sender, args) => args.CanExecute = !(bool) _uriTextBoxControl.GetValue(Validation.HasErrorProperty)));
Upvotes: 1
Reputation: 11
just inhert your ViewModel from System.ComponentModel.IDataErrorInfo for Validate and from INotifyPropertyChanged to notify button
make property:
public bool IsValid
{
get
{
if (this.FloorPlanName.IsEmpty())
return false;
return true;
}
}
in xaml, connect it to button
<Button Margin="4,0,0,0" Style="{StaticResource McVMStdButton_Ok}" Click="btnDialogOk_Click" IsEnabled="{Binding IsValid}"/>
in the IDataErrorInfo overrides, notify btutton
public string this[string columnName]{
get
{
switch (columnName)
{
case "FloorPlanName":
if (this.FloorPlanName.IsEmpty())
{
OnPropertyChanged("IsValid");
return "Floor plan name cant be empty";
}
break;
}
}
}
Upvotes: 1
Reputation: 91
this is it you need to check the HasError control property from the code behaind
and do this code in the save button click
BindingExpression bexp = this.TextBox1.GetBindingExpression(TextBox.TextProperty);
bexp.UpdateSource(); // this to refresh the binding and see if any error exist
bool hasError = bexp.HasError; // this is boolean property indique if there is error
MessageBox.Show(hasError.ToString());
Upvotes: 2
Reputation: 37633
Here is the complete sample what you need.
http://codeblitz.wordpress.com/2009/05/08/wpf-validation-made-easy-with-idataerrorinfo/
https://skydrive.live.com/?cid=2c6600f1c1d5e3be&id=2C6600F1C1D5E3BE%21203
Upvotes: 19
Reputation: 2916
Here is a helper method which tracks validation errors on the dependency objects (and all its children) and calls delegate to notify about the change. It also tracks removal of the children with validation errors.
public static void AddErrorHandler(DependencyObject element, Action<bool> setHasValidationErrors)
{
var errors = new List<Tuple<object, ValidationError>>();
RoutedEventHandler sourceUnloaded = null;
sourceUnloaded = (sender, args) =>
{
if (sender is FrameworkElement)
((FrameworkElement) sender).Unloaded -= sourceUnloaded;
else
((FrameworkContentElement) sender).Unloaded -= sourceUnloaded;
foreach (var error in errors.Where(err => err.Item1 == sender).ToArray())
errors.Remove(error);
setHasValidationErrors(errors.Any());
};
EventHandler<ValidationErrorEventArgs> errorHandler = (_, args) =>
{
if (args.Action == ValidationErrorEventAction.Added)
{
errors.Add(new Tuple<object, ValidationError>(args.OriginalSource, args.Error));
if (args.OriginalSource is FrameworkElement)
((FrameworkElement)args.OriginalSource).Unloaded += sourceUnloaded;
else if (args.OriginalSource is FrameworkContentElement)
((FrameworkContentElement)args.OriginalSource).Unloaded += sourceUnloaded;
}
else
{
var error = errors
.FirstOrDefault(err => err.Item1 == args.OriginalSource && err.Item2 == args.Error);
if (error != null)
errors.Remove(error);
}
setHasValidationErrors(errors.Any());
};
System.Windows.Controls.Validation.AddErrorHandler(element, errorHandler);
}
Upvotes: 2
Reputation: 31
int count = 0;
private void LayoutRoot_BindingValidationError(object sender, ValidationErrorEventArgs e)
{
if (e.Action == ValidationErrorEventAction.Added)
{
button1.IsEnabled = false;
count++;
}
if (e.Action == ValidationErrorEventAction.Removed)
{
count--;
if (count == 0) button1.IsEnabled = true;
}
}
Upvotes: 3
Reputation: 4401
On the codebehind for the view you could wireup the Validation.ErrorEvent like so;
this.AddHandler(Validation.ErrorEvent,new RoutedEventHandler(OnErrorEvent));
And then
private int errorCount;
private void OnErrorEvent(object sender, RoutedEventArgs e)
{
var validationEventArgs = e as ValidationErrorEventArgs;
if (validationEventArgs == null)
throw new Exception("Unexpected event args");
switch(validationEventArgs.Action)
{
case ValidationErrorEventAction.Added:
{
errorCount++; break;
}
case ValidationErrorEventAction.Removed:
{
errorCount--; break;
}
default:
{
throw new Exception("Unknown action");
}
}
Save.IsEnabled = errorCount == 0;
}
This makes the assumption that you will get notified of the removal (which won't happen if you remove the offending element while it is invalid).
Upvotes: 18
Reputation: 7880
You want to use Validation.HasError attached property.
Along the same lines Josh Smith has an interesting read on Binding to (Validation.Errors)[0] without Creating Debug Spew.
Upvotes: 7