środa, 21 marca 2012

Windows 8 Metro na żywo, ale w Internecie - odc. 4

Windows 8 pozwala łatwo integrować wiele swoich funkcjonalności z naszymi aplikacjami. W dzisiejszym odcinku pokażę, jak można skorzystać z mechanizmu wyszukiwania.

Integrację z searchem możemy napisać “od zera” sami (jak choćby w przykładzie MS), możemy wspomóc się też szablonem ‘Search Contract’ jaki oferuje Visual Studio 11.  Po wybraniu tego szablonu odpowiednio modyfikowany jest manifest aplikacji, tworzona jest strona do prezentowania wyników wyszukiwania z predefiniowanym kodem oraz dodawany jest kod do klasy obiektu aplikacji (oczywiście w miarę możliwości, jeśli jakaś metoda jest u nas już wcześniej oprogramowana Visual umieści w innym miejscu zakomentowany kod z sugestią, gdzie powinniśmy go docelowo umieścić).

search_vs

Najważniejsze fragmenty wygenerowanego kodu w utworzonej stronie:

        private UIElement _previousContent;

        /// <summary>
        /// Records the value of the active Window's Content property when the search started.
        /// </summary>
        public static void Activate(String queryText)
        {
            // If the Window isn't already using Frame navigation, insert our own Frame
            var previousContent = Window.Current.Content;
            var frame = previousContent as Frame;
            if (frame == null)
            {
                frame = new Frame();
                Window.Current.Content = frame;
            }

            // Use navigation to display the results, packing both the query text and the previous
            // Window content into a single parameter object
            frame.Navigate(typeof(SearchVideos),
                new Tuple<String, UIElement>(queryText, previousContent));
            Window.Current.Activate();
        }

        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.  The
        /// Parameter property provides the collection of items to be displayed.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            // Unpack the two values passed in the parameter object: query text and previous
            // Window content
            var parameter = (Tuple<String, UIElement>)e.Parameter;
            var queryTxt = parameter.Item1;
            this._previousContent = parameter.Item2;

            // TODO: Application-specific searching logic.  The seach process is responsible for
            //       creating a list of user-selectable result categories:
            //
            //       filterList.Add(new Filter("<filter name>", <result count>));
            //
            //       Only the first filter, typically "All", should pass true as a third argument in
            //       order to start in an active state.  Results for the active filter are provided
            //       in Filter_SelectionChanged below.

            var filterList = new List<Filter>();
            filterList.Add(new Filter("All", 0, true));           

            // Communicate results through the view model
            this.DefaultViewModel["QueryText"] = '\u201c' + queryTxt + '\u201d';
            this.DefaultViewModel["CanGoBack"] = this._previousContent != null;
            this.DefaultViewModel["Filters"] = filterList;
            this.DefaultViewModel["ShowFilters"] = filterList.Count > 1;
        }

        /// <summary>
        /// Invoked when the back button is pressed.
        /// </summary>
        /// <param name="sender">The Button instance representing the back button.</param>
        /// <param name="e">Event data describing how the button was clicked.</param>
        protected override void GoBack(object sender, RoutedEventArgs e)
        {
            // Return the application to the state it was in before search results were requested
            var frame = this._previousContent as Frame;
            if (frame != null)
            {
                frame.GoBack();
            }
            else
            {
                Window.Current.Content = this._previousContent;
            }
        }

         /// <summary>
        /// Invoked when a filter is selected using the ComboBox in snapped view state.
        /// </summary>
        /// <param name="sender">The ComboBox instance.</param>
        /// <param name="e">Event data describing how the selected filter was changed.</param>
        void Filter_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // Determine what filter was selected
            var selectedFilter = e.AddedItems.FirstOrDefault() as Filter;
            if (selectedFilter != null)
            {
                // Mirror the results into the corresponding Filter object to allow the
                // RadioButton representation used when not snapped to reflect the change
                selectedFilter.Active = true;               

                var queryText = (string)this.DefaultViewModel["QueryText"];               
                Search(queryText);     //moja dodana metoda          
            }
        }

         /// <summary>
        /// Invoked when a filter is selected using a RadioButton when not snapped.
        /// </summary>
        /// <param name="sender">The selected RadioButton instance.</param>
        /// <param name="e">Event data describing how the RadioButton was selected.</param>
        void Filter_Checked(object sender, RoutedEventArgs e)
        {
            // Mirror the change into the CollectionViewSource used by the corresponding ComboBox
            // to ensure that the change is reflected when snapped
            if (filtersViewSource.View != null)
            {
                var filter = (sender as FrameworkElement).DataContext;
                filtersViewSource.View.MoveCurrentTo(filter);
            }
        } 

