Xamarin Forms: Avoiding data binding in ListViews

FacebookTwitterGoogle+Share

Source code for the completed version of this sample is available on GitHub here: https://github.com/billreiss/xamlnative/tree/master/XamarinForms/ListViewNoBinding

Note: This post was inspired by Jason Smith’s Evolve session discussing performance in Xamarin Forms. Jason gives a lot of great information and I highly recommend watching the video. At one point he mentions reducing the amount of data binding, and I was asked recently how you would do that in a ListView, so here is my solution. I am not a fan of over optimizing, so I would only use this if you are seeing performance issues in your ListView (scrolling hiccups etc) along with the other techniques Jason discusses in the video.

Data binding is great. It is convenient, promotes good practices of separation of concerns, reduces glue code, and many other things. It’s also not free. It’s actually pretty expensive since it relies on Reflection, and has memory and CPU performance implications. Hopefully someday Xamarin Forms will support compiled bindings like the Universal Windows Platform. Compiled bindings remove a lot of these issues, if you are doing native UWP development, definitely give them a look.

These negative impacts can multiply when displaying items in a ListView. There is a lot more data binding going on, since it is once per row. There are also row reuse strategies that may lead to more data binding while the user scrolls, which is when you want the best performance.

There are clear trade-offs here, but let’s assume you want the optimum performance even if it’s a bit more code intensive. How would you even do this inside a ListView item template? One way is to embed a ContentView and set its display properties in code.

Let’s say we have an app displaying a list of ItemViewModel as a ListView. Here is the ItemViewModel:

 

public class ItemViewModel
{
    public int Id { get; set; }
    public string Text { get; set; }
}

The XAML for displaying the ListView (using bindings) looks like this:

  <ListView x:Name="listView" CachingStrategy="RecycleElement">
    <ListView.ItemTemplate>
      <DataTemplate>
        <ViewCell>
          <Grid>
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="50"/>
              <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Label x:Name="idLabel" Text="{Binding Id}"/>
            <Label x:Name="textLabel" Grid.Column="1" Text="{Binding Text}"/>
          </Grid>
        </ViewCell>
      </DataTemplate>
    </ListView.ItemTemplate>
  </ListView>

And the code behind to populate the ListView:

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        listView.ItemsSource = GetItems();
    }

    public List<ItemViewModel> GetItems()
    {
        var lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum".Split(' ');
        var items = new List<ItemViewModel>();
        for (int i = 0; i < 500; i++)
        {
            items.Add(new ItemViewModel() { Id = i, Text = lorem[i % lorem.Length] });
        }
        return items;
    }
}

To populate some sample code, I’m just taking a lorem ipsum string and assigning a word to each row, restarting when the words run out. This is what the app looks like when running:

image

Now data binding two properties may not be a major performance hit, but consider more complex item templates. So how could we accomplish the same results without data binding? Where would the code go to pump the data into the item view cell? One way would be with a custom renderer, but then you would need to implement this on each platform. Another way is to create a custom ViewCell control, that is what we will do here.

First let’s refactor what we currently have, still using data binding, but moving the XAML for the ViewCell to its own class. In the Portable project, I can add a new Forms Xaml Page and call it ItemCell.

image

This generates a XAML file for layout and a code behind file. In ItemCell.xaml, we can modify it as follows, copying in the XAML from the item template and changing the outer type to ViewCell:

<?xml version="1.0" encoding="utf-8" ?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ListViewNoBinding.ItemCell">
  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="50"/>
      <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Label x:Name="idLabel" Text="{Binding Id}"/>
    <Label x:Name="textLabel" Grid.Column="1" Text="{Binding Text}"/>
  </Grid>
</ViewCell>

And for the ItemCell.xaml.cs code behind we need to tell it to inherit from ViewCell so that it matches the XAML file :

public partial class ItemCell : ViewCell

Finally we can change the MainPage to use this custom ViewCell:

<ListView x:Name="listView" CachingStrategy="RecycleElement">
  <ListView.ItemTemplate>
    <DataTemplate>
      <local:ItemCell/>
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>

Running this again, we get the same result as before, but now we have somewhere to put some code. Let’s see how we can remove the bindings in the ItemCell and update the view manually. In ItemCell.xaml, remove the bindings from the Label.Text properties:

<Grid>
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="50"/>
    <ColumnDefinition Width="*"/>
  </Grid.ColumnDefinitions>
  <Label x:Name="idLabel" />
  <Label x:Name="textLabel" Grid.Column="1" />
</Grid>

Now the other thing we need to do is update the text when the data changes. Even though we aren’t using data binding ourselves, the BindingContext of the ViewCell is set to an item in the list. Since this ListView recycles cells, a limited number of ItemCell objects are created and they are reused, each time the BindingContext is set to the current row that the cell is being used for. So to avoid data binding inside the cell, and to set the text ourselves, we can override the BindingContextChanged method on the ItemCell, and inside there set the text.

public partial class ItemCell : ViewCell
{
    public ItemCell()
    {
        InitializeComponent();
    }

    protected override void OnBindingContextChanged()
    {
        base.OnBindingContextChanged();
        var itm = BindingContext as ItemViewModel;
        if (itm != null)
        {
            idLabel.Text = itm.Id.ToString();
            textLabel.Text = itm.Text.ToString();
        }
        else
        {
            idLabel.Text = textLabel.Text = "";
        }
    }
}

Now if you run the app again, it should behave just as before, but we now have full control over when the data is refreshed and by doing it manually we are saving on Reflection costs. Note this assumes the data is read only, we are not listening for property changed events on the Id and Text properties of the ViewModel. You could add this by wiring and unwiring the PropertyChanged event in the OnBindingContextChanged method. I will cover this in the next post.

One last thing is it can be interesting to set breakpoints or add debug statements to the constructor and OnBindingContextChanged of the ItemCell. It can give you some insight into how row caching works in a Xamarin Forms ListView.

TweetWrap: For the TweetDeck desktop lovers running Windows 10

Download TweetWrap for Windows 10 here: https://www.microsoft.com/en-us/store/apps/tweetwrap/9nblggh4nbpp

A couple of weeks ago I saw a notice that TweetDeck for Desktop was going to be discontinued on April 15 and even if it was already installed it would stop working. I have tried several other Twitter apps for Windows 10, including the official Twitter app, Tweetium, Tweet It!, and more, but each falls short for my needs in one way or another. The biggest reason in these apps is because I value my screen real estate, and even at the smallest font and scale of the app, I can’t seem to fit as much information as I want to. Could be just me, if you are happy with your twitter experience, this isn’t for you.

Personally every time over the past year or so that I reset my laptop or got a new one, the first thing I did was go find an installer for an old version of TweetDeck so I could install it. This ended when Twitter disabled even that option on April 15.

I noticed that the TweetDeck desktop app and the web site are extremely similar. Actually I would be shocked if the old desktop app wasn’t just a wrapper for the web site, so I thought why can’t I just wrap it myself in an app? That was when TweetWrap was born.

Note: After I created TweetWrap, Simon Bisson (@sbisson on twitter) told me about the TwitDuck app written by Mark Allan (@markxa on twitter) which was doing basically the same thing. I was far enough along in my app that I felt I should publish it anyway.

I have no goal or motive with this app except having a twitter client on Windows 10 desktop that I can use. So you may say why not just use the web version? Again I value my screen real estate and I don’t like having and address bar or tabs taking away from that when I just want an app. Also it’s easier to launch and manage.

I have also published the source code to GitHub with a hope that others will contribute to make it better. You can get the source code here: https://github.com/billreiss/TweetWrap 

The main glaring issues I see with the app at this point are that the scrollbars show up on hover, instead of either being hidden always or showing always like they are when you run the TweetDeck site in the browser. I don’t know why this happens and fixing this would be a huge improvement. Also if there was a way to enable push notifications I think that would be something a lot of users would want. Let’s work together to provide a quality Twitter experience for Windows 10 users.

Note: Yes I know there may be other options for this, I think Chrome has a way to launch a web site without all the other stuff. Please post details on other options to the comments section. Thanks!

Xamarin Forms: A Simple Circular Progress Control

Source code for this sample is available on GitHub here: https://github.com/billreiss/xamlnative/tree/master/XamarinForms/CircularProgress

Recently in a Xamarin Forms app I needed to show progress of an operation as a circle that would fill itself in as the progress completed. Something like this:

10uk8w_thumb.gif

 

The built in Xamarin Forms Progress control is a line, and can’t easily be changed to show a circle. I looked at some different options, including finding a native control for the different platforms and render them using custom renderers, or to use a component for the component store, or something on NuGet, but didn’t really find anything suitable. So I did my own thing, and I’m really happy with how it turned out, then I wanted to share what I came up with. Feel free to riff on this design and come up with your own variations.

First I thought about vector graphics. There doesn’t seem to be any built in support for this in Xamarin Forms, and I would have to do custom renderers for each platform. Then I started thinking about using images to do the animation. It certainly would be better with vector graphics, but maybe this would be “good enough”. Then I considered how many different images I would need to make it look smooth. A dozen? More? Something like this?

th_thumb.jpg

 

* this is a copy of a circle sprite sheet published here

I was able to come up with a solution that uses two images. Actually two instances each of two images, so four images total. Combining these images with rotation and changing which images show in front of each other, we can show any percentage of progress smoothly.

The two images we need are semicircles, one for completed progress, and one for incomplete progress (you can download them here https://github.com/billreiss/xamlnative/tree/master/XamarinForms/CircularProgress/CircularProgress/CircularProgress.Droid/Resources/drawable):

progress_pending_thumb.png progress_done_thumb.png

 

These are two semicircles saved as PNG format with transparent backgrounds. First let’s consider the case between 0 and 50% complete. Let’s call the first image the “completed” image, and the second is the “pending” image. We will need three images to handle the case from 0 to 50%, one “completed” and two “pending” images.

The completed image (in the code we’ll call it “progress1”) looks like this:

image.png

 

Then on top of that we display a “pending” image (in the code we’ll call it “background1” looks like this:

image.png

 

With these directly on top of each other, the darker blue circle is completely covered and not visible.

Then we add another copy of this image, but rotated 180 degrees:

image.png

 

This makes a solid light blue circle, which is what we expect to see at 0% complete.

image.png

Then if we rotate the instance of “background1”, we will expose part of the dark blue semicircle. Part of “background1” will overlap “background2”, but that’s ok, since they are the same color you won’t be able to tell.

image.png

 

Once we get to 50%, then things change a little. Now we need 2 instances of the dark blue image, one rotated 180 degrees, and one instance of the light blue image. This light blue image will reveal portions of the 180 degree rotated dark blue image similar to how the less than 50% scenario worked, and the non-rotated dark blue image needs to be in front of this pending image, and the rotated completed image needs to behind the pending image. So the light blue image is sandwiched between the other two, with the non-rotated image all the way to the front, otherwise as the pending image rotates it would obscure this “half-completed” part of the progress.

Are you totally confused yet? Thankfully it’s actually not a lot of code. We will have a dependency property called “Progress” that goes from 0 to 1 to match how the built-in progress control works. And then when that value changes, we do the right thing showing and rotating images to get the right effect.

IMPORTANT NOTE: If you create your Xamarin Forms project from scratch using the built in Visual Studio template, make sure you use NuGet to update Xamarin Forms to the latest stable version. There was a bug in Xamarin Forms on Windows platforms that would make this code not have the desired result.

Here is the code for the circular progress control:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace CircularProgress
{
    public class CircularProgressControl : Grid
    {
        View progress1;
        View progress2;
        View background1;
        View background2;
        public CircularProgressControl()
        {
            progress1 = CreateImage("progress_done");
            background1 = CreateImage("progress_pending");
            background2 = CreateImage("progress_pending");
            progress2 = CreateImage("progress_done");
            HandleProgressChanged(1, 0);
        }

        private View CreateImage(string v1)
        {
            var img = new Image();
            img.Source = ImageSource.FromFile(v1 + ".png");
            this.Children.Add(img);
            return img;
        }

        public static BindableProperty ProgressProperty =
    BindableProperty.Create("Progress", typeof(double), typeof(CircularProgressControl), 0d, propertyChanged: ProgressChanged);

        private static void ProgressChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var c = bindable as CircularProgressControl;
            c.HandleProgressChanged(Clamp((double)oldValue, 0, 1), Clamp((double)newValue, 0, 1));
        }

        static double Clamp(double value, double min, double max)
        {
            if (value <= max && value >= min) return value;
            else if (value > max) return max;
            else return min;
        }

        private void HandleProgressChanged(double oldValue, double p)
        {
            if (p < .5)
            {
                if (oldValue >= .5)
                {
                    // this code is CPU intensive so only do it if we go from >=50% to <50%
                    background1.IsVisible = true;
                    progress2.IsVisible = false;
                    background2.Rotation = 180;
                    progress1.Rotation = 0;
                }
                double rotation = 360 * p;
                background1.Rotation = rotation;
            }
            else
            {
                if (oldValue < .5)
                {
                    // this code is CPU intensive so only do it if we go from <50% to >=50%
                    background1.IsVisible = false;
                    progress2.IsVisible = true;
                    progress1.Rotation = 180;
                }
                double rotation = 360 * p;
                background2.Rotation = rotation;
            }
        }

        public double Progress
        {
            get { return (double)this.GetValue(ProgressProperty); }
            set { SetValue(ProgressProperty, value); }
        }
    }
}

We also need to put the images in the correct images folder for each platform. For iOS this is the Resources folder, and the Build Action should be set to BundleResource. For Android it’s the Resources/drawable folder and the type is AndroidResource. For the Windows platforms they go in the root folder.

Then to use the control. Assume we have a Xamarin Forms XAML page called MainPage.xaml, we can load the control like this:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CircularProgress.MainPage" 
             xmlns:local="clr-namespace:CircularProgress" BackgroundColor="White">
  <Grid>
    <local:CircularProgressControl x:Name="progressControl" Progress="0" HorizontalOptions="Center" VerticalOptions="Center" WidthRequest="60" HeightRequest="60"/>
  </Grid>
</ContentPage>

And in the code behind we can have a timer that increments the Progress property a bit at a time from 0 to 1 and then starts over:

namespace CircularProgress
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            Xamarin.Forms.Device.StartTimer(TimeSpan.FromSeconds(.02), OnTimer);
        }

        private bool OnTimer()
        {
            var progress = (progressControl.Progress + .01) ;
            if (progress > 1) progress = 0;
            progressControl.Progress = progress;
            return true;
        }
    }
}

Now if you run the app you should see something like the animated image at the top of this post. No custom renderers and it runs on every Xamarin Forms platform!

Visual Studio 2015 Update 2 XAML Debugging Improvements

I felt one of the coolest things in Visual Studio 2015 for XAML developers was the Live Visual Tree Debugger. In the past there were tools like Snoop and XAML Spy which were so very useful but it was exciting to see this kind of functionality built in to Visual Studio.

Visual Studio 2015 Update 2 was announced and released at the BUILD conference last week, and when I installed it (download it here) and ran my app in the debugger, I saw an unexpected surprise. Overlaid on top of the app window was a new set of buttons:

image

The first button is to go to the Live Visual Tree debugger window in Visual Studio, not too interesting.

image

The second button enables selection. This one is really interesting, because when you click on a visual element in your app, it will be selected in the visual tree debugger. This button was already available in Visual Studio in the Live Visual Tree pane, but it’s a lot more convenient to have it right in your app.

image

The third button, “Display Layout Adorners”, I’m not really sure how useful this is, but maybe I’m missing something.

These buttons also exist inside Visual Studio 2015 in the Live Visual Tree pane just like they did before, but there is a new button which lets you turn on and off the toolbar that shows up inside your app.

image

If you are debugging an app with source code, then in the Live Visual Tree pane, beside any element can be resolved to the corresponding XAML file, you’ll see an icon that lets you navigate to the XAML definition:

image

Another thing I think is pretty interesting is that you can debug other XAML based applications even if they aren’t yours. One example of this is the Settings dialog in Windows 10. If you open the settings dialog, and then switch to Visual Studio 2015 Update 2 and you select Debug and then Attach to Process, you should be able to pick to debug SystemSettings.exe:

image

You can do the same with the Edge browser, Groove Music, the Store app, or a bunch of other apps. Most of the built in Windows 10 dialogs use XAML. So if you attach the debugger, you should see the same toolbar show up inside this app:

image

The one difference is that since you don’t have the source code, you won’t see the buttons next to elements that let you navigate to the XAML, but you should still be able to see all of the visual elements in the Live Visual Tree pane.

UPDATE: It works for WPF too! Here is a debugger attached to another instance of Visual Studio (Visual Studio uses WPF for its user interface):

image

By running this tool against other apps you can see how they implemented their XAML and it can give you some ideas for your own app.

XAML 101: All Samples Now on GitHub

I just wanted to let everyone know that I have gone back through all of the XAML 101 series of posts and created projects on GitHub for each of them, and will continue adding samples to GitHub for future posts, as well as keeping older samples up to date as new versions of the UWP SDK are released. You can find the GitHub repository here:

https://github.com/billreiss/xamlnative/tree/master/Xaml101

More posts soon!

I’m Joining Microsoft!

It’s an exciting time in app development using .NET technologies. Today Microsoft announced that they are acquiring Xamarin, and I have some news of my own. I will be joining the Microsoft Premier Developer consulting team, focusing on app development, specifically Universal Windows Platform development and Xamarin.

So what does this mean for this blog? It means a lot of new content and continuation of what I have posted so far. The past few months have left me very little time to focus on the blog, but that should change now. I am excited to continue my XAML 101 series, and also to start talking about XAML in Xamarin Forms and related topics.

Windows 10 development for Absolute Beginners

I’m hoping over time that my XAML 101 series will be an indispensable reference for XAML development on Windows 10 but it isn’t meant to be a comprehensive end to end guide to Windows 10 development, and it will take a while just to get through all I want to with XAML specific topics.

The good news is that there is some other great content out there, and I’ll try to post about these as I find them. Here is the first. Bob Tabor had a couple of very popular series on Windows Phone 8 and 8.1 development for beginners, and now Bob is back working with Microsoft’s Channel 9 with a series of video tutorials on getting started with Windows 10 programming. There are several hours of material here, and in the second half of the series he creates 4 different complete apps from start to finish to reinforce the techniques covered in the first half. You can find the series at the following link:

https://channel9.msdn.com/Series/Windows-10-development-for-absolute-beginners

XAML 101: Two Way Data Binding

Source code for this sample on GitHub: https://github.com/billreiss/xamlnative/tree/master/Xaml101/017_TwoWayBinding

In the last couple of posts we looked at getting started with data binding and then some more options available. In all of these cases the data binding was from the source to the target, or the model to the view, but we haven’t looked at sending data the other way through the binding. Let’s look at that now.

Where you would need two way data binding is when you aren’t in read only mode for your view and you want to edit the data. It could be a text box, a check box, a combo box, a selected item in a list, or one of many other ways to provide input. Let’s look at a couple of these. In MainPage.xaml we will have the following, a lot of it we have already seen, and a couple of new controls:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="auto"/>
        <ColumnDefinition Width="200"/>
    </Grid.ColumnDefinitions>
    <TextBlock Text="Checkbox:"/>
    <TextBlock Grid.Row="1" Text="Checkbox Result:"/>
    <TextBlock Grid.Row="2" Text="TextBox:"/>
    <TextBlock Grid.Row="3" Text="TextBox Result:"/>
    <CheckBox Grid.Row="0" Grid.Column="1" IsChecked="{x:Bind Model.CheckBoxValue,Mode=TwoWay}"/>
    <TextBlock Grid.Row="1" Grid.Column="1" Text="{x:Bind Model.CheckBoxValue, Mode=OneWay}"/>
    <TextBox Grid.Row="2" Grid.Column="1" Text="{x:Bind Model.TextBoxValue, Mode=TwoWay}"/>
    <TextBlock Grid.Row="3" Grid.Column="1" Text="{x:Bind Model.TextBoxValue, Mode=OneWay}"/>
    <Button Grid.Row="4" Content="Submit" Click="{x:Bind Model.ClickHandler}"/>
</Grid>

The CheckBox and TextBox are new, but probably pretty well known and self explanatory. The CheckBox has an IsChecked property, which is a nullable boolean value. Null represents the third state of a checkbox, usually used when a mix of child nodes are true and false. We set the initial value to false, or unchecked. The TextBox has a Text property, which is a string value. Notice the Mode=TwoWay in the bindings. Remember that the mode defaults to OneTime, and so in order to make the data flow back to the model we need to set this to TwoWay.

We also have a couple of TextBlock controls which will show what the current values of the model are. Like in the last example, we have a declaration and instantiation of the MainViewModel object in the MainPage.xaml.cs code behind file:

public sealed partial class MainPage : Page
{

    public MainPage()
    {
        this.InitializeComponent();
    }

    MainViewModel Model = new MainViewModel();

}

And then in the MainViewModel.cs file:

class MainViewModel : INotifyPropertyChanged
{
    bool? checkBoxValue = false;
    string textBoxValue;

    public bool? CheckBoxValue
    {
        get
        {
            return checkBoxValue;
        }

        set
        {
            checkBoxValue = value;
            NotifyPropertyChanged();
        }
    }

    public string TextBoxValue
    {
        get
        {
            return textBoxValue;
        }

        set
        {
            textBoxValue = value;
            NotifyPropertyChanged();
        }
    }

    public MainViewModel()
    {
    }


    public async void ClickHandler(object sender, RoutedEventArgs e)
    {
        var dlg = new Windows.UI.Popups.MessageDialog(string.Format("CheckBox:{0}, TextBox:{1}", CheckBoxValue, TextBoxValue));
        await dlg.ShowAsync();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    } 

}

The ClickHandler method has been updated to show a MessageDialog with the current values. Since the MessageDialog.ShowAsync method is anynchronous, we await the result of this method call. Since we await the result, we need to make the calling method async, which is fine, the Click event  still wires up properly.

If you run this sample, you’ll see something like this:

image

If you click the CheckBox, then you will see the TextBlock representing the CheckBox result change:

image

Now if you enter text into the TextBox, you might think that you should see the changes as you type, but for a TextBox the value doesn’t update until the TextBox loses focus, so you can click anywhere else or on the Submit button, and then you will see the TextBox result field show the current value:

image

and then if you click the submit button, you should see the current values in the model:

image

If you needed the current value of the text in the TextBox as you type, you could wire up the TextChanged event and check what the current value of the Text property is. The assumption is that you normally won’t need that value until you are done typing and click away.

Until next time…

XAML 101: More Data Binding

Sample code for this post on GitHub: https://github.com/billreiss/xamlnative/tree/master/Xaml101/016_MoreDataBinding

In the last post there was a brief introduction to data binding in Windows 10 apps. Let’s build upon this a bit. Previously we defined a string field and a string property in the MainPage.xaml.cs code behind file then used the x:Bind markup extension in the XAML to bind to these values.  This works fine in this case, but it is generally considered a best practice to separate your views from your data. Let’s add a new class to the last sample and call it MainViewModel.cs. Here is the body of that class:

class MainViewModel
{
    public string FieldBindingText = "Here is a Field Binding";

    public MainViewModel()
    {
        this.PropertyBindingText = "This is a Property Binding";
    }

    public string PropertyBindingText
    {
        get;
        set;
    }
}

Then in the MainPage.xaml.cs file, we can create an instance of this model class:

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }

    MainViewModel Model = new MainViewModel();
}

And then finally in the MainPage.xaml we can bind the values like this:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel>
        <TextBlock Text="{x:Bind Model.FieldBindingText}"/>
        <TextBlock Text="{x:Bind Model.PropertyBindingText}"/>
    </StackPanel>
</Grid>

Notice the small difference from the last sample, where now we are binding to Model.FieldBindingText instead of just FieldBindingText. This is because we have a field named Model in the MainPage.xaml.cs code behind and then we can reference sub-elements using dot notation.

Running this gives us the same result as before:

image

