Hands-On Mobile Development with .NET Core
上QQ阅读APP看书,第一时间看更新

Data-binding essentials

The simplest data binding in Xamarin.Forms is comprised of the path of the property we want to link to the current view property. In this type of declaration, we assume that the BindingContext of the whole and/or the parent view is set to use the target source view model.

If we take a look at the navigation implementation from our ListItemView to ItemView, you will notice that the selected item from the list is set as the binding context for the ItemView:

private void Handle_ItemTapped(object sender, Xamarin.Forms.ItemTappedEventArgs e)
{
var itemView = new ItemView();
itemView.BindingContext = (ItemViewModel) e.Item;
Navigation.PushAsync(itemView);
}

Once BindingContext is set, we can move on to using the property model of ItemViewModel, given that ItemViewModel is set to trigger PropertyChangedEvent (from INotifyPropertyChanged) for the Title property:

<Label Text="{Binding Title}" FontSize="Large" />

Data binding does not always need to be related to a value property (for example, Text, SelectedItem, and so on) but it can also be used to identify the visual properties of a view.

For instance, the chips that we previously added to ItemView define whether certain features are supported for the currently selected item. Let's assume that we have Boolean properties on the view model side to show or hide these values. The bindings would look similar to the following:

<Label x:Name="Feat1" Text="Feature 1" IsVisible="{Binding HasFeature1}" BackgroundColor="Gray" />
<Label x:Name="Feat2" Text="Feat. 2" IsVisible="{Binding HasFeature2}" BackgroundColor="Lime"/>

In both of these binding scenarios, we are binding a value from the view model to a specific view element. Another valid scenario is where the change of view affects another view (that is, View-to-View binding). Let's assume that, on ItemView, the visibility of our specs depends on the visibility of the label, with x:Name set to Feat1:

<Grid IsVisible="{Binding Path=IsVisible,Source={x:Reference Feat1}}">

It is important to note that, in a real-world project, the View-to-View binding would generally be utilized to reflect the user input in one view on another view. In this example, it would be much more appropriate for the binding to use the same view model property (that is, HasFeature1).

The bindings we have outlined so far do not really depend on any change being reflected in the UI once the visual tree is created. In such a setup, it would be an avoidable performance compromise to listen for any change event on the view model properties. In order to remedy this overhead, we could have set the binding mode to OneTime:

<Label Text="{Binding Title, Mode=OneTime}" FontSize="Large" />

This way, the binding is executed only when the BindingContext changes. If we wanted the changes in the ViewModel (generally referred to as the source) to be reflected in the View (referred to as the target), we could have used OneWay binding. If the direction of this unidirectional data flow provided by the OneWay binding is other way round, we could also utilize OneWayToSource. TwoWay bindings provide the infrastructure to support the bi-directional flow of data.

Although the runtime tries to convert the source type to the target type while establishing a binding, the outcome might not always be desirable (for example, the ToString method of a different type might not provide the correct display value). In these types of situations, developers can resort to using value converters.