WPF validation and CanExecute command - c#

I'm trying to get the essence of my last question and bring some more clarity to the description.
Given is an WPF form to submit new data with two textfields and a "Save" button. Both textfields are required fields and are bound to ViewModel properties. Button is bound to "Execute" relay command with Execute and CanExecute methods. CanExecute returns !HasErrors of the ViewModel. ViewModel implements INotifyPropertyChanged and INotifyDataErrorInfo interfaces. Property setter runs the validation for this property only.
The goals are:
Disable the "Save" button initially.
Enable the "Save" button only if both textfields are validated successfully without validation of the whole ViewModel on every property change.
For the first goal I use the isValidated bool property which is initially false and become true as soon as first validation occurred. This property is then checked together with HasErros in the CanExecute method. Is there a more elegant way?
I have no idea how to implement the second goal. Any thought on this?
THX

Your two goals are only one goal really. Let's look at your second goal first:
• Enable the "Save" button only if both textfields are validated successfully without validation of the whole ViewModel on every property change.
Initially, your text fields will be empty, so presumably, they will not pass validation. Therefore initially, your Save Button will be disabled, as per your first goal:
• Disable the "Save" button initially.
Secondly, in your last question (which you should probably add a link to if you're going to mention it in this post), you had some problem with using !HasErros in the CanExecute handler. This time, you do need to validate all of the properties together to fulfil your second requirement... so just use !HasErros in the CanExecute handler.

Related

WPF monitoring property changes

I frequently have the following task:
I have a collection of objects (f.e. Customers) and want to provide the user with an editor for these objects. Typically I have some list control on the left side of the editor and a form on the right side. The form displays the properties of the object that is currently selected on the left side.
Regarding the confirmation of any changes, there are at least two strategies:
make the editor a modal dialog window and give it OK/Cancel buttons. On OK save all changes for all objects
give the editor a Save button above or below the form on the right side that would allow the user to confirm changes to the currently selected object.
My question is about the second strategy, implemented as an MVVM application with WPF:
I would like to give my user a feedback that there are unsaved changes. Applications like text editors often solve this by enabling the Save button when any changes occurred and disabling it again once the user pressed it to confirm her/his changes.
If I understand correctly I would have to monitor changes to any bound properties in my form (backed by a model class). Usually my model classes use auto properties (no explicit getters and setters). Do I have to write explicit getters for all my properties to enable the Save button when anything changed, or is there a smarter way to achieve this?
Following the MVVM pattern, your ViewModels should implement INotifyPropertyChanged interface, than you can easily subscribe to PropertyChanged event and monitor properties changes
If you don't want to write INPC aware getters and setters in your model classes, then another way is to write a equality compare method instead, and then have your save command availability callback call into that to compare the "live" object with the edited one. I'm assuming you have a cloned object that is being edited in order to rollback if the user chooses not to save.
WPF will call it automatically as the user clicks around and types, or you can give it a hint with CommandManager.InvalidateRequerySuggested()

Different validation rules for different actions

I have a form that has 3 buttons on it: Save, Approve and Reject.
Now I need validate User's input differently based on what button has been clicked.
For example User should be able to Save the object even if most of the fields are empty but in order to Approve some fields are required.
One approach is to put validation to CanSave and CanApprove methods (the buttons are bound to ICommand in the ViewModel). The problem here that buttons get disabled but there is no indication of which fields are required to fill to enable particular button.
Ideally all buttons should be enabled all the time and perform validation when the button is clicked. But how do I mark fields as invalid from VM?
Try adding something like a LastStateValidated property on your class and base your IDataErrorInfo validation off that value.
This means that the first time you load the form, the validation that appears will only be the ones that are in effect regardless of the object's state.
When you try and execute a command, the State changes and this will make IDataErrorInfo validate differently, so different validation errors will show up based on what the last action was that the user tried executing.

Binding Validation Update View with Errors

From my reading and trying to understand and implement MVVM and validation, I need some help on the following scenario.
View - User interface (expected)
Model - based on a data table, and via OnColumnChanging, tests for validation requirements on a column-by-column basis (this part works no problem)
ViewModel - glue the connects the model to the view, this works too.
For each of the textbox controls, I have them respectively binding two-way to the data table, current row (still no problem) and it shows the data as expected. The flags including: NotifyOnTargetUpdated, ValidatesOnDataErrors, ValidatesOnExceptions and NotifyOnValidationError are all set to true.
If I use the interface and put in invalid value, it properly triggers the validation and puts red border around control showing it failed.
Now the problem. I start my form, and click the "Add" button (new record, blank values), textbox controls now enabled for editing content. Click the "Save" button. I want to have all the controls refreshed that are missing "required" data. Since my view model is bound to columns of the data table, and that from my readings, all the validation should be handled in the viewmodel, how should I adjust my scenario.
I don't know how to force which controls are bound to respective controls when they've never received focus. In addition, from other readings, to allow for unit testing, you should be able to test under the assumption that there is never a user interface. So, in theory, I could automate creating my viewmodel, which loads my data model, adds a record, tries to save and forces whatever to test all "required" fields.
Am I close??? way off??? Not quite positive on this.
Implement: IDataErrorInfo and check Error within your SaveCommand
Further Reading:
WPF Apps With The Model-View-ViewModel Design Pattern
Using IDataErrorInfo for validation in MVVM with Silverlight and WPF
Validation should be done in two places. One in ModelView (User input validation), second on Model model consistency validation, if required for specific scenario.
You have every text box (TextProperty) is bound to the ModelView's property. TextBox (I presume) has a dependency property which lets to you specify ether to signal error on UI or not.
What you have to do, IMHO, immidiately inside Add event handler set default values to binded ModelView object. What will happen is: for every control, including required one, will be settuped, thow via DataBinding visualized on UI, default value. For required fields the default value can be just not valid one, so requierd field will be immediately appear with Error signal, signaling a user about presence of mandatory fields.

C#: click (where applicable) versus validating event

I am recently working on windows forms with visual C# and I have a bunch of radio buttons grouped together.
I needed to call some methods if the radio button is clicked and also do some validation.
So I have two methods,
public void doSomeStuff()
public bool valRadioButton1()
I can call doSomeStuff() in the click event and the latter in the validating event of the radiobutton but I could also just call both in either the click event or the validating event.
My question is that are there any advantages and disadvantages as to what event I would use to call these? Or is there any particular way is more efficient. Right now it seems that both events would do the exact same thing so why use one or another or both.
Radio buttons are kind of strange in combination with the conventional validation. The validating event seems to be designed to allow you to validate a value once when the user is done entering a value instead of every time the value changes as the user is entering it. This makes sense for a textbox where you want to look at the completed text instead of after each character that the user types. But it's a little more obscure for radio buttons. In fact I think you should generally avoid the validating event of radio buttons and instead use the validating event of the container (radio buttons should always be in an embedded container). This allows a keyboard user to select/move through different options to arrive at the one they want without repeated validations as they move through the options. Then when they move focus out of the group box (or whatever container you used), you can validate the whole option group at once. This behavior is more consistent, then, with that of other controls' validation. In fact I see very little purpose to using the validating event on individual radio buttons. The only reason I see is if you want to cancel the user's new selection without causing extra click events. But be aware that when no radio button is selected and the user first clicks on one, no validating event will occur! No radio button lost focus and validating events only occur when a control loses focus. So this is why I think you should just avoid the validating event on radio buttons and just use the validating event of the container or the click event of a radio button.
Also, I think if you want to be nice to keyboard users, you should keep the validation logic separate from the click logic and use the events appropriately. Things like enabling controls based on which option is selected would belong in the click event of a radio button, but error and warning messages about the currently selected option should go in the validating event of the container.
Edit: You asked specifically about when one event occurs and not the other. I would add this information in response to that:
Validate will be called without calling click if the code is what causes the selected radio button to change, assuming focus then passes to the radio button (or container, if you are using the container's validate event).
Click will be called without (or should I say before) calling validate if no radio button was selected and the user then clicks on one (validate only occurs when the control loses focus). Validate will eventually occur for the clicked option, though.
Click will be called without (or should I say before) calling validate if your validate handler is not linked to the specific option that was previously selected or its container. It will be called for the option that is now selected (and the container) when this option (or the container) loses focus, though.
Click could be called without validate being called if your code that looks at the value doesn't require the selected option to lose focus before looking at it.
The validation event exist for when a controls value has changes, this is decoupled from how the controls value was changed. Was it changed because a datasource was refreshed, was it changed by an end user, was it changed on a timer? Doesn't matter!
I would use the validating event to evaluate if something is valid. Even if you know that there is "no way ever that it could happen any other way".

Confirmation on switching between tabs

I have two tabitems. User will enter some data and save it on the first tab. The second tab lists the saved data. What I need is when the user select the second tab before saving data in first tab a confirmation message box with Yes, No and Cancel should be shown. If the user clicks Yes button the data should be saved and go to the second tab. If he hits No, the data need not to be saved and finally if Cancel is hit the tab will retain with all entered data. How can i make this?
To keep things simple you can do the follwing in the Code Behind file.
I'd create a Model class of the data you want to display and edit in the WPF Control. Make the Model implement the INotifyPropertyChanged and IEditableObject interfaces.
INotifyPropertyChanged will allow you to Bind to the Model.
IEditableObject will allow you to provide Edit, Save and Cancel functionality.
The TabControl has a SelectionChanged Event you can handle, that will allow you to detect when the user changes tabs, in this handler you can use System.Windows.MessageBox to ask the user to save etc, System.Windows.MessageBox.Show() returns a MessageBoxResult Object you can use to detirmine what button the user clicked and perform the appropiate action.
This is not a geat way to do things, but it keeps things simple, you may want to look into some WPF design Patterns to help with Code Manageability.
If you need anything explained further, just ask.
Although I disagree with the way you interrupt the user's flow from tab to tab I'm going to humor you and answer the question:
You'll need two things to get this done:
The event that occurs when a tab was clicked
The previous tab that was selected (the one you came from)
The first item:
The tab control has a Click method that you can subscribe to:
Click=”MyTabButton_Click”
The second item:
This part you'll have to do manually. You can set a variable in the click event which contains what tab was last selected. Once this is set you can check a variable (which you previously set) as to what tab was previously selected. You can then do all your validation.
Delphi's TPageControl has an OnChanging event with an "AllowChange" parameter. I guess there is something similar in WPF.

Categories

Resources