Dec 052013
 
 December 5, 2013  Posted by at 6:48 am WinRT  Add comments
SearchWindowsStoreApp19

From the start it has been important to provide a cohesive experience for users using the Windows Store applications and the charm bar for common application tasks was a big part of that- and still is. However a few changes were introduced from Windows 8 to 8.1 to build further on the idea of the shared interaction model but give developers and product owners more control so they wouldn’t feel too restricted by the guidelines.

Search was one of the things that were changed, and a SearchBox was provided to allow applications to search within the application, not just by using the Search charm which is accessed from the right side charm menu which can either be swiped in from the right or by taking the mouse pointer to the right. Or use the Windows + S command.

You can only use either the SearchBox OR the charm (the ‘Search pane’ provided through the charm), although this might change,- and you can create your own search and not use the control.

From MSDN:

Note  An app can’t use both the search box (Windows.UI.Xaml.Controls.SearchBox for Windows Store apps using C++, C#, or Visual Basic, WinJS.UI.SearchBox for Windows Store apps using JavaScript) and the SearchPane. Using both the search box and the search pane in the same app causes the app to throw an exception with this message: “Cannot create instance of type ‘Windows.UI.Xaml.Controls.SearchBox.'”

This might change, remind me to update this post if it does.

In this post I’ll walk you through a very basic implementation of both, using code behind. I’m working on a longer post that’s shows several ways of doing this by using the MVVM pattern, but it’s a longer post so give me some time 🙂

I’ve made this post heavy on pictures and code, if I somehow missed something let me know. Code is uploaded on MSDN here 

And on my blog here (just in case one link breaks).

Let’s get started.

SearchWindowsStoreApp

Go ahead and add a SearchBox, and make sure you enable it so that it gets focus if the user starts typing.

SearchWindowsStoreApp1

Here is the full XAML:

[sourcecode language=”XML”]
<Page
x:Class="Search.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<SearchBox
QuerySubmitted="OnQuerySubmitted"
SuggestionsRequested="OnSuggestionsRequested"
FocusOnKeyboardInput="True"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Width="500"
Height="50"
Margin="30"/>
</Grid>
</Page>
[/sourcecode]

Now we need a page that we can show the results on, so right click and create a new SearchPage. It will give us a lot of code, and even more comments. But for this sample I want to show you how this works, and we will therefore remove a lot of the code there – both XAML and cs. By adding the page a few new files will be added as well. Let it do that, chances are you will need them anyway.

SearchWindowsStoreApp2

SearchWindowsStoreApp3

SearchWindowsStoreApp4

SearchWindowsStoreApp5

The SearchPage should provide a sort of list that displays search results. The default one uses a collection with filters, but to keep it simple we will just bind directly to a property that is a list of items that match the search query.

SearchWindowsStoreApp8

Two visual states are provided, one for when there are matches and one for when there are no matches.

SearchWindowsStoreApp11

Here is the full XAML:

[sourcecode language=”XML”]
<Page
x:Name="pageRoot"
x:Class="Search.SearchResultsPage"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:ExtensionType="Search">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid x:Name="resultsPanel" Grid.Row="1">
<Grid x:Name="typicalPanel">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<GridView
x:Name="resultsGridView"
AutomationProperties.AutomationId="ResultsGridView"
AutomationProperties.Name="Search Results"
TabIndex="1"
Grid.Row="1"
Margin="0,-238,0,0"
Padding="110,240,110,46"
SelectionMode="None"
IsSwipeEnabled="false"
IsItemClickEnabled="True"
ItemsSource="{Binding Results}">
<GridView.ItemTemplate>
<DataTemplate>
<Border Background="#FF464646" Width="300" Height="50">
<TextBlock Text="{Binding Title}" Padding="20" FontSize="30" TextWrapping="NoWrap" Style="{StaticResource BodyTextBlockStyle}"/>
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Grid>
</Grid>

<!– Back button and page title –>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
Style="{StaticResource NavigationBackButtonNormalStyle}"
VerticalAlignment="Top"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"/>
<TextBlock x:Name="pageTitle" Text="Result Page" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"/>
<TextBlock x:Name="resultText" Grid.Column="2" Text="Results for &#x00a0;" IsHitTestVisible="false" Style="{StaticResource SubheaderTextBlockStyle}"
TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,0,40" />
<TextBlock x:Name="queryText" Grid.Column="3" Foreground="#FFFF5D00" FontWeight="Bold" Text="{Binding QueryText}" IsHitTestVisible="false" Style="{StaticResource SubheaderTextBlockStyle}"
TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,0,40" />
</Grid>

<TextBlock
x:Name="noResultsTextBlock"
Grid.Row="1"
Margin="120,50,0,0"
Visibility="Collapsed"
Style="{StaticResource SubheaderTextBlockStyle}"
Text="No results match your search." />

<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ResultStates">
<VisualState x:Name="ResultsFound" />
<VisualState x:Name="NoResultsFound">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="resultsGridView" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="noResultsTextBlock" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</Page>

[/sourcecode]

In the code behind we will keep the navigation code, although in this sample we aren’t navigating once an item is selected. I assume you know how to do that.

SearchWindowsStoreApp10

On the navigation helper loadstate event we will get our search list, look for matches and add them to the result. Based on whether or not we have anything to display we will show the appropriate visual state for the grid on the page.

This is how our data looks like:

SearchWindowsStoreApp9

Here is the code for the search page:

[sourcecode language=”csharp”]
using System.Linq;
using Windows.UI.Xaml;
using Search.Common;
using System;
using System.Collections.Generic;
using Windows.UI.Xaml.Navigation;

namespace Search
{
public sealed partial class SearchResultsPage
{
private readonly NavigationHelper _navigationHelper;
public NavigationHelper NavigationHelper { get { return _navigationHelper; } }
public List<DataItem> Results { get; set; }
public string QueryText { get; set; }

public SearchResultsPage()
{
InitializeComponent();
_navigationHelper = new NavigationHelper(this);
_navigationHelper.LoadState += navigationHelper_LoadState;
}

private void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
QueryText = e.NavigationParameter as String;
Results = new List<DataItem>();
var matchingItems = Data.Type.Where(item => item.Title.Contains(QueryText));
Results.AddRange(matchingItems);
VisualStateManager.GoToState(this, Results.Count > 0 ? "ResultsFound" : "NoResultsFound", true);
}

#region NavigationHelper registration
protected override void OnNavigatedTo(NavigationEventArgs e)
{
_navigationHelper.OnNavigatedTo(e);
}

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
_navigationHelper.OnNavigatedFrom(e);
}

#endregion
}

}

