How to show different app bar for different page state?

In the previous two posts, we have learnt how to work with multiple app bars on a pivot/pano page. In this post I’m going to talk about how to work with multiple app bars on a general page. Suppose we have a page containing a MultiselectList control from Silverlight for Windows Phone Toolkit like this:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <toolkit:MultiselectList
        ItemTemplate="{StaticResource TodosItemTemplate}"
        ItemsSource="{Binding Todos}"
        IsSelectionEnabled="{Binding IsSelecting, Mode=TwoWay}"/>
</Grid>

The MultiselectList control can switch between reading and selecting modes. I want to define two different app bars, one for reading mode, the other for selecting mode, and have them shown based on the current mode, just like the built-in email app.

With StateChangedTrigger from the lastest AppBarUtils (2.1), this can be done easily:

<i:Interaction.Triggers>
    <abu:StateChangedTrigger State="{Binding IsSelecting}">
        <abu:SwitchAppBarAction>
            <abu:AppBar Id="0">
                <abu:AppBar.MenuItems>
                    <abu:AppBarMenuItem Text="settings" Command="{Binding SampleCommand}"/>
                </abu:AppBar.MenuItems>
                <abu:AppBarButton IconUri="/icons/appbar.add.rest.png"
                    Text="add" Command="{Binding SampleCommand}"/>
                <abu:AppBarButton IconUri="/icons/appbar.list.check.png" 
                    Text="select" Command="{Binding SelectCommand}"/>
            </abu:AppBar>
            <abu:AppBar Id="1">
                <abu:AppBarButton IconUri="/icons/appbar.delete.rest.png" 
                    Text="delete" Command="{Binding DeleteCommand}"/>
            </abu:AppBar>
        </abu:SwitchAppBarAction>
    </abu:StateChangedTrigger>
</i:Interaction.Triggers>

The above code defines two app bars within SwitchAppBarAction which is then placed into StateChangedTrigger. The key property of StateChangedTrigger is the State property which you can bind to your view model. When your view model changes, StateChangedTrigger will inspect the value of the State property and invoke SwitchAppBarAction with that value.

State property supports int, bool, enum (int underlying type), and string (integer literal). ints will be used directly. enums will be converted to int. false and true will be interpreted as 0 and 1 respectively. Strings will be parsed to int.

MultiselectList provides a IsSelectionEnabled property to indicate whether it’s now in reading mode or selecting mode. We bound the IsSelectionEnabled property to a IsSelecting property of the view model (two-way binding), and then bound the State property to the same property of the view model. In this way, the changes of the IsSelectionEnabled property will be transfered to the State property via the IsSelecting property.

We need this indirection here because we want to have the MultiselectList switched to reading mode when a user presses the hardware back key while in selecting mode, just like the built-in email app, which is done within OnBackKeyPress method (see demo for details).

Now comes your turn. Go ahead to download the demo and run it! Cheers!

How to share an app bar between multiple pivot/pano items?

In the previous post, I’ve shown you how to show different app bar for different pivot/pano items. But what if I want some pivot/pano items to show the same app bar? This is what I’m going to talk about in this post.

Suppose we have the following Pivot control with 3 pivot items:

<controls:Pivot Title="APPBARUTILS">
    <!--Pivot item one-->
    <controls:PivotItem Header="item1">
        <Grid/>
    </controls:PivotItem>
    <!--Pivot item two-->
    <controls:PivotItem Header="item2">
        <Grid/>
    </controls:PivotItem>
    <!--Pivot item three-->
    <controls:PivotItem Header="item3">
        <Grid/>
    </controls:PivotItem>
</controls:Pivot>

I want to define 2 app bars. The first two pivot items will share the first app bar, and the third pivot item will take the remaining app bar. Here’s how this is done:

<i:Interaction.Triggers>
    <abu:SelectedPivotItemChangedTrigger>
        <abu:SelectedPivotItemChangedTrigger.SelectionMappings>
            <!--Map 1st and 2nd pivot item to 1st app bar-->
            <abu:SelectionMapping SourceIndex="0" TargetIndex="0"/>
            <abu:SelectionMapping SourceIndex="1" TargetIndex="0"/>
            <!--Map 3rd pivot item to 2nd app bar-->
            <abu:SelectionMapping SourceIndex="2" TargetIndex="1"/>
        </abu:SelectedPivotItemChangedTrigger.SelectionMappings>
        <abu:SwitchAppBarAction>
            <abu:AppBar Id="0">
                <abu:AppBar.MenuItems>
                    <abu:AppBarMenuItem Text="clear" Command="{Binding SampleCommand}"/>
                </abu:AppBar.MenuItems>
                <abu:AppBarButton IconUri="/icons/appbar.add.rest.png"
                    Text="add" Command="{Binding SampleCommand}"/>
                <abu:AppBarButton IconUri="/icons/appbar.sync.rest.png"
                    Text="sync" Command="{Binding SampleCommand}"/>
            </abu:AppBar>
            <abu:AppBar Id="1" Mode="Minimized">
                <abu:AppBar.MenuItems>
                    <abu:AppBarMenuItem Text="settings" Command="{Binding SampleCommand}"/>
                    <abu:AppBarMenuItem Text="help" Command="{Binding SampleCommand}"/>
                </abu:AppBar.MenuItems>
            </abu:AppBar>
        </abu:SwitchAppBarAction>
    </abu:SelectedPivotItemChangedTrigger>
</i:Interaction.Triggers>

This code is placed inside the Pivot element in XAML but ouside any PivotItem element. This time SelectedPivotItemChangedTrigger is used instead of SelectedPanoItemChangedTrigger. The way of defining app bars within the SwitchAppBarAction remains the same as what we have seen in the previous post.

In order to provide custom mappings from pivot item index to app bar ID, we need to create a list of SelectionMapping for SelectedPivotItemChangedTrigger.SelectionMappings property as shown in the above code. The SourceIndex property of SelectionMapping refers to the pivot item index, while the TargetIndex property refers to the app bar ID.

The above code will have the pivot items whose indexes are 0 and 1 mapped to the app bar whose ID is 0, and the pivot item whose index is 2 mapped to the app bar whose ID is 1. In this case, the first two pivot items will show the first app bar, while the third pivot item will show the second app bar.

Now that you’ve got the idea of this feature, go ahead to download the binary and take advantage of it today! Cheers!

How to show different app bar for different pivot/pano item?

Yesterday I released AppBarUtils 2.0. This major version includes behaviors that enable you to define multiple app bars within a pivot/pano page where different pivot/pano item shows different app bar. This feature has been requested for some time, and now you’ve got what you want.

In this post, I’m going to talk about how to create a pano page with app bars that behave like the built-in people hub. Suppose we have the following Panorama control with 3 pano items:

<controls:Panorama Title="people">
    <!--Panorama item one-->
    <controls:PanoramaItem Header="recent">
        <Grid/>
    </controls:PanoramaItem>
    <!--Panorama item two-->
    <controls:PanoramaItem Header="all">
        <Grid/>
    </controls:PanoramaItem>
    <!--Panorama item three-->
    <controls:PanoramaItem Header="what's new">
        <Grid/>
    </controls:PanoramaItem>
</controls:Panorama>

I want to define 3 app bars for these 3 pano items. Each pano item shows its corresponding app bar just like the built-in people hub. Here’s how this is done:

<i:Interaction.Triggers>
    <abu:SelectedPanoItemChangedTrigger>
        <abu:SwitchAppBarAction>
            <abu:AppBar Id="0" Mode="Minimized">
                <abu:AppBar.MenuItems>
                    <abu:AppBarMenuItem Text="settings" Command="{Binding SampleCommand}"/>
                </abu:AppBar.MenuItems>
            </abu:AppBar>
            <abu:AppBar Id="1">
                <abu:AppBar.MenuItems>
                    <abu:AppBarMenuItem Text="settings" Command="{Binding SampleCommand}"/>
                </abu:AppBar.MenuItems>
                <abu:AppBarButton IconUri="/icons/appbar.add.rest.png"
                    Text="add" Command="{Binding SampleCommand}"/>
                <abu:AppBarButton IconUri="/icons/appbar.feature.search.rest.png"
                    Text="search" Command="{Binding SampleCommand}"/>
            </abu:AppBar>
            <abu:AppBar Id="2" Mode="Minimized">
                <abu:AppBar.MenuItems>
                    <abu:AppBarMenuItem Text="refresh" Command="{Binding SampleCommand}"/>
                    <abu:AppBarMenuItem Text="settings" Command="{Binding SampleCommand}"/>
                </abu:AppBar.MenuItems>
            </abu:AppBar>
        </abu:SwitchAppBarAction>
    </abu:SelectedPanoItemChangedTrigger>
</i:Interaction.Triggers>

This code is placed inside the Panorama element in XAML but ouside any PanoramaItem element. The SelectedPanoItemChangedTrigger subscribes to the SelectionChanged event of Panorama control. When this event raises, it will invoke the SwitchAppBarAction to show the correct app bar.

SwitchAppBarAction is also the place where you define multiple app bars each of which is assigned a unique ID. By default, the ID corresponds to the selected index of Panorama control. For example, when the 2nd pano item shows, the app bar whose ID is 2 will show.

You define app bar with AppBar, app bar button with AppBarButton, and app bar menu item with AppBarMenuItem as the above code shows. AppBarButton is placed directly within AppBar as element content, while AppBarMenuItem is placed within AppBar.MenuItems property, which has the same structure as the built-in ApplicationBar.

The properties they support are also the same as the built-in ApplicationBar, ApplicationBarIconButton, and ApplicationBarMenuItem, plus a Command property and a CommandParameter property. All those properties are dependency properties, so they’re ready for data binding.

Now that you’ve got the idea of this feature, go ahead to download the demo project and run it! Cheers!

Working with PinToStartAction

Last night I released WPUtils 1.1 which brings a PinToStartAction and a demo project. In this post, I’m going to talk about the PinToStartAction.

As its name implies, PinToStartAction is used to enable a control/component to pin a tile to the start screen. Let’s have a look at how it works with Button and app bar button/menu item. If you have a Button like this:

<Button Content="pin" Margin="0,12,0,0"/>

you can attach the PinToStartAction to this Button via EventTrigger:

<Button Content="pin" IsEnabled="{Binding TileAbsent}" Margin="0,12,0,0">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click">
            <WPUtils:PinToStartAction
                NavigationUri="{Binding NavigationUri}"
                BackgroundImage="{Binding BackgroundImage}"
                Title="{Binding TileTitle}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>

Note that I have a view model class behind for data binding. The IsEnabled property is bound to a TileAbsent property of view model class. When the tile already exists in the start screen, the Button will appear disabled. In order to achieve this, you’ll need to add code to check the existence of the tile and set TileAbsent property accordingly in your view model class, and call this code inside OnNavigatedTo method of the page. You shouldn’t rely on the construction point of the page to determine the state of the Button, because the user could switch to Start screen right from this page, remove the tile, and back to this page.

Of couse, this action contains code to check if the tile already exists in case you don’t do the check. This action will just do nothing when the tile exists. But I strongly recommend that you do the check and set the IsEnabled property accordingly so that the users will be aware of what’s happening.

What’s more, if you specify images for background/back background, PinToStartAction will take care of copying them to /Shared/ShellContent folder and reporting the correct URI to the underlying API.

For app bar button, we will need AppBarItemTrigger from AppBarUtils. Once you have that toolkit ready in your project, you can attach PinToStartAction to a app bar button like this:

<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar>
         <shell:ApplicationBarIconButton IconUri="/images/appbar.pin.png" Text="pin"/>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

<i:Interaction.Triggers>
     <AppBarUtils:AppBarItemTrigger Id="pin" IsEnabled="{Binding TileAbsent}">
         <WPUtils:PinToStartAction
             BackgroundImage="{Binding BackgroundImage}"
             Title="{Binding TileTitle}"
             NavigationUri="{Binding NavigationUri}"/>
     </AppBarUtils:AppBarItemTrigger>
</i:Interaction.Triggers>

AppBarItemTrigger and PinToStartAction are both bound to the same view model class as the above Button, and work the same way as expected.

In Expression Blend, you can just drag and drop it onto a control, set/bind the needed properties, and you’re done. Pretty simple. For app bar item, you will need to drag and drop PinToStartAction onto the page, and change the trigger to AppBarItemTrigger on Properties panel.

Last but not the least, go download the code, run to see how the demo works! Cheers!

A First Look at WPUtils

Two days ago, I released WPUtils 1.0. This toolkit contains a set of attached properties/Blend behaviors that you can use to extent your components/controls.

In this first release, WPUtils contains the following stuff:

  1. ListBoxProperties.SelectedItems attached property
  2. ChoosePhotoBehavior
  3. UriToPhotoConverter
  4. and the navigation components migrated from AppBarUtils, see How to Do Navigation for Application Bar with AppBarUtils in XAML? for more infomation.

ListBox control can be used in a multiple selection mode when you set its SelectionMode property to Multiple. In this case, you can obtain a list of selected items from the built-in SelectedItems property. What bothers me is that built-in SelectedItems property is not a dependency property, so you cannot bind it to a property of your view model class. This is where the ListBoxProperties.SelectedItems attached property comes to rescue. What’s more, when the user changes the selection in the ListBox, ListBoxProperties.SelectedItems attached property will update your view model accordingly.

Recall the experience when you new/edit a contact in Windows Phone. You tap the photo to choose a different one from your media library. ChoosePhotoBehavior is used to mimic this experience. You attach it to an Image component, bind its PhotoUri property to your view model, and you’re done. Whenever the user choose a photo, it will copy that photo to the photos folder in your isolated storage, and update your view model with a URI prefixed with “isostore:”. This rule of URI is used to distinguish the photos in the installation folder from those in isolated storage.

To take advantage of the above rule of URI, WPUtils provide a UriToPhotoConverter to work with Image component. For photos shipped with your app, i.e. in the installation folder, you will continue to use the URI format you use before, e.g. /images/photo1.jpg. For photos created in the isolated storage when the app is running, prefix the URI with “isostore:”, and UriToPhotoConverter will take care of the rest.

One more thing, photo URIs pointing to isolated storage or returned from PhotoChooserTask.OriginalFileName doesn’t work with Image.Source in XAML. So you either read the photo to BitmapImage and set it to Image.Source manually, or make use of UriToPhotoConverter.

I hope this toolkit helps. And any comments/suggestions are welcome. Cheers!

AppBarUtils 1.0 RTW is Here!

Friends, I’m very excited to announce the AppBarUtils 1.0 RTW for Windows Phone SDK 7.1!! This project was started on Aug 11, and now it hits RTW!! OK, now it’s your turn to enjoy it! Thanks!

Oops! Forget to place the link here: http://appbarutils.codeplex.com/

Tagged

AppBarUtils Refactored, RC 3 Released

I got feedback that ItemText and DisplayText properties were confusing, so I refactored them to Id and Text respectively. Also, ItemType has been refactored to Type.

For NavigateWithQueryStringAction, originally if you didn’t set its TargetPage property, i.e, leave it empty or simply omit it, it would provide a go-back. However, this behavior also provides confusion because its name doesn’t imply such behavior. I’ve removed this behavior, and provide a new GoBackAction dedicated for go-back purpose.

Previous 4 posts have been updated regarding this refactoring.

For a complete sample code, please visit http://appbarutils.codeplex.com/

Tagged