📜 ⬆️ ⬇️

Create an application for Windows Phone 7 from start to finish. Part 14. Validation of input data.

Previous part

Your application must validate all user input in order to prevent possible errors.

In this part you will learn:

Validation


It is usually useful to limit the data that users can enter. Here are two easy ways to limit user input:
Even with these restrictions, as a rule, you can still enter values ​​that are incorrectly formatted or invalid for other reasons.
')
Your requirements for validation may be different, but as a rule, you will check the data to prevent incorrect values ​​from being saved. In this case, it is useful to bind the user interface to temporary objects rather than bind directly to the data store. For example, in the Fuel Tracker application, new refueling data is not added to the FillupHistory collection until the user clicks the Save button. However, before they are saved, the user can review the data to make sure they are accurate. When the user clicks the “Save” button, the validation code will perform one last error check and warn the user of any problems.

The following code demonstrates a simple validation method used in the Fillup class.
public IEnumerable < string > Validate() { if (OdometerReading <= 0) yield return "The odometer value must be greater than zero." ; if (DistanceDriven <= 0) yield return "The odometer value must be greater than the previous value." ; if (FuelQuantity <= 0) yield return "The fuel quantity must be greater than zero." ; if (PricePerFuelUnit <= 0) yield return "The fuel price must be greater than zero." ; } * This source code was highlighted with Source Code Highlighter .
  1. public IEnumerable < string > Validate() { if (OdometerReading <= 0) yield return "The odometer value must be greater than zero." ; if (DistanceDriven <= 0) yield return "The odometer value must be greater than the previous value." ; if (FuelQuantity <= 0) yield return "The fuel quantity must be greater than zero." ; if (PricePerFuelUnit <= 0) yield return "The fuel price must be greater than zero." ; } * This source code was highlighted with Source Code Highlighter .
  2. public IEnumerable < string > Validate() { if (OdometerReading <= 0) yield return "The odometer value must be greater than zero." ; if (DistanceDriven <= 0) yield return "The odometer value must be greater than the previous value." ; if (FuelQuantity <= 0) yield return "The fuel quantity must be greater than zero." ; if (PricePerFuelUnit <= 0) yield return "The fuel price must be greater than zero." ; } * This source code was highlighted with Source Code Highlighter .
  3. public IEnumerable < string > Validate() { if (OdometerReading <= 0) yield return "The odometer value must be greater than zero." ; if (DistanceDriven <= 0) yield return "The odometer value must be greater than the previous value." ; if (FuelQuantity <= 0) yield return "The fuel quantity must be greater than zero." ; if (PricePerFuelUnit <= 0) yield return "The fuel price must be greater than zero." ; } * This source code was highlighted with Source Code Highlighter .
  4. public IEnumerable < string > Validate() { if (OdometerReading <= 0) yield return "The odometer value must be greater than zero." ; if (DistanceDriven <= 0) yield return "The odometer value must be greater than the previous value." ; if (FuelQuantity <= 0) yield return "The fuel quantity must be greater than zero." ; if (PricePerFuelUnit <= 0) yield return "The fuel price must be greater than zero." ; } * This source code was highlighted with Source Code Highlighter .
  5. public IEnumerable < string > Validate() { if (OdometerReading <= 0) yield return "The odometer value must be greater than zero." ; if (DistanceDriven <= 0) yield return "The odometer value must be greater than the previous value." ; if (FuelQuantity <= 0) yield return "The fuel quantity must be greater than zero." ; if (PricePerFuelUnit <= 0) yield return "The fuel price must be greater than zero." ; } * This source code was highlighted with Source Code Highlighter .
  6. public IEnumerable < string > Validate() { if (OdometerReading <= 0) yield return "The odometer value must be greater than zero." ; if (DistanceDriven <= 0) yield return "The odometer value must be greater than the previous value." ; if (FuelQuantity <= 0) yield return "The fuel quantity must be greater than zero." ; if (PricePerFuelUnit <= 0) yield return "The fuel price must be greater than zero." ; } * This source code was highlighted with Source Code Highlighter .
  7. public IEnumerable < string > Validate() { if (OdometerReading <= 0) yield return "The odometer value must be greater than zero." ; if (DistanceDriven <= 0) yield return "The odometer value must be greater than the previous value." ; if (FuelQuantity <= 0) yield return "The fuel quantity must be greater than zero." ; if (PricePerFuelUnit <= 0) yield return "The fuel price must be greater than zero." ; } * This source code was highlighted with Source Code Highlighter .
  8. public IEnumerable < string > Validate() { if (OdometerReading <= 0) yield return "The odometer value must be greater than zero." ; if (DistanceDriven <= 0) yield return "The odometer value must be greater than the previous value." ; if (FuelQuantity <= 0) yield return "The fuel quantity must be greater than zero." ; if (PricePerFuelUnit <= 0) yield return "The fuel price must be greater than zero." ; } * This source code was highlighted with Source Code Highlighter .
  9. public IEnumerable < string > Validate() { if (OdometerReading <= 0) yield return "The odometer value must be greater than zero." ; if (DistanceDriven <= 0) yield return "The odometer value must be greater than the previous value." ; if (FuelQuantity <= 0) yield return "The fuel quantity must be greater than zero." ; if (PricePerFuelUnit <= 0) yield return "The fuel price must be greater than zero." ; } * This source code was highlighted with Source Code Highlighter .
  10. public IEnumerable < string > Validate() { if (OdometerReading <= 0) yield return "The odometer value must be greater than zero." ; if (DistanceDriven <= 0) yield return "The odometer value must be greater than the previous value." ; if (FuelQuantity <= 0) yield return "The fuel quantity must be greater than zero." ; if (PricePerFuelUnit <= 0) yield return "The fuel price must be greater than zero." ; } * This source code was highlighted with Source Code Highlighter .
  11. public IEnumerable < string > Validate() { if (OdometerReading <= 0) yield return "The odometer value must be greater than zero." ; if (DistanceDriven <= 0) yield return "The odometer value must be greater than the previous value." ; if (FuelQuantity <= 0) yield return "The fuel quantity must be greater than zero." ; if (PricePerFuelUnit <= 0) yield return "The fuel price must be greater than zero." ; } * This source code was highlighted with Source Code Highlighter .
