Is it the right way that command parameter references a view control in MVVM pattern? - wpf

I'm making a page using MVVM pattern.
By the way, I had a question during implementing command.
It is 'Is it the right to send command parameter to UI control'?
For example, I have a TextBox control. When LostFocus event is fired, a validation command binded with TextBox will be raised and I will send an UI control as command parameter to display a validation failure message.
After a command is executed, I will change content value of UI control if validate is failed.
These are my scenario.
But I know a viewmodel should not reference any of views and its controls directly.
Please let me know how.

If your validation is evaluated in the code behind of your view/control then you are fine to send or pass a control.
I have to question what sort of validation is being done at the UI layer - it can be general input logic (like numbers only or only certain letters, etc), but it shouldn't involve any business logic. Your code behind could call a method on the viewmodel to perform business logic based validation, but it should avoid passing a control.
If you are simply looking for a way to make a generic validation method for multiple controls, then you should consider another way such as:
creating a custom usercontrol that has the validation
using an attached behavior

A view model should not have any references or any dependencies upon any UI control such as a TextBox.
Instead of passing a reference to the TextBox as a command parameter to the view model command, you should either pass the value of the Text property of the TextBox as a string argument or - even better - you should simply bind the Text property of the TextBox to a string source property of the view model:
<TextBox Text="{Binding YourViewModelProperty}" />
The source property will be set when the TextBox loses focus and you can easily kick off your validation logic in the setter:
private string _s;
public string YourViewModelProperty
{
get { return _s; }
set { _s = value; Validate(); }
}
This is how the MVVM pattern is supposed to be implemented.

Related

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?

Advantage of Binding?

I am not sure that I fully understand the advantage of binding. For example, if I want to bind a string value to a TextBlock I need to do the following:
Create a class that extends INotifyPropertyChanged
Add a string to that class (say: MyString)
Extend the set method for MyString so that it calls another method (say: OnPropertyChanged)
Create the OnPropertyChanged method to call the PropertyChangedEventHandler event
Then I need to create a new instance of the class, set my TextBlock.DataContext to point to that class, and finally add the XAML bit for the binding.
Can someone explain the advantage of this over simply setting:
TextBlock.Text = MyString;
Thanks!
Any changes to MyString won't be automatically reflected in your UI.
Your code behind will be littered with "when this event occurs, update these pieces of data", so you'll essentially be writing your own messy data binding logic for each and every view.
The advantage is that you can both change and display the value in multiple places, without having to update some method to add another TextBlock assignment each time the value changes. Any new display control just binds itself to the property, the rest is automatic.
Now if you really just set the value in one place and show it in one control, then you're right, there's not much point.
The gain of using Data Binding isn't particularly noticeable for a TextBlock binding to a static string.
However if the value of MyString changes during application runtime it becomes much more useful - especially in a case where the object that owns that property is unaware of the TextBlock. This separation between UI and the underlying data layer can be created using a design pattern such as MVVM.
Data Binding is also useful for more complex properties such as Items in a ListBox control. Just bind the ListBox.Items to a property that is of type ObservableCollection and the UI will automatically update whenever the content of that collection changes.

S/L 4 & IDataErrorInfo - How to force re-validation of a control (when a related control is touched)

I have two controls bound to properties MinCartValue and MaxCartValue. MinCartValue must be less than MaxCartValue. To achieve this validation I have implemented the the IDataErrorInfo interface, and run the above check in the this[columnName] method if either MinCartValue or MaxCartValue are touched. ValidatesOnDataErrors=True is set in the binding of both controls.
The validation works correctly, highlighting each control when a change to its property value violates the rule. The problem is that once a control is flagged as invalid, if the user corrects the problem by altering the other control's value, the first control remains flagged as invalid. This is understandable because the IDataErrorInfo method was not doing validation on the first control's property.
So what I need is a way to force property #1 to be re-validated (or a way to clear the invalid state) when property #2 is validated, and vice versa. I have tried calling RaisePropertyChanged within my this[columnName] method but it does nothing. Also tried setting the property to its own value to try to trick it to validate itself, but again nothing happens.
Thanks
I would recommend looking at the INotifyDataErrorInfo interface (introduced in Silverlight 4). It's able to async-notify if properties become invalid, so I think the framework is better about respecting this across many properties instead of expecting that the property currently being changed is the only one whose validity may be changing.
I had two DateTime properties (DateFrom and DateTo) that needed to be validated against each other. In the setters for these properties I just raised a PropertyChanged event for both DateTo and DateFrom. Worked like a charm.
I'm not sure if I'm understanding your problem exactly, but perhaps this may help. Providing some example XAML and the binding property code would help.
It sounds like an issue of your code depending on the default UpdateSourceTrigger, which in the case of TextBox controls is their focus/unfocus. You can set in the XAML the UpdateSourceTrigger attribute by adding UpdateSourceTrigger=Explicit to your binding where your validation occurs. Then in each TextBox (MinCartValue, MaxCartValue), add an event handler to the TextChanged event.
In the code-behind in the event handler, you can do something like this:
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
TheTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
TheTextBox in this case would be one of your cart controls. The UpdateSource() method is a way to manually update the binding value, which should trigger your validation. This method provides away to tie into a trigger to update values and raising properties have changed outside of the default scope (using text changed intead of focus and unfocus on TextBox in this instance).
Here's how I solved it. Let's say Property1 and Property2 are codependent. I'm not familiar with MVVM (yet), but you're probably extending your entity class to implement IDataErrorInfo. In this case you can also extend On[Property]Changed method and report change in codependent property:
partial class YourEntity : IDataErrorInfo
{
public string this[string columnName]
{
//Your validation logic
}
public string Error
{
//WPF doesn't use it anyway
get { return string.Empty; }
}
partial void OnProperty1Changed()
{
OnPropertyChanging("Property2");
OnPropertyChanged("Property2");
}
partial void OnProperty2Changed()
{
OnPropertyChanging("Property1");
OnPropertyChanged("Property1");
}
}
In this case the update in either one of this properties makes both bound controls re-evaluate themselves.
EDIT2: It appears that you should use OnPropertyChang* instead of ReportPropertyChang*. ReportPropertyChanged will notify the entity framework that there are pending changes in the model, but in fact all you're trying to do is inform the view. You don't want to update the database with the property that didn't really change. ReportPropertyChang* will also fail on computed fields that have no mappings in the database.
EDIT1: Found out that it's essential to call ReportPropertyChanging before ReportPropertyChanged.

