I have a DataGrid
like so:
<DataGrid CanUserSortColumns="False" CanUserAddRows="True" ItemsSource="{Binding Vertices}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn x:Name="YColumn" Width="*" Header="Latitude">
<DataGridTextColumn.Binding>
<Binding Path="Y">
<Binding.ValidationRules>
<validation:DoubleValidationRule />
</Binding.ValidationRules>
</Binding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
<DataGridTextColumn x:Name="XColumn" Width="*" Header="Longitude">
<DataGridTextColumn.Binding>
<Binding Path="X">
<Binding.ValidationRules>
<validation:DoubleValidationRule />
</Binding.ValidationRules>
</Binding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
I have two columns that have the same validation rule (checking to see if the value in the cell is a double):
public class DoubleValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
if (value != null)
{
double proposedValue;
if (!double.TryParse(value.ToString(), out proposedValue))
{
return new ValidationResult(false, "'" + value.ToString() + "' is not a whole double.");
}
}
return new ValidationResult(true, null);
}
}
This works fine, and a red border is displayed around the cells if the user entered value is not a double. Now I would like to disable a button if there is a validation error with any of the cells.
Following some other posts on this topic, I achieved this using MultiDataTriggers
:
<Button>
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="False" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=(Validation.HasError), ElementName=XColumn}" Value="False" />
<Condition Binding="{Binding Path=(Validation.HasError), ElementName=YColumn}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
This isn't working though. The button never disables even when there is a validation error. What am I doing wrong?
Edit: Here's my model and related code in the view model:
public class CustomVertex
{
public double X { get; set; }
public double Y { get; set; }
public CustomVertex()
{ }
}
public class CustomPolygonViewModel : ViewModelBase
{
public ObservableCollection<CustomVertex> Vertices { get; set; }
public CustomPolygonViewModel()
{
Vertices = new ObservableCollection<CustomVertex>();
}
}
My DataContext
is set up correctly and I verified that the model's x and y are being updated on changing the value. The validation rule is being hit properly.
You have to let your view model implement
INotifyDataErrorInfo
MSDN. Example. Example from MSDN (Silverlight). Since .Net 4.5 this is the recommended way to introduce validation to your view models and will help you to solve your propblem. When implementing this interface you will have to provide aHasErrors
property that you can bind to.INotifyDataErrorInfo
replaces the obsoleteIDataErrorInfo
.Binding to the
Validation.HasError
directly, as you did in your triggers, will not work sinceValidation.HasError
is a read-only attached property and therefore doesn't support binding. To prove this I found this statement on MSDN:How
INotifyDataErrorInfo
worksWhen the
ValidatesOnNotifyDataErrors
property ofBinding
is set totrue
, the binding engine will search for anINotifyDataErrorInfo
implementation on the binding source to subscribe to theErrorsChanged
event.If the
ErrorsChanged
event is raised andHasErrors
evaluates totrue
, the binding will invoke theGetErrors()
method for the actual property to retrieve the particular error message and apply the customizable validation error template to visualize the error. By default a red border is drawn around the validated element.How to implement
INotifyDataErrorInfo
The
CustomVertex
class is actually the ViewModel for theDataGrid
columns since you are binding to it's properties. So it has to implement theINotifyDataErrorInfo
. It could look like this:The View:
The following is the validation error template, in case you like to customize the visual representation (optional). It is set on the validated element (in this case the
DataGridTextColumn
) via the attached propertyValidation.ErrorTemplate
(see above):The Button that will be disabled when the validation fails (since I don't know where this button is located in the visual tree I will assume that it shares the
DataContext
of aDataGrid
column, theCustomVertex
data model):There are many examples on the web. I updated the links to provide some content to start with.
I recommend moving the implementation of
INotifyDataErrorInfo
into a base class together withINotifyPropertyChanged
and let all your view models inherit it. This makes the validation logic reusable and keeps your view model classes clean.You can change the implementation details of
INotifyDataErrorInfo
to meet requirements.Remarks: The code is not tested. The snippets should work, but are intended to provide an example how the
INotifyDataErrorInfo
interface could be implemented.