(X) Hide this
    • Login
    • Join
      • Say No Bots Generate New Image
        By clicking 'Register' you accept the terms of use .

Windows 8 Apps: The 8 Must-Know Tricks! Day 5

(4 votes)
Samidip Basu
>
Samidip Basu
Joined Aug 22, 2011
Articles:   16
Comments:   24
More Articles
0 comments   /   posted on Oct 07, 2012
Tags:   windows-8 , samidip-basu
Categories:   Windows 8

After a little gap because of a family relocation, we’re back on schedule :). This is Day # 5 in the Windows 8 development article series on common tips & tricks towards real-world Windows 8 Store apps. Now that our Windows 8 app is well underway, time to make sure we are integrating our app with the rest of the Windows 8 OS through the use of right Contracts.

Over the next several weeks, you’ll see 8 articles talk about some must-do things for Windows 8 app developers. Simple & to the point, with some code examples on XAML/C# stack. Here’s the indexed list for the series:

Day 1: Know the ecosystem; Start
Day 2: Layout, Navigation & Visual States
Day 3: Semantic Zoom
Day 4: Controls & Styling
Day 5: Search, Share & Settings Contracts
Day 6: Data Persistence & Application Life-Cycle Management
Day 7: Use of OData or Web Services
Day 8: Live Services integration

Day 5: Search, Share & Settings Contracts

Win as One – one of the cornerstones of the Modern UI design principles and a very important one to keep in mind as we design our Windows 8 Store apps. Windows 8 as an OS introduces the consumer to a whole new paradigm in UX, where content & users are front & center. This is carried forward by Windows Phone & Xbox ecosystems as well. It is thus important that our Windows 8 apps follow the same principles, lest the application experience feels jarring to the end user. And it is this integration of our app with the rest of the OS that is best achieved through the use of Windows 8 application Contracts & Extensions. In this article, we take a holistic view of Contracts and then break down how to implement three of the most common ones – Search, Share & Settings. So, let’s explore our options on the XAML stack.

A Contract is like an agreement of requirements between one or more apps and Windows, which aids in the participation in some special Windows 8 interaction. Windows 8 Store applications run in their own sandboxed environment, thus affording top-notch protection for the end user against faulty/malware applications having a bigger impact on their systems. The one downside to that can be viewed as lack of communication between different apps. This is where Contracts & Extensions come into play, standardizing the modes of communication between disparate apps and the OS. If one application already has some data or knows how to do something well, why recreate? Let’s dig in …


Search:

Data – it is the main content in almost every app, either user owned or pulled down from some source. Why not let the user search for their data in your app – while in the app and also while not running your app? This is what the Search Contract provides – easy touch-friendly search through the global Charms menu. Muscle memory or keyboard shortcuts – Windows 8 users will be very accustomed to using the Charms bar for common actions, and this is where Search fits. While we can search for Files or Settings, let’s focus on in-app search that is provided by the Search contract implementation. Remember our demo Windows 8 app showing categorized SilverlightShow articles? Here’s the global search on our demo SilverlightShow application:

 

Search Results

As you can see, performing a Search through the ubiquitous Charms bar allows for filtering of data within each application. This can be triggered while the app is running; but also when it is not. If our app participates in the Search contract, users may invoke Search while anywhere else; Search parameters are passed down by the OS as our app activates to show the user the same filtered view that one would expect.

Implementing the Search contract comes down to a few steps:

  • Add the Search Contract through template.
  • Adding Search Contract
  • This will add a named file for the Search results in your app.
  • It also adds a Search Activation event handler on your App.xaml.cs for your app to take action if it is activated by user performing a Search on the app through the Charms.
  • In addition to the activation point, a Declaration is added to the application manifest file letting the OS know that your app can participate in the Search contract.
  • SearchDeclaration
  • The kind of Search filters you support in addition to the text that might come down from the global Search is completely up to your application’s needs & data availability.
  • So, is the flexibility in deciding how to show Search results.
  • In addition to the Search results in the app, one may also provide Search Suggestions, which are automatically touch-friendly and displayed upon exact matches in the Charms menu itself.
  • The quickstart guide to adding Search can be found @ http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh868180.aspx.

Here’s some code: notice the Search activation event handler carrying the search argument into the specialized results page:

   1: /// <summary>
   2: /// Invoked when the application is activated to display search results.
   3: /// </summary>
   4: /// <param name="args">Details about the activation request.</param>
   5: protected async override void OnSearchActivated(Windows.ApplicationModel.Activation.SearchActivatedEventArgs args)
   6: {
   7:     // If the Window isn't already using Frame navigation, insert our own Frame.
   8:     var previousContent = Window.Current.Content;
   9:     var frame = previousContent as Frame;
  10:    
  11:     if (frame == null)
  12:     {
  13:         // Create a Frame to act as the navigation context and associate it with a SuspensionManager key.
  14:         frame = new Frame();
  15:         SilverLightShowDemo.Common.SuspensionManager.RegisterFrame(frame, "AppFrame");
  16:  
  17:         if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
  18:         {
  19:             // Restore the saved session state only when appropriate.
  20:             try
  21:             {
  22:                 await SilverLightShowDemo.Common.SuspensionManager.RestoreAsync();
  23:             }
  24:             catch (Exception)
  25:             {
  26:                 //Something went wrong restoring state.
  27:                 //Assume there is no state and continue
  28:             }
  29:         }
  30:     }
  31:  
  32:     frame.Navigate(typeof(SLShowArticleSearch), args.QueryText);
  33:     Window.Current.Content = frame;
  34:  
  35:     // Ensure the current window is active
  36:     Window.Current.Activate();
  37: }

The Search results page needs to handle back-navigation and know how to save/restore state through the application lifecycle. Supporting filters might often come down to doing LINQ on an existing collection to narrow down results, as shown in the example below – notice how we respond to the Article Name filter change to trim results:

   1: private void articleNameTextBox_TextChanged(object sender, TextChangedEventArgs e)
   2: {
   3:     this.FilterOnArticleName();
   4: }
   5:  
   6: private void FilterOnArticleName()
   7: {
   8:     if (this.articleNameTextBox.Text.Trim() != string.Empty)
   9:     {
  10:         // Apply the filter.
  11:         FilteredArticleList = ((App)Application.Current).ArticleCollection.ToList<SLShowArticle>().FindAll(matchingArticle => matchingArticle.ArticleName.ToUpper().Contains(this.articleNameTextBox.Text.Trim().ToUpper()));
  12:  
  13:         if (FilteredArticleList.Count > 0)
  14:         {
  15:             // Put Filtered Articles into Grouped buckets.
  16:             var query = from individualArticle in FilteredArticleList
  17:                         orderby individualArticle.ArticleTopic
  18:                         group individualArticle by individualArticle.ArticleTopic into g
  19:                         select new SLShowGroupedArticles
  20:                         {
  21:                             SLShowArticleGroupName = g.Key,
  22:                             SLShowArticleGroupCount = g.Count(),
  23:                             ArticleCollection = new ObservableCollection<SLShowArticle>(g.ToList())
  24:                         };
  25:  
  26:             FilteredGroupedArticleList = new ObservableCollection<SLShowGroupedArticles>(query.ToList());
  27:  
  28:             // Set the Data-Binding context to the filtered collection of Articles.
  29:             this.DefaultViewModel["GroupedArticles"] = FilteredGroupedArticleList;
  30:  
  31:             VisualStateManager.GoToState(this, "ResultsFound", true);
  32:         }
  33:         else
  34:         {
  35:             // Empty out binding.
  36:             this.DefaultViewModel["GroupedArticles"] = new ObservableCollection<SLShowGroupedArticles>();
  37:  
  38:             // Display informational text when there are no search results.
  39:             VisualStateManager.GoToState(this, "NoResultsFound", true);
  40:         }
  41:     }
  42:     else
  43:     {
  44:         // Erase filter.
  45:         FilteredArticleList = ((App)Application.Current).ArticleCollection.ToList<SLShowArticle>();
  46:  
  47:         if (FilteredArticleList.Count > 0)
  48:         {
  49:             // Put Filtered Articles into Grouped buckets.
  50:             var query = from individualArticle in FilteredArticleList
  51:                         orderby individualArticle.ArticleTopic
  52:                         group individualArticle by individualArticle.ArticleTopic into g
  53:                         select new SLShowGroupedArticles
  54:                         {
  55:                             SLShowArticleGroupName = g.Key,
  56:                             SLShowArticleGroupCount = g.Count(),
  57:                             ArticleCollection = new ObservableCollection<SLShowArticle>(g.ToList())
  58:                         };
  59:  
  60:             FilteredGroupedArticleList = new ObservableCollection<SLShowGroupedArticles>(query.ToList());
  61:  
  62:             // Set the Data-Binding context to the filtered collection of Articles.
  63:             this.DefaultViewModel["GroupedArticles"] = FilteredGroupedArticleList;
  64:  
  65:             VisualStateManager.GoToState(this, "ResultsFound", true);
  66:         }
  67:         else
  68:         {
  69:             // Empty out binding.
  70:             this.DefaultViewModel["GroupedArticles"] = new ObservableCollection<SLShowGroupedArticles>();
  71:  
  72:             // Display informational text when there are no search results.
  73:             VisualStateManager.GoToState(this, "NoResultsFound", true);
  74:         }
  75:     }
  76: }