Data validation on a ListBox in WPF

I have a ListBox bound to an ObservableCollection of type T - each ListBoxItem is a checkbox with IsChecked bound to a bool property in T. I want to validate the checked items in the ListBox so that at least one checkbox must be ticked - if none of the check boxes are ticked I want to show a red border (standard validation notification) around the ListBox.
How do I go about doing that? Can I use ValidatesOnDataErrors?
Implement IDataErrorInfo in the class that exposes the ObservableCollection<T> property and make the indexer return an error for that property's name if all of the objects in the collection have a value of false in their boolean property. Then use the DataErrorValidationRule in your binding's ValidationRules.
Your goal is to validate selected items of a ListBox: at least one selected item is required. First, there are different ways to handle the selection itself:
a) You can use the ListBox selection mechanism via ListBox.SelectedItem.
I guess, you can specifiy the binding as SelectedItem="{Binding SelectedThing, ValidatesOnNotifyDataErrors=True}" and WPF's regular validation works. Not sure, if this works well with multi-selection.
b) You can use ListBox.SelectedItems. Binding is not possible and needs to be faked via code behind event handler. Thus, WPF's validation cannot trigger here.
c) You use your own IsSelected property (or similar) per item, either explicitely bound to your viewmodel via CheckBox or implicitely via ListBoxItem.IsSelected (via ListBox.ItemContainerStyle).
From here you could create a bool IsValidSelection property in your viewmodel that updates whenever the selection changed. Or you can run validation rules on any of the resulting properties of your viewmodel (e.g. SelectedItems). However, you don't validate the bound ListBox.ItemSource itself! Therefore WPF's standard mechanisms don't work (the list box doesn't get a red border or whatever).
Workarounds
You can explicitely create error notifications by showing an otherwise invisible Label and binding its visibility to the above-mentioned bool IsValidSelection property. This is a very explicit way, not using WPF validation at all.
You can modify your INotifyDataErrorInfo (or similar) implementation to trigger errors on a different property name. This is not possible, if you use a generic ValidatableViewModel<T> (like me). I guess this was the intention of the other answer.

WPF MVVM UpdateSourceTrigger=Excplict

i've a contentcontrol in my Wpf-App (MVVM) which is bound to an object and displays the objects properties in textboxes, so the user can edit the values of the properties.
I want to implement undo/redo functionality with the command pattern of the GoF.
For this i need a point where i can create the command and set it into my undomanager.
My idea was to add a submitbutton. When the button is pressed, i update the sources of the textboxes (my properties) and create my command object to make the changes undoable (saving the old state of the object and the new state).
But:
- For using a submit button i need to set UpdateSourceTrigger of the textboxes to Explicit. If i want to update my sources i need to reference the controls in my view, which is bad as far as i've learned. How can i do that?
With MVVM i have to create a Command (WPF Command, not my undo redo command) for the SubmitButton but i don't see how to apply the changes to the properties from that command without referencing the textboxes (further hey are generated via datatemplates).
Thanks Walter
I assume your TextBox controls are bound to the properties in the ViewModel class. If you bind your submit button to a ViewModel Command which in turn can add appropriate command to you Command Pattern Collection and also changes some of ViewModel properties, the values in the Textbox controls will also be updated. Now, for a Textbox to update it's value when the value of a property it is bound to changes, the ViewModel class needs to implement INotifyPropertyChanged interface and raise the PropertyChanged event from the property setter with that's property's name as an argument.

Resources