-
Telerik and Windows Phone 8 – The JumpList (Multi selection)
We continue our journey to see how to use some of the most interesting features of the RadJumpList, one of the available controls in the RadControls for Windows Phone toolkit developed by Telerik.
Multi selection
Multi selection is a simple but interesting feature that will allow the user to choose more than one element from the list, by checking the checkboxes that are displayed on the left of the item. By default, selection isn’t enabled: the RadJumpList control will behave like a standard list; when the user taps on an item, you can intercept it and implement your own logic (for example, you can redirect the user to a detail page to see the details of the selected item.
The first step to enable it is to set the IsCheckModeEnabled property to True and you can do it in the XAML or in the code. Once it’s enabled, you’ll notice that the left margin of the items will be greater, but the list will continue to work as usual.
You have two ways to effectively enable multi selection mode:
- You can tap on the left of any item: multi selection will be activated and you’ll see the checkboxes appear.
- You can set the IsCheckModeActive property of the RadJumpList control to True: this way, you can also enable the multi select mode in the code if, for example, you want to provide a button for this purpose. The native Mail app shows an example of this behavior: you can enable multi select mode by tapping on the left of any item or by using the appropriate button in the Application Bar. </ul> By default, if you’ve enable grouping like we’ve seen in the previous post, user will be allowed also to select groups and not only single items. If you want to disable this behavior, you need to set the GroupCheckMode to None: this way, checkboxes will be displayed only next to the single items.
Here is a sample of a RadJumpList control with multi selection enabled:
<telerikDataControls:RadJumpList x:Name="People" IsCheckModeEnabled="True" GroupCheckMode="None"> <telerikDataControls:RadJumpList.ItemTemplate> <DataTemplate> <StackPanel> <TextBlock Text="{Binding Path=Name}" /> <TextBlock Text="{Binding Path=Surname}" /> </StackPanel> </DataTemplate> </telerikDataControls:RadJumpList.ItemTemplate> </telerikDataControls:RadJumpList>
Managing the selected items is really simple: the RadJumpList control exposes a property called CheckedItems, which contains the collection of items that have been selected. Here is a really sample code that can be used to show the number of items that have been selected by the user.
private void OnShowItemsClicked(object sender, RoutedEventArgs e) { MessageBox.Show(People.CheckedItems.Count.ToString()); }
Supporting multi selection with MVVM
One issue of the RadJumpList control is that, at the moment, CheckedItems doesn’t support binding, so you can’t have a property in your ViewModel with the collection of the items that have been selected. The best workaround I’ve found and that I’m using in my Speaker Timer app is to use a custom behavior, that has been developed by my dear friend Marco Leoncini. The purpose of this behavior is to automatically store, in a property of the ViewModel, the list of selected items.
First, you need to define in your ViewModel a collection, that will hold the items that will be checked by the user. For the following samples, I’m going to use the helpers provided by the MVVM Light Toolkit by Laurent Bugnion, that you can install using NuGet. Here is a ViewModel sample:
public class MultiSelectViewModel : ViewModelBase { private ObservableCollection<Person> people; public ObservableCollection<Person> People { get { return people; } set { people = value; RaisePropertyChanged("People"); } } private ObservableCollection<Person> itemsToDelete; public ObservableCollection<Person> ItemsToDelete { get { return itemsToDelete; } set { itemsToDelete = value; RaisePropertyChanged("ItemsToDelete"); } } public MultiSelectViewModel() { ItemsToDelete = new ObservableCollection<Person>(); } }
In this sample, ItemsToDelete is the property that will store the items selected by the user (this sample is taken from my Speaker Timer app, where multi selection is enabled to delete one or more sessions). The second step is to create a new class, that will contain our behavior:
public class AddoToCheckedItemBehavior : Behavior<RadDataBoundListBox> { public AddoToCheckedItemBehavior() { } protected override void OnAttached() { base.OnAttached(); this.AssociatedObject.ItemCheckedStateChanged += AssociatedObject_ItemCheckedStateChanged; } void AssociatedObject_ItemCheckedStateChanged(object sender, ItemCheckedStateChangedEventArgs e) { var viewModel = ((FrameworkElement)sender).DataContext as MainPageViewModel; if (viewModel == null) return; if (e.IsChecked) { viewModel.ItemsToDelete.Add(((Telerik.Windows.Data.IDataSourceItem)e.Item).Value as Session); } else { viewModel.ItemsToDelete.Remove(((Telerik.Windows.Data.IDataSourceItem)e.Item).Value as Session); } } protected override void OnDetaching() { base.OnDetaching(); this.AssociatedObject.ItemCheckedStateChanged -= AssociatedObject_ItemCheckedStateChanged; } }
The purpose of this behavior is very simple: it inherits from the **Behavior
** class (so that it can be used in combination with a **RadJumpList** control) and it subscribes to the **ItemCheckedStateChanged** event, that is triggered every time the user checks or unchecks an item from the list. When this event is triggered, we get a reference to the current ViewModel and we add or remove the selected item from the **ItemsToDelete** collection. To do this, we use a property returned by the event handler’s parameter: the **e** object (which type is **ItemCheckedStateChangedEventArgs**) contains the properties **Item** (which is the selected item) and **IsChecked** (which is a boolean, that tells you if the item has been checked or unchecked). There’s space for improvements in this behavior: one limitation is that it works just for our specific ViewModel, since when we get the reference to the ViewModel associated to the current view (using the DataContext property) we cast it to the MainViewModel class. If we need to apply the same behavior to multiple RadJumpList controls in different page, we can create a base class which our ViewModels can inherit from. This base class will host the the ItemsToDelete property, so that every ViewModel will be able to use it. This way, we can change the cast from MainViewModel to our new base class and reuse the same behavior for different ViewModels.
How to apply the behavior? First, we need to add a reference to the System.Windows.Interactivity library, that is available by clicking with the right click on your project and choosing Add new reference: you’ll find it in the Assemblies, Extensions sections.
After that, you can add the behavior directly in the XAML, by declaring the following namespace (plus the namespace of the behavior’s class you’ve created):
xmlns:i=”clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity”
<telerikDataControls:RadJumpList IsCheckModeEnabled="True" GroupCheckMode="None" ItemsSource="{Binding Path=People}"> <telerikDataControls:RadJumpList.ItemTemplate> <DataTemplate> <StackPanel> <TextBlock Text="{Binding Path=Name}" /> <TextBlock Text="{Binding Path=Surname}" /> </StackPanel> </DataTemplate> </telerikDataControls:RadJumpList.ItemTemplate> <i:Interaction.Behaviors> <behaviors:AddoToCheckedItemBehavior /> </i:Interaction.Behaviors> </telerikDataControls:RadJumpList>
</pre>
Now in the ViewModel, automatically, the **ItemsToDelete** property will always contain the items that have been checked by the user, so that you can use them for your own needs. In the following sample, we define a command to simply show a message with the number of selected items: <pre class="brush: csharp;">private RelayCommand showCount;
public RelayCommand ShowCount { get { if (showCount == null) { showCount = new RelayCommand(() => { MessageBox.Show(ItemsToDelete.Count.ToString()); }); } return showCount; } }
</pre>
Happy coding! As usual, remember that the sample project doesn’t contain the Telerik libraries, since we’re talking about a commercial suite. <div id="scid:fb3a1972-4489-4e52-abe7-25a00bb07fdf:8ede1e39-9fe4-42ad-8795-cf44e0d86eac" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <p> <a href="http://wp.qmatteoq.com/wp-content/uploads/2013/04/JumpList_MultiSelect.zip" target="_blank">Download the sample project</a> </p> </div>
in
- You can tap on the left of any item: multi selection will be activated and you’ll see the checkboxes appear.
-
How to override the theme of a custom Application Bar
I recently published a big update for an application I’ve developed a long time ago and that I “abandoned” until today: Speaker Timer. It’s a simple application for speakers, that can be used to track the time of your speech and to immediately see if you’re exceeding or not the time you’ve been assigned.
During the development I’ve encountered some issues to correctly manage the phone’s theme: as you can see from the screenshot on the Store, the application has a blue background and white texts. For this reason, everything works fine when the app is used with the dark theme but, if the user is using the light one, things start to get messy: texts starts to get hard to read and the overall user experience is really bad. In the following screenshots you can see, on the left, the application with the dark theme and, on the right, with the light theme: as you can see, since texts are in black over a blue background, they aren’t easy to read.
Luckily Jeff Wilcox, that works in Microsoft as leader of the Windows Azure SDK and many other open source projects, has developed a really great library called PhoneThemeManager, that is described in this blog post. The purpose of this library is really simple: with one line of code you’re able to force a specific theme in your application so that, if your application looks bad with another theme because you’ve used custom colors, you can force all the elements in the page to use your own theme.
Once you’ve installed the library using NuGet, you’ll just have to use the singleton ThemeManager class when your application starts (it can be inside the App.xaml.cs constructor or, for example, since Speaker Timer is built using Calibur Micro, I used it in the OnStartup() method of the boostrapper).
This class exposes some methods to change the theme: the most important ones are ToDarkTheme() and ToLightTheme(), that will override every color and force all the elements to use the theme that matches best with your application. In my case, since Speaker Timer looks bad with a light theme, I’ve forced the dark theme by including the following code:
public App() { // Standard Silverlight initialization InitializeComponent(); ThemeManager.ToDarkTheme(); // Show graphics profiling information while debugging. if (System.Diagnostics.Debugger.IsAttached) { // Display the current frame rate counters. Application.Current.Host.Settings.EnableFrameRateCounter = false; // Show the areas of the app that are being redrawn in each frame. //Application.Current.Host.Settings.EnableRedrawRegions = true; // Enable non-production analysis visualization mode, // which shows areas of a page that are being GPU accelerated with a colored overlay. //Application.Current.Host.Settings.EnableCacheVisualization = true; } }
The result, with this simple code, is that, regardless of the theme chosen by the user, the application will always look like in the first screenshot.
###
Managing a custom Application Bar
As I’ve already mentioned, Speaker Timer is developed using Caliburn Micro as a MVVM Framework: for this reason, I’m not using the native Application Bar, but the one I’ve talked about in this post, that supports binding and Caliburn Micro conventions. However, by using this bar I’ve found an issue connected to the user theme: even if I’ve forced the dark theme using Jeff Wilcox’s library, the application bar was still using the light theme. It’s not a big deal, but the biggest issue was that, when I moved to the other pages and then I came back to the first one, the Application Bar suddendly changed and became dark. This happens because the PhoneThemeManager library can’t immediately ovveride the Application Bar colors, so you can see the change only at a second time.
How to fix this situation? The library exposes two methods for this scenario: the first one is called CreateApplicationBar() and can be used to create a new application bar with the proper theme. This method is useful in case you’re creating and managing the Application Bar in the code behind and not in the XAML. But this is not my case, since Speaker Timer is built using MVVM and the custom application bar is added directly in the XAML. Here comes another method offered by the library, called MatchOverriddenTheme(): it’s an extension method applied to the IApplicationBar interface, so every custom Application Bar implemention should support it. When you call this method, the application bar colors are immediately overwritten, so you don’t have to deal with the graphic glitch I’ve mentioned before.
There’s only one important thing to keep in mind: this method should be called in the Loaded event of every page with an application bar and not in the page costructor, otherwise it won’t work properly, as you can see in the following screenshot.
So, the first operation is to give a Name to your custom application bar using the x:Name property, like in the following sample:
<bindableAppBar:BindableAppBar x:Name="AppBar"> <bindableAppBar:BindableAppBarButton Text="{Binding Source={StaticResource LocalizedStrings}, Path=LocalizedResources.Add}" IconUri="/Assets/Icons/Add.png" x:Name="NewSession" Visibility="{Binding Path=IsCheckModeEnabled, Converter={StaticResource NegativeBooleanToVisibilityConverter}}" /> <bindableAppBar:BindableAppBarButton Text="{Binding Source={StaticResource LocalizedStrings}, Path=LocalizedResources.Select}" IconUri="/Assets/Icons/Selection.png" x:Name="Selection" Visibility="{Binding Path=IsCheckModeEnabled, Converter={StaticResource NegativeBooleanToVisibilityConverter}}" /> </bindableAppBar:BindableAppBar>
Then, in the code behind, you should subscribe to the Loaded event and call the MatchOverriddenTheme() on the custom application bar, like in the following sample:
public partial class MainPage : PhoneApplicationPage { // Constructor public MainPage() { InitializeComponent(); Loaded += (obj, args) => { AppBar.MatchOverriddenTheme(); }; } }
That’s all! Happy coding!
in
-
First steps with Caliburn Micro in Windows Phone 8 – The complete series
This post is simply a collector of all the blog posts I wrote about using Caliburn Micro, a very powerfull Model-View-ViewModel framework, in Windows Phone 8. The posts are listed in chronological order, so it will be easier for you to follow the logic path of the posts: from the basic concepts (what’s Caliburn, conventions, etc.) to the most complex stuff (tombstoning, collections, messaging).
This list will be published also at the bottom of every post of the series, so that it will be easier for you to jump from a post to another.
Here we go!
in
subscribe via RSS