Web and Windows Phone 7 by Ben Cull

Using Multiple Application Bars in Windows Phone Development

Posted on December 12th, 2011 by Benjii

You know when you select multiple emails and the application bar swaps out for a different one? No? We’ll get ready to learn. This trick looks much better than enabling and disabling menu items on your application bar and is dead simple to achieve.

Step 1: Create your Application Bars

Make sure you have the shell namespace reference at the top of your PhoneApplicationPage:

xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"

Create your application bars and add them to your page resources using the following XAML. I’ve created three, naming them “DefaultAppBar”, “SiungleSelectionAppBar” and “MultiSelectionAppBar”.

<phone:PhoneApplicationPage.Resources>
    <shell:ApplicationBar x:Key="DefaultAppBar" IsVisible="True">
        <shell:ApplicationBarIconButton x:Name="mnuAdd" IconUri="/icons/appbar.add.rest.png" IsEnabled="True" Text="Add" Click="mnuAdd_Click"/>
    </shell:ApplicationBar>
    <shell:ApplicationBar x:Key="SingleSelectionAppBar" IsVisible="True">
        <shell:ApplicationBarIconButton x:Name="mnuPin" IconUri="/icons/appbar.pushpin.png" IsEnabled="True" Text="Pin" Click="mnuPin_Click" />
        <shell:ApplicationBarIconButton x:Name="mnuDelete" IconUri="/icons/appbar.delete.rest.png" IsEnabled="True" Text="Delete" Click="mnuDelete_Click"/>
        <shell:ApplicationBarIconButton x:Name="mnuEdit" IconUri="/icons/appbar.edit.rest.png" IsEnabled="True" Text="Edit" Click="mnuEdit_Click"/>
    </shell:ApplicationBar>
    <shell:ApplicationBar x:Key="MultiSelectionAppBar" IsVisible="True">
        <shell:ApplicationBarIconButton x:Name="mnuDeleteMulti" IconUri="/icons/appbar.delete.rest.png" IsEnabled="True" Text="Delete" Click="mnuDelete_Click"/>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.Resources>

 

Step 2: Use Code to Swap Between Them

Firstly, we need to set a default application bar to load with the page. We can do this by adding the following code to the constructor:

ApplicationBar = (Microsoft.Phone.Shell.ApplicationBar)Resources["DefaultAppBar"];

It’s actually the exact same code to change the application bar to one of our other ones. Check out the following code I use to swap between them when my list fires a selection changed event:

if (list.SelectedItems.Count == 1)
{
    ApplicationBar = (Microsoft.Phone.Shell.ApplicationBar)Resources["SingleSelectionAppBar"];
}
else if (list.SelectedItems.Count > 1)
{
    ApplicationBar = (Microsoft.Phone.Shell.ApplicationBar)Resources["MultiSelectionAppBar"];
}
else
{
    ApplicationBar = (Microsoft.Phone.Shell.ApplicationBar)Resources["DefaultAppBar"];
}

That’s it. You’ll also notice that when you change application bars, it uses a cool swoosh down and then back up animation. All for free.

Continuum Transition for Windows Phone Silverlight Toolkit

Posted on November 8th, 2011 by Benjii

Check out my awesome Paint.NET skills!You know that cool swooshing transition you get when you click an email on your Windows Phone? Now you too can make use of that awesome transition using only the Silverlight Toolkit and this handy dandy Continuum Transition class.

Step 1 – Go get the Silverlight Toolkit!!

As always, I suggest you use Nuget to include it, but feel free to download it from Codeplex

 

Step 2 – Let’s See How to Use It

The actual continuum transition class is pretty big for a blog post, so I’ll show you how to use it first and then you can copy the Continuum Transition class after that.

If you are already using the Silverlight Toolkit transitions using code, then this should hopefully be familar:

private void ListBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
    var selectedElement = (sender as ListBox).ItemContainerGenerator.ContainerFromIndex(lstSeeding.SelectedIndex) as FrameworkElement;

    var navOutTransition = new NavigationOutTransition();
    navOutTransition.Forward = new ContinuumTransition(ContinuumTransitionMode.ContinuumForwardOutStoryboard, selectedElement);

    var navInTransition = new NavigationInTransition();
    navInTransition.Backward = new ContinuumTransition(ContinuumTransitionMode.ContinuumBackwardInStoryboard, selectedElement);

    PhoneApplicationPage phoneApplicationPage =
    (PhoneApplicationPage)(((PhoneApplicationFrame)Application.Current.RootVisual)).Content;

    TransitionService.SetNavigationOutTransition(phoneApplicationPage, navOutTransition);
    TransitionService.SetNavigationInTransition(phoneApplicationPage, navInTransition);

    NavigationService.Navigate(new Uri("/DetailPage.xaml", UriKind.Relative));
}

I’ve used a listbox’s selection changed event as an example, as this is the most common scenario. To break it down, firstly we get the selected item’s framework element. This is the containing element that we are going to animate, in this case a list item.

Next, we setup the actual transitions using the awesome continuum transition class. The first parameter is the type of transition you are making and the second is the element you wish to animate.

After this, we obtain our phone application page and assign the transitions to the transition service.

Once we have done that we can navigate as per normal and the page will automatically transition for you!

 

Step 3 – Copy the Continuum Transition Class

The code in full is below. It is hosted on Github so you can be confident the below code is up to date.

Also please note the actual storyboards have come from the awesome guys at Clarity Consulting. There is an especially useful post about transitions you should go read right now.

namespace BenjiiMe.Animation
{
    public class ContinuumTransition : TransitionElement
    {
        public const string ContinuumElementPropertyName = "ContinuumElement";
        public const string ContinuumModePropertyName = "Mode";

        public FrameworkElement ContinuumElement
        {
            get { return (FrameworkElement)GetValue(ContinuumElementProperty); }
            set { SetValue(ContinuumElementProperty, value); }
        }

        public ContinuumTransitionMode Mode
        {
            get { return (ContinuumTransitionMode)GetValue(ModeProperty); }
            set { SetValue(ModeProperty, value); }
        }