Now after the TextBlock in the XAML, let’s add a Button we can click:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel>
        <TextBlock Text="{x:Bind Model.FieldBindingText}"/>
        <TextBlock Text="{x:Bind Model.PropertyBindingText}"/>
        <Button Content="Click Me!" Click="Button_Click"/>
    </StackPanel>
</Grid>

Notice the Button has a Content property and not a Text property like a TextBlock has. This is because the button content can be just about anything. More about that in future posts. There is a click event specified here, so we need to implement this in the MainPage.xaml.cs file:

private void Button_Click(object sender, RoutedEventArgs e)
{
    Model.PropertyBindingText = "Clicked!";
}

Notice that the button’s click even changes the value of Model.PropertyBindingText. If we run the app, we get this:

image

And if we click the button, what will happen? You might expect the text of the second TextBlock to change. Actually what happens is nothing. There are a couple of reasons for this. The first is that there are a few data binding modes available, and the default is OneTime. One time binding means that the value is evaluated when the page is loaded and never again. This is the default because it is the most efficient and fast, and so unless you need a different mode for some reason XAML tries to help you write fast apps.

Other options are OneWay and TwoWay. A one way binding only goes from the source to the target, and  any changes to the target aren’t reflected in the source. With a two way binding, data flows in both directions.

NOTE: In other XAML platforms like WPF, the default is OneWay or in some cases TwoWay depending on the control.

Let’s change the TextBlock binding to a OneWay binding.

<TextBlock Text="{x:Bind Model.PropertyBindingText, Mode=OneWay}"/>

This however is still not enough. It’s not enough for the source value to change. We also need to notify XAML that the value has changed. This is done through the INotifyPropertyChanged interface. The INotifyPropertyChanged interface has a single event defined in it, an event we need to fire when the property changes. Here is a refactored MainViewModel.cs that implements INotifyPropertyChanged:

class MainViewModel : INotifyPropertyChanged
{
    public string FieldBindingText = "Here is a Field Binding";

    public MainViewModel()
    {
        this.PropertyBindingText = "This is a Property Binding";
    }

    string propertyBindingText;
    public string PropertyBindingText
    {
        get
        {
            return propertyBindingText;
        }
        set
        {
            propertyBindingText = value;
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("PropertyBindingText"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Now if you run it again and click the button you should see this:

image

It is so common to have to raise the property changed event that most helper frameworks like the great MVVM Light framework created by Laurent Bugnion provide a base class for this with a simple method call to raise the property changed event. In our MainViewModel class we can add the following method:

private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
} 

Notice the [CallerMemberName] attribute. This takes the name of the caller as the property name if it’s not specified. This keeps us from having to use hard coded strings when calling the notify property changed method. Hard coded strings can lead to problems when renaming and refactoring, and can also be easily mistyped. Then to use this method, the PropertyBindingText property can be changed to look like this:

public string PropertyBindingText
{
    get
    {
        return propertyBindingText;
    }
    set
    {
        propertyBindingText = value;
        NotifyPropertyChanged();
    }
}

And if you run the app again you should see the same result as before.

So we’re doing a good job of separating the view from the data and linking them together with data binding, but there is one thing that isn’t in the model and that’s the click event handler. It’s generally not considered good design to call into the model directly from the view, but in some cases it makes sense to do it. Here we can easily fix this with xBind by moving the click handler to the model as well.

Remove the event handler from MainPage.xaml.cs and then add this to MainViewModel.cs:

public void ClickHandler(object sender, RoutedEventArgs e)
{
    PropertyBindingText = "Clicked!";
}

And the XAML changes to this:

<Button Content="Click Me!" Click="{x:Bind Model.ClickHandler}"/>

Running this gives us the same result, but the view has nothing special in it except a declaration and instantiation of the MainViewModel class, and all of our data and logic is in one place. If you don’t need the sender and the args in the method that handles the event, you can make it a method without arguments like this and it will work fine:

public void ClickHandler()
{
    PropertyBindingText = "Clicked!";
}

Note that you may need to rebuild all on the solution for it to regenerate the bindings. Next time we’ll dig even deeper into data binding.