How to use Validation Rule on Button Click Command - c#

I have a TextBox with OneWay Mode, so validation does not happen automatically.
<TextBox.Text>
<Binding Path="SelectedValue.Customername"
ElementName="customerListBox"
Mode="OneWay"
>
<Binding.ValidationRules>
<validators:NameValidator ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
I have a button :
<Button Content="Save" Command="{Binding SaveCommand}"/>
Now on ViewModel , I want to validate the Text Input before doing anything else:
SaveCommand = new RelayCommand(
param=>
{
//If validation is true
//Then Execute Res
}
);

Ditch the UI validation rules and have your VM implement IDataErrorInfo and INotifyDataErrorInfo.
Think about it--your save command should not execute unless the data in your VM is valid. That means the validation logic should be in your VM, and not in your UI.
Implementing these interfaces makes it trivial to check whether or not you execute/can execute and fire appropriate events when CanExecute has changed.

Related

Set errors from code to xaml after calling a post method

I'm using ValidationRules in my xaml forms like this
<TextBox Name="email">
<TextBox.Text>
<Binding Path="email" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:NotEmptyString ValidationStep="RawProposedValue" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
In code, before my transaction begins i check for errors like this
email.GetBindingExpression(TextBox.TextProperty).UpdateSource();
if (Validation.GetHasError(item))
return false;
I have classes that inherit ValidationRule for every validation i need, and this works fine.
But now, i need to call a post method and that method returns me an JSON error when the email already exists, I want to show that error as a validation error. is there a way to set the error to the TextBox?
If you want to stick to this quite unflexible binding validation, then the simplest solution would be to use Binding.ValidatesOnException:
<TextBox Name="email">
<TextBox.Text>
<Binding Path="email"
UpdateSourceTrigger="PropertyChanged"
ValidatesOnException="True">
<Binding.ValidationRules>
<local:NotEmptyString ValidationStep="RawProposedValue" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Then validate your JSON response:
class ViewModel
{
public string Email { get; set; }
private ValidateJsonResponse(Json jsonObject)
{
if (!JsonIsValid(jsonObject))
{
throw new ArgumentException("Email already exists.");
}
}
}
But since exceptions can have some impact on the performance, you generally should avoid them. You can argue, if failed input validation is a good reason to throw an exception. I doubt it is.
Alternatively implement a second ValidationRule and trigger the binding to execute it. You must find a way to access the JSON response to check whether it is valid or not. Since the ValidationRule is instantiated in the XAML and can't have a DependencyProperty to allow binding, this could be slightly complicated. That's why binding validation is not very flexible. The ValidationRule instance of the binding is quite isolated from the rest of the code e.g., the view model.
The recommended pattern is to have your view model implement he INotifyDataErrorInfo interface. See this post for an example or links regarding the implementation: How to add validation to view model properties or how to implement INotifyDataErrorInfo

Validation MarkInvalid Textbox using MVVM

I have a textbox that has validation on it, in the validation checks to see if its isNullOrEmpty. The validation works but what I am having difficulty is if the user never selects the textbox and clicks save, I want the validation to run again.
I was able to accomplish this in the XAML.cs file using:
Validation.MarkInvalid(cb.GetBindingExpression(dp), validationError);
Now with MVVM I'm confused how I am able to accomplish this from the viewmodel.
Textbox in question:
<TextBox>
<TextBox.Text>
<Binding Path="LastName">
<Binding.ValidationRules>
<validationRules:IsNullOrEmptyValidationRule/>
</Binding.ValidationRules>
<Binding.UpdateSourceTrigger>PropertyChanged</Binding.UpdateSourceTrigger>
<Binding.Mode>TwoWay</Binding.Mode>
</Binding>
</TextBox.Text>
</TextBox>
Any ideas?

Using XAML <Binding> sytax to bind a DataContext?

I have a WPF UserControl which allows the editing of an data object (VariableDataGroup). When I use the following syntax the binding works and my user control displays correctly:
<vdi:VariableDataPageView DataContext="{Binding VariableData}" Grid.Row="4" Grid.Column="1" >
Where VariableData is a property on the parent screen's ViewModel.
However, when I try and use the "<Binding>" syntax then my user control doesn't display the data.
<vdi:VariableDataPageView Grid.Row="4" Grid.Column="1" >
<Binding Path="VariableData" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
<!--<Binding.ValidationRules>
<validation:VDIComittedValidationRule ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>-->
</Binding>
</vdi:VariableDataPageView>
The reason I want to switch to using the syntax is to use a custom validation rule to fit in with the exiting code.
First Question: What would be the equivalent syntax to 'DataContext="{Binding VariableData}"' ?
Second Question: Is there a recommended way of performing validation in a UserControl and integrating that with the validation on the parent view? The way I think it should work is that the UserControl performs all its own validation and just passes a validated yes/no result to the parent page but the whole WPF model of programming is new to me.
Thanks,
Canice.
The equivalent syntax to DataContext="{Binding VariableData}"
<vdi:VariableDataPageView Grid.Row="4" Grid.Column="1" >
<vdi:VariableDataPageView.DataContext>
<Binding Path="VariableData" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
<!--<Binding.ValidationRules>
<validation:VDIComittedValidationRule ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>-->
</Binding>
</vdi:VariableDataPageView.DataContext>
</vdi:VariableDataPageView>

DependencyProperty Binding with ValidationRule not updating Source when ValidationResult is false

A ComboBox.SelectedItemProperty is bound TowWay to a DependencyProperty in
The Control.
In the ControlTemplate :
<ComboBox IsEditable="True">
<ComboBox.SelectedItem>
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="SomeDP" Mode="TwoWay" NotifyOnValidationError="True">
<Binding.ValidationRules>
<vld:DeleteAfterInitValidationRule ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</ComboBox.SelectedItem>
</ComboBox>
When the ValidationRule returns false
return ValidationResult(false,msg);
The Dependency Property bound to selected item is not updated.
Is there a way to force the binding to update the source ?
*Please if any one is going to raise a discussion here about
BindingExpression.UpdateSource() , please supply a working example, and not just blurt it out because it sounds like the solution ,
i do not intend to use Explicit mode on my Binding.
Further more i can easily code my way around this but a good .net programmer should thrive to write less code and use the built in mechanisms supplied by the .net framework ,
And that is the soul purpose of this question , is there a built in way of updating the source while
notifying of a DataError ?

How to let View Model know about successfully validated form

I have a registration form with few fields. It´s a PRISM MVVM application.
XAML of one of the field looks like this (RegisterView.xaml):
<TextBlock>Surname</TextBlock>
<TextBox Validation.ErrorTemplate="{StaticResource validationTemplate}" HorizontalAlignment="Left" Margin="0" Name="Surname" VerticalAlignment="Top" >
<TextBox.Text>
<Binding Path="Surname" UpdateSourceTrigger="LostFocus" >
<Binding.ValidationRules>
<val:Required />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
As you can see from the code above, I use class Required to validate the field. Function Validate() of class Required then returns ValidationResult object. I also defined some triggers to styles for inputs, so I´m able to show validation result to the user in view.
What I don't know is how to detect the state of validation of all the inputs in ViewModel. In the ViewModel, I have the SaveUserCanExecute function which should enable/disable registration form submit button on the basic on a validation state of all the inputs.
So is there any simple way how to achieve this?
I could make some workaround for this, but I think is not the proper way.
Now I made a Submit_Click function in a View code behind fired on a Click event of a Submit Button.
In RegisterView.xaml
<Button Content="Register" HorizontalAlignment="Left" Margin="0" Name="Submit" VerticalAlignment="Top" Command="{x:Static inf:Commands.SaveUser}" Click="Submit_Click" />
I also created new public boolean variable "formIsValid" in code behind. When submit button is pressed, then I check whether all inputs has no validation error (with Validation.GetHasError(InputName) function). If so, I set the formIsValid variable to true, otherwise, I set it to false.
In RegisterView.xaml.cs
private void Submit_Click(object sender, RoutedEventArgs e)
{
if (Validation.GetHasError(Firstname) == false && Validation.GetHasError(Surname) == false)
{
registerFormValid = true;
}
else
{
registerFormValid = false;
}
}
Then in ViewModel SaveUserCanExecute function looks like this:
private bool SaveUserCanExecute(string parameter)
{
if (View.registerFormValid == true)
{
return true;
}
return false;
}
But as I mentioned before, I think it´s not the proper way, and I´m looking for some more clear way.
implement IDataErrorInfo in your ViewModel then you have all information you need in your VM. Your XAML just need the ValidatesOnDataErrors=true
<TextBlock>Surname</TextBlock>
<TextBox Validation.ErrorTemplate="{StaticResource validationTemplate}" HorizontalAlignment="Left" Margin="0" Name="Surname" VerticalAlignment="Top" >
<TextBox.Text>
<Binding Path="Surname" UpdateSourceTrigger="LostFocus" ValidatesOnDataErrors="True">
</Binding>
</TextBox.Text>
</TextBox>
EDIT: check the use of DelegeCommand and then your command CanExecute can simply check then for string.IsNullOrEmpty(this.Error).
Bind a command (ICommand) to your Submit Button and implement this logic in Its CanExecute method. Here is a classic ICommand implementation.

Categories