Silverlight Data Validation on Custom Client-Side Class - silverlight

I have a simple viewmodel class with two properties: sEmailAddress and sEmailCompare.
I have a simple view (window) with two text boxes. The data context is set to an instance of the viewmodel. The two text boxes are bound to the two properties on the viewmodel, with the validation properties set, as follows:
Text="{Binding sEmailAddress,
Mode=TwoWay,
NotifyOnValidationError=True,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True,
ValidatesOnExceptions=True}"
My viewmodel has a Validate method as follows:
Public Function Validate() As Boolean
If Validator.TryValidateObject(Me, New ValidationContext(Me), New List(Of ValidationResult), True) Then
Return True
Else
Validator.ValidateObject(Me, New ValidationContext(Me), True)
Return False
End If
End Function
And Data Annotated properties:
Private _sEmailAddress As String
<Required>
Public Property sEmailAddress As String
Get
Return _sEmailAddress
End Get
Set(ByVal value As String)
If _sEmailAddress <> value Then
_sEmailAddress = value
OnPropertyChanged("sEmailAddress")
End If
End Set
End Property
Private _sEmailAddressConfirm As String
<Required>
<CustomValidation(GetType(MatchingEmailValidator), "ValidateEmail")>
Public Property sEmailAddressConfirm As String
Get
Return _sEmailAddressConfirm
End Get
Set(ByVal value As String)
If _sEmailAddressConfirm <> value Then
_sEmailAddressConfirm = value
OnPropertyChanged("sEmailAddressConfirm")
End If
End Set
End Property
The actual validation part is working correctly- if I leave a field blank or if the two field values do not match, then TryValidateObject returns false. But the UI does not change. So I try calling ValidateObject, and that throws a validation exception, as it should, but I don't know how to handle that exception so that the UI is updated and the textbox is outlined in red as normal.
Can anyone shed some light on what I'm missing here?

There are two ways to do this kind of validation in Silverlight. The first is to throw an exception inside the property setter -- since you have ValidatesOnExceptions=true, that should cause the validation error to appear on the UI. To implement this, you would basically just need to call ValidateObject inside the setter.
The other way, which is useful for more advanced scenarios (ie, when there may be validation rules depending on multiple properties), is to have your view-model implement INotifyDataErrorInfo. This interface has a collection of errors that you maintain, and an event ErrorsChanged that you need to call when an error is added or removed.

Related

WPF INotifyErrorInfo Validation.Error Event Not Raising

I'm encountering a strange problem. Despite setting everything correctly, the Validation.Error doesn't get fired.
Here are the details:
<DataTemplate x:Key="dtLateComers">
<TextBox Text="{Binding ParticipantTag, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True, NotifyOnSourceUpdated=True}" Validation.Error="Validation_Error" >
</DataTemplate>
Code behind (VB.Net) to set ItemsSource of HeaderedItemsControl:
hicLateComers.ItemsSource = _LateComersViewModels
_LateComersViewModels is ObservableCollection(Of ParticipantViewModel)
Implementation of ParticipantViewMode:
Public Class ParticipantViewModel
Implements INotifyPropertyChanged, IDataErrorInfo
Private _ParticipantTag As String = ""
Public Property ParticipantTag() As String
Get
Return _ParticipantTag
End Get
Set(ByVal value As String)
_ParticipantTag = value
_ParticipantTag= _ParticipantTag.ToUpper
NotifyPropertyChanged("ParticipantTag")
End Set
End Property
Public ReadOnly Property Item(byVal columnName As String) As String Implements IDataErrorInfo.Item
Get
Dim errorString As String = String.Empty
If columnName.Equals("ParticipantTag") Then
If not ParticipantValidationManager.IsValidKeypadTag(_ParticipantTag, True) then
errorString = "Incorrect entry. Please try again."
End If
End If
Return errorString
End Get
End Property
Public ReadOnly Property [Error] As String Implements IDataErrorInfo.Error
Get
Throw New NotImplementedException()
End Get
End Property
End Class
Problem
When I set ItemSource property (as mentioned above in code), Item index is called as many times as there are items in _LaterComersViewModels. Validation works and as a result I get red circle next to TextBox. However, Validation_Error never gets fired until I start typing in Textbox. Typing in TextBox changes the Property binds to it and validate it. Base on validation Validation.Error event is raised, and handled by application. Within that event handler I maintain a count of errors.
So the Question is, why Validation.Error doesn't get raised when one/more items fail on a validation rule during initial data binding? Though it does get raised once property is changed by typing into that TextBox.
Feel free to share any idea, assumption or a solution. Any type of help will be appreciated. Thanks.
Side note: I've a simple C# application which doesn't use data templating. In that application, Validation.Error event gets raised perfectly on start, and on property change. Though in that application, Model is binding to DataContext property of Grid.
Since Validation.Error is an attached event, you could hook up the event handler on the HeaderedItemsControl:
<HeaderedItemsControl x:Name="hicLateComers" ItemTemplate="{StaticResource dtLateComers}" Validation.Error="Validation_Error" />
The result should be pretty much the same since you can easily access both the TextBox and the ParticipantViewModel object in the event handler:
Private Sub Validation_Error(sender As Object, e As ValidationErrorEventArgs)
Dim textBox = CType(e.OriginalSource, TextBox)
Dim participant = CType(textBox.DataContext, ParticipantViewModel)
'...
End Sub