The XAML for the Search page is shown below – notice how Visual States can be used to display results or when nothing matches the filters, in addition to supporting Snapped state:

   1: <common:LayoutAwarePage
   2: x:Name="pageRoot"
   3: x:Class="SilverLightShowDemo.SLShowArticleSearch"
   4: DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
   5: IsTabStop="false"
   6: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   7: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   8: xmlns:local="using:SilverLightShowDemo"
   9: xmlns:common="using:SilverLightShowDemo.Common"
  10: xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  11: xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"    
  12: mc:Ignorable="d"
  13: d:ExtensionType="Search">
  14:  
  15: <Page.Resources>
  16:  
  17:     <!-- Collection of grouped Articles displayed by this page -->
  18:     <CollectionViewSource
  19:         x:Name="groupedItemsViewSource"
  20:         Source="{Binding GroupedArticles}"
  21:         IsSourceGrouped="true"
  22:         ItemsPath="ArticleCollection"/>
  23:  
  24: </Page.Resources>
  25:  
  26: <Grid Style="{StaticResource SLShowDemoGridLayoutRootStyle}">
  27:     <Grid.RowDefinitions>
  28:         <RowDefinition Height="140"/>
  29:         <RowDefinition Height="*"/>
  30:         <RowDefinition Height="100"/>
  31:     </Grid.RowDefinitions>
  32:  
  33:     <!-- Logo Area -->
  34:     <Grid Grid.Row="0">
  35:         <StackPanel Orientation="Horizontal">
  36:             <Button x:Name="backButton" Margin="40, 0, 25, 0" Click="backButton_Click">
  37:                 <Button.Template>
  38:                     <ControlTemplate>
  39:                         <StackPanel Orientation="Vertical">
  40:                             <Image x:Name="backIcon" Source="/Assets/BackArrow.png" Height="50" Width="50" />
  41:                         </StackPanel>
  42:                     </ControlTemplate>
  43:                 </Button.Template>
  44:             </Button>
  45:             <Image x:Name="Logo" Source="/SilverlightShowLogo.png" Width="195" Height="90" Margin="30, 30, -80, 0" VerticalAlignment="Center"/>
  46:             <TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Style="{StaticResource SLShowPageHeaderTextStyle}" VerticalAlignment="Center" Margin="0"/>
  47:         </StackPanel>
  48:     </Grid>
  49:  
  50:     <Grid x:Name="mainSearchResultArea" Grid.Row="1">
  51:  
  52:         <Grid x:Name="searchResultsPanel">
  53:             <Grid.RowDefinitions>
  54:                 <RowDefinition Height="Auto"/>
  55:                 <RowDefinition Height="*"/>
  56:             </Grid.RowDefinitions>
  57:  
  58:             <StackPanel Orientation="Horizontal" Margin="180,20,25,50" x:Name="searchFilters">
  59:                 <TextBlock Text="Article Name:" Style="{StaticResource AppWideMediumTextStyle}"/>
  60:                 <TextBox x:Name="articleNameTextBox" Width="200" Margin="20,0,0,0" TextChanged="articleNameTextBox_TextChanged"/>
  61:                 <TextBlock Text="Publish Date:" Style="{StaticResource AppWideMediumTextStyle}" Margin="40,0,0,0"/>
  62:                 <ComboBox x:Name="publishDateDropdown" Margin="20,0,0,0" Width="150" SelectionChanged="publishDateDropdown_SelectionChanged" >
  63:                     <ComboBoxItem Content="All" IsSelected="True"/>
  64:                     <ComboBoxItem Content="Today"/>
  65:                     <ComboBoxItem Content="Yesterday"/>
  66:                     <ComboBoxItem Content="Day before Yesterday"/>
  67:                 </ComboBox>
  68:             </StackPanel>
  69:  
  70:             <!-- Horizontal scrolling grid used in most view states -->
  71:             <ScrollViewer
  72:             x:Name="itemGridScrollViewer"
  73:             AutomationProperties.AutomationId="ItemGridScrollViewer"
  74:             AutomationProperties.Name="Search Results"
  75:             Grid.Row="1"
  76:             Margin="0,-3,0,0"
  77:             Style="{StaticResource HorizontalScrollViewerStyle}">
  78:  
  79:                 <GridView
  80:                 x:Name="itemGridView"
  81:                 AutomationProperties.AutomationId="ItemGridView"
  82:                 AutomationProperties.Name="Grouped Items"
  83:                 Margin="170,0,100,46"
  84:                 ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
  85:                 ItemTemplate="{StaticResource SLShow250x100ArticleTemplate}">
  86:  
  87:                     <GridView.ItemsPanel>
  88:                         <ItemsPanelTemplate>
  89:                             <VirtualizingStackPanel Orientation="Horizontal"/>
  90:                         </ItemsPanelTemplate>
  91:                     </GridView.ItemsPanel>
  92:                     <GridView.GroupStyle>
  93:                         <GroupStyle>
  94:                             <GroupStyle.HeaderTemplate>
  95:                                 <DataTemplate>
  96:                                     <Grid Margin="1,0,0,6">
  97:                                         <StackPanel Orientation="Horizontal">
  98:                                             <Button AutomationProperties.Name="Group Title" Content="{Binding SLShowArticleGroupName}" Style="{StaticResource SLShowTextButtonStyle}"/>
  99:                                             <TextBlock Text="-" Style="{StaticResource SubheaderTextStyle}" VerticalAlignment="Top" Margin="5,0,5,0"/>
 100:                                             <TextBlock Text="{Binding SLShowArticleGroupCount}" Style="{StaticResource SubheaderTextStyle}" VerticalAlignment="Top"/>
 101:                                         </StackPanel>
 102:                                     </Grid>
 103:                                 </DataTemplate>
 104:                             </GroupStyle.HeaderTemplate>
 105:                             <GroupStyle.Panel>
 106:                                 <ItemsPanelTemplate>
 107:                                     <VariableSizedWrapGrid Orientation="Vertical" Margin="0,0,100,0"/>
 108:                                 </ItemsPanelTemplate>
 109:                             </GroupStyle.Panel>
 110:                         </GroupStyle>
 111:                     </GridView.GroupStyle>
 112:                 </GridView>
 113:  
 114:             </ScrollViewer>
 115:  
 116:             <!-- Vertical scrolling list only used when snapped -->
 117:             <ScrollViewer
 118:             x:Name="itemListScrollViewer"
 119:             AutomationProperties.AutomationId="ItemListScrollViewer"
 120:             Grid.Row="1"
 121:             Visibility="Collapsed"
 122:             Style="{StaticResource VerticalScrollViewerStyle}">
 123:  
 124:                 <ListView
 125:                 x:Name="itemListView"
 126:                 AutomationProperties.AutomationId="ItemListView"
 127:                 AutomationProperties.Name="Grouped Items"
 128:                 Margin="10,-10,0,60"
 129:                 ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
 130:                 ItemTemplate="{StaticResource SLShowSnappedArticleTemplate}">
 131:  
 132:                     <ListView.GroupStyle>
 133:                         <GroupStyle>
 134:                             <GroupStyle.HeaderTemplate>
 135:                                 <DataTemplate>
 136:                                     <Grid Margin="7,7,0,0">
 137:                                         <Button AutomationProperties.Name="Group Title" Content="{Binding SLShowArticleGroupName}" Style="{StaticResource SLShowTextButtonStyle}"/>
 138:                                     </Grid>
 139:                                 </DataTemplate>
 140:                             </GroupStyle.HeaderTemplate>
 141:                         </GroupStyle>
 142:                     </ListView.GroupStyle>
 143:                 </ListView>
 144:             </ScrollViewer>
 145:  
 146:         </Grid>
 147:  
 148:     </Grid>
 149:   
 150:  
 151:     <TextBlock
 152:         x:Name="noResultsTextBlock"
 153:         Grid.Row="1"
 154:         Margin="220,100,0,0"
 155:         Visibility="Collapsed"
 156:         Style="{StaticResource SubheaderTextStyle}"
 157:         Text="No results match your search." />
 158:  
 159:     <VisualStateManager.VisualStateGroups>
 160:         <!-- Visual states reflect the page's view state -->
 161:         <VisualStateGroup x:Name = "PageStates">
 162:             <VisualState x:Name="FullScreenLandscape"/>
 163:             <VisualState x:Name="Filled"/>
 164:  
 165:             <VisualState x:Name="Snapped">
 166:                 <Storyboard>
 167:                     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListScrollViewer" Storyboard.TargetProperty="Visibility">
 168:                         <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
 169:                     </ObjectAnimationUsingKeyFrames>
 170:                     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemGridScrollViewer" Storyboard.TargetProperty="Visibility">
 171:                         <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
 172:                     </ObjectAnimationUsingKeyFrames>
 173:                     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="searchFilters" Storyboard.TargetProperty="Visibility">
 174:                         <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
 175:                     </ObjectAnimationUsingKeyFrames>
 176:                 </Storyboard>
 177:             </VisualState>
 178:         </VisualStateGroup>
 179:  
 180:         <VisualStateGroup x:Name = "ResultStates">
 181:             <VisualState x:Name="ResultsFound" />
 182:             <!-- When there are no results, the results panel is replaced with an informational TextBlock -->
 183:             <VisualState x:Name="NoResultsFound">
 184:                 <Storyboard>
 185:                     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemGridScrollViewer" Storyboard.TargetProperty="Visibility">
 186:                         <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
 187:                     </ObjectAnimationUsingKeyFrames>
 188:                     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListScrollViewer" Storyboard.TargetProperty="Visibility">
 189:                         <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
 190:                     </ObjectAnimationUsingKeyFrames>
 191:                     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="noResultsTextBlock" Storyboard.TargetProperty="Visibility">
 192:                         <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
 193:                     </ObjectAnimationUsingKeyFrames>
 194:                 </Storyboard>
 195:             </VisualState>
 196:         </VisualStateGroup>
 197:  
 198:     </VisualStateManager.VisualStateGroups>
 199: </Grid>
 200:  
 201: mmon:LayoutAwarePage>

 

