Статьи

31 день Windows 8 | День № 6: Поиск контракта


Эта статья является 6-м днем ​​в серии под названием «
31 день Windows 8» . Каждая из статей этой серии будет опубликована как для
HTML5 / JS, так и для
XAML / C # . Вы можете найти дополнительные ресурсы, загрузки и исходный код на нашем
сайте .


 

advertisementsample

day5-амулеты сторонаВ прошлый раз мы вводили Контракты, исследуя, как добавить Настройки в наши приложения. Сегодня мы будем опираться на это с помощью Search, а завтра — Share. Search и Share — два очень интересных контракта, потому что они оживляют ваше приложение, даже когда оно на самом деле не работает. В контексте поиска это означает, что ваше приложение может потенциально представлять себя пользователю по-новому.

В течение последних 10 лет «поиск» обычно ассоциировался с поисковыми системами. Несколько лет назад поиск стал основным направлением в Windows, и если вы были похожи на меня, вы очень привыкли нажимать кнопку «Пуск» и просто набирать имя. ваша программа, а не ее поиск. Сегодня в Windows 8 вы можете просто набрать, и эти результаты хорошо отображаются.

Более того, Windows 8 действительно расширяет наши представления о поиске и вносит его прямо в наши приложения для Магазина Windows. Теперь это на самом деле не какая-то магия, мы просто подключаем к нескольким событиям и показываем правильные вещи нашим пользователям, но это меняет типичную «точку входа» в наши приложения. Теперь вместо того, чтобы наши пользователи открывали наше приложение, находили ваше окно поиска и выполняли поиск, они могут нажимать кнопку поиска и переходить непосредственно к тому, что искали внутри вашего приложения, без каких-либо других шагов.

Начало настройки

Как и каждый день с начала этой серии, мы начнем с шаблона «Пустое приложение». Опять же, причина, по которой мы это делаем, заключается в том, что существует так много дополнительного кода, который нужно отфильтровать, чтобы найти то, что вы ищете.

Как только вы соберете свой новый проект (и замените набор изображений по умолчанию… вы сделали это правильно? Разве вы не помните День №1Вы все еще можете загрузить этот набор изображений здесь .) Теперь мы собираемся добавить Поиск к нашему приложению. К счастью, основной поиск — только щелчок далеко.

Щелкните правой кнопкой мыши свой проект и выберите «Добавить»> «Новый элемент»> «Поиск контракта».

6-XAML-AddSearchContract

Он запрашивает у вас имя страницы, чтобы пойти с этим. Я использовал SearchResults.xaml, но вы можете называть свою страницу как хотите. Когда вы нажимаете кнопку «Добавить», вам будет предложено добавить дополнительные файлы в ваш проект.

6-XAML-ConfirmationBox

Мы кратко обсудим каждый из этих файлов чуть позже, но сейчас вы должны увидеть проектное решение, похожее на это:

6-XAML-SolutionExplorer

На этом этапе вы сможете запустить свой проект, выбрать брелок поиска и начать печатать. Вот как выглядит мое приложение, когда я ищу «тако».

6-XAML-EmptySearchBox

Как видите, это работает, но мы не предоставляем никакой ценности, потому что нам нечего искать. Это будет дальше. Прежде чем мы перейдем к этому, давайте поговорим о философии поиска в Windows 8.

Философия поиска

Когда вы думаете о традиционных приложениях Windows 8, они, как правило, меньше. Например, вы не найдете версию Outlook, которая управляет таким количеством типов данных. Вместо этого есть приложение «Календарь», приложение «Почта» и приложение «Люди».

Приложения для Windows 8 должны делать что-то одно и делать это хорошо. Надеюсь, я описываю приложение, которое вы сейчас создаете. Приложения Windows 8 также можно искать, даже если они в данный момент не запущены. Это означает, что когда пользователь что-то ищет, имя и значок вашего приложения должны немедленно принести в голову определенные типы данных.

Например, приложение iHeartRadio. Если я ищу это, я, вероятно, ищу радиостанцию.

6-XAML-IHeartRadio

Для приложения Погода я, скорее всего, ищу город.

6-XAML-Погода

Netflix, я, наверное, ищу название фильма или шоу.

6-XAML-Netflix

Наконец, приложение «Люди» предполагает, что я ищу имя человека.

6-XAML-People

Итак, когда вы создаете свое приложение, задайте себе этот вопрос:

«Что пользователи будут искать в моем приложении?»

Другое важное обсуждение о поиске вращается вокруг концепции «Поиск» против «Найти». Очарование поиска НЕ ​​должно использоваться для поиска внутри документа. Поиск всегда должен быть чем-то, что можно сделать с вашим приложением, даже если оно в данный момент не запущено.

Вот конкретное руководство Microsoft о том, как использовать Очарование Поиска .

Заставить поиск работать

Таким образом, как мы видели в наших первых параграфах, добавление поискового контракта в ваш проект — это только первый шаг. Далее нам нужно предоставить некоторые ценные данные для поиска пользователем. Если вы помните мою статью в четвертый день , я создал небольшой набор элементов из периодической таблицы, чтобы продемонстрировать элемент управления SemanticZoom.

Мы можем использовать те же данные в этом примере. Если вы пропустили это, вот класс Элемент:

namespace Day6_SearchContract
{
    class Element
    {
        public double AtomicWeight { get; set; }
        public int AtomicNumber { get; set; }
        public string Symbol { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public string State { get; set; }
    }
}

Для простоты я собираюсь заполнить List <Element> в нашем файле SearchResults.xaml.cs. Я начну с создания нового метода BuildElementList () и вызова его из нашего конструктора, а затем создания нового объекта Element для 36 элементов, которые мы будем использовать.

List<Element> elements = new List<Element>();
 
public SearchResults()
{
    this.InitializeComponent();
    BuildElementList();
}
 
private void BuildElementList()
{
    elements.Add(new Element { AtomicNumber = 1, AtomicWeight = 1.01, Category = "Alkali Metals", Name = "Hydrogen", Symbol = "H", State = "Gas" });
    elements.Add(new Element { AtomicNumber = 2, AtomicWeight = 4.003, Category = "Noble Gases", Name = "Helium", Symbol = "He", State = "Gas" });
    elements.Add(new Element { AtomicNumber = 3, AtomicWeight = 6.94, Category = "Alkali Metals", Name = "Lithium", Symbol = "Li", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 4, AtomicWeight = 9.01, Category = "Alkaline Earth Metals", Name = "Beryllium", Symbol = "Be", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 5, AtomicWeight = 10.81, Category = "Non Metals", Name = "Boron", Symbol = "B", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 6, AtomicWeight = 12.01, Category = "Non Metals", Name = "Carbon", Symbol = "C", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 7, AtomicWeight = 14.01, Category = "Non Metals", Name = "Nitrogen", Symbol = "N", State = "Gas" });
    elements.Add(new Element { AtomicNumber = 8, AtomicWeight = 15.999, Category = "Non Metals", Name = "Oxygen", Symbol = "O", State = "Gas" });
    elements.Add(new Element { AtomicNumber = 9, AtomicWeight = 18.998, Category = "Non Metals", Name = "Fluorine", Symbol = "F", State = "Gas" });
    elements.Add(new Element { AtomicNumber = 10, AtomicWeight = 20.18, Category = "Noble Gases", Name = "Neon", Symbol = "Ne", State = "Gas" });
    elements.Add(new Element { AtomicNumber = 11, AtomicWeight = 22.99, Category = "Alkali Metals", Name = "Sodium", Symbol = "Na", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 12, AtomicWeight = 24.31, Category = "Alkaline Earth Metals", Name = "Magnesium", Symbol = "Mg", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 13, AtomicWeight = 26.98, Category = "Other Metals", Name = "Aluminum", Symbol = "Al", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 14, AtomicWeight = 28.09, Category = "Non Metals", Name = "Silicon", Symbol = "Si", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 15, AtomicWeight = 30.97, Category = "Non Metals", Name = "Phosphorus", Symbol = "P", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 16, AtomicWeight = 32.06, Category = "Non Metals", Name = "Sulfur", Symbol = "S", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 17, AtomicWeight = 35.45, Category = "Non Metals", Name = "Chlorine", Symbol = "Cl", State = "Gas" });
    elements.Add(new Element { AtomicNumber = 18, AtomicWeight = 39.95, Category = "Noble Gases", Name = "Argon", Symbol = "Ar", State = "Gas" });
    elements.Add(new Element { AtomicNumber = 19, AtomicWeight = 39.10, Category = "Alkali Metals", Name = "Potassium", Symbol = "K", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 20, AtomicWeight = 40.08, Category = "Alkaline Earth Metals", Name = "Calcium", Symbol = "Ca", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 21, AtomicWeight = 44.96, Category = "Transitional Metals", Name = "Scandium", Symbol = "Sc", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 22, AtomicWeight = 47.90, Category = "Transitional Metals", Name = "Titanium", Symbol = "Ti", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 23, AtomicWeight = 50.94, Category = "Transitional Metals", Name = "Vanadium", Symbol = "V", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 24, AtomicWeight = 51.996, Category = "Transitional Metals", Name = "Chromium", Symbol = "Cr", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 25, AtomicWeight = 54.94, Category = "Transitional Metals", Name = "Manganese", Symbol = "Mn", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 26, AtomicWeight = 55.85, Category = "Transitional Metals", Name = "Iron", Symbol = "Fe", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 27, AtomicWeight = 58.93, Category = "Transitional Metals", Name = "Cobalt", Symbol = "Co", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 28, AtomicWeight = 58.70, Category = "Transitional Metals", Name = "Nickel", Symbol = "Ni", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 29, AtomicWeight = 63.55, Category = "Transitional Metals", Name = "Copper", Symbol = "Cu", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 30, AtomicWeight = 65.37, Category = "Transitional Metals", Name = "Zinc", Symbol = "Zn", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 31, AtomicWeight = 69.72, Category = "Other Metals", Name = "Gallium", Symbol = "Ga", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 32, AtomicWeight = 72.59, Category = "Other Metals", Name = "Germanium", Symbol = "Ge", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 33, AtomicWeight = 74.92, Category = "Non Metals", Name = "Arsenic", Symbol = "As", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 34, AtomicWeight = 78.96, Category = "Non Metals", Name = "Selenium", Symbol = "Se", State = "Solid" });
    elements.Add(new Element { AtomicNumber = 35, AtomicWeight = 79.90, Category = "Non Metals", Name = "Bromine", Symbol = "Br", State = "Liquid" });
    elements.Add(new Element { AtomicNumber = 36, AtomicWeight = 83.80, Category = "Noble Gases", Name = "Krypton", Symbol = "Kr", State = "Gas" });
}

Очевидно, вы захотите получить свои данные из другого места, но это статья о поиске, а не об источниках данных. :)

