Showing posts with label Data Binding. Show all posts
Showing posts with label Data Binding. Show all posts

Wednesday, 15 May 2013

Knockout - What MVVM should have been like in XAML

I've not got a lot of JavaScript programming experience, but I've been learning a bit recently, and decided to try knockout.js. I was pleasantly surprised at how quickly I was able to pick it up. In part this is because it follows a MVVM approach very similar to what I am used to with WPF. I've blogged a three-part MVVM tutorial here before as well as occasionally expressing my frustrations with MVVM. So I was interested to see what the MVVM story is like for Javascript developers. What I wasn't expecting, was to find that MVVM in JavaScript using knockout.js is a much nicer experience than I was used to with XAML + C#. In this post I'll run through a few reasons why I was so impressed with knockout.

Clean Data-Binding Syntax


The first impressive thing is how straightforward the binding syntax is. Any HTML element can have a data-bind property attached to it, and that can hold a series of binding expressions. Here's a simple binding expression in knockout that puts text from your viewmodel into a div:

<div data-bind="text: message" ></div>

To bind multiple properties, you can just add extra binding statements into the single data-bind attribute as follows:

<button data-bind="click: prev, enable: enablePrev">

In XAML the syntax for basic bindings isn't too cumbersome, but a bit more repetitive nonetheless:

<TextBox Text="{Binding Message}" 
Enabled="{Binding EnableEditMessage}" />

Event Binding

One frustration with data binding in XAML is that you can't bind a function directly to an event. So for example when an animation finishes, it would be great to be able to call into a method on our ViewModel with something like this:

<Storyboard Completed="{Binding OnFinished}" />

Sadly, that is invalid XAML, but with knockout.js, binding to any event is trivial:

<div data-bind="click: handleItem"/>

Arbitrary expressions in binding syntax


XAML data binding does allow you to drill down into members of properties on your ViewModel. For example, you can do this:

<TextBlock Text="{Binding CurrentUser.LastName}" />

And it also does give you the ability to do a bit of string formatting, albeit with a ridiculously hard to remember syntax:

<TextBlock Text="{Binding Path=OrderDate, StringFormat='{}{0:dd.MM.yyyy}'}" />

But you can't call into a method on your ViewModel, or write expressions like this

<Button IsEnabled="{Binding Items.Count > 0}" />

However, in knockout.js, we have the freedom to write arbitrary bits of JavaScript right there in our binding syntax. It's simple to understand, and it just works:

<button data-bind="click: next, enable: index() < questions.length -1">Next</button>

This is brilliant, and it keeps the ViewModel from having to do unnecessary work just to manipulate data into the exact type and format needed for the data binding expression. (Anyone who's done MVVM with XAML will be all too familiar with the task of of turning booleans into Visibility values using converters)

Property Changed Notifications


Obviously, knockout needs some way for the ViewModel to report that a property has changed, so that the view can update itself. In the world of XAML this is done via the INotifyPropertyChanged interface, which is cumbersome to implement, even if you have a ViewModelBase class that you can ask to do the raising of the event for you.

private bool selected;
public bool Selected 
{
   get { return selected; }
   set   
   {
        if (selected != value)
        {
            selected = value;
            OnPropertyChanged("Selected");
        }
   }
}

Contrast this with the gloriously straightforward knockout approach which uses a ko.observable:

this.selected = ko.observable(false);

Now selected is a function that you call with no arguments to get the current value, and with a boolean argument to set its value. It's delightfully simple. I can't help but wonder if a similar idea could be somehow shoehorned into C#.

To be fair to the XAML MVVM world, you can alleviate some of the pain with Simon Cropp's superb Fody extension, which allows you to add in a compile time code weaver to automatically raise PropertyChanged events on your ViewModels. I use this on just about all my WPF projects now. It is a great timesaver and leaves your code looking a lot cleaner to boot. However, in my opinion, if you have to use code weaving its usually a sign that your language is lacking expressivity. I'd rather directly express myself in code.

Computed properties


Computed properties can be a pain in C# as well, as you have to remember to explicitly raise the PropertyChanged notification. (Although Fody is very powerful in this regard and can spot that a property getter on your ViewModel depends on other property getters.) Here's an example of a calculated FullName property in a C# ViewModel:

private string firstName;
public string FirstName 
{
   get { return firstName; }
   set   
   {
        if (firstName!= value)
        {
            firstName= value;
            OnPropertyChanged("FirstName");
            OnPropertyChanged("FullName");
        }
   }
}

public string FullName 
{
    get { return FirstName + " " + Surname; }
}

Knockout's solution to this is once again elegant and simple. You simply declare a ko.computed type on your ViewModel:
this.fullName = ko.computed(function() {
     return this.firstName() + " " + this.lastName();
}, this);

Elegant handling of binding to parent and root data contexts


Another area that causes regular pain for me with XAML databinding is when you need to bind to your parent's context or the root context. I think I've just about got the syntax memorized now, but I must have searched for it on StackOverflow hundreds of times before it finally stuck. You end up writing monstrosities like this:

...Binding="{Binding RelativeSource={RelativeSource FindAncestor, 
AncestorType={x:Type Window}}, Path=DataContext.AllowItemCommand}" ...

In knockout, once again, the solution is simple and elegant, allowing you to use $parent to access your parent data context (and grandparents with $parent[1] etc), or $root. Read more about knockout's binding context here.

<div data-bind="foreach: currentQuestion().answers">
    <div data-bind="html: answer, click: $parent.currentQuestion().select"></div>
</div>

Custom binding extensions!


Finally, the killer feature. If only we could add custom binding expressions to XAML, then maybe we could work around some of its limitations and upgrade it with powerful capabilities. Whilst I have a feeling that this is in fact technically possible, I don't know of anyone who has actually done it. Once again knockout completely knocks XAML out of the water on this front, with a very straightforward extensibility model for you to create your own powerful extensions. If you run through the knockout tutorial, you'll implement one yourself and be amazed at how easy it is.


Conclusion

I've only used knockout.js for a few hours (here's what I made) and all I can say is I am very jealous of its powers. This is what data binding in XAML ought to have been like. XAML has been around for some time now, but there have been very few innovations in the data-binding space (we have seen the arrival of behaviours, visual state managers, merged dictionaries, format strings, but all of them suffer from the same clunky, hard to remember syntax). And now we have another subtly different flavour of XAML to learn for Windows Store 8 apps, it seems that XAML is here to stay. Maybe it is time for us to put some pressure onto Microsoft to give XAML data binding power to rival what the JavaScript community seem to be able to take for granted.

Wednesday, 10 November 2010

How to Invoke a Command on the ViewModel by Pressing the Enter Key in a TextBox with Silverlight and MVVM

I recently attempted to upgrade a WPF application to compile for Silverlight as well. One of the many issues I ran into was that in the WPF version, pressing the Enter key while I was in a TextBox caused the OK button I had on the form to be clicked by virtue of the fact that I could set the IsDefault property on the button. However, after porting to Silverlight, that no longer worked, since the IsDefault property is missing..

A web-search revealed that someone had made a “behavior” that allows a specified button to be clicked when you press Enter within a TextBox (available for download here). However, it had one big problem: at the point that the command fired in my ViewModel, the value bound to the textbox contents had not been updated, since the textbox had not lost focus.

The original WPF binding I had an UpdateSourceTrigger ensuring that the ViewModel was always kept up to date with what was in the TextBox:

<TextBox Text="{Binding Answer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

but in Silverlight, the PropertyChanged UpdateSourceTrigger is not available, so  I was left with the following:

<TextBox Text="{Binding Answer, Mode=TwoWay}" />

I decided to make my own EnterKeyCommand binding that would allow you to specify for a TextBox which command on the ViewModel should be run. Here’s the code:

public static class EnterKeyHelpers
{
    public static ICommand GetEnterKeyCommand(DependencyObject target)
    {
        return (ICommand)target.GetValue(EnterKeyCommandProperty);
    }

    public static void SetEnterKeyCommand(DependencyObject target, ICommand value)
    {
        target.SetValue(EnterKeyCommandProperty, value);
    }

    public static readonly DependencyProperty EnterKeyCommandProperty =
        DependencyProperty.RegisterAttached(
            "EnterKeyCommand",
            typeof(ICommand),
            typeof(EnterKeyHelpers),
            new PropertyMetadata(null, OnEnterKeyCommandChanged));

    static void OnEnterKeyCommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        ICommand command = (ICommand)e.NewValue;
        FrameworkElement fe = (FrameworkElement)target;
        Control control = (Control)target;
        control.KeyDown += (s, args) =>
        {
            if (args.Key == Key.Enter)
            {
                // make sure the textbox binding updates its source first
                BindingExpression b = control.GetBindingExpression(TextBox.TextProperty);
                if (b != null)
                {
                    b.UpdateSource();
                }
                command.Execute(null);
            }
        };
    }
}

Most of it is pretty simple, and it will allow an Enter key command to be specified for any control, not just textboxes. However, if you have bound to a textbox, it will call UpdateSource on any Text binding you have made, to ensure your ViewModel operates on the latest data.

Here’s how you use it in XAML:

<TextBox 
    Text="{Binding Answer, Mode=TwoWay}" 
    my:EnterKeyHelpers.EnterKeyCommand="{Binding SubmitAnswerCommand}"/>

It also has the advantage of being considerably more succinct than the equivalent XAML for using the behavior I linked to earlier.

Friday, 27 March 2009

Binding Combo Boxes in WPF with MVVM

I was recently creating a simple WPF application and was trying to use the MVVM pattern. In this pattern, all the controls on your form are data bound to properties on your “View Model” class. While there are lots of examples of how to do this with Text boxes, List boxes, and even master-detail views, it seems that examples for binding Combo boxes are a little thin on the ground.

What I wanted to do was bind the items in the ComboBox to a list in my ViewModel and to track the currently selected item. I found that there does not seem to be one “official” way of doing this. One approach bound both the ItemsSource and the SelectedValue properties of the Combo box to corresponding properties on ViewModel. The approach I went with uses a CollectionView which is a class included with .NET that encapsulates a list and the concept of a current item as well as supporting the INotifyPropertyChanged interface.

So here is the XAML first. I set the IsSychronizedWithCurrentItem to true to allow us to track the current item on the ItemsSource.

<ComboBox ItemsSource="{Binding Path=Queries}"                 
          IsSynchronizedWithCurrentItem="True"
          DisplayMemberPath="Name" />

The code in the ViewModel first creates the List, and then creates a CollectionView based on that list. This allows us to set the CurrentItem from the ViewModel as well as get notified whenever the CurrentItem changes.

public MainWindowViewModel()
{
    IList<Query> availableQueries = new List<Query>();
    // fill the list...

    Queries = new CollectionView(availableQueries);
    Queries.MoveCurrentTo(availableQueries[0]);
    Queries.CurrentChanged += new EventHandler(queries_CurrentChanged);
}

public CollectionView Queries { get; private set; }

void queries_CurrentChanged(object sender, EventArgs e)
{
    Query currentQuery = (Query)Queries.CurrentItem;
}

I’m not sure yet whether using CollectionView is a better approach than the alternatives I have seen which bind the SelectedValue or SelectedItem property. I would be interested to hear in the comments if you think either approach has benefits over the other. One consideration is that Silverlight doesn’t seem to support CollectionView at the moment.

Saturday, 8 November 2008

Model View View-Model (MVVM) in Silverlight

I watched an excellent screencast by Jason Dolinger recently, showing how to implement the Model View View-Model pattern in WPF. A lot of the documentation on the MVVM pattern seems unnecessarily complicated, but Jason's demonstration explains it very clearly.

The basic idea of MVVM is that we would like to use data binding to connect our View to our Model, but data binding is often tricky because our model doesn't have the right properties. So we create a View Model that is perfectly set up to have just the right properties for our View to bind to, and gets its actual data from the Model.

To try the technique out I decided to refactor a small piece of a Silverlight game I have written, called SilverNibbles, which is a port of the old QBasic Nibbles game, keeping the graphics fairly similar. At the top of the screen there is a scoreboard, whose role it is to keep track of the scores, number of lives, level, speed etc. This is the piece I will attempt to modify to use MVVM.

SilverNibbles Scoreboard

Let's have a look at the original code-behind for the Scoreboard user control. As you can see, there is a not lot going on here. Whenever a property on the Scoreboard is set, the appropriate changes are made to the graphical components. This is very similar to the typical code that would be written for a Windows Forms user control.

using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace SilverNibbles
{
    public partial class Scoreboard : UserControl
    {
        private int players;
        private int level;
        private int speed;
        private int jakeScore;
        private int sammyScore;

        public Scoreboard()
        {
            InitializeComponent();
        }

        public int Players
        {
            get 
            {
                return players; 
            }
            set 
            {
                players = value;
                jakeLives.Visibility = players == 2 ? Visibility.Visible : Visibility.Collapsed;
                jakeScoreLabel.Visibility = players == 2 ? Visibility.Visible : Visibility.Collapsed;
                jakeScoreTextBlock.Visibility = players == 2 ? Visibility.Visible : Visibility.Collapsed;
            }
        }

        public int Level
        {
            get
            {
                return level;
            }
            set
            {
                level = value;
                levelTextBlock.Text = value.ToString();
            }
        }

        public int Speed
        {
            get
            {
                return speed;
            }
            set
            {
                speed = value;
                speedTextBlock.Text = value.ToString();
            }
        }

        public int SammyScore
        {
            get
            {
                return sammyScore;
            }
            set
            {
                sammyScore = value;
                sammyScoreTextBlock.Text = value.ToString();
            }
        }

        public int JakeScore
        {
            get
            {
                return jakeScore;
            }
            set
            {
                jakeScore = value;
                jakeScoreTextBlock.Text = value.ToString();
            }
        }

        public int JakeLives
        {
            get
            {
                return jakeLives.Lives;
            }
            set
            {
                jakeLives.Lives = value;
            }
        }

        public int SammyLives
        {
            get
            {
                return sammyLives.Lives;
            }
            set
            {
                sammyLives.Lives = value;
            }
        }
    }
}

Instead of having all these properties, the Scoreboard will become a very simple view. Now the code-behind of Scoreboard is completely minimalised:

namespace SilverNibbles
{
    public partial class Scoreboard : UserControl
    {
        public Scoreboard()
        {
            InitializeComponent();
        }
    }
}

Now it is the job of whoever creates Scoreboard to give it a ScoreboardViewModel as its DataContext. It doesn't even have to be a ScoreboardViewModel either, so long as it has the appropriate properties. This means that a graphic designer could use an XML file instead to create dummy data to help while designing the appearance. Here's my code elsewhere in the project that sets up the data context of the Scoreboard with its View Model.

scoreData = new ScoreboardViewModel();
scoreboard.DataContext = scoreData;

Now I simply modify the scoreData object's properties, and the Scoreboard will update its view automatically.

What has changed in the Scoreboard user control's XAML? Well first, it has Binding statements for every property that gets its value from the view model. I was also able to remove all the x:Name attributes from the markup, which is a sign that MVVM has been done right. I also needed to make my Lives property on my LivesControl user control into a dependency property to allow it to accept the binding syntax as a value.

<UserControl x:Class="SilverNibbles.Scoreboard"
    xmlns="http://schemas.microsoft.com/client/2007" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:sn="clr-namespace:SilverNibbles"
     >
    <Grid x:Name="LayoutRoot" Background="LightYellow" ShowGridLines="False">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="20*" />
            <ColumnDefinition Width="35*" />
            <ColumnDefinition Width="25*" />
            <ColumnDefinition Width="10*" />
        </Grid.ColumnDefinitions>
        
        <!-- row 0 -->
        <TextBlock 
            Grid.Row="0" 
            Grid.Column="0"
            Text="High Score" 
            FontSize="18"
            FontFamily="teen bold.ttf#Teen" />
        <TextBlock 
            Grid.Row="0" 
            Grid.Column="1"
            Margin="5,0,5,0"
            Text="{Binding Record}" 
            FontSize="18"
            FontFamily="teen bold.ttf#Teen" />
        
        <TextBlock 
            Grid.Column="2" 
            Text="Level" 
            HorizontalAlignment="Right" 
            Margin="5,0,5,0"
            FontSize="18"
            FontFamily="teen bold.ttf#Teen" />
        <TextBlock 
            Grid.Column="3" 
            Margin="5,0,5,0"
            Text="{Binding Level}" 
            FontSize="18"
            FontFamily="teen bold.ttf#Teen" />
                
        <!-- row 1 -->        
        <TextBlock 
            Grid.Row="1"
            Text="Sammy" 
            FontSize="18"
            FontFamily="teen bold.ttf#Teen" />
       <sn:LivesControl
            Grid.Row="1"
            Grid.Column="1"
            HorizontalAlignment="Right"
           Fill="{StaticResource SammyBrush}"
           Lives="{Binding SammyLives}"
             />        
       <TextBlock 
            Grid.Row="1"
            Grid.Column="1"
            Margin="5,0,5,0"
            Text="{Binding SammyScore}" 
            FontSize="18"
            FontFamily="teen bold.ttf#Teen"
            />
        
       <TextBlock 
           Grid.Row="1" 
           Grid.Column="2" 
            Text="Speed" 
            HorizontalAlignment="Right" 
            Margin="5,0,5,0"
            FontSize="18"
            FontFamily="teen bold.ttf#Teen"
           />
        <TextBlock 
            Grid.Row="1"
            Grid.Column="3" 
            Margin="5,0,5,0"
            Text="{Binding Speed}" 
            FontSize="18"
            FontFamily="teen bold.ttf#Teen"
            />
        
        <!-- row 2 -->
        <TextBlock 
            Grid.Row="2"
            Grid.Column="0"
            Text="Jake" 
            FontSize="18"
            FontFamily="teen bold.ttf#Teen"
            Visibility="{Binding JakeVisible}"            
            />
        <sn:LivesControl
            Grid.Row="2"
            Grid.Column="1"
            HorizontalAlignment="Right"
            Fill="{StaticResource JakeBrush}"
            Visibility="{Binding JakeVisible}"
            Lives="{Binding JakeLives}"
             />
       <TextBlock 
            Grid.Row="2"
            Grid.Column="1"
            FontSize="18"
            Margin="5,0,5,0"
            Text="{Binding JakeScore}" 
            FontFamily="teen bold.ttf#Teen"
            Visibility="{Binding JakeVisible}"
           />
    </Grid>
</UserControl>

Here's what my ScoreboardViewModel looks like. The main thing to notice is that I have implemented the INotifyPropertyChanged interface. I rather lazily raise the changed event every time the setter is called irrespective of whether the value really changed. Notice also I have created a JakeVisible property. This highlights how the View Model can be used to create properties that are exactly what the View needs.

namespace SilverNibbles
{
    public class ScoreboardViewModel : INotifyPropertyChanged
    {
        private int players;
        private int level;
        private int speed;
        private int jakeScore;
        private int sammyScore;

        public int Players
        {
            get
            {
                return players;
            }
            set
            {
                players = value;
                RaisePropertyChanged("Players");
                RaisePropertyChanged("JakeVisible");
            }
        }

        public Visibility JakeVisible
        {
            get
            {
                return players == 2 ? Visibility.Visible : Visibility.Collapsed;
            }
        }

        public int Level
        {
            get
            {
                return level;
            }
            set
            {
                level = value;
                RaisePropertyChanged("Level");
            }
        }

        public int Speed
        {
            get
            {
                return speed;
            }
            set
            {
                speed = value;
                RaisePropertyChanged("Speed");
            }
        }

        public int SammyScore
        {
            get
            {
                return sammyScore;
            }
            set
            {
                sammyScore = value;
                RaisePropertyChanged("SammyScore");
            }
        }

        public int JakeScore
        {
            get
            {
                return jakeScore;
            }
            set
            {
                jakeScore = value;
                RaisePropertyChanged("JakeScore");
            }
        }

        int jakeLives;
        int sammyLives;

        public int JakeLives
        {
            get
            {
                return jakeLives;
            }
            set
            {
                jakeLives = value;
                RaisePropertyChanged("JakeLives");
            }
        }

        public int SammyLives
        {
            get
            {
                return sammyLives;
            }
            set
            {
                sammyLives = value;
                RaisePropertyChanged("SammyLives");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }
    }
}

I think there are alternatives to using INotifyPropertyChanged such as creating dependency properties or inheriting from DependencyObject. I don't know what the advantages or disadvantages of taking that approach would be. That is something for a future investigation.

There are lots of other parts of the SilverNibbles application that could be refactored to use this pattern, but that is also a task for another day. View the code at CodePlex, and play SilverNibbles here.