Share:

Users like sharing content and successful apps make it easy for users to share what they are doing with their friends, family or coworkers. This is achieved through the Share Contract in Windows 8. This has the obvious benefit of preventing re-invention of the wheels – if your app needs to allow the user to share content on social networks, you don’t need to write logic to do that; simply leverage what another app already knows how to do.

Share Contract has two types of apps participating – Source & Target. As the names suggests, the Source apps have some content which the user might like to share, and the Target apps allow that sharing. As the Source application gets ready to share, the OS throws up a list of Target applications that can handle the data being shared followed by the Target application being activated to deal with the data being passed to it for sharing, as illustrated below in sharing details about the selected SilverlightShow article:

ShareCharm

SharingContent

At the core of this hand-shaking between between Source & Target applications for sharing is the DataPackage with its supported data formats. Essentially, the Source declares what type of content is being shared and only registered Targets who have declared that they can handle the given data type, get listed for invocation. The supported data formats are

  • Plain text
  • Uniform Resource Identifiers (URIs)
  • HTML
  • Formatted text
  • Bitmaps
  • Files
  • Custom-defined data

Here’s some code to add to make your app/ page in it a Share Source doling out Text content:

   1: using Windows.ApplicationModel.DataTransfer;
   2:  
   3: protected override void OnNavigatedTo(NavigationEventArgs e)
   4: {
   5:     base.OnNavigatedTo(e);            
   6:  
   7:     // Sign up to Share.
   8:     var dataTransferManager = DataTransferManager.GetForCurrentView();
   9:     dataTransferManager.DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(dataTransferManager_DataRequested);
  10: }
  11:  
  12: private void dataTransferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
  13: {
  14:     if (this.itemGridView.SelectedItem != null)
  15:     {
  16:         SLShowArticle selectedArticle = (SLShowArticle)this.itemGridView.SelectedItem;
  17:  
  18:         // Set what to share.
  19:         string textToShare = "Article Information: Name# " + selectedArticle.ArticleName + " | Author: " + selectedArticle.AuthorName + " @ " + selectedArticle.DisplayablePublishDate + ".";
  20:         args.Request.Data.Properties.Title = "SilverlightShow Article Share:";
  21:         args.Request.Data.SetText(textToShare);
  22:     }
  23: }

As you can see, we attached a event handler to listen in when the user wants to share a SilverlightShow article information through the Charms menu. When invoked, our code simply packaged up the DataPackage including the content to share; this gets passed up to the OS and made available to the Share Target applications. More details about Sharing & exchanging content between Windows 8 apps can be found @ http://msdn.microsoft.com/en-us/library/windows/apps/xaml/Hh871373(v=win.10).aspx.

 

Settings:

Most Windows 8 apps will have settings, and if they are all done in different ways, it leads to a learning curve for the user. In comes the Settings Contract, the implementation of which provides a consolidated place for fast, in-context access to settings that affect the current Windows Store app running. Muscle memory & ease of flyout animations should make editing Settings second nature to users. A light-dismiss surface (tapping anywhere outside) hides away the Settings, thus allowing the user to get back to the application experience quickly. The ApplicationSettings class provides a one-stop place to hold all our application settings, which may be data bound for easy edits & persistence.

There is one little problem in the C#/XAML stack – the Settings Flyout commonly seen in Windows 8 isn’t a native control, unlike the web stack. There are extensions/nugets like Callisto which help, explained as “A control suite for some common things a Windows 8 XAML application might want to include such as a Flyout, Menu, Rating, Settings Pane, and Pivot control”. However, if you really wanted to roll your own, it is not that difficult a task either. The trick is to define a UserControl to hold the app Settings and then house it in the XAML PopUp control, complete with animation & the light-dismiss. Here’s a sample Settings flyout for our demo SilverlightShow Articles application:

SettingsCharm

SettingsFlyout

The name of the Settings & the complete look and feel of it is customizable to fit the brand of your app. Here’s the XAML for our UserControl to house Settings:

   1: <UserControl
   2:     x:Class="SilverLightShowDemo.SettingsFlyOut"
   3:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   4:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   5:     xmlns:local="using:SilverLightShowDemo"
   6:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   7:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   8:     mc:Ignorable="d"
   9:     d:DesignHeight="300"
  10:     d:DesignWidth="400">
  11:  
  12:     <Grid Style="{StaticResource SLShowDemoGridLayoutRootStyle}" Background="Orange">
  13:         <Grid.RowDefinitions>
  14:             <RowDefinition Height="140"/>
  15:             <RowDefinition Height="*"/>           
  16:         </Grid.RowDefinitions>
  17:  
  18:         <!-- Logo Area -->
  19:         <Grid Grid.Row="0">
  20:             <StackPanel Orientation="Horizontal">
  21:                 <Button x:Name="backButton" Margin="40, 0, 25, 0" Click="MySettingsBackClicked">
  22:                     <Button.Template>
  23:                         <ControlTemplate>
  24:                             <StackPanel Orientation="Vertical">
  25:                                 <Image x:Name="backIcon" Source="/Assets/BackArrow.png" Height="50" Width="50" />
  26:                             </StackPanel>
  27:                         </ControlTemplate>
  28:                     </Button.Template>
  29:                 </Button>
  30:                 <Image x:Name="Logo" Source="/SilverlightShowLogo.png" Width="195" Height="90" Margin="15, 15, -80, 0" VerticalAlignment="Center"/>                
  31:             </StackPanel>
  32:         </Grid>
  33:  
  34:         <!-- App Settings -->
  35:         <Grid x:Name="settingsContent" Grid.Row="1" Margin="50">
  36:             <StackPanel Margin="0, 40, 0, 0" Orientation="Vertical">
  37:                 <TextBlock FontWeight="Bold" Text="Put your App Settings here:" TextWrapping="Wrap" Style="{StaticResource BasicTextStyle}" HorizontalAlignment="Left"/>
  38:                 <ToggleSwitch Margin="0,25, 0, 0" Header = "Download updates automatically" HorizontalAlignment="Left" HorizontalContentAlignment="Left"/>
  39:                 <ToggleSwitch Margin="0, 25, 0, 0" Header = "Push Notifications" HorizontalAlignment="Stretch"/>
  40:             </StackPanel>
  41:         </Grid>
  42:         
  43:     </Grid>
  44:     
  45: </UserControl>

The next trick is to wire it up as the user launches Settings from the Charms. Here’s some code:

   1: using Windows.UI.ApplicationSettings;
   2: using Windows.UI.Popups;
   3: using Windows.UI.Xaml.Media.Animation;
   4:  
   5: region "Members"
   6:  
   7: private Popup settingsPopup;
   8:  
   9: // Used to determine the correct height to ensure our custom UI fills the screen.
  10: private Rect windowBounds;
  11:  
  12: // Desired width for the settings UI. UI guidelines specify this should be 346 or 646 depending on your needs.
  13: private double settingsWidth = 346;
  14:  
  15: #endregion
  16:  
  17: protected override void OnNavigatedTo(NavigationEventArgs e)
  18: {
  19:     base.OnNavigatedTo(e);            
  20:  
  21:     // Settings
  22:     SettingsPane.GetForCurrentView().CommandsRequested += Settings_CommandsRequested;
  23: }
  24:  
  25: private void Settings_CommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
  26: {
  27:     UICommandInvokedHandler handler = new UICommandInvokedHandler(onSettingsCommand);
  28:  
  29:     SettingsCommand settingsCommand = new SettingsCommand("SLShowSettings", "SLShow Settings", handler);
  30:     args.Request.ApplicationCommands.Add(settingsCommand);
  31: }
  32:  
  33: void onSettingsCommand(IUICommand command)
  34: {
  35:     // Create a Popup window which will contain our flyout.
  36:     settingsPopup = new Popup();
  37:     settingsPopup.Closed += OnPopupClosed;
  38:     Window.Current.Activated += OnWindowActivated;
  39:     settingsPopup.IsLightDismissEnabled = true;
  40:     settingsPopup.Width = settingsWidth;
  41:     settingsPopup.Height = windowBounds.Height;
  42:  
  43:     // Add the proper animation for the panel.
  44:     settingsPopup.ChildTransitions = new TransitionCollection();
  45:     settingsPopup.ChildTransitions.Add(new PaneThemeTransition()
  46:     {
  47:         Edge = (SettingsPane.Edge == SettingsEdgeLocation.Right) ?
  48:                EdgeTransitionLocation.Right :
  49:                EdgeTransitionLocation.Left
  50:     });
  51:  
  52:     // Create a SettingsFlyout the same dimenssions as the Popup.
  53:     SettingsFlyOut mypane = new SettingsFlyOut();
  54:     mypane.Width = settingsWidth;
  55:     mypane.Height = windowBounds.Height;
  56:  
  57:     // Place the SettingsFlyout inside our Popup window.
  58:     settingsPopup.Child = mypane;
  59:  
  60:     // Let's define the location of our Popup.
  61:     settingsPopup.SetValue(Canvas.LeftProperty, SettingsPane.Edge == SettingsEdgeLocation.Right ? (windowBounds.Width - settingsWidth) : 0);
  62:     settingsPopup.SetValue(Canvas.TopProperty, 0);
  63:     settingsPopup.IsOpen = true;
  64: }
  65:  
  66: void OnPopupClosed(object sender, object e)
  67: {
  68:     Window.Current.Activated -= OnWindowActivated;
  69: }
  70:  
  71: private void OnWindowActivated(object sender, Windows.UI.Core.WindowActivatedEventArgs e)
  72: {
  73:     if (e.WindowActivationState == Windows.UI.Core.CoreWindowActivationState.Deactivated)
  74:     {
  75:         settingsPopup.IsOpen = false;
  76:     }
  77: }
  78:  
  79:  
  80:  