Если вы посмотрите на метод Filter_SelectionChanged () по умолчанию, который был создан в SearchResults.xaml.cs, вы должны увидеть некоторые комментарии, которые выглядят так:

// TODO: Respond to the change in active filter by setting this.DefaultViewModel["Results"]// to a collection of items with bindable Image, Title, Subtitle, and Description properties

Прямо под этими комментариями мы собираемся добавить нашу логику для извлечения соответствующих записей из нашего источника данных. Однако, прежде чем мы сделаем это, в этом методе для вас сделано несколько предположений, и важно понять, что это за предположения, прежде чем писать что-то еще.

Первое предположение заключается в том, что ваши результаты поиска будут представлять собой набор объектов, каждый из которых имеет свойства Image, Title, Subtitle и Description. Маловероятно, что ваши данные имеют эти свойства, и вы не обязательно хотите, чтобы ваши данные соответствовали тому, что они для вас настроили. Причина такого предположения довольно проста.

Кратко взломайте вашу страницу SearchResults.xaml. Найдите элемент управления GridView с именем «resultsGridView», а также элемент управления ListView с именем «resultsListView». Мы уже рассмотрели эти типы элементов управления в День № 4 , но у каждого из них уже есть ItemTemplate. Здесь живут все предположения. Они используют DataTemplate по умолчанию с именем «StandardSmallIcon300x70ItemTemplate». Если вы посмотрите на файл Common / StandardStyles.xaml, вы найдете его там, и он пытается связать эти четыре свойства, которые я упоминал ранее.  Мы не хотим использовать этот шаблон, но это хорошее начало.

Я скопировал весь этот DataTemplate и переместил его на мою страницу SearchResults.xaml в разделе Page.Resources. Вот как я изменил этот раздел, чтобы он выглядел так:

<Page.Resources>
    <CollectionViewSource x:Name="resultsViewSource" Source="{Binding Results}"/>
    <CollectionViewSource x:Name="filtersViewSource" Source="{Binding Filters}"/>
    <common:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
    <!-- TODO: Update the following string to be the name of your app -->
    <x:String x:Key="AppName">Search Contract Example</x:String>
    <DataTemplate x:Key="ModifiedSmallIcon300x70ItemTemplate">
        <Grid Width="294" Margin="6">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,0,0,10" Width="40" Height="40">
                <TextBlock Text="{Binding Symbol}" FontSize="32" FontWeight="Bold" TextAlignment="Center" VerticalAlignment="Center" />
            </Border>
            <StackPanel Grid.Column="1" Margin="10,-10,0,0">
                <TextBlock Text="{Binding Name}" Style="{StaticResource BodyTextStyle}" TextWrapping="NoWrap"/>
                <TextBlock Text="{Binding Category}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>
                <TextBlock Text="{Binding State}" Style="{StaticResource BodyTextStyle}" Foreground="{StaticResource ApplicationSecondaryForegroundThemeBrush}" TextWrapping="NoWrap"/>
            </StackPanel>
        </Grid>
    </DataTemplate>
</Page.Resources> 

Вы можете видеть, что он почти идентичен оригинальному, но я переименовал его в «ModifiedSmallIcon300x70ItemTemplate».
Другие отличия заключаются в том, что я изменил элемент управления Image на другой TextBlock и изменил привязки на свойства, которыми обладают мои объекты: Symbol, Name, Category и State. Я также изменил имя ItemTemplates, которое используется GridView и ListView, чтобы указать на мою измененную версию. Теперь мы можем начать предоставлять некоторые результаты на нашей странице SearchResults.

Помните те комментарии от ранее? Что в методе Filter_SelectionChanged ()? Давайте вернемся к ним и добавим немного кода. 

IEnumerable<Element> searchResults = from el in elements
                                     where el.Name.ToLower().Contains(searchString)
   orderby el.Name ascending
                                     select el;
                
this.DefaultViewModel["Results"] = searchResults;
 
// Ensure results are found
object results;
IEnumerable<Element> resultsCollection;
 
if (this.DefaultViewModel.TryGetValue("Results", out results) &&
    (resultsCollection = results as IEnumerable<Element>) != null &&
    resultsCollection.Count() != 0)
{
    VisualStateManager.GoToState(this, "ResultsFound", true);
    return;
}

Я также изменил часть кода по умолчанию в разделе ниже, чтобы явно указать, что я использую коллекцию объектов IEnumerable Element. Если наш поисковый термин действительно имеет некоторые результаты, эта логика будет тем, что показывает эти результаты.

На этом этапе, если вы запустите свой проект и выполните поиск, вы должны получить что-то похожее на это:

6-XAML-SearchResults

Вы также можете полностью закрыть приложение, выполнить поиск на своем компьютере, а затем выбрать свое приложение. Это работает точно так же.

На данный момент, вы думаете, что мы закончили. Вы не правы. Нам еще нужно много поговорить. В частности, как запросить у пользователя фактические поисковые значения из вашего приложения, которые соответствуют уже набранным. Давайте посмотрим на это дальше.

Определение поисковых предложений