[/sourcecode]

And the data:

[sourcecode language=”csharp”]
using System.Collections.Generic;

namespace Search
{
public static class Data
{
public static IEnumerable<DataItem> Type = new List<DataItem>
{
new DataItem(){Description = "Fruit item",Title = "Banana",UniqueId = "%123"},
new DataItem(){Description = "Fruit item",Title = "Apple",UniqueId = "%556"},
new DataItem(){Description = "Dairy item",Title = "Cheese",UniqueId = "%768"},
new DataItem(){Description = "Meat item",Title = "Ham",UniqueId = "%4892"},
};
}

public class DataItem
{
public string Title { get; set; }
public string SubTitle { get; set; }
public string Description { get; set; }
public string UniqueId { get; set; }
}
}

[/sourcecode]

If course nothing is happening yet, we haven’t handled the actual search from the SearchBox on our mainpage. Go back to that page and add to event, one for QuerySubmitted and SuggestionsRequested.

SearchWindowsStoreApp6

The first one is invoked when the user submits a query, and the second one provides suggestions to the user as they type in a query, from which he/she can then select and use as a search query. In the events we will append suggestions, and navigate to the searchpage passing the querytext. Add the code below and you should be able to run the sample and do a search, because this is all you need to do to do a simple search with the SearchBox control.

Code behind:

[sourcecode language=”csharp”]
using System;
using Windows.UI.Xaml.Controls;

namespace Search
{

public sealed partial class MainPage
{
public MainPage()
{
this.InitializeComponent();
}

private void OnQuerySubmitted(SearchBox sender, SearchBoxQuerySubmittedEventArgs args)
{
Frame.Navigate(typeof(SearchResultsPage), args.QueryText);
}

private readonly string[] _suggestions = {"Apple","Banana","Orange"};

private void OnSuggestionsRequested(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args)
{
var request = args.Request;
foreach (var suggestion in _suggestions)
{
if (suggestion.StartsWith(args.QueryText, StringComparison.CurrentCultureIgnoreCase))
{
request.SearchSuggestionCollection.AppendQuerySuggestion(suggestion);
}
}
}
}
}