public IEnumerable < string > Validate() { if (OdometerReading <= 0) yield return "The odometer value must be greater than zero." ; if (DistanceDriven <= 0) yield return "The odometer value must be greater than the previous value." ; if (FuelQuantity <= 0) yield return "The fuel quantity must be greater than zero." ; if (PricePerFuelUnit <= 0) yield return "The fuel price must be greater than zero." ; } * This source code was highlighted with Source Code Highlighter .

The following image shows an example of a message that appears when you click the Save button, when the odometer reading value is less than the previous value.

image

This code simply checks the values ​​of the Fillup property and returns error messages for any invalid values. However, not all verifiable values ​​are specified by the user, so some initialization must occur in advance. In particular, the distance should be calculated by comparing the Fillup.OdometerReading value with the previous odometer reading value. However, the current Fillup object does not have access to the previous value, so this action is performed by the CarDataStore.SaveFillup method, as shown in the following code snippet.
  1. public static SaveResult SaveFillup (Fillup fillup, Action errorCallback)
  2. {
  3. var lastReading =
  4. Car.FillupHistory.Count> 0?
  5. Car.FillupHistory.First (). OdometerReading:
  6. Car.InitialOdometerReading;
  7. fillup.DistanceDriven = fillup.OdometerReading - lastReading;
  8. var saveResult = new SaveResult ();
  9. var validationResults = fillup.Validate ();
  10. if (validationResults.Count ()> 0)
  11. {
  12. saveResult.SaveSuccessful = false ;
  13. saveResult.ErrorMessages = validationResults;
  14. }
  15. else
  16. {
  17. Car.FillupHistory.Insert (0, fillup);
  18. saveResult.SaveSuccessful = true ;
  19. SaveCar ( delegate {
  20. saveResult.SaveSuccessful = false ;
  21. errorCallback (); });
  22. }
  23. return saveResult;
  24. }
* This source code was highlighted with Source Code Highlighter .

The CarDataStore.SaveFillup method accepts a Fillup object for saving and an Action delegate for error handling. This method begins by subtracting from the current odometer reading the previous reading value and stores the results in the Fillup.DistanceDriven property so that the check of the molgs will be carried out in this class. Next, the SaveFillup method calls the Fillup.Validate method and saves the results to a special SaveResult object, which it returns to the caller. If validation errors do not occur, the SaveFillup method saves the fuel and vehicle data. Finally, the SaveFillup method returns a SaveResult object.

Note:
Silverlight for Windows Phone provides basic, built-in binding validation through the Binding.ValidatesOnExceptions property. However, versions of controls for Windows Phone do not provide validation templates. To support validation, one option is to provide your own validation templates for controls.

The following code snippet shows what happens when the user clicks the Save button on the refueling information page.
  1. private void SaveButton_Click ( object sender, EventArgs e)
  2. {
  3. CommitTextBoxes ();
  4. SaveResult result = CarDataStore.SaveFillup (currentFillup,
  5. delegate {
  6. MessageBox.Show ( "There is not enough space for your phone to" +
  7. "save your fill-up data. Free space and try again." ,
  8. "Warning" , MessageBoxButton.OK);
  9. });
  10. if (result.SaveSuccessful)
  11. {
  12. Microsoft.Phone.Shell.PhoneApplicationService.Current
  13. .State [ "FillupSaved" ] = true ;
  14. cacheChanges = false ;
  15. NavigationService.GoBack ();
  16. }
  17. else
  18. {
  19. string errorMessages = String .Join (
  20. Environment.NewLine + Environment.NewLine,
  21. result.ErrorMessages.ToArray ());
  22. if (! String .IsNullOrEmpty (errorMessages))
  23. {
  24. MessageBox.Show (errorMessages,
  25. "Warning: Invalid Values" , MessageBoxButton.OK);
  26. }
  27. }
  28. }
* This source code was highlighted with Source Code Highlighter .

This code calls the SaveFillup method, passing it a callback error, as described above. This delegate simply displays an error condition in a message box. The method then checks if there are validation errors, the event handler displays them in a message box.

If the save was successful, the SaveButton_Click method uses the application level status dictionary to store the value indicating that the new refueling information has just been saved. Then the method goes back, although it first sets the flag to prevent caching, as described earlier in the “Restoring an application after deactivation” section. After completing the transition, if SummaryPage detects that the value is stored in the state dictionary, the page displays the first item of the summary information so that the user can immediately see the results of the refueling. Otherwise, the SummaryPage will automatically be displayed depending on which item of the summary information was displayed before moving to the FillupPage .

Only one method of validation is described here, but there are many others besides it.

Check for unsaved changes when going back


By default, pressing the back button takes you backward. If you have a data entry page with unsaved changes, and the user clicks the "Back" button, you must inform the user. To implement this behavior, you can overload the PhoneApplicationPage.OnBackKeyPress method and check for unsaved changes. If there are unsaved changes, you can notify the user and, possibly, cancel the transition back.

Certification Requirement:
To support a consistent user experience, the back button should only be used to go back in the app.

To fulfill this requirement, you should not use the OnBackKeyPress overload to cancel the reverse transition and completely change the behavior of the Back button. The ability to cancel back navigation is provided in order to perform the necessary navigation-related operations, such as waiting for the user to confirm deletion of data.
The Fuel Tracker application has two data entry pages: CarDetailsPage and FillupPage . If there are unsaved changes on these pages and the user clicks "Back", a message appears on the screen requiring confirmation, which allows you to cancel the backward transition. The following image shows an example of a message that requires confirmation.

image

Interface Design Recommendation:
Application actions that overwrite or delete data or cannot be reversible should have a Cancel button.
The following code snippet shows how the Fuel Tracker application overloads the PhoneApplicationPage.OnBackKeyPress method to display a message that requires confirmation if there is unsaved data.
  1. protected override void OnBackKeyPress (
  2. System.ComponentModel.CancelEventArgs e)
  3. {
  4. base .OnBackKeyPress (e);
  5. // If there are no changes, do nothing.
  6. if (! hasUnsavedChanges) return ;
  7. var result = MessageBox.Show ( "You are about to discard your" +
  8. "changes. Continue?" , "Warning" , MessageBoxButton.OKCancel);
  9. if (result == MessageBoxResult.OK)
  10. {
  11. // Normal navigation; there's no need to handle tombstoning,
  12. // so set the flag and the current cache.
  13. cacheChanges = false ;
  14. this .State.Remove (CURRENT_FILLUP_KEY);
  15. }
  16. else
  17. {
  18. // Cancel backward navigation.
  19. e.Cancel = true ;
  20. }
  21. }
* This source code was highlighted with Source Code Highlighter .

First, this method checks if there are unsaved changes. If not, then the method does nothing, and the transition back is automatic. Otherwise, the method displays a message that requires confirmation. If the user clicks "OK" to continue the transition back, then the page does not need to handle tombstoning, so the method performs some actions and performs the transition back. If the user clicks Cancel, the transition back will be canceled.

Here is a link to the next part.
First part

Source: https://habr.com/ru/post/118461/


All Articles