        public static readonly DependencyProperty ContinuumElementProperty =
           DependencyProperty.Register(ContinuumElementPropertyName, typeof(FrameworkElement), typeof(ContinuumTransition), new PropertyMetadata(null));

        public static readonly DependencyProperty ModeProperty =
            DependencyProperty.Register(ContinuumModePropertyName, typeof(ContinuumTransitionMode), typeof(ContinuumTransition), null);


        public ContinuumTransition() { }
        public ContinuumTransition(ContinuumTransitionMode mode)
        {
            Mode = mode;
        }
        public ContinuumTransition(ContinuumTransitionMode mode, FrameworkElement element)
        {
            Mode = mode;
            ContinuumElement = element;
        }

        public override ITransition GetTransition(UIElement element)
        {
            Storyboard storyboard = null;
            if (Mode == ContinuumTransitionMode.ContinuumBackwardInStoryboard)
                storyboard = XamlReader.Load(ContinuumBackwardInStoryboard) as Storyboard;
            else if (Mode == ContinuumTransitionMode.ContinuumBackwardOutStoryboard)
                storyboard = XamlReader.Load(ContinuumBackwardOutStoryboard) as Storyboard;
            else if (Mode == ContinuumTransitionMode.ContinuumForwardInStoryboard)
                storyboard = XamlReader.Load(ContinuumForwardInStoryboard) as Storyboard;
            else if (Mode == ContinuumTransitionMode.ContinuumForwardOutStoryboard)
                storyboard = XamlReader.Load(ContinuumForwardOutStoryboard) as Storyboard;

            ContinuumElement.GetTransform<CompositeTransform>(TransformCreationMode.CreateOrAddAndIgnoreMatrix);

            SetTargets(new Dictionary<string, FrameworkElement>()
                {
                    { "LayoutRoot", element as FrameworkElement },
                    { ContinuumElementPropertyName, ContinuumElement }
                },
                storyboard);

            return new Transition(element, storyboard);
        }

        public void SetTargets(Dictionary<string, FrameworkElement> targets, Storyboard sb)
        {
            foreach (var kvp in targets)
            {
                var timelines = sb.Children.Where(t => Storyboard.GetTargetName(t) == kvp.Key);
                foreach (Timeline t in timelines)
                    Storyboard.SetTarget(t, kvp.Value);
            }
        }

        internal static readonly string ContinuumForwardOutStoryboard =
        @"<Storyboard xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.RenderTransform).(CompositeTransform.TranslateY)"" Storyboard.TargetName=""LayoutRoot"">
                <EasingDoubleKeyFrame KeyTime=""0"" Value=""0""/>
                <EasingDoubleKeyFrame KeyTime=""0:0:0.15"" Value=""70"">
                    <EasingDoubleKeyFrame.EasingFunction>
                        <ExponentialEase EasingMode=""EaseIn"" Exponent=""3""/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
            </DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.Opacity)"" Storyboard.TargetName=""LayoutRoot"">
<EasingDoubleKeyFrame KeyTime=""0"" Value=""1""/>
<EasingDoubleKeyFrame KeyTime=""0:0:0.15"" Value=""0"">
<EasingDoubleKeyFrame.EasingFunction>
<ExponentialEase EasingMode=""EaseIn"" Exponent=""3""/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.RenderTransform).(CompositeTransform.TranslateY)"" Storyboard.TargetName=""ContinuumElement"">
                <EasingDoubleKeyFrame KeyTime=""0"" Value=""0""/>
                <EasingDoubleKeyFrame KeyTime=""0:0:0.15"" Value=""73"">
                    <EasingDoubleKeyFrame.EasingFunction>
                        <ExponentialEase EasingMode=""EaseIn"" Exponent=""3""/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.RenderTransform).(CompositeTransform.TranslateX)"" Storyboard.TargetName=""ContinuumElement"">
                <EasingDoubleKeyFrame KeyTime=""0"" Value=""0""/>
                <EasingDoubleKeyFrame KeyTime=""0:0:0.15"" Value=""225"">
                    <EasingDoubleKeyFrame.EasingFunction>
                        <ExponentialEase EasingMode=""EaseIn"" Exponent=""3""/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
            </DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName=""ContinuumElement"" Storyboard.TargetProperty=""(UIElement.Opacity)"">