Notice how we listen in with a event handler when the user launches the SettingsPane. Our SettingsFlyout is lodged inside the PopUp controls, which we animate to open & close, along with light-dismiss behavior, when the app window is activated. The Settings page could have any data-bound XAML controls you need to manage app level settings; the only interesting piece of code may be the back button navigation:

   1: private void MySettingsBackClicked(object sender, RoutedEventArgs e)
   2: {
   3:     // First close our Flyout.
   4:     Popup parent = this.Parent as Popup;
   5:     if (parent != null)
   6:     {
   7:         parent.IsOpen = false;
   8:     }
   9:  
  10:     // If the app is not snapped, then the back button shows the Settings pane again.
  11:     if (Windows.UI.ViewManagement.ApplicationView.Value != Windows.UI.ViewManagement.ApplicationViewState.Snapped)
  12:     {
  13:         SettingsPane.Show();
  14:     }
  15: }

More details about adding Settings contract in C#/XAML can be found @ http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh872190.aspx.

 

Conclusion

That’s it for today. The crux of this article was to talk about the various ways our Windows 8 apps could integrate with the OS through the implementation of Contracts. In particular, we saw how in just a few steps, one could support common traits like Search, Share & Settings in Windows 8 apps.
See you next time as we dive into Application Lifecycle Management and how to deal with data persistence. Thanks for reading!

 

About Author

ActualPic

Samidip Basu (@samidip) is a technologist, gadget-lover and MSFT Mobility Solutions Lead for Sogeti USA working out of Columbus OH. With a strong developer background in Microsoft technology stack, he now spends much of his time evangelizing Windows Phone/Windows 8 platforms & cloud-supported mobile solutions in general. He passionately helps run The Windows Developer User Group (http://thewindowsdeveloperusergroup.com/), labors in M3 Conf (http://m3conf.com/) organization and can be found with at-least a couple of hobbyist projects at any time. His spare times call for travel and culinary adventures with the wife. Find out more at http://samidipbasu.com.


Subscribe

Comments

No comments

Add Comment

Login to comment:
  *      *       

From this series