Myślę, że szczegółowy kod z komentarzami nie wymaga już komentarza, sam się opisuje. W wygenerowanym kodzie strony jest jeszcze klasa Filter będąca view modelem do obsługi filtrów (u siebie na razie ich nie wykorzystałem).

mks2_5

W klasie App nadpisana zostaje metoda OnSearchActivated (obsługa przypadku aktywacji aplikacji przez samą wyszukiwarkę - pamiętajmy, że z poziomu wyszukiwarki przy tym samym query możemy się szybko przełączać między różnymi aplikacjami)

        protected override void OnSearchActivated(Windows.ApplicationModel.Activation.SearchActivatedEventArgs args)
        {
            MyTube.SearchVideos.Activate(args.QueryText);           
        }

Zgodnie z sugestią w komentarzu wstawiłem do implementacji metody OnLaunched następujący kawałek kodu:

          var searchPane = Windows.ApplicationModel.Search.SearchPane.GetForCurrentView();        

          searchPane.QuerySubmitted +=
                 (sender, queryArgs) =>
                 {
                     MyTube.SearchVideos.Activate(queryArgs.QueryText);
                 };

Obiekt klasy SearchPane i obsługa jego zdarzenia QuerySubmitted  są - oprócz manifestu - podstawą integracji z wyszukiwarką i można go zobaczyć w każdej prezentacji lub przykładzie, które związane są z tym zagadnieniem.

mks2_6

Po wpisaniu wyrażenia wywoływana jest w końcu moja metoda Search, która - jak ostatnim razem - przy użyciu file query i klasy FileInformationFactory tworzy szybko podgląd przechowywanych plików w danej lokalizacji. Parametr przekazywany przez użytkownika jest tutaj dodatkowym parametrem UserSearchFilter w obiekcie klasy QueryOptions.

        void Search(string queryText)
        {   
            var folder = ApplicationData.Current.LocalFolder;
            var queryOptions = new Windows.Storage.Search.QueryOptions();
            queryOptions.FolderDepth = Windows.Storage.Search.FolderDepth.Deep;
            queryOptions.IndexerOption = Windows.Storage.Search.IndexerOption.UseIndexerWhenAvailable;

            if (!string.IsNullOrEmpty(queryText))
                queryOptions.UserSearchFilter = queryText;

            var fileQuery = folder.CreateFileQueryWithOptions(queryOptions);

            var fif = new Windows.Storage.BulkAccess.FileInformationFactory(
                fileQuery,
                Windows.Storage.FileProperties.ThumbnailMode.PicturesView,
                95,
                Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale,
                false
                );

            var dataSource = fif.GetVirtualizedFilesVector();
            resultsGridView.ItemsSource = dataSource;
        }

mks2_7

To nie wszystko, jeśli chodzi o wyszukiwanie. Poprzez obsługę zdarzeń SuggestionsRequested i ResultSuggestionChosen w obiekcie SearchPane możemy uczynić wyszukiwanie jeszcze bardziej przyjemniejszym dostarczając sugestie pytań lub sugerowane wyniki z miniaturkami obrazków oraz odpowiednią obsługę po ich wybraniu.  Mamy różne możliwości generowania podpowiedzi - w pamięci, z sieci, z pliku, z systemu Windows. Wspomniane funkcjonalności są dosyć często prezentowane, jak również możemy znaleźć ich opis w dokumentacji.

Brak komentarzy: