📜 ⬆️ ⬇️

Create an application for Windows Phone 7 from start to finish. Part 10. Converting values, creating data classes

Previous part

In this part you will learn:

Value conversion


You may have noticed that the examples in the previous section show bindings that include the Converter setting. This parameter allows the control to display the values ​​of the bound property in some other format. In these particular examples, the values ​​are displayed using the format of the string specified in the ConverterParameter setting. The following XAML shows Converter and ConverterParameter for Date TextBlock.
< TextBlock Style ="{StaticResource SummaryStyle}" Text ="{Binding Date, Converter={StaticResource StringFormatter}, ConverterParameter=\{0:d\} }" Width ="105" TextWrapping ="Wrap" /> * This source code was highlighted with Source Code Highlighter .
  1. < TextBlock Style ="{StaticResource SummaryStyle}" Text ="{Binding Date, Converter={StaticResource StringFormatter}, ConverterParameter=\{0:d\} }" Width ="105" TextWrapping ="Wrap" /> * This source code was highlighted with Source Code Highlighter .
  2. < TextBlock Style ="{StaticResource SummaryStyle}" Text ="{Binding Date, Converter={StaticResource StringFormatter}, ConverterParameter=\{0:d\} }" Width ="105" TextWrapping ="Wrap" /> * This source code was highlighted with Source Code Highlighter .
  3. < TextBlock Style ="{StaticResource SummaryStyle}" Text ="{Binding Date, Converter={StaticResource StringFormatter}, ConverterParameter=\{0:d\} }" Width ="105" TextWrapping ="Wrap" /> * This source code was highlighted with Source Code Highlighter .
< TextBlock Style ="{StaticResource SummaryStyle}" Text ="{Binding Date, Converter={StaticResource StringFormatter}, ConverterParameter=\{0:d\} }" Width ="105" TextWrapping ="Wrap" /> * This source code was highlighted with Source Code Highlighter .

In general, value converters allow you to bind controls to arbitrary properties, even if the property is of incompatible type or its value is not stored in the desired format. You can implement additional properties of your data objects that simply wrap (wrap) other properties and provide the necessary conversion or formatting for binding purposes. However, it is better not to clutter your data model with code that is only required for individual views (views). By creating value converters, you can encapsulate the conversion code separately from both the data model and the representations that use it.

In the previous examples, the Converter property has the value of the object defined as a resource. The following XAML code creates a new StringFormatter instance and declares it available to the Grid as a StaticResource called "StringFormatter".
  1. < Grid.Resources >
  2. < local: StringFormatter x: Key = "StringFormatter" />
  3. </ Grid.Resources >
* This source code was highlighted with Source Code Highlighter .

In order to perform value conversions, the StringFormatter class must implement the IValueConverter interface, which defines the Convert and ConvertBack methods. Because the binding is OneWay, the StringFormatter class only needs the Convert method, which formats the string using the String.Format method, as shown in the following code:
  1. public class StringFormatter: IValueConverter
  2. {
  3. public object Convert ( object value , Type targetType, object parameter,
  4. CultureInfo culture)
  5. {
  6. // Retrieve the format.
  7. var formatString = parameter as string ;
  8. if (! string .IsNullOrEmpty (formatString))
  9. {
  10. return string .Format (culture, formatString, value );
  11. }
  12. // If the format string is null or empty, simply call ToString ()
  13. // on the value.
  14. return value .ToString ();
  15. }
  16. // No need to implement converting back on a one-way binding
  17. public object ConvertBack ( object value , Type targetType,
  18. object parameter, CultureInfo culture)
  19. {
  20. throw new NotImplementedException ();
  21. }
  22. }
* This source code was highlighted with Source Code Highlighter .

The following image shows the behavior when the StringFormatter converter is not used, and, accordingly, when it is used.
')
image

Another example of a value conversion in the ZeroFormatter.cs file. The Fuel Tracker application uses the ZeroFormatter class to display null values ​​as empty strings. This is useful when binding to objects for which users specify values ​​during data entry. For example, integer properties for new objects are set to zero by default, but controls that display these properties must appear empty before the user enters a value.

Creating data classes


Before you can display data in the user interface, you usually need to organize the data into classes.
You can create data classes in various ways. For example, in applications that interact with external data sources, you can use data classes generated using the ADO.NET Entity Framework or WCF Data Services. For simple applications, you can use simple old CLR object classes (POCO - plain old CLR object), which are created manually. These classes often contain little more than properties to store data and a small change notification code.

Tip:
In the event that your application is not self-contained and self-contained, and it is expected to be further developed, it can benefit from the use of a more modern architecture. This usually means encapsulating additional types of code into separate classes or layers in order to minimize the side effects of future changes, ease the debugging process, and also implement unit-testing support.

In XAML applications, a common template for use is Model-View-ViewModel (MVVM). Since the Fuel Tracker application is relatively simple, it does not implement the MVVM pattern, and this article does not describe this model in more detail. For more information on this topic, you can click on the following links: Implementing the Windows Phone Development Guide .

Fuel Tracker is a simple application and uses CLR objects for its data model. The following image shows the Car , Fillup , and CarDataStore classes for the Fuel Tracker application. There are other classes in the app, but these are basic.

image

Car class stores information about the user's car. The Fillup class contains information about each refueling. All Car and Fillup properties are simple value types, with the exception of the Car.FillupHistory property, which is a collection of refills. The CarDataStore class is a general class that contains methods for saving and loading Car and Fillup data to which the user interface of the pages is attached.

Notice that the Car and Fillup classes implement the INotifyPropertyChanged interface. INotifyPropertyChanged is required for most types of data binding in order to always display the user interface up to date. For more information about data binding, see the previous section, Data Mapping.

Change Notification


Applications use data classes in a variety of ways, including: directly mapping their values, allowing users to change values; using values ​​to calculate other values ​​or changing the state of the user interface. In all these cases, the application must be updated when the property values ​​change. For this reason, it is very convenient to implement change notifications for classes.

In order to allow change notifications, classes must implement the INotifyPropertyChanged interface. Change notification is not always necessary, but it is required in many common scenarios, so it is useful to implement INotifyPropertyChanged just in case. The implementation is simple, as shown in the following code example from Car.cs.
  1. public class Car: INotifyPropertyChanged
  2. {
  3. private string _name;
  4. public string Name
  5. {
  6. get { return _name; }
  7. set
  8. {
  9. _name = value ;
  10. NotifyPropertyChanged ( "Name" );
  11. }
  12. }
  13. // ... other properties ...
  14. public event PropertyChangedEventHandler PropertyChanged = delegate {};
  15. private void NotifyPropertyChanged ( string propertyName)
  16. {
  17. PropertyChanged ( this , new PropertyChangedEventArgs (propertyName));
  18. }
  19. }
* This source code was highlighted with Source Code Highlighter .

When you bind objects to the user interface, the binding engine subscribes to the event of a PropertyChanged object so that it can update all bound controls whenever a property value changes. Note that the PropertyChanged event is initialized by an empty delegate to ensure that it is never null . This allows the NotifyPropertyChanged method to raise an event without having to first check whether it has any subscribers. If you are not familiar with this interface, you can simply repeat this pattern for implementation.

Change notification for collection


To fully support change notifications for a collection, such as adding or removing from a collection, the collection must implement the INotifyCollectionChanged interface. ObservableCollection is a dynamic collection of data provided by the framework, it may contain generic types. The ObservableCollection implements both INotifyPropertyChanged and INotifyCollectionChanged, so the easiest way to support change notification for a collection is to simply place your items in an ObservableCollection. Items that you put in an ObservableCollection must implement INotifyPropertyChanged if you want to also receive property change notifications for objects in the collection.

The Fuel Tracker application uses one collection called FillupHistory , containing information about all the refills. FillupHistory is an ObservableCollection of Fillup objects, as shown in the following code example from Car.cs.
  1. public ObservableCollection <Fillup> FillupHistory
  2. {
  3. get { return _fillupHistory; }
  4. set
  5. {
  6. _fillupHistory = value ;
  7. if (_fillupHistory! = null )
  8. {
  9. _fillupHistory.CollectionChanged + = delegate
  10. {
  11. NotifyPropertyChanged ( "AverageFuelEfficiency" );
  12. };
  13. }
  14. NotifyPropertyChanged ( "FillupHistory" );
  15. NotifyPropertyChanged ( "AverageFuelEfficiency" );
  16. }
  17. }
* This source code was highlighted with Source Code Highlighter .

This code also demonstrates how to use change notifications to interact with other properties. Changing the history of refills (fill-up history) or any elements inside will affect the calculation of average fuel efficiency (average fuel efficiency). Therefore, setting the FillupHistory property causes a change notification for itself and for the AverageFuelEfficiency property. In addition, by setting the property associated with the CollectionChanged event handler for a new collection, you will receive a change notification for AverageFuelEfficiency whenever an item in the collection is added, deleted, or changed.

Next part

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


All Articles