André Alves de Lima

Talking about Software Development and more…

Utilizando o controle SearchBox em aplicações para a Windows Store

Olá caro(a) leitor(a)!

No post de hoje resolvi abordar a questão de pesquisas em aplicações para a Windows Store. Até o Windows 8, as guidelines para pesquisa em aplicações “metro” diziam de que tínhamos que implementar o Search Contract. Dessa forma, quando o usuário estava dentro do nosso aplicativo, ia até a charms bar e clicava em “Search“, a nossa aplicação tinha a capacidade de “escutar” essa busca e exibir os resultados da forma que desejássemos. Os aplicativos “metro” da própria Microsoft seguiam, obviamente, essas guidelines. A aplicação mais notável que utilizava essa forma de busca era o aplicativo da Windows Store.

Já no Windows 8.1, a Microsoft resolveu mudar um pouco de estratégia (veja as novas guidelines de pesquisa aqui). Essa, a propósito, é uma dificuldade ao sermos early adopters. Normalmente, nos primórdios de uma tecnologia, muitas mudanças ocorrem nas APIs e guidelines, e com isso temos que nos readaptarmos a cada nova versão da plataforma. Mas, enfim, faz parte. Com o Windows 8.1, aparentemente a Microsoft está encorajando que utilizemos um controle de busca que fique visível no nosso aplicativo, de forma que o usuário não tenha que utilizar mais a charms bar para executar uma busca na nossa aplicação. Ela inclusive disponibiliza um novo controle que podemos utilizar: o SearchBox. Veja a versão da Windows Store para o Windows 8.1, que também se adaptou a esse novo paradigma e incluiu uma SearchBox direto na aplicação:

Esse é o controle que vamos aprender a utilizar no artigo de hoje. Para isso, comece criando um novo projeto do tipo Windows Store Blank App. Na MainPage, adicione dentro do Grid um controle SearchBox com as seguintes propriedades:

        <SearchBox HorizontalAlignment="Right"
                   VerticalAlignment="Top"
                   Width="250"
                   Margin="40,60"
                   PlaceholderText="Digite para pesquisar..."/>

Execute o projeto e veja que o controle SearchBox é exibido no canto superior direito da tela, conforme configuramos:

Já que vamos implementar a funcionalidade de pesquisa no nosso aplicativo, precisamos ter o quê pesquisar. Por isso, adicione ao projeto uma pasta chamada “Model” e dentro dela adicione uma classe chamada “Produto“, com as seguintes propriedades:

    public class Produto
    {
        public int ID { get; set; }
        public string Nome { get; set; }
    }

Em seguida, adicione a biblioteca MVVM Light utilizando o NuGet (caso não saiba como, dê uma olhada neste outro artigo). Dentro da pasta ViewModel, abra o arquivo MainViewModel.cs. Nessa classe, que será utilizada como ViewModel da MainPage, vamos criar uma ObservableCollection de Produtos e vamos preenchê-la no construtor de MainViewModel. Além disso, vamos aproveitar e criar o método que realizará a pesquisa de Produtos e retornará uma lista com os Produtos encontrados:

    public class MainViewModel : ViewModelBase
    {
        public System.Collections.ObjectModel.ObservableCollection<Model.Produto> Produtos { get; private set; }

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            Produtos = new System.Collections.ObjectModel.ObservableCollection<Model.Produto>();
            Produtos.Add(new Model.Produto() { ID = 1, Nome = "Pão" });
            Produtos.Add(new Model.Produto() { ID = 2, Nome = "Queijo" });
            Produtos.Add(new Model.Produto() { ID = 3, Nome = "Leite" });
            Produtos.Add(new Model.Produto() { ID = 4, Nome = "Açúcar" });
            Produtos.Add(new Model.Produto() { ID = 5, Nome = "Sal" });
            Produtos.Add(new Model.Produto() { ID = 6, Nome = "Pão de queijo" });
        }

        public System.Collections.Generic.IEnumerable<Model.Produto> Pesquisar(string pesquisa)
        {
            string pesquisaLowercase = pesquisa.ToLowerInvariant();
            return Produtos.Where(produto => produto.Nome.ToLowerInvariant().Contains(pesquisaLowercase));
        }
    }

Obs.: como estamos utilizando o método “Where“, não se esqueça de adicionar a cláusula “using System.Linq” no cabeçalho de MainViewModel, caso contrário você receberá um erro de compilação.

Volte até o XAML da nossa MainPage e adicione a linha na declaração da página para indicar que o DataContext da MainPage é a MainViewModel:

<Page
    x:Class="ExemploWinRT_SearchBox.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ExemploWinRT_SearchBox"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    DataContext="{Binding Source={StaticResource Locator}, Path=Main}">

Feito isso, agora é hora de tratarmos o evento da SearchBox que será disparado quando o usuário efetuar uma pesquisa. Esse evento é o QuerySubmitted:

        <SearchBox HorizontalAlignment="Right"
                   VerticalAlignment="Top"
                   Width="250"
                   Margin="40,60"
                   PlaceholderText="Digite para pesquisar..."
                   QuerySubmitted="SearchBox_QuerySubmitted"/>

Vá até o code behind da MainPage e implemente o handler para o evento QuerySubmitted da seguinte maneira:

    public sealed partial class MainPage : Page
    {
        ViewModel.MainViewModel viewModel;

        public MainPage()
        {
            this.InitializeComponent();
            viewModel = (ViewModel.MainViewModel)this.DataContext;
        }

        private void SearchBox_QuerySubmitted(SearchBox sender, SearchBoxQuerySubmittedEventArgs args)
        {
            IEnumerable<Model.Produto> produtosEncontrados = viewModel.Pesquisar(args.QueryText);
        }
    }

