Using LoopingSelector from the Silverlight Toolkit

Posted Sunday, November 21, 2010 by

Looping Selector from Silverlight ToolkitThe Silverlight Toolkit for Windows Phone 7 contains a lot of really useful controls, including the DateSelector with it's very cool infinite list selector. What would be nice to be to have that sort of list for other types of data. Thankfully the team at Microsoft has made this possible.

Edit: I've uploaded some sample code at Silverlight Toolkit LoopingSelector Demo.

The DateSelector control builds off a simpler control named LoopingSelector which contains all the functionality for the looping control, we just need to provide the description of the available data. We do this through the interface ILoopingSelectorDataSource, for our simple example we'll use a numerical data source with a minimum and maximum.

The interface is pretty self explanatory, GetPrevious and GetNext methods allow you to define the order of the data. We use the SelectedItem property to hold a reference to currently selected item and use the SelectionChanged event to notify the control when the SelectedItem changes (we change it from within code for instance).

Here's our implementation of NumericDataSource.

public class NumbersDataSource : ILoopingSelectorDataSource

{

    private int minimum = 1;

    private int maximum = 100;

    private int selectedItem = 1;

 

    public event EventHandler<SelectionChangedEventArgs> SelectionChanged;

 

    protected virtual void OnSelectedChanged(SelectionChangedEventArgs e)

    {

        var selectionChanged = SelectionChanged;

 

        if(selectionChanged != null)

            selectionChanged(this, e);

    }

 

    public object GetNext(object relativeTo)

    {

        var nextValue = ((int)relativeTo) + 1;

 

        return nextValue <= Maximum ? nextValue : Minimum;

    }

 

    public object GetPrevious(object relativeTo)

    {

        var previousValue = ((int)relativeTo) - 1;

 

        return previousValue >= Minimum ? previousValue : Maximum;

    }

 

    public object SelectedItem

    {

        get

        {

            return selectedItem;

        }

        set

        {

            var oldValue = selectedItem;

            var newValue = (int)value;

 

            if(oldValue == newValue)

                return;

 

            selectedItem = newValue;

 

            OnSelectedChanged(new SelectionChangedEventArgs(new[] { oldValue}, new[] { newValue }));

        }

    }

 

    public int Minimum

    {

        get

        {

            return minimum;

        }

        set

        {

            minimum = value;

 

            if(selectedItem < minimum)

                SelectedItem = value;

        }

    }

 

 

    public int Maximum

    {

        get

        {

            return maximum;

        }

        set

        {

            maximum = value;

 

            if(selectedItem > maximum)

                SelectedItem = value;

        }

    }

}

Now that we have our data source it's time to add a few controls to our page for this example we'll add three to mimic the style of the DateSelector. From my experimentaion I've found that a number of properties need to be set on the LoopingSelector for it to work correctly, specifically ItemSize and Width, both are fairly obvious but nessecary. For each Selector we'll create a seperate instance of our data source with some different parameters. We'll also create a DataTemplate resource and share that amongst the selectors.

The resulting xaml is as follows.

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

    <Grid.Resources>

        <DataTemplate x:Key="NumberTemplate">

            <Grid>

                <TextBlock Text="{Binding}" FontSize="54" FontFamily="{StaticResource PhoneFontFamilySemiBold}" HorizontalAlignment="Center" VerticalAlignment="Center" />

            </Grid>

        </DataTemplate>

    </Grid.Resources>

    <Grid.ColumnDefinitions>

        <ColumnDefinition Width="*"/>

        <ColumnDefinition Width="*"/>

        <ColumnDefinition Width="*"/>

    </Grid.ColumnDefinitions>

    <controls:LoopingSelector Margin="12" Width="128" ItemSize="128,128" ItemTemplate="{StaticResource NumberTemplate}">

        <controls:LoopingSelector.DataSource>

            <local:NumbersDataSource />

        </controls:LoopingSelector.DataSource>

    </controls:LoopingSelector>

    <controls:LoopingSelector Grid.Column="1" Margin="12" Width="128" ItemSize="128,128" ItemTemplate="{StaticResource NumberTemplate}">

        <controls:LoopingSelector.DataSource>

            <local:NumbersDataSource Minimum="10" Maximum="20" />

        </controls:LoopingSelector.DataSource>

    </controls:LoopingSelector>

    <controls:LoopingSelector Grid.Column="2" Margin="12" Width="128" ItemSize="128,128" ItemTemplate="{StaticResource NumberTemplate}">

        <controls:LoopingSelector.DataSource>

            <local:NumbersDataSource Maximum="0" Minimum="-100" />

        </controls:LoopingSelector.DataSource>

    </controls:LoopingSelector>

</Grid>

Edit: I've uploaded some sample code at Silverlight Toolkit LoopingSelector Demo.

Binding Html to the Web Browser Control

Posted Wednesday, September 29, 2010 by

Currently the fastest way to display a string of html on the Windows Phone 7 is to use the Web Browser control, simply call the NavigateToString method passing the html you want to display.

When trying to use this in an MVVM pattern the method call becomes slightly problematic, we'd much rather have a bindable property. Thankfully we can use attached properties to achieve this.

public static class WebBrowserHelper

{

    public static readonly DependencyProperty HtmlProperty = DependencyProperty.RegisterAttached(

        "Html", typeof(string), typeof(WebBrowserHelper), new PropertyMetadata(OnHtmlChanged));

 

    public static string GetHtml(DependencyObject dependencyObject)

    {

        return (string)dependencyObject.GetValue(HtmlProperty);

    }

 

    public static void SetHtml(DependencyObject dependencyObject, string value)

    {

        dependencyObject.SetValue(HtmlProperty, value);

    }

 

    private static void OnHtmlChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)

    {

        var browser = d as WebBrowser;

 

        if(browser == null)

            return;

 

        var html = e.NewValue.ToString();

 

        browser.NavigateToString(html);

    }

}

Once you have the attached property the binding becomes very simple.

<phone:WebBrowser cxi:WebBrowserHelper.Html="{Binding Question.Body}" />

If you really want to add some polish to your app I suggest looking at fellow kiwi Ben Gracewood's solution to having your html respect the current phone theme at Integrated Links and Styling for Windows Phone 7 Web Browser Control.

Windows Phone 7 Tutorials: Maps, GPS and Accelerometer

Posted Wednesday, September 22, 2010 by

I've just uploaded the next three tutorials in the Windows Phone 7 Tutorials series. I ended up doing a batch of three this time due the simplistic nature of any tutorials involving features the emulator doesn't support well such as GPS and the Accelerometer.

Christchurch Earthquake Quake covers something very close to New Zealand at the moment, earthquakes. In the tutorial we cover retrieving earthquake data, parising it using Linq to Xml and placing quake markers on the new Bing Maps control.

Where am I? shows how you can use Reactive Extensions to emulate the GeoCoordinateWatcher class in the phone emulator.

Hold it Level again uses Reactive Extensions, this time to emulate the Accelerometer, we also use the VibrateController to provide tactile feedback to the user.

Relative Date Time Converter

Posted Saturday, September 11, 2010 by

Value converters are really what help make good applications become great, they plug into the binding infrastructure and help convert values appropriate to the model into values appropriate for the view. Being able to convert say a temperature from a simple integer into something like a SolidColourBrush gives a very easy representation of heat.

These work in all three of the xaml based frameworks, Windows Phone 7, Silverlight and WPF and because value converters are so compact they're also very testable.

A lot of sites represent days in a more interesting "5 minutes ago" format, so I set about throwing together a quick value converter for this, converting from a date to a string. I've become a big fan of using dictionaries of functions in this way for times when many if statements become too messy and a strategy style pattern is overkill.

public class RelativeDateTimeConverter : IValueConverter

{

    private const int Minute = 60;

    private const int Hour = Minute * 60;

    private const int Day = Hour * 24;

    private const int Year = Day * 365;

 

    private readonly Dictionary<long, Func<TimeSpan, string>> thresholds = new Dictionary<long, Func<TimeSpan, string>>

    {

        {2, t => "a second ago"},

        {Minute,  t => String.Format("{0} seconds ago", (int)t.TotalSeconds)},

        {Minute * 2,  t => "a minute ago"},

        {Hour,  t => String.Format("{0} minutes ago", (int)t.TotalMinutes)},

        {Hour * 2,  t => "an hour ago"},

        {Day,  t => String.Format("{0} hours ago", (int)t.TotalHours)},

        {Day * 2,  t => "yesterday"},

        {Day * 30,  t => String.Format("{0} days ago", (int)t.TotalDays)},

        {Day * 60,  t => "last month"},

        {Year,  t => String.Format("{0} months ago", (int)t.TotalDays / 30)},

        {Year * 2,  t => "last year"},

        {Int64.MaxValue,  t => String.Format("{0} years ago", (int)t.TotalDays / 365)}

    };

 

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)

    {

        var dateTime = (DateTime)value;

        var difference = DateTime.UtcNow - dateTime.ToUniversalTime();

 

        return thresholds.First(t => difference.TotalSeconds < t.Key).Value(difference);

    }

 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

    {

        throw new NotSupportedException();

    }

}

<TextBlock Style="{StaticResource PhoneTextSubtleStyle}" Text="{Binding Created, Converter={StaticResource RelativeDateTimeConverter}}" />

Page 5 of 16<34567>

Professional Windows App Development