На самом деле это довольно простой процесс, особенно по сравнению с тем, что мы уже сделали в этой статье. На той же странице SearchResults.xaml нам нужно начать со ссылки на панель поиска (для этого также необходимо добавить пространство имен Windows.ApplicationModel.Search). Мы также создаем экземпляр объекта SearchPane в нашем конструкторе.

SearchPane searchPane;
public SearchResults()
{
    this.InitializeComponent();
    BuildElementList();
    searchPane = SearchPane.GetForCurrentView();
}

Как только вы это настроите, следующим шагом будет создание обработчика события для события SuggestionsRequested. Создайте новый метод OnNavigatedTo, как показано ниже, и добавьте новый обработчик событий. (Также удалите его, создав метод OnNavigatingFrom).

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
    searchPane.SuggestionsRequested += searchPane_SuggestionsRequested;
}
 
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
    base.OnNavigatingFrom(e);
    searchPane.SuggestionsRequested -= searchPane_SuggestionsRequested;
}
 
void searchPane_SuggestionsRequested(SearchPane sender, SearchPaneSuggestionsRequestedEventArgs args)
{
    args.Request.SearchSuggestionCollection.AppendQuerySuggestions((from el in elements 
                                        where el.Name.ToLower().StartsWith(args.QueryText.ToLower()) || el.Symbol.ToLower().StartsWith(args.QueryText.ToLower())
                                        orderby el.Name ascending
                                        select el.Name).Take(5));
}

Вы можете видеть, что я просто предоставляю SearchSuggestionCollection набор (максимум) пяти строковых значений в качестве рекомендаций.
Это самое большее, что я могу взять, поэтому я ограничиваю свои результаты максимум пятью. 

Это оно! Если вы ищете элемент в этом приложении сейчас, вы должны увидеть (основываясь на моем запросе), что если ваши введенные символы соответствуют либо имени элемента, либо символу, и вы получите до пяти рекомендаций.

Принудительный поиск при обнаружении нажатий клавиш

Есть еще один трюк, о котором мы должны поговорить, и он позволяет пользователю открывать окно поиска, просто набирая. Добавив одно простое утверждение к нашим методам OnLaunched и OnSuspending в нашем файле App.xaml.cs, мы можем включить эту функцию во всем нашем приложении.

Добавьте эту строку в ваш метод OnLaunched:

 

SearchPane.GetForCurrentView().ShowOnKeyboardInput = true;
 

И эта строка для вашего метода OnSuspending, чтобы отключить его, когда приложение закрыто:

 

SearchPane.GetForCurrentView().ShowOnKeyboardInput = false;
 

Вот и все! Запустите ваше приложение и просто начните печатать. Search Charm автоматически откроется и начнет захватывать ваши нажатия клавиш!

Объявления

Есть еще одна вещь, которую я хочу осветить в этой статье, и это больше для вашего знания, чем то, что действительно нужно сделать. Когда вы добавили поисковый контракт в свой проект, в ваш проект было добавлено несколько файлов, но в вашем файле appmanifest было также сделано объявление. Если вы откроете этот файл и перейдете на вкладку объявлений, вы должны увидеть что-то вроде этого:

6-XAML-декларация

Без этого объявления ни одна из добавленных нами функций поиска не будет работать. Вы получите сообщение о том, что доступ запрещен.

6-XAML-DeclarationError

Просто помните, что вам нужно объявить, что вы используете Поиск, прежде чем вашему приложению действительно будет разрешено использовать эти API.

Резюме

Сегодня мы очень быстро взглянули на добавление поиска в ваше приложение. Контракт поиска предоставляет новый способ для вашего приложения, чтобы представить ваше приложение для своих пользователей. Теперь вам просто нужно определить, что именно вы хотите выставить. Если вы предстанете перед своими пользователями, люди будут вовлечены в ваше приложение. Чем больше способов их использовать, тем лучше жизнь.

Вы можете скачать весь пример решения здесь здесь:

downloadXAML

В следующий раз мы подойдем к последнему этапу нашего исследования контрактов, Sharing. Увидимся позже!