How to make ViewModel aware of conversion error

I am designing a WPF application following MVVM. My ViewModel is exposing one Double property called DoubleValue, which is binding to a TextBox in the View. I have set "ValidatesOnDataErrors=True" for the binding. So if the user types a string which can't be converted to a Double, it display the red background.
In my ViewModel I also have a Command object, let's call SaveCommand, whose CanExecute delegate is depending on whether there is any error in the VM (my ViewModelBase class implements IDataErrorInfo, I have an overridable ValidatePropertyByName function and the validation errors are stored in a dictionary.) But now my problem is, if I give an invalid string in the TextBox, since the conversion fails, it never calls the setter of the binding property value. In another word, the ValidatePropertyByName is not called and the error dictionary remains the previous state, which normally is clean. So if now the user click the Save button (which is enabled since the error dictionary is clean), the SaveCommand executes with the previous valid double value to save. This is obviously not good.
So how can I make my ViewModel aware of such conversion errors?
UPDATE:
Some code example:
The binding property is like this:
Public Property DoubleValue As Double
Get
Return _doubleValue
End Get
Set(value As Double)
If value <> _doubleValue Then
_doubleValue = value
RaisePropertyChanged("DoubleValue")
End If
End Set
End Property
Private _doubleValue As Double
My binding is like this:
<TextBox Grid.Row="3" Text="{Binding DoubleValue, ValidatesOnDataErrors=True}" />
And now my problem is: if I give a string "XXX" in the text box, since it can't be converted to a double value, the setter of DoubleValue is never get called. And so the property value remains the previous(valid) value. Now if my SaveCommand gets executed, it will do the save operation with this previous valid value, which will make the user confused.
the most easy way is to just use string properties in your viewmodel. then you get all input from the user and can validate it in your viewmodel. the drawback is that you have to convert the values to the right type when you go to the model.
if you dont want this you have to create your own controls or better behaviors so that the use can just input values that your viewmodel expect. eg. NumericInputBehavior.
You cannot simply put these two things together. One is the regular validation inside the ViewModel. The other are control-specific problems, like unconvertible values.
So there are two possible ways to solve this:
1) Don't use a converter. Just bind the string. Inside the ViewModel you can then use the validation to check for a valid value. (More MVVM)
2) Store your ValidationErrors on the controlside and merge them with the viewmodel errors. This is not easy but a good way to create one source for binding against ALL problems within your UI. We are doing this for complex textboxes at work. This means manual code in the controls but for complex customcontrols this is OK, I believe.
edit: just to elaborate a little on the 2nd point. We are having a DependencyProperty of Type ObservableCollection inside the Control. Then you can bind this Collection to a ViewModel Property and as soon as your control moves an Error inside the collection it is available inside the viewModel. You can then use this collection inside your validation implementation. This works pretty well for larger controls.
Edit2: For the MarkInvalid Stuff I mentioned in the comment. It would look like this:
DataErrorValidationRule validationRule = new DataErrorValidationRule();
ValidationError validationError = new ValidationError(validationRule, myTextBox.GetBindingExpression(TextBox.TextProperty)) { ErrorContent = "My custom message" };
Validation.MarkInvalid(myTextBox.GetBindingExpression(TextBox.TextProperty), validationError);
You would call in from inside a TextChanged when you can't convert the new given value or
Validation.ClearInvalid(myTextBox.GetBindingExpression(TextBox.TextProperty))
Maybe that will help?

WPF updating parameterized bindings

I have a property in a class:
Default Public ReadOnly Property GetLiteral(Key As String) As String
Get
Try
Dim Row() As String = _LookupTable.Item(Key)
Return Row(_CurrentLanguage)
Catch ex As Exception
Return ("Error")
End Try
End Get
End Property
And I bind a label to it:
<Label Content="{Binding SLP.[LITERAL_USERNAME]}"/>
Where SLP is a public property in my view model containing an instance of the class containing the default property GetLiteral.
The above binding works fine. I can select different languages in my view. However, I wish to also be able to change languages on the fly, and I can't figure out how to raise INotifyPropertyChanged in order to make this binding update. I know I could probably achieve this easily with a value converter and parameter, but I like the simplicity of the XAML above.
Thanks.
I figured it out. I was calling NotifyPropertyChanged from inside SLP, where what I really needed to do was call NotifyPropertyChanged(Me, "SLP") from the View Model.

WPF checkbox IsChecked binding, OnpropertyChanged not working

I have a checkbox in my view and the binding on it is working fine if i do any modification to my object and trigger the propertyChanged event.
On the other side when i click my checkbox i perform some logic if the property where it is bound to can be changed. If not i change it back to False and trigger the propertyChanged event.
My view is not updating this state of the checbox and it is still checked.
Is this a know problem with any bypass?
Code:
<ctrls:CheckBox
IsChecked="{Binding IsConverted, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
When i change the value and raise the property change event the property is retrieved with the false value:
Private _isConverted As Boolean
Public Property IsConverted As Boolean
Get
Return _isConverted
End Get
Set(value As Boolean)
_isConverted = value
End Set
End Property
In your Binding in Xaml, make sure that Mode is set to TwoWay so the updates work in both directions.
<CheckBox IsChecked="{Binding Path=ViewModelIsChecked, Mode=TwoWay}"/>
Also, now that you posted code, you seem to do things "manually", that should be implemented using the interface INotifyPropertyChanged. Can you drop the UpdateSourceTrigger and just implement INotifyPropertyChanged in your ViewModel?
There is a good guide here.
Edit:
And because the Poster isn't willing or able to read and understand the guide, here comes the code that is posted there:
Public Property PhoneNumber() As String
Get
Return Me.phoneNumberValue
End Get
Set(ByVal value As String)
If Not (value = phoneNumberValue) Then
Me.phoneNumberValue = value
NotifyPropertyChanged() <===== THIS LINE IS DIFFERENT! YOU DON'T HAVE IT. YOU NEED IT.
End If
End Set
End Property

Is there any general procedure to implement command in MVVM if the element doesn't support command?

Is there any particular way to implement command in MVVM if the element doesn't support Command. Example how to implement the TextChanged_event for the TextBox?.
There is no need to use the TextChanged_event or the SelectionchangedEvent as you can achieve the same using binding them to your ViewModel properties and waiting for their notification message (check MVVMLight's Messenger helper class).
If you desperately need a handler for those events, you can try the EventToCommand behaviour helper class which uses RelayCommand
You can check out this illustration and example program for details on messenger class and this example for getting a clear picture on EventToCommand behaviour
What you do is watch for the change in your ViewModel's property set method.
The XAML would look something like this:
<TextBox Text="{Binding Mode=TwoWay,
Path=ViewModelProperty,
UpdateSourceTrigger=PropertyChanged}" />
And on the ViewModel class, you define a property like this:
Private _ViewModelProperty As String
Public Property ViewModelProperty As String
Get
Return _ViewModelProperty
End Get
Set(ByVal value As String)
' your test for "TextChanged" goes here
If value <> _ViewModelProperty Then
_ViewModelProperty = value
OnViewModelPropertyChanged()
End If
End Set
End Property
Private Sub OnViewModelPropertyChanged()
' logic for handling changes goes here
End Sub
This has the side effect of executing OnViewModelPropertyChanged() every time you assign a new value to the ViewModelProperty, but you can avoid that by assigning to the backing field instead of the property.
Otherwise, you're implementing ICommand interfaces, which have their use; it depends on how complex you need things to get.

Resources