<DiscreteDoubleKeyFrame KeyTime=""0:0:0.15"" Value=""0"" />
</DoubleAnimationUsingKeyFrames>
        </Storyboard>";

        internal static readonly string ContinuumForwardInStoryboard =
        @"<Storyboard xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.RenderTransform).(CompositeTransform.TranslateY)"" Storyboard.TargetName=""LayoutRoot"">
                <EasingDoubleKeyFrame KeyTime=""0"" Value=""50""/>
                <EasingDoubleKeyFrame KeyTime=""0:0:0.15"" Value=""0"">
                    <EasingDoubleKeyFrame.EasingFunction>
                        <ExponentialEase EasingMode=""EaseOut"" Exponent=""3""/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.RenderTransform).(CompositeTransform.TranslateY)"" Storyboard.TargetName=""ContinuumElement"">
                <EasingDoubleKeyFrame KeyTime=""0"" Value=""-70""/>
                <EasingDoubleKeyFrame KeyTime=""0:0:0.15"" Value=""0"">
                    <EasingDoubleKeyFrame.EasingFunction>
                        <ExponentialEase EasingMode=""EaseOut"" Exponent=""3""/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.RenderTransform).(CompositeTransform.TranslateX)"" Storyboard.TargetName=""ContinuumElement"">
                <EasingDoubleKeyFrame KeyTime=""0"" Value=""130""/>
                <EasingDoubleKeyFrame KeyTime=""0:0:0.15"" Value=""0"">
                    <EasingDoubleKeyFrame.EasingFunction>
                        <ExponentialEase EasingMode=""EaseOut"" Exponent=""3""/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimation Storyboard.TargetProperty=""(UIElement.Opacity)"" From=""0"" To=""1"" Duration=""0:0:0.15""
                                 Storyboard.TargetName=""LayoutRoot"">
                <DoubleAnimation.EasingFunction>
                    <ExponentialEase EasingMode=""EaseOut"" Exponent=""6""/>
                </DoubleAnimation.EasingFunction>
            </DoubleAnimation>
        </Storyboard>";

        internal static readonly string ContinuumBackwardOutStoryboard =
        @"<Storyboard xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.RenderTransform).(CompositeTransform.TranslateY)""
                                           Storyboard.TargetName=""LayoutRoot"">
                <EasingDoubleKeyFrame KeyTime=""0"" Value=""0""/>
                <EasingDoubleKeyFrame KeyTime=""0:0:0.15"" Value=""50"">
                    <EasingDoubleKeyFrame.EasingFunction>
                        <ExponentialEase EasingMode=""EaseIn"" Exponent=""6""/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimation Storyboard.TargetProperty=""(UIElement.Opacity)"" From=""1"" To=""0"" Duration=""0:0:0.15""
                                 Storyboard.TargetName=""LayoutRoot"">
                <DoubleAnimation.EasingFunction>
                    <ExponentialEase EasingMode=""EaseIn"" Exponent=""6""/>
                </DoubleAnimation.EasingFunction>
            </DoubleAnimation>
        </Storyboard>";

        internal static readonly string ContinuumBackwardInStoryboard =
        @"<Storyboard xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.RenderTransform).(CompositeTransform.TranslateX)"" Storyboard.TargetName=""ContinuumElement"">
                <EasingDoubleKeyFrame KeyTime=""0"" Value=""-70""/>
                <EasingDoubleKeyFrame KeyTime=""0:0:0.15"" Value=""0"">
                    <EasingDoubleKeyFrame.EasingFunction>
                        <ExponentialEase EasingMode=""EaseOut"" Exponent=""3""/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.RenderTransform).(CompositeTransform.TranslateY)"" Storyboard.TargetName=""ContinuumElement"">
                <EasingDoubleKeyFrame KeyTime=""0"" Value=""-30""/>
                <EasingDoubleKeyFrame KeyTime=""0:0:0.15"" Value=""0"">
                    <EasingDoubleKeyFrame.EasingFunction>
                        <ExponentialEase EasingMode=""EaseOut"" Exponent=""3""/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
            </DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName=""ContinuumElement"" Storyboard.TargetProperty=""(UIElement.Opacity)"">
<DiscreteDoubleKeyFrame KeyTime=""0:0:0"" Value=""1"" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.Opacity)"" Storyboard.TargetName=""LayoutRoot"">
<EasingDoubleKeyFrame KeyTime=""0"" Value=""0""/>
<EasingDoubleKeyFrame KeyTime=""0:0:0.15"" Value=""1"">
<EasingDoubleKeyFrame.EasingFunction>
<ExponentialEase EasingMode=""EaseOut"" Exponent=""6""/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
            <DoubleAnimation Duration=""0"" To=""0"" Storyboard.TargetProperty=""(UIElement.RenderTransform).(CompositeTransform.TranslateY)"" Storyboard.TargetName=""LayoutRoot""/>
        </Storyboard>";
    }

    public enum ContinuumTransitionMode
    {
        ContinuumForwardOutStoryboard,
        ContinuumForwardInStoryboard,
        ContinuumBackwardOutStoryboard,
        ContinuumBackwardInStoryboard
    }
}

Welcome to the bottom of the post, please spread the word about this transition to better the Windows Phone platform for everyone :)

How to Use the Long List Selector for Windows Phone Mango

Posted on October 10th, 2011 by Benjii

One of the cool new controls in the Silverlight Toolkit for Windows Phone Mango is the Long List Selector. This post will show you how to get one looking nice, complete with alphabetised grouping.

Step 1: Make sure you have the Silverlight Toolkit referenced

The best way to do this is via Nuget: nuget_silverlight_toolkit

 

 

or directly from Codeplex:

augtk_3

 

Step 2: Drop that bad boy onto the page

Paste the following XAML where you would like to use the control.

<toolkit:LongListSelector x:Name="lstMain"
GroupHeaderTemplate="{StaticResource LongListGroupHeader}" GroupItemTemplate="{StaticResource LongListGroupItemTemplate}">
    <toolkit:LongListSelector.ItemTemplate>
        <DataTemplate>
            <Grid Margin="0,12.5,12.5,12.5">
                <!-- Main List Item Template Here -->
            </Grid>
        </DataTemplate>
    </toolkit:LongListSelector.ItemTemplate>
    <toolkit:LongListSelector.GroupItemsPanel>
        <ItemsPanelTemplate>
            <toolkit:WrapPanel />
        </ItemsPanelTemplate>
    </toolkit:LongListSelector.GroupItemsPanel>
</toolkit:LongListSelector>

You’ll notice we need to implement a few templates, these include:

  • ItemTemplate – Each actual list item
  • GroupItemsPanel – The panel that holds the popup overlay for selecting a new group
  • GroupHeaderTemplate – The item at the beginning of each group in the list
  • GroupItemTemplate – The individual items inside the popup overlay

The first two I’ve included above for simplicity, this includes the item template (which you will need to create yourself) and the group items panel, in which I’ve used a wrap panel. The second two templates we will add to the resources section of the page as follows:

<phone:PhoneApplicationPage.Resources>
    <DataTemplate x:Key="LongListGroupHeader">
        <Grid Margin="12,0,0,0">
            <Grid Width="75" Height="75" HorizontalAlignment="Left">
                <TextBlock Margin="12,0,1,7" TextWrapping="Wrap" d:LayoutOverrides="Width, Height" Style="{StaticResource PhoneTextLargeStyle}" Text="{Binding Title}" VerticalAlignment="Bottom"/>
                <Border BorderThickness="1">
                    <Border.BorderBrush>
                        <SolidColorBrush Color="{StaticResource PhoneAccentColor}"/>
                    </Border.BorderBrush>
                </Border>
            </Grid>
        </Grid>
    </DataTemplate>
    <DataTemplate x:Key="LongListGroupItemTemplate">
        <Border Background="{Binding GroupBackgroundBrush}" Width="99" Height="99" Margin="6" IsHitTestVisible="{Binding HasItems}">
            <TextBlock Text="{Binding Title}"
                                   FontFamily="{StaticResource PhoneFontFamilySemiBold}"
                                   FontSize="36"
                                   Margin="{StaticResource PhoneTouchTargetOverhang}"
                                   Foreground="{StaticResource PhoneForegroundBrush}"
                                   VerticalAlignment="Bottom"/>
        </Border>
    </DataTemplate>
