Visually Located

XAML and GIS

Synching the scroll position of two LongListSelectors

I was looking at Stackoverflow and found a question asking about how to sync two LongListSelectors so that their scroll position was always the same. I thought this was so cool that it was worth sharing it with the masses.

First create a new class called MyLongListSelector. Unlike the ListBox, the LLS does not use a ScrollViewer to scroll the content. Instead, it uses a ViewportControl. We need to override the OnApplyTemplate and hook into the ViewportChanged event of the ViewportControl .

public class MyLongListSelector : LongListSelector
{
    private ViewportControl _viewport;
 
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
 
        _viewport = (ViewportControl)GetTemplateChild("ViewportControl");
        _viewport.ViewportChanged += OnViewportChanged;
    }
}

Within the event handler for the ViewportChanged event, we’ll set a DependencyProperty that represent the top of the scroll position.

private void OnViewportChanged(object sender, ViewportChangedEventArgs args)
{
    ScrollPosition = _viewport.Viewport.Top;
}

The ScrollPosition property will represent the top of our own scroll position. We’ll create a DependencyProperty so that other MyLLS controls can bind to it and set it from xaml.

public double ScrollPosition
{
    get { return (double)GetValue(ViewPortProperty); }
    set { SetValue(ViewPortProperty, value); }
}
 
public static readonly DependencyProperty ViewPortProperty = DependencyProperty.Register(
    "ScrollPosition", 
    typeof(double), 
    typeof(MyLongListSelector), 
    new PropertyMetadata(0d, OnViewPortChanged));

When our own ScrollPosition changes, we’ll attempt to change the Viewport of the ViewportControl. We’ll do this because the value could be changing from xaml, where another control is setting it based on it’s value. We cannot set the Viewport directly, and we cannot set the top of the Viewport. Luckily the ViewportControl does have a SetViewportOrigin method that allows us to set the top of the scroll.

private static void OnViewPortChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var lls = (MyLongListSelector) d;
    
    if (lls._viewport.Viewport.Top.Equals(lls.ScrollPosition)) return;
 
    lls._viewport.SetViewportOrigin(new Point(0, lls.ScrollPosition));
}

Now we can place two MyLLS controls within a Grid in our page. I used the default DataboundApp from the Windows Phone 8 template and duplicated the existing LLS.

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <dataBoundApp1:MyLongListSelector x:Name="MainLongListSelector" Margin="0,0,-12,0" ItemsSource="{Binding Items}" SelectionChanged="MainLongListSelector_SelectionChanged"
                                      ScrollPosition="{Binding ScrollPosition, ElementName=MainLongListSelector2, Mode=TwoWay}">
        <dataBoundApp1:MyLongListSelector.ItemTemplate>
            <DataTemplate>
                <StackPanel Margin="0,0,0,17">
                    <TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                    <TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
                </StackPanel>
            </DataTemplate>
        </dataBoundApp1:MyLongListSelector.ItemTemplate>
    </dataBoundApp1:MyLongListSelector>
    <dataBoundApp1:MyLongListSelector x:Name="MainLongListSelector2" Grid.Column="1" Margin="0,0,-12,0" ItemsSource="{Binding Items}" 
                                      ScrollPosition="{Binding ScrollPosition, ElementName=MainLongListSelector, Mode=TwoWay}">
        <dataBoundApp1:MyLongListSelector.ItemTemplate>
            <DataTemplate>
                <StackPanel Margin="0,0,0,17">
                    <TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                    <TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
                </StackPanel>
            </DataTemplate>
        </dataBoundApp1:MyLongListSelector.ItemTemplate>
    </dataBoundApp1:MyLongListSelector>
</Grid>

Notice that the two LLS are binding to each others ScrollPosition property and are using TwoWay binding. This allows each control to set the other controls position.

This approach unfortunately causes some choppy scrolling and causes the scrolling to abruptly stop. We can help control that by checking the ManipulationState of the ViewportControl before we attempt to change the value of the Viewport. If the ManipulationState is Idle, we can be pretty sure that we should set the value because the ViewportControl is not the one being animated. We want to make sure that we do not set the Viewport for the LLS that is being animated.

private static void OnViewPortChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var lls = (MyLongListSelector) d;
 
    if (lls._viewport.Viewport.Top.Equals(lls.ScrollPosition)) return;
 
    if (lls._viewport.ManipulationState == ManipulationState.Idle)
    {
        lls._viewport.SetViewportOrigin(new Point(0, lls.ScrollPosition));
    }
}
With this approach we have a much smoother experience. Download a working sample.

Strongly type your settings saved in IsolatedStorageSettings

When creating an app you’ll need some way to save user settings. In Windows Phone (and Windows Store) Apps there are four possible ways to save settings.

  1. Key/Value pairs in IsolatedStorageSettings
  2. Save a file in IsolatedStorage
  3. Save values in a local database
  4. Save values in the cloud

Usually everyone starts by saving values into IsolatedStorageSettings, and for the most part, it’s probably the best way to go. If you are creating a music app, you can not use IsolatedStorageSettings. The AudioPlayerAgent simply will not get the correct setting values. If you are creating an app with a background agent, you should consider not using IsolatedStorageSettings for the same reason as before. You are less likely to have a problem with incorrect values with a normal background agent than you are when using an audio agent.

While using the IsolatedStorageSettings can be easy, the code can get ugly fast. You may have usage scattered throughout your code.

public MainPage()
{
    InitializeComponent();
 
    IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
 
    bool useLocation;
    if (settings.TryGetValue("UseLocation", out useLocation) == false)
    {
        useLocation = true;
    }
 
    // do something with the setting
}

Eventually you’ll get tired of all of this code everywhere and probably create a class and wrap the access within the properties.

public class Settings
{
    public bool UseLocation
    {
        get
        {
            IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
 
            bool useLocation;
            if (settings.TryGetValue("UseLocation", out useLocation) == false)
            {
                useLocation = true;
            }
            return useLocation;
        }
        set
        {
            IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
 
            settings["UseLocation"] = value;
            settings.Save();
        }
    }
 