Até agora a única coisa que fizemos foi chamar o método “Pesquisar” da nossa ViewModel passando o texto que o usuário digitou na SearchBox. Isso fará com que a lista de Produtos encontrados seja retornada e armazenada na variável produtosEncontrados. Agora chegou a hora de exibirmos o resultado, mas, antes disso, precisamos criar uma nova página onde exibiremos a lista com os Produtos encontrados. Para a nossa sorte, a Microsoft disponibilizou um item template que representa uma página de resultados de busca. Adicione um novo item ao projeto, do tipo “Search Results Page” e dê o nome de “ResultadoPesquisaProduto” para esse novo item:

O item template de página de resultados de busca é bem complexo. Muito provavelmente ele possui muito mais funcionalidades do que nós vamos precisar nos nossos aplicativos. Como o foco deste artigo não é abordarmos o conteúdo desse item template, não vou entrar muito em detalhes sobre ele no momento.

Agora que já temos a página que exibirá os resultados da busca, volte ao code behind da MainPage e adicione a linha no handler do evento QuerySubmitted que fará a navegação até a página de resultados passando os Produtos a serem exibidos:

        private void SearchBox_QuerySubmitted(SearchBox sender, SearchBoxQuerySubmittedEventArgs args)
        {
            IEnumerable<Model.Produto> produtosEncontrados = viewModel.Pesquisar(args.QueryText);
            Frame.Navigate(typeof(ResultadoPesquisaProduto), new object[] {args.QueryText, produtosEncontrados});
        }

Note que estamos fazendo a navegação para a página de resultados passando um object array contendo o query text (string com termo que o usuário pesquisou) e a lista de produtos a serem exibidos. Portanto, do outro lado (na página de resultados da busca), precisamos lidar com esses parâmetros. No code behind de ResultadoPesquisaProduto, encontre o handler chamado navigationHelper_LoadState e substitua o seu código por:

        private void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
        {
            object[] parametros = e.NavigationParameter as object[];
            if ((parametros != null) &&
                (parametros.Count().Equals(2)))
            {
                string queryText = parametros[0].ToString();
                this.DefaultViewModel["QueryText"] = '\u201c' + queryText + '\u201d';

                var resultados = parametros[1] as IEnumerable<Model.Produto>;
                if ((resultados != null) &&
                    (resultados.Any()))
                {
                    this.DefaultViewModel["Results"] = resultados;
                    VisualStateManager.GoToState(this, "ResultsFound", true);
                }
                else
                    VisualStateManager.GoToState(this, "ResultsNotFound", true);
            }
        }

Aqui nós basicamente estamos convertendo os parâmetros passados no NavigationParameter e setando os atributos “QueryText” e “Results” de DefaultViewModel. Isso fará com que os Produtos sejam listados na página de resultados.

Porém, isso não é o suficiente. O item template não conhece a estrutura da nossa classe (Produto), portanto, precisamos alterar o DataTemplate que será utilizado para exibir a lista de Produtos nessa página. Vá até o XAML de ResultadoPesquisaProduto, encontre o elemento “resultsGridView” e altere o seu ItemTemplate desta forma:

                    <GridView.ItemTemplate>
                        <DataTemplate>
                            <Grid Width="294" Margin="6">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="*"/>
                                </Grid.ColumnDefinitions>
                                <Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,0,0,10" Width="40" Height="40">
                                    <Image Source="Assets/SmallLogo.scale-100.png" Stretch="UniformToFill"/>
                                </Border>
                                <StackPanel Grid.Column="1" Margin="10,-10,0,0">
                                    <TextBlock Text="{Binding Nome}" TextWrapping="NoWrap" Style="{StaticResource BodyTextBlockStyle}"/>
                                </StackPanel>
                            </Grid>
                        </DataTemplate>
                    </GridView.ItemTemplate>

Note que a única coisa que fizemos aqui foi alterar o template em que cada resultado será exibido, de forma que uma imagem “indefinida” seja exibida juntamente com o Nome do Produto.

Execute a aplicação, faça uma busca por “pão” e veja que a página de resultados mostra os Produtos “Pão” e “Pão de queijo”.

E com isso vocês aprenderam a utilização básica do controle SearchBox. Ainda faltou abordar a questão de search history, search suggestions e outras propriedades interessantes do SearchBox, mas, isso fica para um próximo artigo! O código desse exemplo está disponível no meu repositório no GitHub.

Até a próxima!

André Lima

3 thoughts on “Utilizando o controle SearchBox em aplicações para a Windows Store

  • […] post de hoje é um complemento do artigo da semana passada, cujo tema foi o controle SearchBox em aplicações para a Windows Store. Gostaria de abordar mais […]

  • Marcelo disse:

    Boa noite Andre, tudo bem?

    Tentei fazer isso, mas não consegui.

    Estou criando um app para ter minhas materias do curso em um so local e assim facilitar meus estudos.

    Eu criei um text box1 e um botao ir. Depois criei um outro text box2 para digitar o que desejo procurar no text box 1. Não consigo fazer esse caminho no botao ir de jeito algum. Pode me ajudar? Estou usando Visual Studio 15.

    Orbrigado,

    Marcelo

    • andrealveslima disse:

      Olá Marcelo!

      Estranho hein.. Dê mais detalhes sobre o seu projeto.. Como é que está o seu código? Como é que está a interface? Se quiser mandar mais detalhes do projeto por e-mail, fique à vontade: contato [arroba] andrealveslima [ponto] com [ponto] br..

      Abraço!
      André Lima

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *