Data binding (Android versus Windows Store apps)

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

In this article, you'll learn how to bind to data sources in Windows Store apps for Windows 8.

In data-driven apps, developers often write a lot of boilerplate code to map business objects to the way that these objects' data is viewed by end users. The model-view-view model (MVVM) pattern makes it easier to code these mappings by flattening the business objects' sometimes complex hierarchies into view model data sets that can be bound more easily to UI elements. To explore this concept, here's a simple UI mockup for an example learning management app.

The corresponding business object model looks like this.

The Subject class has a one-to-one association with the Image class and a one-to-many relationship with the Task class. This business object model helps in coping with the complexity of the application by encapsulating business rules through strongly-typed abstractions. However, this model requires some adaptation before it can be bound to controls in the UI. While writing code for displaying business object model in a flat UI can sometimes be tedious and boring, if the developer doesn't flatten the business object model, it can lead to inefficient code. Binding business objects to UI in Windows Store apps is easier with flatter business object models.

Here is a view model class that comes close to flattening the business object model.

Note that this view model adds a prefix to some of the fields, such as SubjectName, to avoid name collisions, such as with the Task class's Name field.

Windows 8 implementation

You can see how Windows 8 helps with binding the view model to the UI markup through the following code snippets.

Step 1: Create the markup

<Page x:Class="LearningMgmt.MainPage">
    <ListView Name="subjectListView" ItemsSource="{Binding}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <Image x:Name="tnail" Source="{Binding ThumbnailUri}"/>
                    <TextBlock Text="{Binding SubjectName}"/>
                    <TextBlock Text="{Binding TasksCompleted}"/>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView> 
</Page>

Notice that the preceding markup uses XAML data binding language, which specifies that the data binding expressions to be enclosed by curly brackets. The data binding context flows top-down from the container to the contained objects. The <ListView> element's ItemSource="{Binding}" attribute indicates that the ListView control should expect data from its DataContext property. The DataContext property is set inside the code-behind file discussed in Step 3 later in this article. The Image and TextBlock controls that display the content are set by using the field names of the LearningSubject class, which is covered in the next step.

Step 2: Create the view model

public class LearningSubject 
{ 
    public int SubjectID { get; set; } 
    public string SubjectName { get; set; } 
    public string SubjectDesc { get; set; } 
    public string ThumbnailUri { get; set; } 
    public int TasksCompleted { get; set; } 
} 

public class SubjectDB 
{ 
    public static ObservableCollection<LearningSubject> GetSubjectViewModel(); 
    {
        ObservableCollection<LearningSubject> subjects = 
            new ObservableCollection<LearningSubject>();

        // Build the view model from the data returned
        // from procesing the business object model on the server. 
    } 
}

Composition of the view model code is not shown here, as we're not concerned with that right now. However, it is worth mentioning that Language Integrated Query (LINQ) expedites the process of automatically populating view model objects directly from the messaging/business model object collections.

Step 3: Attach data fields to the ListView

After implementing the view model, the XAML file's code-behind simply sets the ListView control's DataContext property.

public sealed partial class MainPage : Page    
{    
    public MainPage() 
    { 
        InitializeComponent();    
        subjectListView.DataContext = SubjectDB.GetSubjectViewModel(); 
    } 
}

All of the boilerplate code is encapsulated in the data binding language that is the part of the XAML implementation. The data binding language can be used for binding not only the data but also the app's static and dynamic resources. Using the ObservableCollection class ensures that the data bound to the ListView controls is automatically refreshed as needed.

Android implemenation

Here are similar steps to implement the UI for Android apps. The Android layout XML doesn't support data binding language similar to XAML. Because of this, the boilerplate code that maps the business object model to UI controls is largely unavoidable.

Step 1: Create the markup

The markup creation effort doesn't vary much between Windows Store apps and Android apps. Note that for readability, most of the attributes are removed, except for the id attribute. Also, to use an ArrayAdapter for data binding, this markup must contain a <ListView> element with the reserved name list, which is defined at the system level.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android">
    <ListView android:id="@android:id/list"/>
    <LinearLayout android:id="@+id/linearLayout1" android:orientation="horizontal">
        <ImageView android:id="@+id/tnail"/>
        <TextView android:id="@+id/subjectName"/>
        <TextView android:id="@+id/tasksComplete"/>
    </LinearLayout> 
</RelativeLayout>

Step 2: Create the view model

Since our focus here is data binding, let's ignore the code that's needed for mapping the data from server or local storage to the view model object. The code here is fairly similar to that in Windows Store apps.

public class LearningSubject {
 
    private int subjectId; 
    private String subjectName; 
    private String subjectDesc; 
    private String thumbnailUri; 
    private int tasksComplete; 

    public LearningSubject() { super(); } 

    public LearningSubject (int id, 
                            String name, 
                            String desc, 
                            String uri, 
                            int tasksComplete) {
        this.subjectId = id; 
        this.subjectName = name; 
        this.subjectDesc = desc; 
        this.thumbNailUri = uri; 
        this.tasksComplete = tasksComplete; 
    } 

    public int getSubjectId() { 
        return this.subjectId; 
    } 

    public String getSubjectName() { 
        return this.subjectName; 
    } 

    public String getSubjectDesc() { 
        return this.subjectDesc; 
    } 

    public String getThumbNailUri() { 
        return this.thumbNailUri; 
    }
 
    public int getTasksComplete() { 
        return this.tasksComplete; 
    } 
}

public class SubjectDB {
 
    public static ArrayList<LearningSubject> GetSubjectViewModel() { 
        ArrayList<LearningSubject> subjects = new ArrayList<LearningSubject>(); 
        
        // Build the view model from the data returned   
        // from processing the business object model on the server.
  
        return subjects; 
    } 
}

Note that in Windows Store apps, generating collections that are ready for data binding avoids a lot of boilerplate code that maps fields from a data source to a view model.

Step 3: Create the adapter for data binding

In this Android implementation, a child object of the ArrayAdapter is created for mapping the view model to the UI. This is needed because of the lack of support for declarative data binding support in the XML markup.

public class LearningSubjectAdapter extends ArrayAdapter<LearningSubject> { 
    private Context _context; 
    private int _layoutResourceID;
 
    public LearningSubjectAdapter(Context context, 
                                  int layoutResourceId, 
                                  List<LearningSubject> subjects) { 
        super(context, layoutResourceId, subjects); 
        this._context = context; 
        this._layoutResourceID = layoutResourceId; 
    }
 
    @Override 
    public View getView(int position, 
                        View convertView, 
                        ViewGroup parent) { 
        View displayRow = convertView;   
        
        if (displayRow == null) { 
            LayoutInflater inflater = ((Activity)_context).getLayoutInflater(); 
            displayRow = inflater.inflate(_layoutResourceID,parent, false); 
        } 

        TextView tvSubject = (TextView)displayRow.findViewById(R.id.subjectName); 
        TextView tvDesc = (TextView)displayRow.findViewById(R.id.tasksComplete); 
        ImageView iView = (ImageView)displayRow.findViewById(R.id.tnail); 
        LearningSubject ls = (LearningSubject)getItem(position); 
        tvSubject.setText(ls.getSubjectName()); 
        tvDesc.setText(ls.getSubjectDesc()); 
        iView.setImageURI(Uri.parse(ls.getThumbNailUri())); 
        return displayRow; 
    }
}

Step 4: Attach data fields to the ListView

Attaching the adapter along with the view model to the UI for data binding is similar to Step 3 of the Windows 8 implementation.

public class SubjectListActivity extends ListActivity {   
    
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_subject_list);      
        LearningSubjectAdapter sa =  
            new LearningSubjectAdapter(
                this,R.layout.activity_subject_list,SubjectDB.GetSubjectViewModel());      
        this.setListAdapter(sa); 
    } 
}