    public double SomeOtherValue
    {
        get
        {
            IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
 
            double someOtherValue;
            settings.TryGetValue("SomeOtherValue", out someOtherValue);
            return someOtherValue;
        }
        set
        {
            IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
 
            settings["SomeOtherValue"] = value;
            settings.Save();
        }
    }
}

As you can see this eventually gets out of control. Let’s take this one step further. Instead of littering our code with all of this dictionary access, why don’t we just create a class with automatic properties that has a Save and Load method.

public class Settings
{
    public bool UseLocation { get; set; }
 
    public double SomeOtherValue { get; set; }
 
    public void Save()
    {
        IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
 
        settings["UseLocation"] = UseLocation;
        settings["SomeOtherValue"] = SomeOtherValue;
        settings.Save();
    }
 
    public void Load()
    {
        IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
 
        bool useLocation;
        if (settings.TryGetValue("UseLocation", out useLocation) == false)
        {
            useLocation = true;
        }
        UseLocation = useLocation;
 
        double someOtherValue;
        settings.TryGetValue("SomeOtherValue", out someOtherValue);
        SomeOtherValue = someOtherValue;
    }
}

Our code got a little better, but it’s not very extensible. Every time you add a new property you have to add keys/values to saving and loading. When you write your next app, you’ll copy and paste the file and change the strings. I enjoy reusing code, not copy/paste. I enjoy doing less work, not adding key/values for every setting property I have.

We can make this even better. We can make a reusable class, in which we do not have to worry about adding more than our auto properties. We do this with reflection and a base class.

public class Settings
{
    public void Save()
    {
        IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
        PropertyInfo[] properties = GetType().GetProperties();
        foreach (var property in properties)
        {
            settings[property.Name] = property.GetValue(this);
        }
 
        settings.Save();
    }
 
    public void Load()
    {
        IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
        PropertyInfo[] properties = GetType().GetProperties();
        foreach (var property in properties)
        {
            if (settings.Contains(property.Name))
            {
                property.SetValue(this, settings[property.Name]);
            }
        }
    }
}

Now that we have a base class for settings, we can create an instance of settings that are unique for our application. All this file needs is the properties that define the application settings.

public class AppSettings : Settings
{
    public AppSettings()
    {
        // default to true, let the user override this value
        UseLocation = true;
    }
 
    public bool UseLocation { get; set; }
 
    public double SomeOtherValue { get; set; }
}

With this approach, you’ll want to make sure that you load and save your settings when needed. I personally like to create a static method/property that creates my settings and loads the values.

Note: You could just save the file itself in iso settings rather than each property, but that is not the point of this post :-)

Creating simple page transitions using Storyboards: Fly away transition

This is the third post is a series about creating simple page transitions using Storyboards. We’ve created two basic page transitions with the  slide and turnstile transition. Now it’s time to get a little more complex. The mail app has what I like to call a “fly away” transition. When you tap an email, the subject flies away and the email slides up.

This transition is a little more complex. Instead of animating the entire page, we only animate one control that is contained within one item of a ListBox. When the selection changes we need to animate the “header” of the selected item. The problem is that the SelectedItem of a ListBox is the bound item, and not the UI representation of that item. Good news is if we are displaying items with any type of ItemsControl, we can get the container for the bound item. An ItemsControl has a ItemContainerGenerator property that returns an instance of an ItemContainerGenerator. The ItemContainerGenerator has a ContainerFromItem method that returns the DependencyObject used to display the bound item.

DependencyObject obj = itemsControl.ItemContainerGenerator.ContainerFromItem(selectedItem);

When we have that item, we need to get the first child that is a TextBlock.

private static T GetChildOfType<T>(this DependencyObject depObj) where T : DependencyObject
{
    if (depObj == null) return null;
 
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
    {
        var child = VisualTreeHelper.GetChild(depObj, i);
 
        var result = (child as T) ?? GetChildOfType<T>(child);
        if (result != null) return result;
    }
    return null;
}

Once we have the first TextBlock, we’ll need to animate it to the left and down. Like the Slide transition, we’ll animate the TranslateY and TranslateX properties of a CompositeTransform. We’ve been creating a lot of DoubleAnimations, so let’s make a method to help.

private static DoubleAnimation CreateDoubleAnimation(FrameworkElement element, double duration, 
                                                     double from, double to, string path)
{
    var animation = new DoubleAnimation { Duration = TimeSpan.FromSeconds(duration), To = to, From = from };
    Storyboard.SetTargetProperty(animation, new PropertyPath(path));
    Storyboard.SetTarget(animation, element);
    return animation;
}

From here the transition is very similar to the others.

public static async Task TransitionOutFlyaway(this ItemsControl source, object item)
{
    await source.Flyaway(item, 0, 480, 0, 100);
}
 
public static async Task TransitionInFlyaway(this ItemsControl source, object item)
{
    await source.Flyaway(item, -480, 0, -100, 0);
}
 
private static async Task Flyaway(this ItemsControl source, object item, double xFrom, double xTo, double yFrom, double yTo)
{
    if (source == null) return;
    if (item == null) return;
 
    DependencyObject obj = source.ItemContainerGenerator.ContainerFromItem(item);
    if (obj == null) return;
 
    TextBlock textBlock = obj.GetChildOfType<TextBlock>();
    if (textBlock == null) return;
 
    var story = new Storyboard();
    var xAnimation = CreateDoubleAnimation(textBlock, .2, xFrom, xTo, "(UIElement.RenderTransform).(CompositeTransform.TranslateX)");
    var yAnimation = CreateDoubleAnimation(textBlock, .2, yFrom, yTo, "(UIElement.RenderTransform).(CompositeTransform.TranslateY)");
 
    story.Children.Add(xAnimation);
    story.Children.Add(yAnimation);
    await story.BeginAsync();
}

In the video above, I used the Windows Phone Databound App project template. By default, this template uses the new LongListSelector. Unfortunately, the LongListSelector is not supported for this transition. The LongListSelector is not an ItemsControl, and as such, does not have a way to get the generated UI element from a bound item. To use this project, you’ll need to change the LongListSelector to a ListBox.

Now that we have our transition complete, we’ll need to use it! When the selection changes, we’ll call our new extension method. We’ll also need to store the selected item to ensure that we can animate it back in when the back is navigated to.

private async void MainLongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    // If selected item is null (no selection) do nothing
    if (MainLongListSelector.SelectedItem == null)
        return;
 
    // save the item so we can animate it when coming back.
    _selectedItem = MainLongListSelector.SelectedItem;
 
    try
    {
        await MainLongListSelector.TransitionOutFlyaway(_selectedItem);
    }
    catch { }
 
    // Navigate to the new page
    NavigationService.Navigate(new Uri("/DetailsPage.xaml?selectedItem=" + (_selectedItem as ItemViewModel).ID, UriKind.Relative));
 
    // Reset selected item to null (no selection)
    MainLongListSelector.SelectedItem = null;
}

When the page is navigated to, we’ll animate the item back in.

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    if (!App.ViewModel.IsDataLoaded)
    {
        App.ViewModel.LoadData();
    }
    try
    {
        await MainLongListSelector.TransitionInFlyaway(_selectedItem);
    }
    catch { }
 
    // set it to null to ensure it is not animated at the wrong time.
    _selectedItem = null;
}

You can download the sample application from this post which contains all of the transitions so far and uses the animations like in the video above.

Creating simple page transitions using Storyboards: Turnstile transition

In my last post, I talked about creating a simple transition to slide a page in or out. While that transition was functionality I needed for my app, Santa Calls, I thought it would be fun to do more of these posts. For this post we’ll create a turnstile transition. The turnstile transition is like a page turning.

For the turnstile transition, we need to use the animate the Projection of the page. We’ll set the Projection to be a PlaneProjection. To get the rotation to turn like a page would, we need to rotate around the vertical, or y-axis. The RotationY property of the PlaneProjection is how we accomplish this.

public static async Task TransitionInTurnstile(this FrameworkElement source)
{
    await source.Turnstile(75, 0);
}
 
public static async Task TransitionOutTurnstile(this FrameworkElement source)
{
    await source.Turnstile(0, -90);
}
 
private static async Task Turnstile(this FrameworkElement source, double from, double to)
{
    if (source == null) return;
 
    // make sure the projection pivots on the left side of the phone
    source.Projection = new PlaneProjection { CenterOfRotationX = 0 };
    
    var story = new Storyboard();
    var animation = new DoubleAnimation { Duration = TimeSpan.FromSeconds(.35), To = to, From = from };
    Storyboard.SetTargetProperty(animation, new PropertyPath("(UIElement.Projection).(PlaneProjection.RotationY)"));
    Storyboard.SetTarget(animation, source);
    story.Children.Add(animation);
    await story.BeginAsync();
}

The code is very similar to the slide transition, and still very simple. Just as before, I am using Morten’s Storyboard as a Task extension method. Adding these animations to pages is the same as before. simply call the transition method on the object you want to animate.

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    try
    {
        await LayoutRoot.TransitionInTurnstile();
    }
    catch { }
}

Remember that we are performing an async operation within a void method, so wrap the call in a try/catch. This code shouldn’t error, but you never know.

When navigating backwards, first cancel the navigation, animate, and then navigate!

protected override async void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
    e.Cancel = true;
    base.OnBackKeyPress(e);
 
    try
    {
        await LayoutRoot.TransitionOutTurnstile();
    }
    catch { }
    NavigationService.GoBack();
}

Creating simple page transitions using Storyboards: Slide transition

I recently updated my app Santa Calls to include a settings page that would allow the user to delay a call or pin lock the application. When navigating to this page I wanted a nice transition, but I wanted the tree in the background to remain. 

To accomplish this I set the background of these pages to be the tree image and moved the content in/out when needed. I would not be able to accomplish this type of transition using an SDK like the Windows Phone Toolkit or Telerik unless I set the application background to be the tree. I did not want to do this because the phone call pages do not have this same background.

The transitions are pretty simple and can be used by any page to move content up/down. I created a handy extension method that can be used by any FrameworkElement. To slide the content up into view, we need to create a Storyboard with a DoubleAnimation that will move the content (assumed to be a page) from the bottom of the page to the top.

public static async Task TransitionOutSlideDown(this FrameworkElement source)
{
    await source.SlideVertically(0, 800);
}
 
public static async Task TransitionInSlideUp(this FrameworkElement source)
{
    await source.SlideVertically(800, 0);
}
 
private static async Task SlideVertically(this FrameworkElement source, int from, int to)
{
    if (source == null) return;
 
    source.RenderTransform = new CompositeTransform();
    var story = new Storyboard();
    var animation = new DoubleAnimation { Duration = TimeSpan.FromSeconds(.2), To = to, From = from };
    Storyboard.SetTargetProperty(animation, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.TranslateY)"));
    Storyboard.SetTarget(animation, source);
    story.Children.Add(animation);
    await story.BeginAsync();          
}

This uses a BeginAsync extension method from Morten Nielsen to run a Storyboard as a Task. To slide the content up or down we pass the from and to Y values to the SlideVertically method.

Now that we have our extension methods, we need to use them. When navigating to a page, override the OnNavigatedTo method and call the TransitionInSlideUp method.

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
 
    try
    {
        await LayoutRoot.TransitionInSlideUp();
    }
    catch { }
}

The slide out transition is a little different. You already know how to exit app when awaiting an operation. This will be very similar. Instead of exiting the app, you’ll continue to navigate backwards.

protected override async void OnBackKeyPress(CancelEventArgs e)
{
    e.Cancel = true;
    base.OnBackKeyPress(e);
 
    try
    {
        await LayoutRoot.TransitionOutSlideDown();
    }
    catch { }
    NavigationService.GoBack();
}

With the SlideVertically method you can easily create slide updown transitions for navigating in or out.

Update: Creating a custom MessageBox for your Windows Phone apps.

I was looking at my site traffic and noticed that one of my most popular posts was about creating a custom MessageBox. This post was written two years ago and continues to get a lot of traffic. Since writing that post, I’ve updated my MessageBox a lot. I’ve changed it to use async/await, modified the style, corrected some bugs, and added functionality. Since I’ve made a lot of changes, and that post continues to get a lot of readers, I thought it would be good to give the latest version.

I continue to use a UserControl approach for this because I don’t want any overriding of styles. It has a set look, and should not be allowed to be changed (aside from the use of static resources). The xaml only need a small change to the bottom margin. Instead of 12 all around, it needed 18 on the bottom. I also changed the name of the first Grid from MessagePanel to LayoutRoot, this wasn’t needed, but made some code behind easier to understand what element was being modified.

<Grid x:Class="Pinnacle.Controls.MessageBox"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"             
             Margin="0">
    <Grid.Background>
        <SolidColorBrush Color="{StaticResource PhoneBackgroundColor}" Opacity=".5"/>
    </Grid.Background>
    <Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}"
          VerticalAlignment="Top" HorizontalAlignment="Stretch"
          toolkit:TiltEffect.IsTiltEnabled="True" >
        <StackPanel x:Name="MessagePanel" Margin="12">
            <TextBlock x:Name="HeaderTextBlock" TextWrapping="Wrap"
                       Style="{StaticResource PhoneTextLargeStyle}"
                       FontFamily="{StaticResource PhoneFontFamilySemiBold}"
                       HorizontalAlignment="Left"/>
            <TextBlock x:Name="MessageTextBlock" TextWrapping="Wrap"
                       Style="{StaticResource PhoneTextNormalStyle}"
                       FontSize="{StaticResource PhoneFontSizeMedium}"
                       Margin="12,24,12,24"
                       HorizontalAlignment="Left"/>
            <Grid HorizontalAlignment="Stretch" Margin="0,6,0,0">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Button x:Name="YesButton" Click="YesButton_Click"/>
                <Button x:Name="NoButton" Grid.Column="1" Click="NoButton_Click"/>
            </Grid>
        </StackPanel>
    </Grid>
</Grid>

The code behind saw a lot of changes as can be seen in this diff comparison from WinMerge.

image

The first change was to make the Show method awaitable. This requires removing the Closed event, and the use of a TaskCompletionSource. We need to wait awhile for user interaction, so we’ll create a member wide TaskCompletionSource. Bold is for new code.

private readonly TaskCompletionSource<MessageBoxResult> _completeionSource;
 
private MessageBox()
{
    InitializeComponent();
    _completeionSource = new TaskCompletionSource<MessageBoxResult>();
}
 
public static Task<MessageBoxResult> ShowAsync(string message, string caption, string yesButtonText, string noButtonText = null)
{
    MessageBox msgBox = new MessageBox();
    msgBox.HeaderTextBlock.Text = caption;
    msgBox.MessageTextBlock.Text = message;
    msgBox.YesButton.Content = yesButtonText;
    if (string.IsNullOrWhiteSpace(noButtonText))
    {
        msgBox.NoButton.Visibility = Visibility.Collapsed;
    }
    else
    {
        msgBox.NoButton.Content = noButtonText;
    }
    msgBox.Insert();
 
    return msgBox._completeionSource.Task;
}

The previous MessageBox fired the Closed event and then removed the MessageBox from the active page. When changing to using the TaskCompletionSource, I changed to set the result after the MessageBox was removed from the page. Within the Completed event handler of the transition, set the result.

_completeionSource.SetResult(result);

I intentionally left out most of the code of the remove method because I wanted to save the best for last. The last bit that I added is something no other custom MessageBox does. We need to account for apps that have changed the SystemTray of the page.

The MessageBox needs to change the BackgroundColor of the SystemTray to be the PhoneChromeBrush color. If the user changes the Opacity of the SystemTray to be less than 1 the MessageBox needs to account for it. If the Opacity is less than one, then the page will be shifted up into the tray. This has always bugged me about the Opacity property of the SystemTray and the ApplicationBar. Rather than just changing the opacity (whether you can see behind it), it also changes  the page layout by shifting the page content up. If you show any other custom MessageBox in this state, you will see the background of the page in the SystemTray rather than seeing the PhoneChromeBrush resource color. To account for this, We need to detect if the tray has an opacity. If it does, add a large margin to the top.

private void Insert()
{
    // Make an assumption that this is within a phone application that is developed "normally"
    var frame = Application.Current.RootVisual as Microsoft.Phone.Controls.PhoneApplicationFrame;
    _page = frame.Content as PhoneApplicationPage;
 
    // store the original color, and change the tray to the chrome brush
    _originalTrayColor = SystemTray.BackgroundColor;
    SystemTray.BackgroundColor = ((SolidColorBrush)Application.Current.Resources["PhoneChromeBrush"]).Color;
 
    bool shouldBuffer = SystemTray.Opacity < 1;
    if (shouldBuffer)
    {
        // adjust the margin to account for the page shifting up
        Margin = new Thickness(0, -32, 0, 0);
        var margin = MessagePanel.Margin;
        MessagePanel.Margin = new Thickness(margin.Left, 64, margin.Right, margin.Bottom);
    }
 
    _page.BackKeyPress += Page_BackKeyPress;
 
    // assume the child is a Grid, span all of the rows
    var grid = System.Windows.Media.VisualTreeHelper.GetChild(_page, 0) as Grid;
    if (grid.RowDefinitions.Count > 0)
    {
        Grid.SetRowSpan(this, grid.RowDefinitions.Count);
    }
    grid.Children.Add(this);
 
    // Create a transition like the regular MessageBox
    SwivelTransition transitionIn = new SwivelTransition();
    transitionIn.Mode = SwivelTransitionMode.BackwardIn;
 
    ITransition transition = transitionIn.GetTransition(LayoutRoot);
    EventHandler transitionCompletedHandler = null;
    transitionCompletedHandler = (s, e) =>
    {
        transition.Completed -= transitionCompletedHandler;
        transition.Stop();
    };
    transition.Completed += transitionCompletedHandler;
    transition.Begin();
 
    if (_page.ApplicationBar != null)
    {
        // Hide the app bar so they cannot open more message boxes
        _page.ApplicationBar.IsVisible = false;
    }
}

When the MessageBox is removed, we then need to set the BackgroundColor back to it’s original value.

private void Remove(MessageBoxResult result)
{
    _page.BackKeyPress -= Page_BackKeyPress;
 
    var frame = (PhoneApplicationFrame)Application.Current.RootVisual;
    var page = frame.Content as PhoneApplicationPage;
    var grid = VisualTreeHelper.GetChild(page, 0) as Grid;
 
    // Create a transition like the regular MessageBox
    SwivelTransition transitionOut = new SwivelTransition();
    transitionOut.Mode = SwivelTransitionMode.BackwardOut;
 
    ITransition transition = transitionOut.GetTransition(LayoutRoot);
    EventHandler transitionCompletedHandler = null;
    transitionCompletedHandler = (s, e) =>
    {
        transition.Completed -= transitionCompletedHandler;
        SystemTray.BackgroundColor = _originalTrayColor;
        transition.Stop();
        grid.Children.Remove(this);
        if (page.ApplicationBar != null)
        {
            page.ApplicationBar.IsVisible = true;
        }
        _completeionSource.SetResult(result);
    };
    transition.Completed += transitionCompletedHandler;
    transition.Begin();
}

In both methods, I also corrected a memory leak with the event handlers. Always remember to unsubscribe from those inline events! As always, here is a zip of the source.

Use the [beta] Nokia Imaging SDK to crop and resize any image to create a lockscreen for your phone

When the new Nokia Imaging SDK was released I was really excited to start using it within one of my apps. Unlike most, I was not interested in the image filters that change how it looks. I was initially interested in using the resize and crop functionality it had. The day after it was released my wife had surgery, so I had a good amount of time to play with the SDK while I sat in the waiting room. What I wanted to accomplish that was to take a random photo from the users phone, crop and resize it to fit the device and set it as the lockscreen. I know that you can set any image to be the lockscreen and  if the image is too big, it will center the image. I needed to do it manually because I wanted to overlay information on the image.

Getting the random image is pretty easy. We’ll just get one from the MediaLibrary.

private Picture GetRandomImage()
{
    var rand = new Random(DateTime.Now.Millisecond);
 
    MediaLibrary library = new MediaLibrary();
    var album = library.RootPictureAlbum;
 
    int albumIndex = rand.Next(0, album.Albums.Count - 1);
    album = album.Albums[albumIndex];
 
    var pictureIndex = rand.Next(0, album.Pictures.Count - 1);
    var picture = album.Pictures[pictureIndex];
 
    return picture;
}

Now that we have a Picture, we need to crop and resize it. All actions with an image are done through an EditingSession. You create an EditingSession with the EditingSessionFactory. The EditingSession has two key methods that allow us to crop and/or resize any image to fit the phone screen. The AddFilter method will allow us to crop the image while the RenderToJpegAsync method will allow us to resize the image. The AddFilter method is bar far the best part of the SDK. It allows you to do anything to the image., and I do mean anything. The method takes an IFilter as an argument. Lucky for us you can create a cropping filter. To create a cropping filter, you need to specify an area that will be the crop.

/// <summary>
/// Returns the area needed to crop an image to the desired height and width.
/// </summary>
/// <param name="imageSize">The size of the image.</param>
/// <param name="desiredSize">The desired size to crop the image to.</param>
/// <returns></returns>
private static Rect? GetCropArea(Size imageSize, Size desiredSize)
{
    // how big is the picture compared to the phone?
    var widthRatio = desiredSize.Width / imageSize.Width;
    var heightRatio = desiredSize.Height / imageSize.Height;
 
    // the ratio is the same, no need to crop it
    if (widthRatio == heightRatio) return null;
 
    double cropWidth;
    double cropheight;
    if (widthRatio < heightRatio)
    {
        cropheight = imageSize.Height;
        cropWidth = desiredSize.Width / heightRatio;
    }
    else
    {
        cropheight = desiredSize.Height / widthRatio;
        cropWidth = imageSize.Width;
    }
 
    int left = (int)(imageSize.Width - cropWidth) / 2;
    int top = (int)(imageSize.Height - cropheight) / 2;
 
    var rect = new Windows.Foundation.Rect(left, top, cropWidth, cropheight);
    return rect;
}
I like to keep things generic and reusable, so the method above will crop any image to any size. If the desired size is larger than the image size it will crop to the same dimensions. 

Now, given the Picture from the MediaLibrary, we can crop it using the Nokia Imaging SDK.

/// <summary>
/// Crops a Picture to the desired size.
/// </summary>
/// <param name="picture">The Picture to crop.</param>
/// <returns>A copy of the Picture which is cropped.</returns>
private static async Task<IBuffer> CropPicture(Picture picture, Size desiredSize)
{
    using (var stream = picture.GetImage())
    {
        using (EditingSession session = await EditingSessionFactory.CreateEditingSessionAsync(stream))
        {
            // Get the crop area of the image, we need to ensure that
            // the image does not get skewed
            Rect? rect = GetCropArea(new Size(picture.Width, picture.Height), desiredSize);
            if (rect.HasValue)
            {
                IFilter filter = FilterFactory.CreateCropFilter(rect.Value);
                session.AddFilter(filter);
            }
 
            // We always want the image to be the size of the phone. 
            // That may mean that it needs to be scaled up also
            var finalImageSize = new Size(desiredSize.Width, desiredSize.Height);
 
            return await session.RenderToJpegAsync(finalImageSize, OutputOption.PreserveAspectRatio);
        }
    }
}

Again, the CropPicture method is generic, allowing for any size. This method could easily be an extension method on the Picture. Now we need to wrap everything up by calling these methods with the phone size. There are already examples of getting the resolution of the phone, so I won’t go into that. Now just put the pieces together.

private async Task<Stream> GetRandomLockscreen()
{
    // Get the width and height of the phone
    double phoneWidth = Resolution.PhoneWidth;
    double phoneHeight = Resolution.PhoneHeight;
 
    Picture mediaPicture  = GetRandomImage();
    IBuffer croppedImage = await CropPicture(mediaPicture, new Size(phoneWidth, phoneHeight));
 
    if (croppedImage == null) return null;
 
    return croppedImage.AsStream();
}

From there you can save the image to disk, and then maybe save it as a lockscreen. While this code works great, do not expect it to work in a background task. Here is the one place where the imaging SDK falls short. I was wanting to use the SDK in a background task. Background tasks are limited on the amount of resources allowed. The biggest limitation is the memory constraint. Working with images is very memory intensive, especially in C#. So when trying to manipulate an image in a background task, you can quickly run out of memory. I was hoping the imaging SDK would help alleviate some of these issues by working with images smarter. Instead of reading in every pixel of the image and then processing it, it should read n rows of pixels from the stream and then process those rows. This is a hard thing to accomplish, but I’m hoping the awesome people at Nokia can update their SDK to allow for this.

Keeping ads in the same location when the phone orientation changes to landscape

There has been a lot of information flying around about ads in apps these days. Microsoft recently updated PubCenter reporting to include fill rates and number of requests for ads. Dvlup recently partnered with AdDuplex to in its reward program. With all of this hype, I thought I would talk about a common problem with placing ads in apps. That issue is keeping ads in the same location when rotating the phone. Most apps are Portrait apps and display ads either at the top or bottom of the app.

image

Displaying ads like this can be done with the following xaml

<Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <StackPanel Grid.Row="0" Margin="12,17,0,28">
        <TextBlock Text="STATIONARY AD" Style="{StaticResource PhoneTextNormalStyle}"/>
        <TextBlock Text="sample" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
    </StackPanel>
    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <!-- Other content here -->
    </Grid>
    <adDuplex:AdControl Grid.Row="2"/>
</Grid>

This style works really well for most apps, but what happens when you are displaying data that goes off of the screen? You could either say “Oh well”, you could wrap the text, or you could allow the user to rotate the phone so they can see more. Allowing the content of the page to display in Landscape is as simple as setting SupportedOrientations of the page to PortraitOrLandscape. Doing so allows the user to see more content, but has a side effect of the ad control taking up a lot of room at the bottom of the page.

image

When supporting Landscape, you really want the ads to stay stationary.

image

To solve this we’ll create a new Panel like control that will replace the “LayoutRoot” Grid of the standard page. I recently discovered that in two years of blogging I have yet to talk about creating a custom control. We’ll solve this problem by doing just that! A custom control is a control that is not derived from UserControl. Custom controls have a lot of benefits. My top two benefits are reusability and templating (ability to restyle the control). For this we’ll need to create a new ContentControl. A ContentControl allows for a single child to be placed within the xaml element or set from the Content property.

We’ll start by creating two files, one for code and one for xaml. Create a folder named StationaryAdPanel. In the folder add a new code file names StationaryAdPanel.cs and one code file named StationaryAdPanel.theme.xaml.

image

In the cs file, start defining the control.

public class StationaryAdPanel : ContentControl
{
    public StationaryAdPanel()
    {
        DefaultStyleKey = typeof(StationaryAdPanel);
    }
}

The constructor defines a DefaultStyleKey. This is the style that defines how the control looks. The style for the control needs to have a simple grid that will allow the ads to be moved to the sides that it needs to be on. Within the file add the following xaml that will define the layout of our control.

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Visual.Controls"
    xmlns:advertising="clr-namespace:Microsoft.Advertising.Mobile.UI;assembly=Microsoft.Advertising.Mobile.UI"
    xmlns:adDuplex="clr-namespace:AdDuplex;assembly=AdDuplex.WindowsPhone">
 
    <Style TargetType="local:StationaryAdPanel">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:StationaryAdPanel">
                    <Grid x:Name="PART_Root">
                        <Grid.RowDefinitions>
                            <!-- main content area -->
                            <RowDefinition Height="*"/>
                            <!-- ad area -->
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <!-- location for ads when rotated LandscapeRight -->
                            <ColumnDefinition Width="Auto"/>
                            <!-- Main content area -->
                            <ColumnDefinition Width="*"/>
                            <!-- location for ads when rotated LandscapeLeft -->
                            <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                        
                        <!-- main content of the page -->
                        <ContentPresenter  Grid.Column="1" Content="{TemplateBinding Content}"/>
                        
                        <!-- simple ad rotator, defaulted to bottom of page -->
                        <Grid x:Name="PART_StationaryPanel" Grid.Row="1" Grid.Column="1" 
                              RenderTransformOrigin=".5,.5" Width="480" Height="80">
                            <adDuplex:AdControl x:Name="AdDuplexControl" Visibility="Collapsed"/>
                            <advertising:AdControl x:Name="PubCenterControl" 
                                                   AdUnitId="Image480_80" ApplicationId="test_client"
                                                   Height="80" Width="480" 
                                                   IsAutoCollapseEnabled="True"/>
                        </Grid>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Next we’ll define the code for the control. The first step is to override the OnApplyTemplate. This is the method that we use to subscribe to any control events, or just get a control to use later. To keep the ads stationary, we’ll need to listen to when the Orientation of the page changes.

private PhoneApplicationPage _page;
private FrameworkElement _stationaryPanel;
private Grid _rootGrid;
private UIElement _adDuplexControl;
 
public override void OnApplyTemplate()
{
    base.OnApplyTemplate();
 
    _rootGrid = GetTemplateChild("PART_Root") as Grid ?? new Grid();
    _stationaryPanel = GetTemplateChild("PART_StationaryPanel") as FrameworkElement ?? new ContentPresenter();
    _stationaryPanel.RenderTransform = new RotateTransform();
    _adDuplexControl = (UIElement)GetTemplateChild("AdDuplexControl");
 
    var adControl = (AdControl)GetTemplateChild("PubCenterControl");
 
    // simple "ad rotator" methods to set visibility of ad controls
    adControl.ErrorOccurred += OnPubCenterErrorOccurred;
    adControl.AdRefreshed += OnPubCenterAdRefreshed;
 
    if (DesignerProperties.GetIsInDesignMode(this) == false)
    {
        // We will need to get the page we are in and listen for the
        // OrientationChanged event
        var frame = (Frame)Application.Current.RootVisual;
        _page = ((PhoneApplicationPage)frame.Content);
        if (_page.SupportedOrientations == SupportedPageOrientation.PortraitOrLandscape)
        {
            _page.OrientationChanged += OnOrientationChanged;
            OnOrientationChanged(_page, new OrientationChangedEventArgs(_page.Orientation));
        }
    }
}

The difficult part about keeping the ad stationary when in landscape is that we will need to rotate it. The downside to rotation is that other controls around the rotated control to not adjust for the rotation. This is solved with controls like the LayoutTransformer from Telerik or by porting the LayoutTransformer from the Silverlight toolkit. However, using these controls requires a dependency on a third party library that we may not want. To overcome this we will ne to adjust for the rotation ourselves. We’ll handle this within the OnOrientationChanged event handler.

private void OnOrientationChanged(object sender, OrientationChangedEventArgs args)
{
    // margin for shifting ad panel when in landscape mode
    // the margin is equal to (width - height)/2 = (480 - 80)/2 = 200
    const int margin = 200;
 
    // initial value of the margin of the page. If the SystemTray has an opacity
    // we need to shift the page content over
    Thickness pageMargin = _page.Margin;
 
    // margin use when rotating the ad
    Thickness rotateMargin = new Thickness(0, 0, 0, 0);
    
    // angle to rotate the ad (if in landscape)
    double angle = 0;
 
    int rotateRow = 0;
    int rotateRowspan = 0;
    int rotateColumn = 0;
 
    switch (args.Orientation)
    {
        case PageOrientation.None:
            break;
        case PageOrientation.Portrait:
        case PageOrientation.PortraitUp:
        case PageOrientation.PortraitDown:
            rotateRow = 2;
            rotateColumn = 1;
            rotateRowspan = 1;
            if ((SystemTray.Opacity > 0) && (SystemTray.Opacity < 1))
            {
                pageMargin = new Thickness(0, 32, 0, 0);
            }
            break;
        case PageOrientation.Landscape:
        case PageOrientation.LandscapeRight:
            rotateRowspan = _rootGrid.RowDefinitions.Count;
            rotateMargin = new Thickness(-margin, 0, -margin, 0);
            angle = 90;
            if ((SystemTray.Opacity > 0) && (SystemTray.Opacity < 1))
            {
                pageMargin = new Thickness(0, 0, 72, 0);
            }
            break;
        case PageOrientation.LandscapeLeft:
            rotateRowspan = _rootGrid.RowDefinitions.Count;
            rotateColumn = 2;
            rotateMargin = new Thickness(-margin, 0, -margin, 0);
            angle = -90;
            if ((SystemTray.Opacity > 0) && (SystemTray.Opacity < 1))
            {
                pageMargin = new Thickness(72, 0, 0, 0);
            }
            break;
        default:
            throw new ArgumentOutOfRangeException();
    }
    _page.Margin = pageMargin;
 
    // now set location and rotation of ad panel
    _stationaryPanel.Margin = rotateMargin;
    ((RotateTransform)_stationaryPanel.RenderTransform).Angle = angle;
    Grid.SetRow(_stationaryPanel, rotateRow);
    Grid.SetColumn(_stationaryPanel, rotateColumn);
    Grid.SetRowSpan(_stationaryPanel, rotateRowspan);
}

We’ll wrap up the control by adding the methods to rotate the ads as needed.

private void OnPubCenterErrorOccurred(object sender, AdErrorEventArgs e)
{
    var pubCenterAd = ((UIElement)sender);
    if (pubCenterAd.Visibility == Visibility.Visible)
    {
        pubCenterAd.Visibility = Visibility.Collapsed;
        _adDuplexControl.Visibility = Visibility.Visible;
    }
}
 
private void OnPubCenterAdRefreshed(object sender, EventArgs eventArgs)
{
    var pubCenterAd = ((UIElement)sender);
    if (pubCenterAd.Visibility == Visibility.Collapsed)
    {
        pubCenterAd.Visibility = Visibility.Visible;
        _adDuplexControl.Visibility = Visibility.Collapsed;
    }
}

When writing custom controls, you need to define a “Generic.xaml” file located in a Themes folder in the root of the project. This is a requirement for custom controls. Nice file can contain the xaml style directly (the template we defined above) or it can reference the xaml files used to define the templates. I prefer to put the styles and templates in separate files rather than flood the Generic.xaml file. To start, create a folder named “Themes” in the root of the project. Right click the folder and add a new code file named “Generic.xaml”. In the file paste the following code (changing YOUR_PROJECT_NAME with the name of the project).

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="/YOUR_PROJECT_NAME;component/Controls/StationaryAdPanel/StationaryAdPanel.Theme.xaml" />
    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

We are now ready to use the control in our pages. The control is really simple to use. Take the LayoutRoot grid that you normally have, and wrap it with the new StationaryAdPanel.

<local:StationaryAdPanel>
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <!-- regular content -->
    </Grid>
</local:StationaryAdPanel>

With the root grid wrapped, we can rotate our apps to see more text.

image                     image

You can download a complete working sample from here.

Creating a Behavior to change text to any case

In a previous post we created a behavior that would capitalize any text. This Behavior served a great purpose allowing you to capitalize the text for any TextBlock. What happens when you need to lower case the text? You could create a second Behavior for this, or we could modify the Behavior so that it allows for multiple options. The first option just seems silly when we can reuse code, but how can we specify which text case we want to apply?

In the previous post I talked about some reasons why I like behaviors. One reason I did not list is that you can add Dependency Properties to them. Dependency Properties allow us to set properties in xaml or even better bind to other properties. You can add Dependency Properties to value converters, but that requires your value converter to inherit from DependencyObject. A Behavior already is a DependencyObject!

We’ll start with the ToUpperBehavior from before. We’ll first change the name to ChangeCaseBehavior and we’ll create an enum that will specify what case style we want to change to

public class ChangeCaseBehavior : Behavior<TextBlock>
{
    ...
}
 
public enum TextCase
{
    Upper,
    Lower,
    Title
}

This example will show three styles of text casing. I’ll leave it up to you to add others like camelCase or PascalCase. In our previous Behavior, we called ToUpper on the Text of the TextBlock, now we’ll want to respond to the different possibilities for TextCase. We can accomplish this with a simple switch statement within the UpdateText method

private void UpdateText()
{
    if (AssociatedObject == null) return;
    if (string.IsNullOrEmpty(AssociatedObject.Text)) return;
 
    switch (TextCase)
    {
        case TextCase.Upper:
            AssociatedObject.Text = AssociatedObject.Text.ToUpper();
            break;
        case TextCase.Lower:
            AssociatedObject.Text = AssociatedObject.Text.ToLower();
            break;
        case TextCase.Title:
            char[] text = AssociatedObject.Text.ToCharArray();
            
            // always upper case the first letter
            bool shouldUpper = true;
            for (int i = 0; i < text.Length; i++)
            {
                text[i] = shouldUpper 
                    ? Char.ToUpper(text[i]) 
                    : Char.ToLower(text[i]);
 
                // next letter should be upper case if this is a space
                shouldUpper = text[i] == ' ';
            }
            AssociatedObject.Text = new string(text);
            break;
        default:
            throw new ArgumentOutOfRangeException();
    }
    AssociatedObject.LayoutUpdated -= AssociatedObjectOnLayoutUpdated;
}

Next,  we’ll want the ability to set which text casing we want. To do this we’ll add a dependency property to the behavior and respond to the changing of the value.

public TextCase TextCase
{
    get { return (TextCase)GetValue(TextCaseProperty); }
    set { SetValue(TextCaseProperty, value); }
}
 
// Using a DependencyProperty as the backing store for TextCase.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty TextCaseProperty = DependencyProperty.Register(
    "TextCase",
    typeof(TextCase),
    typeof(ChangeCaseBehavior),
    new PropertyMetadata(TextCase.Upper, OnTextCaseChanged));
 
private static void OnTextCaseChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
    var behavior = (ChangeCaseBehavior)o;
    behavior.UpdateText();
}

Last time we setup the Behavior through Blend, this time we’ll modify the xaml to specify which case we want.

<TextBlock x:Name="TitleText" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0" 
           Text="{Binding Name}">
    <i:Interaction.Behaviors>
        <Behaviors:ChangeCaseBehavior TextCase="Title"/>
    </i:Interaction.Behaviors>
</TextBlock>

This ensures that our text will be title cased

image

Responding to the Windows Phone Toolkit ListPicker closing

I’ve often wondered why the ListPicker in the Windows Phone Toolkit does not have an Opened or Closed event, especially since the SelectionChanged event fires only when the selection changes (as it should). So how are you suppose to know when the ListPicker opens or closes. After all, the ComboBox control has DropDownOpened and DropDownClosed. But then I thought, “Do you really need those? You can always use the IsDropDownOpened property.” The ListPicker does not have an Opened or Closed event and it does not have an IsDropDownOpened property. What the ListPicker does have, is the ListPickerMode property.

//
// Summary:
//     Gets or sets the ListPickerMode (ex: Normal/Expanded/Full).
public ListPickerMode ListPickerMode { get; }

This property is actually a DependencyProperty that we can bind to! Let’s say you need to change the visibility of another control when the ListPicker opens/closes. Or maybe you need to change some text based on the picker being opened. We can use the Expression SDK to change values within xaml. For this example we’ll look at changing text based on the picker being opened. We have the following xaml defined.

<StackPanel>
    <TextBlock Text="Closed" Style="{StaticResource PhoneTextLargeStyle}"/>
    <toolkit:ListPicker x:Name="Picker">
        <sys:String>Option one</sys:String>
        <sys:String>Option two</sys:String>
        <sys:String>Option three</sys:String>
        <sys:String>Option four</sys:String>
    </toolkit:ListPicker>
</StackPanel>

With the above xaml, we will always show “Closed”. Let’s start using the Expression SDK. The first step is to add the Microsoft.Expression.Interactions and System.Windows.Interactivity references to your project.

image

Next we need to add a DataTrigger to the TextBlock. The DataTrigger will respond to the ListPickerMode property changing and then change the text of the TextBlock.

<TextBlock  Text="Closed" Style="{StaticResource PhoneTextLargeStyle}">
    <i:Interaction.Triggers>
        <e:DataTrigger Binding="{Binding ListPickerMode, ElementName=Picker}" Value="Expanded">
            <e:ChangePropertyAction PropertyName="Text" Value="Open" TargetName="PickerState"/>
        </e:DataTrigger>
        <e:DataTrigger Binding="{Binding ListPickerMode, ElementName=Picker}" Value="Normal">
            <e:ChangePropertyAction PropertyName="Text" Value="Closed" TargetName="PickerState"/>
        </e:DataTrigger>
    </i:Interaction.Triggers>
</TextBlock>

Maybe you want to use a ValueConverter and show/hide the TextBlock based on the picker showing. First we’ll need to create a new ValueConverter.

public class PickerModeToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null) return Visibility.Collapsed;
 
        var mode = (ListPickerMode) value;
 
        return mode == ListPickerMode.Expanded ? Visibility.Visible : Visibility.Collapsed;
    }
 
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Next we’ll add the converter to the resources and use it to show the TextBlock.

<phone:PhoneApplicationPage.Resources>
    <local:PickerModeToVisibilityConverter x:Key="PickerModeConverter"/>
</phone:PhoneApplicationPage.Resources>
 
...
 
<TextBlock Text="Open" Style="{StaticResource PhoneTextLargeStyle}" 
           Visibility="{Binding ListPickerMode, ElementName=Picker, Converter={StaticResource PickerModeConverter}}" />

These were a couple of simple examples. You can do more by using the other built-in behaviors of the Expression SDK.