Enable Search Charm Settings For Windows 8 App


This blog discuss on how to enable Search Charm settings and direct the search query to the custom Search Xaml Page. Below are the steps needs to be done to enable search

  1. Enable Search Contract in VS 2012
  2. Create SearchPage.Xaml for the search result
  3. Add the code to the SearchPage.Xaml to search and show the result.
  4. Modify App.Xaml.cs to enable Search and query submit event.

 

Step 1:Enable Search Contract in VS 2012

In the Visual Studio 2012 open the App project. Once the project is loaded locate the file “Package.appxmanifest” and open it by double click on it.

image

On the windows select the declaration tab, you can see the Available Declarations. Drop down to select the Search and Add to the list below. Then save it and build.

image

Step 2: Create SearchPage.Xaml for the search result

Using the VS 2012 Basic Page template create a SearchPage.Xaml for the application. For the sample purpose i will use my App to show the screenshot of the page and the code.

image

Below is the sample Xaml from my screen

<Grid Style="{StaticResource LayoutRootStyle}">
        <Grid.RowDefinitions>
            <RowDefinition Height="140"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <StackPanel Grid.Row="1">
            <TextBlock Margin="15,0,0,0" Text="Search Results" FontSize="25" FontStyle="Oblique"/>
            <ItemsControl Margin="15" Name="lstLinks" Width="450" HorizontalAlignment="Left" >
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <HyperlinkButton Content="{Binding Path=Name}" Tag="{Binding Path=URL}" Click="HyperlinkButton_Click_1" FontSize="20"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
        <!-- Back button and page title -->
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Button x:Name="backButton" Click="GoBack" IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}" Style="{StaticResource BackButtonStyle}"/>
            <TextBlock x:Name="pageTitle" Grid.Column="1" Text="{StaticResource AppName}" Style="{StaticResource PageHeaderTextStyle}"/>
        </Grid>
        
        <VisualStateManager.VisualStateGroups>

            <!-- Visual states reflect the application's view state -->
            <VisualStateGroup x:Name="ApplicationViewStates">
                <VisualState x:Name="FullScreenLandscape"/>
                <VisualState x:Name="Filled"/>

                <!-- The entire page respects the narrower 100-pixel margin convention for portrait -->
                <VisualState x:Name="FullScreenPortrait">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PortraitBackButtonStyle}"/>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>

                <!-- The back button and title have different styles when snapped -->
                <VisualState x:Name="Snapped">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedBackButtonStyle}"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="pageTitle" Storyboard.TargetProperty="Style">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedPageHeaderTextStyle}"/>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </Grid>

The sample screen consists of the ItemsControl to display the search result and will navigate to the page when selected. The ItemsControl consists of hyperlink button, when click on the HyperLinkButton it navigates to the Page.

image

Step 3: Add the code to the SearchPage.Xaml to search and show the result

Add the following code to the search page xaml.cs for the search result to be handled. _searchPane_SuggestionsRequested event will search for the keyword entered with the suggestion list and show the suggested text when the user type the keyword in the search pane. ProcessSearchQuery will be fired everytime when the user press enter or click search after typing keyword. In that method we can write our processing logic. In the below sample i am matching the search keyword with the suggestion list and if it matches i am adding the suggestion text to the List. The List has the HyperLink button which will navigate to the relevant page. You can add your own code in here to process.

 

public sealed partial class SearchPage : W8.MathFormulas.Common.LayoutAwarePage
    {
        private SearchPane _searchPane;
        public static SearchPage Current;

        private readonly string[] _suggestedNames = { "Algebra", "Calculus", "Geometry", "Trigonometry", "Statistics" };
        public SearchPage()
        {
            this.InitializeComponent();
            Current = this;
            _searchPane = SearchPane.GetForCurrentView();

        }

        protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
        {
            //searchText.Text = (string)navigationParameter;
        }

        protected override void SaveState(Dictionary<String, Object> pageState)
        {
        }
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            _searchPane.SuggestionsRequested += new TypedEventHandler<SearchPane, SearchPaneSuggestionsRequestedEventArgs>(_searchPane_SuggestionsRequested);
        }

        void _searchPane_SuggestionsRequested(SearchPane sender, SearchPaneSuggestionsRequestedEventArgs args)
        {
            var queryText = args.QueryText;

            if (string.IsNullOrEmpty(queryText))
            {
                //searchText.Text = "Enter search keyword";
            }
            else
            {
                var request = args.Request;
                foreach (string suggestion in _suggestedNames)
                {
                    if (suggestion.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase))
                    {
                        // Add suggestion to Search Pane
                        request.SearchSuggestionCollection.AppendQuerySuggestion(suggestion);

                        // Break since the Search Pane can show at most 5 suggestions
                        if (request.SearchSuggestionCollection.Size >= 5)
                        {
                            break;
                        }
                    }
                }
            }
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            _searchPane.SuggestionsRequested -= new TypedEventHandler<SearchPane, SearchPaneSuggestionsRequestedEventArgs>(_searchPane_SuggestionsRequested);
        }

        internal void ProcessSearchQuery(string queryText, string language)
        {
            lstLinks.Items.Clear();
            foreach (string suggestion in _suggestedNames)
            {
                if (suggestion.Contains(queryText))
                {
                    DataModel.SearchResult search = new DataModel.SearchResult();
                    search.Name = suggestion;
                    search.URL = "SplitPage.Xaml|" + suggestion;
                    lstLinks.Items.Add(search);
                }
            }
        }

        private void HyperlinkButton_Click_1(object sender, RoutedEventArgs e)
        {
            HyperlinkButton btn = ((HyperlinkButton)sender);
            if (btn.Tag != null)
            {
                string urlText = btn.Tag.ToString();
                string[] parse = urlText.Split('|');
                if (parse[0] == "ItemsPage.Xaml")
                {
                    this.Frame.Navigate(typeof(ItemsPage), parse[1]);
                }
                else if (parse[0] == "SplitPage.Xaml")
                {
                    this.Frame.Navigate(typeof(SplitPage), parse[1]);
                }
            }


        }
    }

 

Step 4: Modify App.Xaml.cs to enable Search and query submit event

Now edit the App.xaml.cs file to enable the search events. Create a function called “EnsureMainPageActivatedAsync” in the App.Xaml.cs and move all the code from the OnLaunched event to the new function. Once modified the code of OnLaunched and the function EnsureMainPageActivatedAsync will look like below

protected override async void OnLaunched(LaunchActivatedEventArgs args)
        {
            await EnsureMainPageActivatedAsync(args);
        }

 

async private Task EnsureMainPageActivatedAsync(IActivatedEventArgs args)
        {
            Frame rootFrame = Window.Current.Content as Frame;

            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active

            if (rootFrame == null)
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame();
                //Associate the frame with a SuspensionManager key                                
                SuspensionManager.RegisterFrame(rootFrame, "AppFrame");

                if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    // Restore the saved session state only when appropriate
                    try
                    {
                        await SuspensionManager.RestoreAsync();
                    }
                    catch (SuspensionManagerException)
                    {
                        //Something went wrong restoring state.
                        //Assume there is no state and continue
                    }
                }

                // Place the frame in the current Window
                Window.Current.Content = rootFrame;
            }
            if (rootFrame.Content == null)
            {
                // When the navigation stack isn't restored navigate to the first page,
                // configuring the new page by passing required information as a navigation
                // parameter
                if (!rootFrame.Navigate(typeof(ItemsPage), "AllGroups"))
                {
                    throw new Exception("Failed to create search page");
                }
            }
            // Ensure the current window is active
            Window.Current.Activate();
        }

 

I am using the Split Page Template project for the windows 8 App so you can see some code related to that at the last lines. Basically it make sure that mainpage (ItemPage) is initialised. Next step is to enable three main event for the search contract windows OnSearchActivated, OnQuerySubmitted and OnWindowCreated

 
The below event is fired when you enable search charm from desktop and touch or click on your app.
protected override async void OnSearchActivated(SearchActivatedEventArgs args)
        {
            await EnsureMainPageActivatedAsync(args);
            Frame page = (Frame)Window.Current.Content;
            if (SearchPage.Current != null)
            {
                if (page.Content.GetType().Name != "SearchPage")
                {
                    page.Navigate(typeof(SearchPage));
                }
                SearchPage.Current.ProcessSearchQuery(args.QueryText, args.Language);
            }
            else
            {
                if (page!= null)
                {
                    page.Navigate(typeof(SearchPage));
                    SearchPage.Current.ProcessSearchQuery(args.QueryText, args.Language);
                }
            }
        }
The below event is fired everytime when you type the search keyword and click on the search button or press enter. 
In the below event we check whether the search page is active if not show the page and then call the Process SearchQuery
private void OnQuerySubmitted(object sender, SearchPaneQuerySubmittedEventArgs args)
        {
            Frame page = (Frame)Window.Current.Content;
            if (SearchPage.Current != null)
            {
                if (page.Content.GetType().Name !="SearchPage")
                {
                    page.Navigate(typeof(SearchPage));
                }
                SearchPage.Current.ProcessSearchQuery(args.QueryText, args.Language);
            }
            else
            {
                if (page != null)
                {
                    page.Navigate(typeof(SearchPage));
                    SearchPage.Current.ProcessSearchQuery(args.QueryText, args.Language);
                }
            }
        }

        protected override void OnWindowCreated(WindowCreatedEventArgs args)
        {
            // Register QuerySubmitted handler for the window at window creation time and only registered once
            // so that the app can receive user queries at any time.
            SearchPane.GetForCurrentView().QuerySubmitted += new TypedEventHandler<SearchPane, SearchPaneQuerySubmittedEventArgs>(OnQuerySubmitted);
        }
Advertisements

9 thoughts on “Enable Search Charm Settings For Windows 8 App

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s