</phone:PhoneApplicationPage.Resources>

You now have all the XAML you need to rock a kick ass selector like the below screenshots:

ItemTemplatesItemTemplates2

 

Step 3: Sweet XAML bro, but how do I bind data?

Excellent question, you cant just bind a list of items to the itemsource with a long list selector. What you need is the following class and linq queries.

public class Group<T> : IEnumerable<T>
{
    public Group(string name, IEnumerable<T> items)
    {
        this.Title = name;
        this.Items = new List<T>(items);
    }

    public override bool Equals(object obj)
    {
        Group<T> that = obj as Group<T>;

        return (that != null) && (this.Title.Equals(that.Title));
    }

    public override int GetHashCode()
    {
        return this.Title.GetHashCode();
    }

    public string Title
    {
        get;
        set;
    }

    public IList<T> Items
    {
        get;
        set;
    }

    public bool HasItems
    {
        get
        {
            return Items.Count > 0;
        }
    }

    public Brush GroupBackgroundBrush
    {
        get
        {
            if (HasItems)
                return (SolidColorBrush)Application.Current.Resources["PhoneAccentBrush"];
            else
                return (SolidColorBrush)Application.Current.Resources["PhoneChromeBrush"];
        }
    }

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator()
    {
        return this.Items.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.Items.GetEnumerator();
    }

    #endregion
}

The group class is fairly simple, and is mostly just a container with a title and a list of items. What makes it handy are the other properties that it provides to the long list selector, such as the background colour and hit test (whether or not a group item can be clicked).

The idea is we are going to bind a list of groups to our long list selector. Check out the following code and then meet me on the other side for an explanation.

var allTorrents = (from torrent in model
                select new TorrentListItemViewModel(torrent));

var emptyGroups = new List<Group<TorrentListItemViewModel>>()
{
    new Group<TorrentListItemViewModel>("#", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("a", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("b", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("c", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("d", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("e", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("f", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("g", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("h", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("i", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("j", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("k", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("l", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("m", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("n", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("o", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("p", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("q", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("r", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("s", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("t", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("u", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("v", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("w", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("x", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("y", new List<TorrentListItemViewModel>()),
    new Group<TorrentListItemViewModel>("z", new List<TorrentListItemViewModel>())
};

var groupedTorrents = (from t in allTorrents
                group t by t.GroupHeader into grp
                orderby grp.Key
                select new Group<TorrentListItemViewModel>(grp.Key, grp));

lstMain.ItemsSource = (from t in groupedTorrents.Union(emptyGroups)
                orderby t.Title
                select t).ToList();

We start off by fetching the data I need and creating a list of my item view models (TorrentListItemViewModel). Next I create a base list of groups that I want to appear on my screen, regardless of whether or not they have data.

Next I turn my flat list of torrents into a list of groups based on the first letter of their name. The magic coming from the GroupHeader property on my view model, which i’ve shared below:

public string GroupHeader
{
    get
    {
        switch (Name.ToLower().Substring(0,1))
        {
            case "a": return "a";
            case "b": return "b";
            case "c": return "c";
            case "d": return "d";
            case "e": return "e";
            case "f": return "f";
            case "g": return "g";
            case "h": return "h";
            case "i": return "i";
            case "j": return "j";
            case "k": return "k";
            case "l": return "l";
            case "m": return "m";
            case "n": return "n";
            case "o": return "o";
            case "p": return "p";
            case "q": return "q";
            case "r": return "r";
            case "s": return "s";
            case "t": return "t";
            case "u": return "u";
            case "v": return "v";
            case "w": return "w";
            case "x": return "x";
            case "y": return "y";
            case "z": return "z";
            default: return "#";
        }
    }
}

Basically I calculate on the fly which group the torrent belongs to based on it’s Name property, defaulting to symbol if no letter is found.

Lastly I Union together the list of torrent groups and empty groups to create a fully filled list of groups that I bind to the long list selector’s ItemsSource property.

That should be all you need to create an alphabetical long list selector. A bit long winded in places, but it does the job nicely.

 

Step 4: Where else can I get info?

Please check out the silverlight toolkit’s sample project for a succinct and easy to follow piece of sample code for the long list selector and other cool controls.

Using the MVC-Mini-Profiler with Entity Framework

Posted on July 26th, 2011 by Benjii

Notice how the success sign points at the title?The MVC Mini Profiler is an awesome tool that can help you reduce your page load times by showing you exactly how long each action, database query, view and even partial view took to load. It can be a little tricky to get right, especially using Entity Framework, but if you follow these steps you should be up and running in no time.

Step 1: Nuget it Baby

PM> Install-Package MiniProfiler

If you haven’t used Nuget yet, now would be an excellent time to start as it makes adding references to third party assemblies a breeze. Click the link above to go to the package page to find out more.

By the time you’re done with this step, you should end up with a reference to the mini profiler in your project.

Step 2: Hook it up

To get the profiler profiling all we need to do is add the following to our Global.asax.cs:

protected void Application_BeginRequest()
{
    if (Request.IsLocal)
    {
        MiniProfiler.Start();
    }
}

protected void Application_EndRequest()
{
    MiniProfiler.Stop();
}

Adding the check for Request.IsLocal means that the profiler will only run when the site is loaded locally. Feel free to add your own check’s. It’s usually handy to check for a cookie or an admin login, to see performance on a production website.

Step 3: Add the includes (and you’re finished)

Add this quick line of code to your _layout view or at the end of your head section. This adds the necessary javascript and css to keep it neat and tidy in the corner.

@MvcMiniProfiler.MiniProfiler.RenderIncludes()

By this point you can fire up your web pages and watch the pretty profiler in action, however it will be a bit bare. Read on to add extra bits and pieces to your fancy new profiler.

Step 4 (Optional): I want SQL profiling!

Very good sir, the profiler supports quite a number of database providers. I’m going to focus on using Entity Framework, as that’s where I had the most trouble. Luckily the good community at Stack Overflow have done the leg work here so we can benefit from their genius. Simply use the following code to retrieve your DB context:

public static T GetProfiledContext<T>() where T : System.Data.Objects.ObjectContext
{
    var conn = ProfiledDbConnection.Get(GetStoreConnection<T>());
    return ObjectContextUtils.CreateObjectContext<T>(conn);
}

public static DbConnection GetStoreConnection<T>() where T : System.Data.Objects.ObjectContext
{
    return GetStoreConnection("name=" + typeof(T).Name);
}

public static DbConnection GetStoreConnection(string entityConnectionString)
{
    // Build the initial connection string.
    var builder = new EntityConnectionStringBuilder(entityConnectionString);

    // If the initial connection string refers to an entry in the configuration, load that as the builder.
    object configName;
    if (builder.TryGetValue("name", out configName))
    {
        var configEntry = WebConfigurationManager.ConnectionStrings[configName.ToString()];
        builder = new EntityConnectionStringBuilder(configEntry.ConnectionString);
    }

    // Find the proper factory for the underlying connection.
    var factory = DbProviderFactories.GetFactory(builder.Provider);

    // Build the new connection.
    DbConnection tempConnection = null;
    try
    {
        tempConnection = factory.CreateConnection();
        tempConnection.ConnectionString = builder.ProviderConnectionString;

        var connection = tempConnection;
        tempConnection = null;
        return connection;
    }
    finally
    {
        // If creating of the connection failed, dispose the connection.
        if (tempConnection != null)
        {
            tempConnection.Dispose();
        }
    }
}

Just call the GetProfiledContext Method with your entities class like so:

var context = GetProfiledContext<MyModelEntities>();

Finally, we need to add this snippet to our web.config to allow for the DbProviderFactories class to do it’s job properly:

<system.data>
    <dbproviderfactories>
        <remove invariant="MvcMiniProfiler.Data.ProfiledDbProvider" />
        <add description="MvcMiniProfiler.Data.ProfiledDbProvider" invariant="MvcMiniProfiler.Data.ProfiledDbProvider" type="MvcMiniProfiler.Data.ProfiledDbProviderFactory, MvcMiniProfiler, Version=1.6.0.0, Culture=neutral, PublicKeyToken=b44f9351044011a3" name="MvcMiniProfiler.Data.ProfiledDbProvider" />
    </dbproviderfactories>
</system.data>

Note that this config is for assembly version 1.6.0.0, in future you may need to change this number.

That’s it! You should now see some sql load times to your page load times, and if you click the link it will show you the queries generated, duplicate queries and other sql goodies.

Step 5 (Optional): I want Automatic Controller, View and Partial Profiling!

Well have you come to the right place. Just the other day Sam Saffron posted about automatic controller and view profiling and it couldn’t be simpler. Create these two classes:

ProfilingActionFilter.cs

public class ProfilingActionFilter : ActionFilterAttribute
{
    const string stackKey = "ProfilingActionFilterStack";

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var mp = MiniProfiler.Current;
        if (mp != null)
        {
            var stack = HttpContext.Current.Items[stackKey] as Stack<IDisposable>;
            if (stack == null)
            {
                stack = new Stack<IDisposable>();
                HttpContext.Current.Items[stackKey] = stack;
            }

            var prof = MiniProfiler.Current.Step("Controller: " + filterContext.Controller.ToString() + "." + filterContext.ActionDescriptor.ActionName);
            stack.Push(prof);

        }
        base.OnActionExecuting(filterContext);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);
        var stack = HttpContext.Current.Items[stackKey] as Stack<IDisposable>;
        if (stack != null && stack.Count > 0)
        {
            stack.Pop().Dispose();
        }
    }
}

ProfilingViewEngine.cs

public class ProfilingViewEngine : IViewEngine
{
    class WrappedView : IView
    {
        IView wrapped;
        string name;
        bool isPartial;

        public WrappedView(IView wrapped, string name, bool isPartial)
        {
            this.wrapped = wrapped;
            this.name = name;
            this.isPartial = isPartial;
        }

        public void Render(ViewContext viewContext, System.IO.TextWriter writer)
        {
            using (MiniProfiler.Current.Step("Render " + (isPartial ? "partial" : "") + ": " + name))
            {
                wrapped.Render(viewContext, writer);
            }
        }
    }

    IViewEngine wrapped;

    public ProfilingViewEngine(IViewEngine wrapped)
    {
        this.wrapped = wrapped;
    }

    public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
    {
        var found = wrapped.FindPartialView(controllerContext, partialViewName, useCache);
        if (found != null && found.View != null)
        {
            found = new ViewEngineResult(new WrappedView(found.View, partialViewName, isPartial: true), this);
        }
        return found;
    }

    public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
    {
        var found = wrapped.FindView(controllerContext, viewName, masterName, useCache);
        if (found != null && found.View != null)
        {
            found = new ViewEngineResult(new WrappedView(found.View, viewName, isPartial: false), this);
        }
        return found;
    }

    public void ReleaseView(ControllerContext controllerContext, IView view)
    {
        wrapped.ReleaseView(controllerContext, view);
    }
}

Finally, add this snippet of code to your Global.asax.cs – Application_Start() method to hook it up:

// Add Profiling Action Filter (mvc mini profiler)
GlobalFilters.Filters.Add(new ProfilingActionFilter());

// Add Profiling View Engine (mvc mini profiler)
var copy = ViewEngines.Engines.ToList();
ViewEngines.Engines.Clear();
foreach (var item in copy)
{
    ViewEngines.Engines.Add(new ProfilingViewEngine(item));
}

RegisterGlobalFilters(GlobalFilters.Filters);

References and More Information

Enjoy your brand new MVC Mini Profiler. Many thanks go to the following collection of awesome links that I’ve sourced the different code snippets from. Be sure to check them out for extra details and clarity.

The MVC Mini Profiler Home Page

Scott Hanselman’s post about using the MVC Mini Profiler

Stack Overflow solution to the EF connection problem

Stack Overflow inspiration for web config changes

Sam Saffron’s post on Automatic Controller and View Profiling

Scrollfixed Sidebar Tutorial with jQuery

Posted on June 6th, 2011 by Benjii

With the unveiling of my brand new blog theme, you may notice a cool little quirk that appears every time you scroll past the header. That’s right not only does the sidebar follow you, but it also pops up a cool mini version of my main header. This post will describe the surprisingly difficult time I had getting my head around the usage of the position fixed css property.

A little bit of CSS

The CSS for this isn’t actually the hard part, see below:

#wrapper
{
    margin: 0 auto;
    min-width: 1000px;
    width:80%;
    max-width:1400px;
    overflow:hidden;
    position:relative;
}
aside
{
    padding-top:30px;
    overflow: hidden;
    width: 300px;
    float: right;
    clear: none;
}
aside.fixed
{
    position:fixed;
    top:0;
}

Here I include the fluid width for the wrapper, and set the sidebar to it’s regular place of floated right with 300px width. Simple. The tricky part comes when trying to position the sidebar with a fixed position. Little did I know that position relative has no effect on the position fixed child. Position fixed is always relative to the viewport.

jQuery to the Rescue

Luckily for us, jQuery makes this a very simple problem to fix. At the same time we detect if the sidebar has scrolled far enough, we’ll also measure the correct position for the left property of the sidebar. Thus solving the problem forever. Take a look:

$(document).ready(function () {

    var $sidebar = $("aside"),
        $window = $(window),
        offset = $sidebar.offset();

    var bodyRight = $("#wrapper").offset().left + $("#wrapper").width();

    $window.scroll(function () {
        if ($window.scrollTop() > offset.top) {
            $("aside").addClass("fixed");
            $("aside").css("left", bodyRight - $("aside").width());
            $(".miniHeader").slideDown("fast");
        } else {
            $("aside").removeClass("fixed");
            $(".miniHeader").slideUp("fast");
        }
    });

});

Here you’ll notice that we calculate the correct position of the sidebar, then if the window has scrolled far enough, we apply the class and position. You’ll also notice I slide down the mini header. It was just hiding with display none.

These simple pieces of code result in quite a snappy looking sidebar if I do say so myself :)

TwitLinkz Wp7 App – Summarizing a Twitter Feed Near You!

Posted on May 24th, 2011 by Benjii

SmallMobileTile_99x99

TwitLinkz takes your favourite twitter feeds, pulls out the links and summarizes the articles for you. It’s as simple as that.

If you source your up to the minute news from twitter, Twitlinkz is a great way to browse the headlines.

Available in the marketplace now, TwitLinkz is free and is set to change the way you get your real-time information.

 

loadingScreensearchwatching2linklistarticle

What does it actually do?

Here’s the step by step:

  1. Follow a twitter feed like #wp7, @benjii22 or “Australia”
  2. TwitLinkz searches the twitter feed for any links that were posted
  3. TwitLinkz fetches and summarizes the articles
  4. You enjoy a list of headlines with article summaries relevant to you

 

Future Features

I love building Windows Phone 7 apps, so you guys can expect these features to land on your doorstep just as soon as you can say, “Why the hell did he put a z on the end…”

  • Save your articles for later with Instapaper and other exciting web apps
  • Push Notifications and Live Tiles
  • Sign in with twitter for more personalised use

 

Download TwitLinkz Now, It’s Free!

 

wp7_278x92_blue

Torrential – uTorrent WP7 App – Version 1.3 Available Now!

Posted on May 8th, 2011 by Benjii

After taking a while to explore other ventures, I’ve finally come back strong with a gleaming new update to Torrential, the WP7 uTorrent Remote Control App.

The update is now with Microsoft so you should be receiving it soon, but while we wait, let’s check out what I’ve done:

Live Tiles

Expect to see these tiles hitting your home screen during the course of this week. At the moment they’ll be swapping out intermittently during downloads, but you should be able to select your preferred version next release.

nextupgreenupdowngreen

 

EzTv Integrated Search

Just like the Isohunt integrated search you’ve come to know and love, you can now search EzTv for your favourite goodies while on the run.

favouritegoodies

 

Favourite Websites

Tired of navigating to the same websites every time you want to download a new torrent? So was I, so now you can save your favourite websites and navigate to them with ease.

AddFavouriteFavourites

 

There’s also a couple of performance updates in there helping speed things up.

As always you can Request a New Feature thanks to the awesome people at Uservoice.

A Brand New Set of Live Tiles for Torrential Coming Soon

Posted on May 6th, 2011 by Benjii

Last week I was asked to provide the live tiles for my application to put towards a “Wall of Live Tiles” display that Microsoft will be showing off in an upcoming event for Windows Phone 7. I was more than happy to join the wall, but my tiles weren’t exactly the greatest looking things in the world. I’d like to say they were “practical” but really I was just being lazy. Either way I needed to come up with some new tiles, and fast. Here are the results of my spree in my favourite accent colours:

 

mainbluenextupblueupdownblue

maingreennextupgreenupdowngreen

mainorangenextuporangeupdownorange

 

Expect these bad boys to be hitting your copy of Torrential soon.

Push Notifications in Windows Phone 7 – #3 Push that Notification

Posted on April 23rd, 2011 by Benjii

Finally we arrive at the thrilling conclusion to my push notifications miniseries. This time we will actually push a notification to a device we have previously recorded.

Posts in the series:

  1. Code on the device – This will handle everything you need to put into your wp7 app
  2. Code on the server – This will handle receiving and storing your user’s device URIs on the server
  3. Push that Notification – This will handle actually pushing notifications

 

Step 1 – Create a Project to do the Pushing

When we left off, we had created a WP7 App Project and a WCF Rest Service Project. Now we need to create a Windows Service or Console Application to run on the server, pushing notifications when it is necessary. So that we can see the results of our pushing easily, I’ve opted to create a Console Application.

 

Step 2 – Copy this Super Useful Notifications Class

If you take a quick look at this code, you’ll see that we are simply sending some XML to a URI given to us by Microsoft. We picked up the URI in my last post, so read back if you haven’t got one yet. Regardless, here’s the code:

public class PushNotifications
{
    public string SendToast(string uri, string text1, string text2)
    {
        string template =
	        "<?xml version='1.0' encoding='utf-8'?>" +
	        "<wp:notification xmlns:wp='WPNotification'>" +
	            "<wp:toast>" +
	                "<wp:text1>{0}</wp:text1>" +
	                "<wp:text2>{1}</wp:text2>" +
	            "</wp:toast>" +
	        "</wp:notification>";

        string toastXML = string.Format(template, text1, text2);

        HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(uri);

        sendNotificationRequest.Method = "POST";
        sendNotificationRequest.Headers = new WebHeaderCollection();

        sendNotificationRequest.ContentType = "text/xml";
        sendNotificationRequest.Headers.Add("X-WindowsPhone-Target", "toast");
        sendNotificationRequest.Headers.Add("X-NotificationClass", "2");

        byte[] notificationMessage = new UTF8Encoding().GetBytes(toastXML);
        sendNotificationRequest.ContentLength = notificationMessage.Length;

        using (Stream requestStream = sendNotificationRequest.GetRequestStream())
        {
            requestStream.Write(notificationMessage, 0, notificationMessage.Length);
        }

        // Sends the notification and gets the response.
        HttpWebResponse response = (HttpWebResponse)sendNotificationRequest.GetResponse();
        string notificationStatus = response.Headers["X-NotificationStatus"];
        string notificationChannelStatus = response.Headers["X-SubscriptionStatus"];
        string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];

        return notificationStatus;
    }

    public string SendLiveTile(string uri, string backgroundImageUrl, string title, int count)
    {
        string template =
            "<?xml version='1.0' encoding='utf-8'?>" +
            "<wp:notification xmlns:wp='WPNotification'>" +
                "<wp:tile>" +
                    "<wp:backgroundimage>{0}</wp:backgroundimage>" +
                    "<wp:count>{1}</wp:count>" +
                    "<wp:title>{2}</wp:title>" +
                "</wp:tile>" +
            "</wp:notification>";

        string tileXML = string.Format(template, backgroundImageUrl, count.ToString(), title);

        HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(uri);

        sendNotificationRequest.Method = "POST";
        sendNotificationRequest.Headers = new WebHeaderCollection();

        sendNotificationRequest.ContentType = "text/xml";
        sendNotificationRequest.Headers.Add("X-WindowsPhone-Target", "token");
        sendNotificationRequest.Headers.Add("X-NotificationClass", "1");

        byte[] notificationMessage = new UTF8Encoding().GetBytes(tileXML);
        sendNotificationRequest.ContentLength = notificationMessage.Length;

        using (Stream requestStream = sendNotificationRequest.GetRequestStream())
        {
            requestStream.Write(notificationMessage, 0, notificationMessage.Length);
        }

        // Sends the notification and gets the response.
        HttpWebResponse response = (HttpWebResponse)sendNotificationRequest.GetResponse();
        string notificationStatus = response.Headers["X-NotificationStatus"];
        string notificationChannelStatus = response.Headers["X-SubscriptionStatus"];
        string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];

        return notificationStatus;
    }
}

There are a few things to note with the above code. First of all, the XML should be used exactly as is. I’ve heard of a few people having trouble because they missed a capital letter, or didn’t have all of the correct parameters.

Secondly, pay close attention to the headers we are sending. The X-WindowsPhone-Target header sets the type of notification we are pushing (Toast, Live Tile or Raw) and the X-NotificationClass header sets how quickly the notification will be delivered. I’ve only ever seen people use the most urgent value.

You can find some more info about the above code from How To: Send a Push Notification for Windows Phone

Lastly, to see if our message was successfully received by Microsoft’s servers, pay attention to the three headers we receive back from our web call. I pass the X-NotificationStatus back as it gives the best indicator of whether or not the notification worked.

I encourage you to take a look at the Push Notification Service Response Codes for Windows Phone to get an idea of the kinds of responses you could receive.

 

Step 3 – Push that Notification!

Just as an example, I’m going to setup our console app so that it pushes a notification to the last device added to our database whenever you press “P”. Check out the code:

public class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Welcome to the sample push notification server");

        Console.WriteLine("Running... Press enter to exit");

        bool running = true;
        while (running)
        {
            var key = Console.ReadKey(true);

            if (key.Key == ConsoleKey.Enter)
            {
                running = false;
            }
            else if (key.Key == ConsoleKey.P)
            {
                using (var context = new WP7AppDBEntities())
                {
                    // First try to find an existing device
                    var device = (from d in context.Devices
                                    orderby d.Added descending
                                    select d).FirstOrDefault();

                    if (device != null)
                    {
                        PushMessage(device.URI, "Success", "We sent you a message!");
                    }
                }
            }
        }
    }

    static void PushMessage(string uri, string mainText, string secondaryText)
    {
        Console.WriteLine(DateTime.UtcNow.ToString("dd/MM/yyyy hh:mm:ss ") + "Ready to Push Message - " + mainText);

        if (!string.IsNullOrEmpty(uri))
        {
            try
            {
                PushNotifications p = new PushNotifications();
                var pushresult = p.SendToast(uri, mainText, secondaryText);

                Console.WriteLine(DateTime.UtcNow.ToString("dd/MM/yyyy hh:mm:ss ") + "Push Notification Sent: " + pushresult);
            }
            catch (Exception ex)
            {
                Console.WriteLine(DateTime.UtcNow.ToString("dd/MM/yyyy hh:mm:ss ") + "Error pushing toast notification: " + ex.Message + " - " + ex.StackTrace);
            }
        }
    }
}

Essentially the above code waits for you to press “P”, pulls the most recently added device from the database we setup earlier and then sends a toast message to the device. I’ve also added some Console.Writes around the place so you can see what’s going on.

You can also use the above code to send a Live Tile update to a device, but that’s a blog post for another day.

Push Notifications in Windows Phone 7 – #2 Code on the Server

Posted on January 19th, 2011 by Benjii

This is the second part in the mini series that is taking us through the steps of setting up push notifications for your windows phone 7 app.

Posts in the series:

  1. Code on the device – This will handle everything you need to put into your wp7 app
  2. Code on the server – This will handle receiving and storing your user’s device URIs on the server
  3. Push that Notification – This will handle actually pushing notifications

 

To handle the requests from our devices, we will be setting up a WCF REST Service Project. This will allow us to keep track of all the devices that have downloaded our app, as well as keep track of which users want push notifications or live tiles.

Step 1: Create a New WCF REST Service Project

newproject

  • Open the New Project dialog
  • Select the Online Templates menu on the left
  • Select the WCF sub-menu
  • Select the WCF REST Service Template 40(CS) project template
  • Choose a project name and click OK

To clean up our project a bit, delete SampleItem.cs and rename the Service1.cs file to Notifications.cs. If you are asked to rename all references, do that as well.

Also, you must edit the Global.asax.cs and change the string “Service1” to “Notifications” in the RegisterRoutes method.

Lets get rid of all the rubbish that is in the Notifications class by default. Make your class look like this:

[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class Notifications
{

}

 

Step 2: Handle the API Call from the Device

Now that we have a clean project add the following method to your Notifications class:

[WebInvoke(UriTemplate = "register?deviceid={deviceid}&uri={uri}",
    ResponseFormat = WebMessageFormat.Json,
    Method = "GET")]
public string Register(string deviceid, string uri)
{
    // TODO: Save DeviceID and URI here
}

 

For those of you who are not used to WCF REST services, the above method is essentially just a web method. It is decorated with a WebInvoke Attribute which contains the following parameters:

  • UriTemplate – This is the URL we want the method to respond to. Using the above as an example, you would call http://example.com/notifications/register?deviceid=123&uri=456
  • ResponseFormat – This format you want to return your data in. JSON is recommended as it is lightweight.
  • Method – Either GET or POST, your choice.

We’ve done enough now, that if you were to publish your service, the device would think everything was working. Now it’s time to actually save the data.

 

Step 3: Setup the Database

Now we are going to create the database to store the device information in. Most of you, like me, might be lazy when it comes to DB work, so here is a diagram and script (SQL Server) to create the table for storing devices.

Go ahead and create a new database called WP7AppDB and add the table below.

tablediagram

/****** Object:  Table [dbo].[Devices]    Script Date: 01/18/2011 23:21:39 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Devices](
    [DeviceID] [nvarchar](200) NOT NULL,
    [URI] [nvarchar](400) NOT NULL,
    [PushNotifications] [bit] NOT NULL,
    [Added] [datetime] NOT NULL,
    [Modified] [datetime] NOT NULL,
    [LiveTile] [bit] NOT NULL,
    [LiveTileFrequency] [int] NOT NULL,
    [LiveTileLastUpdate] [datetime] NULL,
 CONSTRAINT [PK_Devices_1] PRIMARY KEY CLUSTERED
(
    [DeviceID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[Devices] ADD  CONSTRAINT [DF_Devices_PushNotifications]  DEFAULT ((0)) FOR [PushNotifications]
GO

ALTER TABLE [dbo].[Devices] ADD  CONSTRAINT [DF_Devices_Added]  DEFAULT (getdate()) FOR [Added]
GO

ALTER TABLE [dbo].[Devices] ADD  CONSTRAINT [DF_Devices_Modified]  DEFAULT (getdate()) FOR [Modified]
GO

ALTER TABLE [dbo].[Devices] ADD  CONSTRAINT [DF_Devices_LiveTile]  DEFAULT ((0)) FOR [LiveTile]
GO

ALTER TABLE [dbo].[Devices] ADD  CONSTRAINT [DF_Devices_LiveTileFrequency]  DEFAULT ((15)) FOR [LiveTileFrequency]
GO

 

Step 4: Connect to the Database

newentityThere are a million ways to connect to a database, so I’m going to run through my favourite way: Entity Framework.

Start by right clicking your project in Visual Studio and selecting Add > New Item…

Next select the ADO.NET Entity Data Model and change the name to WP7AppDB.edmx. I like to use the Database name as the model name. Click OK.

The Entity Data Model Wizard should appear so follow these steps:

  1. Click Generate from Database, Click Next
  2. Click New Connection, enter your server details, select the WP7AppDB database and click ok
  3. Click the “Yes, include the sensitive data in the connection string” option
  4. Ensure the “Save entity connection settings in Web.Config as:” checkbox is ticked and that “WP7AppDBEntities” is in the textbox below. Click Next
  5. Tick the Tables checkbox and Click Finish.

You should now have WP7AppDB.edmx at the bottom of your project.

 

Step 5: Actually Save the Data

Now that everything is setup, we can finally save the device information. Copy this code into the Register method we created before, replacing the TODO comment we left:

Device device = null;

using (var context = new WP7AppDBEntities())
{
    if (!String.IsNullOrWhiteSpace(deviceid))
    {
        // First try to find an existing device
        device = (from d in context.Devices
                  where d.DeviceID == deviceid
                  select d).FirstOrDefault();

        if (device != null)
        {
            // Do we need to update the URI?
            if (device.URI != uri)
                device.URI = uri;
        }
        else
        {
            device = new Device()
            {
                DeviceID = deviceid,
                URI = uri,
                Added = DateTime.Now,
                Modified = DateTime.Now
            };

            context.Devices.AddObject(device);
        }

        context.SaveChanges();
    }
}

return deviceid;

If you are not familiar with Entity Framework or LINQ, let me explain from the top.

The code in the using statement produces our connection to the database and places it into the context variable. After checking that we were actually given a value, we then use LINQ to see if we can find an existing device in the database.

If we do find a device, we check the existing URI against the one just given to us by the device and if they’re different, we update the database.

If we don’t find a device, we create a new instance of the Device class, which was generated by entity framework, fill in the details and add the new device to the database.

The SaveChanges method actually commits the changes we just made to the database, nothing would happen without it.

We then return the device id passed to us just as a reference. You might like to pass something more meaningful back, but I consider this just a fire and forget method.

 

Step 6: Give it a Try

You’re done. Go ahead and hit Ctrl+F5 and hit the register method with some dummy data. I did this: http://localhost:9542/notifications/register?deviceid=123&uri=456 (replace the port number with whatever your visual studio created)

If you completed the steps in the previous post to this one, you can even try running your WP7 application and seeing if it connects with some actual data.

 

Stay tuned for the next exciting episode, where we look at actually pushing notifications to the phone