[/sourcecode]

SearchWindowsStoreApp12

SearchWindowsStoreApp13

SearchWindowsStoreApp14

Using the Search charm is also straightforward. But keep in mind that according to the documentation (and today is December 5th 2013) you can not use both.

SearchWindowsStoreApp16

So first thing to do is to comment out the SearchBox.

SearchWindowsStoreApp17

Once we have done that the next step, and the first step if you were to choose the search charm for search, would be to add a search contract. This is done in the app manifest file under declarations. Simply add a Search declaration and save.

SearchWindowsStoreApp15

Then we need to handle the invocation. This is done by overriding the OnSearchActivated method in App.cs.

SearchWindowsStoreApp18

There you can access the query through the event arguments and navigate the user to the search result page. To append query suggestions like we did for the SearchBox you use

SearchPane.GetForCurrentView().SuggestionsRequested += OnSuggestionsRequested;

[sourcecode language=”csharp”]
using System;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.Search;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace Search
{
sealed partial class App {

public App()
{
this.InitializeComponent();
}

protected override void OnSearchActivated(SearchActivatedEventArgs args)
{
if (Window.Current.Content == null) return;
var rootFrame = Window.Current.Content as Frame;
if (rootFrame != null) rootFrame.Navigate(typeof(SearchResultsPage), args.QueryText);
}

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
var rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
{
rootFrame = new Frame {Language = Windows.Globalization.ApplicationLanguages.Languages[0]};
rootFrame.NavigationFailed += OnNavigationFailed;
Window.Current.Content = rootFrame;
}

if (rootFrame.Content == null)
{
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
Window.Current.Activate();

SearchPane.GetForCurrentView().SuggestionsRequested += OnSuggestionsRequested;
}

// Same code as we used for the Search Box – different event args
private readonly string[] _suggestions = { "Apple", "Banana", "Orange" };

private void OnSuggestionsRequested(SearchPane sender, SearchPaneSuggestionsRequestedEventArgs args)
{
var request = args.Request;
foreach (var suggestion in _suggestions)
{
if (suggestion.StartsWith(args.QueryText, StringComparison.CurrentCultureIgnoreCase))
{
request.SearchSuggestionCollection.AppendQuerySuggestion(suggestion);
}
}
}

void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
}
}

[/sourcecode]

And that’s it. Don’t forget to add filters and so on, separate blog post on that coming soon. Happy coding 🙂

SearchWindowsStoreApp19

SearchWindowsStoreApp20

SearchWindowsStoreApp13

  13 Responses to “Implementing Search in Windows Store Apps 8.1 – screenshot guide”

  1. Great Post Iris, shame you can’t use both (right now). I love how the Search Contract tightly integrates your App with the OS (Win + S -> Search -> Oh Hai App Results!), but then again the new search box makes searching more accessible to users unfamiliar with Win8, choices choices….

  2. How to implement the windows8.1 search box in MVVM way?

  3. FYI – looking at the MSDN quickstart on adding search, it shows the SearchBox and search contract (step 4) used together. I haven’t tried it yet but it doesn’t suggest that it would be a problem so maybe they have changed their minds.

    See this link http://msdn.microsoft.com/en-us/library/windows/apps/hh868180.aspx

  4. Thanks Iris, for a wonderful post. I am getting an exception in my app, kindly help me iut of this:

    An exception of type ‘Windows.UI.Xaml.Markup.XamlParseException’ occurred in Karachi Food Prices.exe but was not handled in user code

    WinRT information: Cannot find a Resource with the Name/Key AppName [Line: 109 Position: 43]

    • It seems to have issues locating that resource, have you removed that resource but try to reference it somewhere else?

      • Ya I solved It. this tut is really good and easy to understand. Thanks for this, love u bye, waiting for your nest tut.

  5. It’d be awesome if you could find out how to do the same in Universal Apps. I’ve got an open StackOverflow question on it here http://stackoverflow.com/questions/23826212/wiring-up-the-search-charm-in-universal-winrt-apps

  6. Great article .. thanks 🙂

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)

What is 3 + 11 ?
Please leave these two fields as-is:
IMPORTANT! To be able to proceed, you need to solve the following simple math (so we know that you are a human) :-)