Monthly Archives: October 2014

Automatically focus on the newest item in a ListView using WinRT, MVVM and Behaviors SDK

The Behaviors SDK was introduced recently for WinRT. Here’s how you could use it to add functionality to a Listview to scroll to the newest item.

ListView

To start with, here’s the listview XAML:

            <ListView ItemsSource="{Binding DataLog}" x:Name="StatusUpdate">                
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}" />
                    </DataTemplate>
                </ListView.ItemTemplate>

            </ListView>

And here’s the ViewModel that updates it:

        private void UpdateStatusLog(string msg)
        {
            Log.Add(msg);
        }

If you add to the listview, what you should notice is that once you’ve filled it, you’ll start to add items off the bottom.

Behaviors (sic)

The Behaviors SDK is now available for WinRT. Once you’ve included it in your project, you get a number of build in behaviours and, you can create your own. Before you build your own, a base class will probably make you feel more at home (Reference):

    public abstract class Behaviour<T> : DependencyObject, IBehavior where T : DependencyObject
    {

        [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
        public T AssociatedObject { get; set; }

        protected virtual void OnAttached()
        {
        }

        protected virtual void OnDetaching()
        {
        }

        public void Attach(Windows.UI.Xaml.DependencyObject associatedObject)
        {
            this.AssociatedObject = (T)associatedObject;
            OnAttached();
        }

        public void Detach()
        {
            OnDetaching();
        }

        DependencyObject IBehavior.AssociatedObject
        {
            get { return this.AssociatedObject; }
        }
    }

Not least, because you can correct the spelling of Behaviour 🙂

ListView Behaviour

    public sealed class AutoScrollToLastItemBehavior : Behaviour<ListView>
    {
        // Need to track whether we've attached to the collection changed event
        bool _collectionChangedSubscribed = false;

        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.SelectionChanged += SelectionChanged;

            // The ItemSource of the listView will not be set yet, 
            // so get a method that we can hook up to later
            AssociatedObject.DataContextChanged += DataContextChanged;
        }

        private void SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ScrollIntoView();
        }

        private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            ScrollIntoView();
        }

        private void DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
        {
            // The ObservableCollection implements the INotifyCollectionChanged interface
            // However, if this is bound to something that doesn't then just don't hook the event
            var collection = AssociatedObject.ItemsSource as INotifyCollectionChanged;
            if (collection != null && !_collectionChangedSubscribed)
            {
                // The data context has been changed, so now hook 
                // into the collection changed event
                collection.CollectionChanged += CollectionChanged;
                _collectionChangedSubscribed = true;
            }

        }

        private void ScrollIntoView()
        {
            int count = AssociatedObject.Items.Count;
            if (count > 0)
            {
                var last = AssociatedObject.Items[count - 1];
                AssociatedObject.ScrollIntoView(last);
            }
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.SelectionChanged -= SelectionChanged;
            AssociatedObject.DataContextChanged -= DataContextChanged;

            // Detach from the collection changed event
            var collection = AssociatedObject.ItemsSource as INotifyCollectionChanged;
            if (collection != null && _collectionChangedSubscribed)
            {
                collection.CollectionChanged -= CollectionChanged;
                _collectionChangedSubscribed = false;

            }
        }

Attaching the behaviour

Finally, change the listview to include a call to the behaviour:

            <ListView ItemsSource="{Binding Log}" x:Name="StatusUpdate">                
                <i:Interaction.Behaviors>
                    <behaviours:AutoScrollToLastItemBehavior />
                </i:Interaction.Behaviors>
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}"/>
                    </DataTemplate>
                </ListView.ItemTemplate>

            </ListView>

Also, you need to reference Interactivity, and your behaviours:

    xmlns:behaviours="using:KingMaker.Behaviours"    
    xmlns:i="using:Microsoft.Xaml.Interactivity" 

The listview should now behave as expected.

Creating and Binding to a User Control in MVVM Cross

While creating my game, I recently came across the problem of navigation. This post describes how to create a custom user control and react to the event inside.

The usual disclaimer still applies here; although this is an MVVM Cross post, the contents of it should be applicable for any MVVM Framework or, in fact, any XAML binding at all.

User Control

The user control that I’m going to use is simply a navigation bar to appear at the top of each screen.

The XAML for the user control is here:

<UserControl
    x:Name="NavigationControlRoot">
    
    <Grid DataContext="{Binding ElementName=NavigationControlRoot}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <Button Content="Home" Grid.Column="0" Command="{Binding HomeClick}" />
        <Button Content="Back" Grid.Column="1" Command="{Binding BackClick}" />
    </Grid>
</UserControl>

So, I’ve got two commands, binding to the code behind. The key point here is that the root control (grid) binds to the DataContext of the user control – effectively this binds it to the containing DataContext. Here’s the code behind:


    public sealed partial class NavigationPanel : UserControl
    {
        public static DependencyProperty BackCommandProperty =
            DependencyProperty.Register(
                "BackClick",
                typeof(ICommand),
                typeof(NavigationPanel),
                new PropertyMetadata(null));

        public static DependencyProperty HomeCommandProperty =
            DependencyProperty.Register(
                "HomeClick",
                typeof(ICommand),
                typeof(NavigationPanel),
                new PropertyMetadata(null));

        public NavigationPanel()
        {
            this.InitializeComponent();
        }
        
        public ICommand BackClick
        {
            get
            {
                return (ICommand)GetValue(BackCommandProperty);
            }

            set
            {
                SetValue(BackCommandProperty, value);
            }
        }

        public ICommand HomeClick
        {
            get
            {
                return (ICommand)GetValue(HomeCommandProperty);
            }

            set
            {
                SetValue(HomeCommandProperty, value);
            }
        }        
    }

So, you’ve now exposed two dependency properties and bound them to the XAML in the user control.

Host App

Here’s the relevant XAML for the hosting view:

<controls:NavigationPanel BackClick="{Binding BackClickCommand}"                                  
                                  HomeClick="{Binding HomeClickCommand}"/>

And that just binds to the ViewModel as you would expect.

        private IMvxCommand _homeClickCommand;
        public IMvxCommand HomeClickCommand
        {
            get
            {
                if (_homeClickCommand == null)
                {
                    _homeClickCommand = new MvxCommand(() => GoHome());
                }
                return _homeClickCommand;
            }
            set
            {
                _homeClickCommand = value;
            }
        }

        private void GoHome()
        {
            ShowViewModel<MainViewModel>();
        }

For me, this is in a base view model, so it responds to the same command in every view.

Conclusion

I think the final version of this code will interact with an IMvxPresenter in some way, which may make another post.

Now using Visual Studio 14 CTP

I was previously using VS2013, but after running 14 just once, I saw no reason to go back. I thought I’d jot down some notes on my experience of this update. A quick caveat though: VS14 is a CTP, so if you’re thinking about using it to maintain the code for your nuclear reactor, then probably don’t.

Faster

Okay, so this is my perception. I’m working on a personal project on the train to and from work. VS14 loads almost a station before VS2013 did. Now, if that’s not a fair and accurate benchmark, I don’t know what is!

Debugging

One particularly cool feature is that you can see the CPU execution time for previous statement during debugging. Typically, this is a meaninglessly low figure of x milliseconds; but occasionally it shows an unexpectedly high figure.

This can be dangerous, because if you saw a statement that took 30ms, which executed once when the user presses a button in your app, you might think nothing of it… unless all the other statements take 2ms. At this point, you might spend 1/2 hour trying to work out what the bottleneck is. You might even find it: and speed up your app by 28ms.

Refactor

Shift-Alt-F10 on a non-existent method now gives you a preview of what it’s about to do. Possibly not the most useful feature in the world to be honest, but if you want a field and it tries to create a property then you know (obviously, if you’d just read the description of what it will create, that would also tell you). The Using statement tells you where it will create the statement:

preview

However, I can’t find any way you can affect the preview – which would have been a nice feature; for example, say you wanted to create a public field rather than internal; or, you wanted to define a constant and give it a constant value.

Light bulbs now appear next to potential refactoring. One example was an unnecessary using statement I had. It had turned grey; I didn’t understand this until I noticed that a light bulb had appeared next to it, which helpfully informed me that I didn’t need the statement.

Also, code analysis seems to be an opt-in thing now – rather than the previous version which seemed to constantly kidnap whichever tab it was on.

It is still a CTP

Line numbers were turned on in VS2013 and were off in 14.

Windows 10 – The Return of the Overrated Start Menu

Yesterday, for the first time, I saw Windows 10. I set it up in a VM inside Win 8. The following post in a combination of conjecture, opinions, and under researched hearsay.

First, let’s think about what I just said: I’m running a later version of Windows inside an earlier one. And it works.

9

Don’t mention 9! I mentioned it once, but I think I got away with it.

Start menu

The start menu never went anywhere for Win 8. You just press the Windows key and you’re in it (although it was more of a screen than a menu). Anyway, MS finally bowed to pressure, and it’s back:

start1

Looks good eh? Actually, after restarting it seemed to gather its thoughts:

start2

Anyway, as I understand the intention, if I were running this on a tablet, I’d be able to see the Win 8 start screen when I pressed the Windows Key. I can’t test that on a VM and, if I could, I’d be surprised if it worked properly for the TP.

Multiple Desktops

This is a neat feature: the ability to maintain more than one desktop. The way this appears to work is that you can open apps into any desktop, and then just switch between desktop to access them, or access them from the taskbar, and Windows will select the desktop they are on.

desktop

You appear to be able to maintain as many desktops as you like.

Search

There is a large magnifying glass on the taskbar, which brings up a huge, unwieldy, blank box with a small text search. Maybe this will be back on the charms bar for tablets, but the idea is that it will find stuff on your machine. It failed:

search

I don’t understand why this was separated from the start menu. Searching for, and running programs are basically the same thing. If I want to run word, I want to type “Word”; if I want to open a word document on my desktop, I want to type: “My word doc” and have it find it. Results seemed to be sporadic, sometimes it found what I expected, but generally it didn’t.

What happened to the charms bar?

It now seems to be a sub menu of each store window:

charmsbar

As I said earlier, I expect it will behave differently for the tablet and phone.

Universal Apps

These have been around for a while. They basically allow sharing of code between Windows store and Windows phone apps. As I understand it, these will now be effectively mandatory – you will have to produce a phone & store version, in the same way as you had to provide a snapped and filled version of your app.

Conclusion

The split desktop is nice, and the idea of having “One Windows” is a nice one. In fact, it’s effectively what I understood to be the driving force behind 8. It looks like it’s going to be a good OS, but I can’t see any killer features.

What I mean by that is this: imagine that you want to upgrade to Windows 10, and you put the idea to your boss. The first question they should ask is: “Why?”.

For Windows 8, the answer was simple: Hyper-V. An astoundingly useful and undersold feature.

Windows 10 doesn’t have that, and it needs it, because a lot of people are sticking on 7 and need a reason to upgrade – having the start menu back is not that reason.