Udostępnij za pośrednictwem


Dodawanie wyszukiwania do okna narzędzi

Podczas tworzenia lub aktualizowania okna narzędzi w rozszerzeniu można dodać tę samą funkcję wyszukiwania, która jest wyświetlana w innym miejscu w programie Visual Studio. Ta funkcja obejmuje następujące funkcje:

  • Pole wyszukiwania, które zawsze znajduje się w niestandardowym obszarze paska narzędzi.

  • Wskaźnik postępu, który jest nakładany na samo pole wyszukiwania.

  • Możliwość wyświetlania wyników zaraz po wprowadzeniu każdego znaku (wyszukiwanie błyskawiczne) lub dopiero po wybraniu klawisza Enter (wyszukiwanie na żądanie).

  • Lista zawierająca terminy, dla których ostatnio wyszukiwano.

  • Możliwość filtrowania wyszukiwań według określonych pól lub aspektów elementów docelowych wyszukiwania.

Korzystając z tego przewodnika, dowiesz się, jak wykonywać następujące zadania:

  1. Utwórz projekt VSPackage.

  2. Utwórz okno narzędzia zawierające kontrolkę UserControl z polem tekstowym tylko do odczytu.

  3. Dodaj pole wyszukiwania do okna narzędzi.

  4. Dodaj implementację wyszukiwania.

  5. Włącz natychmiastowe wyszukiwanie i wyświetlanie paska postępu.

  6. Dodaj opcję Dopasowywanie wielkości liter .

  7. Dodaj filtr Tylko wiersze wyszukiwania.

Aby utworzyć projekt VSIX

  1. Utwórz projekt VSIX o nazwie TestToolWindowSearch z oknem narzędzia o nazwie TestSearch. Jeśli potrzebujesz pomocy w tym celu, zobacz Tworzenie rozszerzenia za pomocą okna narzędzi.

Aby utworzyć okno narzędzia

  1. W projekcie TestToolWindowSearch otwórz plik TestSearchControl.xaml .

  2. Zastąp istniejący <StackPanel> blok następującym blokiem, który dodaje do okna narzędzia tylko TextBox do UserControl odczytu.

    <StackPanel Orientation="Vertical">
        <TextBox Name="resultsTextBox" Height="800.0"
            Width="800.0"
            IsReadOnly="True">
        </TextBox>
    </StackPanel>
    
  3. W pliku TestSearchControl.xaml.cs dodaj następującą dyrektywę using:

    using System.Text;
    
  4. Usuń metodę button1_Click() .

    W klasie TestSearchControl dodaj następujący kod.

    Ten kod dodaje właściwość publiczną TextBox o nazwie SearchResultsTextBox i właściwość ciągu publicznego o nazwie SearchContent. W konstruktorze właściwość SearchResultsTextBox jest ustawiona na pole tekstowe, a element SearchContent jest inicjowany do zestawu ciągów rozdzielanych nowym wierszem. Zawartość pola tekstowego jest również inicjowana do zestawu ciągów.

    public partial class MyControl : UserControl
    {
        public TextBox SearchResultsTextBox { get; set; }
        public string SearchContent { get; set; }
    
        public MyControl()
        {
            InitializeComponent();
    
            this.SearchResultsTextBox = resultsTextBox;
            this.SearchContent = BuildContent();
    
            this.SearchResultsTextBox.Text = this.SearchContent;
        }
    
        private string BuildContent()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("1 go");
            sb.AppendLine("2 good");
            sb.AppendLine("3 Go");
            sb.AppendLine("4 Good");
            sb.AppendLine("5 goodbye");
            sb.AppendLine("6 Goodbye");
    
            return sb.ToString();
        }
    }
    
  5. Skompiluj projekt i rozpocznij debugowanie. Zostanie wyświetlone eksperymentalne wystąpienie programu Visual Studio.

  6. Na pasku menu wybierz pozycję Wyświetl>inne>windows TestSearch.

    Zostanie wyświetlone okno narzędzia, ale kontrolka wyszukiwania nie zostanie jeszcze wyświetlona.

Aby dodać pole wyszukiwania do okna narzędzi

  1. W pliku TestSearch.cs dodaj następujący kod do TestSearch klasy . Kod zastępuje SearchEnabled właściwość , aby metoda get zwróciła metodę truedostępu .

    Aby włączyć wyszukiwanie, należy zastąpić SearchEnabled właściwość . Klasa ToolWindowPane implementuje IVsWindowSearch i udostępnia domyślną implementację, która nie włącza wyszukiwania.

    public override bool SearchEnabled
    {
        get { return true; }
    }
    
  2. Skompiluj projekt i rozpocznij debugowanie. Zostanie wyświetlone wystąpienie eksperymentalne.

  3. W eksperymentalnym wystąpieniu programu Visual Studio otwórz narzędzie TestSearch.

    W górnej części okna narzędzia zostanie wyświetlona kontrolka wyszukiwania z znakiem wodnym Search i ikoną lupy. Jednak wyszukiwanie nie działa jeszcze, ponieważ proces wyszukiwania nie został zaimplementowany.

Aby dodać implementację wyszukiwania

Po włączeniu ToolWindowPanewyszukiwania w obiekcie, tak jak w poprzedniej procedurze, okno narzędzia tworzy hosta wyszukiwania. Ten host konfiguruje procesy wyszukiwania i zarządza nimi, które zawsze występują w wątku w tle. ToolWindowPane Ponieważ klasa zarządza tworzeniem hosta wyszukiwania i konfigurowaniem wyszukiwania, musisz utworzyć tylko zadanie wyszukiwania i podać metodę wyszukiwania. Proces wyszukiwania występuje w wątku w tle i wywołuje kontrolkę okna narzędzi w wątku interfejsu użytkownika. W związku z tym należy użyć metody ThreadHelper.Invoke* , aby zarządzać wywołaniami, które wykonujesz w radzeniu sobie z kontrolką.

  1. W pliku TestSearch.cs dodaj następujące using dyrektywy:

    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Windows.Controls;
    using Microsoft.Internal.VisualStudio.PlatformUI;
    using Microsoft.VisualStudio;
    using Microsoft.VisualStudio.PlatformUI;
    using Microsoft.VisualStudio.Shell;
    using Microsoft.VisualStudio.Shell.Interop;
    
  2. TestSearch W klasie dodaj następujący kod, który wykonuje następujące akcje:

    • Zastępuje metodę CreateSearch w celu utworzenia zadania wyszukiwania.

    • Zastępuje metodę ClearSearch w celu przywrócenia stanu pola tekstowego. Ta metoda jest wywoływana, gdy użytkownik anuluje zadanie wyszukiwania i gdy użytkownik ustawia lub nie ustawia opcji lub filtrów. Oba CreateSearch elementy i ClearSearch są wywoływane w wątku interfejsu użytkownika. W związku z tym nie musisz uzyskiwać dostępu do pola tekstowego za pomocą metody ThreadHelper.Invoke* .

    • Tworzy klasę o nazwie TestSearchTask , która dziedziczy z VsSearchTaskklasy , która zapewnia domyślną implementację IVsSearchTaskklasy .

      W TestSearchTaskprogramie konstruktor ustawia pole prywatne, które odwołuje się do okna narzędzia. Aby podać metodę wyszukiwania, należy zastąpić OnStartSearch metody i OnStopSearch . Metoda OnStartSearch to miejsce, w którym implementujesz proces wyszukiwania. Ten proces obejmuje wykonywanie wyszukiwania, wyświetlanie wyników wyszukiwania w polu tekstowym oraz wywoływanie implementacji klasy bazowej tej metody w celu zgłoszenia, że wyszukiwanie zostało ukończone.

    public override IVsSearchTask CreateSearch(uint dwCookie, IVsSearchQuery pSearchQuery, IVsSearchCallback pSearchCallback)
    {
        if (pSearchQuery == null || pSearchCallback == null)
            return null;
         return new TestSearchTask(dwCookie, pSearchQuery, pSearchCallback, this);
    }
    
    public override void ClearSearch()
    {
        TestSearchControl control = (TestSearchControl)this.Content;
        control.SearchResultsTextBox.Text = control.SearchContent;
    }
    
    internal class TestSearchTask : VsSearchTask
    {
        private TestSearch m_toolWindow;
    
        public TestSearchTask(uint dwCookie, IVsSearchQuery pSearchQuery, IVsSearchCallback pSearchCallback, TestSearch toolwindow)
            : base(dwCookie, pSearchQuery, pSearchCallback)
        {
            m_toolWindow = toolwindow;
        }
    
        protected override void OnStartSearch()
        {
            // Use the original content of the text box as the target of the search.
            var separator = new string[] { Environment.NewLine };
            TestSearchControl control = (TestSearchControl)m_toolWindow.Content;
            string[] contentArr = control.SearchContent.Split(separator, StringSplitOptions.None);
    
            // Get the search option.
            bool matchCase = false;
            // matchCase = m_toolWindow.MatchCaseOption.Value;
    
                // Set variables that are used in the finally block.
                StringBuilder sb = new StringBuilder("");
                uint resultCount = 0;
                this.ErrorCode = VSConstants.S_OK;
    
                try
                {
                    string searchString = this.SearchQuery.SearchString;
    
                    // Determine the results.
                    uint progress = 0;
                    foreach (string line in contentArr)
                    {
                        if (matchCase == true)
                        {
                            if (line.Contains(searchString))
                            {
                                sb.AppendLine(line);
                                resultCount++;
                            }
                        }
                        else
                            {
                                if (line.ToLower().Contains(searchString.ToLower()))
                                {
                                    sb.AppendLine(line);
                                    resultCount++;
                                }
                            }
    
                            // SearchCallback.ReportProgress(this, progress++, (uint)contentArr.GetLength(0));
    
                            // Uncomment the following line to demonstrate the progress bar.
                            // System.Threading.Thread.Sleep(100);
                        }
                    }
                    catch (Exception e)
                    {
                        this.ErrorCode = VSConstants.E_FAIL;
                    }
                    finally
                    {
                        ThreadHelper.Generic.Invoke(() =>
                        { ((TextBox)((TestSearchControl)m_toolWindow.Content).SearchResultsTextBox).Text = sb.ToString(); });
    
                        this.SearchResults = resultCount;
                    }
    
            // Call the implementation of this method in the base class.
            // This sets the task status to complete and reports task completion.
            base.OnStartSearch();
        }
    
        protected override void OnStopSearch()
        {
            this.SearchResults = 0;
        }
    }
    
  3. Przetestuj implementację wyszukiwania, wykonując następujące kroki:

    1. Skompiluj projekt i rozpocznij debugowanie.

    2. W eksperymentalnym wystąpieniu programu Visual Studio otwórz ponownie okno narzędzia, wprowadź tekst wyszukiwania w oknie wyszukiwania i kliknij przycisk ENTER.

      Powinny zostać wyświetlone poprawne wyniki.

Aby dostosować zachowanie wyszukiwania

Zmieniając ustawienia wyszukiwania, możesz wprowadzić różne zmiany w sposobie wyświetlania kontrolki wyszukiwania i sposobie przeprowadzania wyszukiwania. Można na przykład zmienić znak wodny (domyślny tekst wyświetlany w polu wyszukiwania), minimalną i maksymalną szerokość kontrolki wyszukiwania oraz informację o tym, czy ma być wyświetlany pasek postępu. Możesz również zmienić punkt, w którym wyniki wyszukiwania zaczynają się pojawiać (na żądanie lub wyszukiwanie błyskawiczne) i czy mają być wyświetlane listy terminów, dla których ostatnio wyszukiwano. Pełną listę ustawień można znaleźć w SearchSettingsDataSource klasie.

  1. W pliku* TestSearch.cs* dodaj następujący kod do TestSearch klasy . Ten kod umożliwia wyszukiwanie błyskawiczne zamiast wyszukiwania na żądanie (co oznacza, że użytkownik nie musi klikać klawisza ENTER). Kod zastępuje metodę ProvideSearchSettings w TestSearch klasie, co jest niezbędne do zmiany ustawień domyślnych.

    public override void ProvideSearchSettings(IVsUIDataSource pSearchSettings)
    {
        Utilities.SetValue(pSearchSettings,
            SearchSettingsDataSource.SearchStartTypeProperty.Name,
            (uint)VSSEARCHSTARTTYPE.SST_INSTANT);}
    
  2. Przetestuj nowe ustawienie, ponownie skompilując rozwiązanie i ponownie uruchom debuger.

    Wyniki wyszukiwania są wyświetlane za każdym razem, gdy wprowadzisz znak w polu wyszukiwania.

  3. W metodzie ProvideSearchSettings dodaj następujący wiersz, który umożliwia wyświetlanie paska postępu.

    public override void ProvideSearchSettings(IVsUIDataSource pSearchSettings)
    {
        Utilities.SetValue(pSearchSettings,
            SearchSettingsDataSource.SearchStartTypeProperty.Name,
             (uint)VSSEARCHSTARTTYPE.SST_INSTANT);
        Utilities.SetValue(pSearchSettings,
            SearchSettingsDataSource.SearchProgressTypeProperty.Name,
             (uint)VSSEARCHPROGRESSTYPE.SPT_DETERMINATE);
    }
    

    Aby pasek postępu był wyświetlany, należy zgłosić postęp. Aby zgłosić postęp, usuń komentarz z następującego kodu w OnStartSearch metodzie TestSearchTask klasy:

    SearchCallback.ReportProgress(this, progress++, (uint)contentArr.GetLength(0));
    
  4. Aby zwolnić przetwarzanie wystarczające, aby pasek postępu był widoczny, usuń komentarz z następującego wiersza w OnStartSearch metodzie TestSearchTask klasy:

    System.Threading.Thread.Sleep(100);
    
  5. Przetestuj nowe ustawienia, ponownie skompilując rozwiązanie i rozpoczynając debugowanie.

    Pasek postępu jest wyświetlany w oknie wyszukiwania (jako niebieska linia poniżej pola tekstowego wyszukiwania) za każdym razem, gdy wykonujesz wyszukiwanie.

Aby umożliwić użytkownikom uściślenie wyszukiwań

Możesz zezwolić użytkownikom na uściślenie wyszukiwań za pomocą opcji, takich jak Przypadek dopasowania lub Dopasowanie całego wyrazu. Opcje mogą być wartością logiczną, która jest wyświetlana jako pola wyboru lub polecenia, które są wyświetlane jako przyciski. W tym przewodniku utworzysz opcję logiczną.

  1. W pliku TestSearch.cs dodaj następujący kod do TestSearch klasy . Kod zastępuje metodę SearchOptionsEnum , która umożliwia implementacji wyszukiwania wykrywanie, czy dana opcja jest włączona, czy wyłączona. Kod w pliku SearchOptionsEnum dodaje opcję dopasowania wielkości liter do modułu IVsEnumWindowSearchOptions wyliczającego. Opcja dopasowania wielkości liter jest również udostępniana jako MatchCaseOption właściwość.

    private IVsEnumWindowSearchOptions m_optionsEnum;
    public override IVsEnumWindowSearchOptions SearchOptionsEnum
    {
        get
        {
            if (m_optionsEnum == null)
            {
                List<IVsWindowSearchOption> list = new List<IVsWindowSearchOption>();
    
                list.Add(this.MatchCaseOption);
    
                m_optionsEnum = new WindowSearchOptionEnumerator(list) as IVsEnumWindowSearchOptions;
            }
            return m_optionsEnum;
        }
    }
    
    private WindowSearchBooleanOption m_matchCaseOption;
    public WindowSearchBooleanOption MatchCaseOption
    {
        get
        {
            if (m_matchCaseOption == null)
            {
                m_matchCaseOption = new WindowSearchBooleanOption("Match case", "Match case", false);
            }
            return m_matchCaseOption;
        }
    }
    
  2. TestSearchTask W klasie usuń komentarz z następującego wiersza w metodzie OnStartSearch :

    matchCase = m_toolWindow.MatchCaseOption.Value;
    
  3. Przetestuj opcję:

    1. Skompiluj projekt i rozpocznij debugowanie. Zostanie wyświetlone wystąpienie eksperymentalne.

    2. W oknie narzędzia wybierz strzałkę w dół po prawej stronie pola tekstowego.

      Zostanie wyświetlone pole wyboru Dopasowywanie wielkości liter .

    3. Zaznacz pole wyboru Uwzględnij przypadek , a następnie wykonaj kilka wyszukiwań.

Aby dodać filtr wyszukiwania

Możesz dodać filtry wyszukiwania, które umożliwiają użytkownikom uściślenie zestawu elementów docelowych wyszukiwania. Można na przykład filtrować pliki w Eksplorator plików według dat, w których zostały one ostatnio zmodyfikowane, oraz rozszerzeń nazw plików. W tym przewodniku dodasz filtr tylko dla wierszy parzysty. Gdy użytkownik wybierze ten filtr, host wyszukiwania dodaje ciągi określone do zapytania wyszukiwania. Następnie można zidentyfikować te ciągi w metodzie wyszukiwania i odpowiednio filtrować elementy docelowe wyszukiwania.

  1. W pliku TestSearch.cs dodaj następujący kod do TestSearch klasy . Kod implementuje SearchFiltersEnum przez dodanie elementu WindowSearchSimpleFilter , który określa, aby filtrować wyniki wyszukiwania, aby wyświetlić tylko wiersze.

    public override IVsEnumWindowSearchFilters SearchFiltersEnum
    {
        get
        {
            List<IVsWindowSearchFilter> list = new List<IVsWindowSearchFilter>();
            list.Add(new WindowSearchSimpleFilter("Search even lines only", "Search even lines only", "lines", "even"));
            return new WindowSearchFilterEnumerator(list) as IVsEnumWindowSearchFilters;
        }
    }
    
    

    Teraz kontrolka wyszukiwania wyświetla filtr Search even lines onlywyszukiwania . Gdy użytkownik wybierze filtr, ciąg lines:"even" pojawi się w polu wyszukiwania. Inne kryteria wyszukiwania mogą być wyświetlane w tym samym czasie co filtr. Ciągi wyszukiwania mogą pojawić się przed filtrem, po filtrze lub obu tych ciągach.

  2. W pliku TestSearch.cs dodaj następujące metody do TestSearchTask klasy , która znajduje się w TestSearch klasie . Te metody obsługują metodę OnStartSearch , którą zmodyfikujesz w następnym kroku.

    private string RemoveFromString(string origString, string stringToRemove)
    {
        int index = origString.IndexOf(stringToRemove);
        if (index == -1)
            return origString;
        else 
             return (origString.Substring(0, index) + origString.Substring(index + stringToRemove.Length)).Trim();
    }
    
    private string[] GetEvenItems(string[] contentArr)
    {
        int length = contentArr.Length / 2;
        string[] evenContentArr = new string[length];
    
        int indexB = 0;
        for (int index = 1; index < contentArr.Length; index += 2)
        {
            evenContentArr[indexB] = contentArr[index];
            indexB++;
        }
    
        return evenContentArr;
    }
    
  3. TestSearchTask W klasie zaktualizuj metodę OnStartSearch przy użyciu następującego kodu. Ta zmiana spowoduje zaktualizowanie kodu w celu obsługi filtru.

    protected override void OnStartSearch()
    {
        // Use the original content of the text box as the target of the search. 
        var separator = new string[] { Environment.NewLine };
        string[] contentArr = ((TestSearchControl)m_toolWindow.Content).SearchContent.Split(separator, StringSplitOptions.None);
    
        // Get the search option. 
        bool matchCase = false;
        matchCase = m_toolWindow.MatchCaseOption.Value;
    
        // Set variables that are used in the finally block.
        StringBuilder sb = new StringBuilder("");
        uint resultCount = 0;
        this.ErrorCode = VSConstants.S_OK;
    
        try
        {
            string searchString = this.SearchQuery.SearchString;
    
            // If the search string contains the filter string, filter the content array. 
            string filterString = "lines:\"even\"";
    
            if (this.SearchQuery.SearchString.Contains(filterString))
            {
                // Retain only the even items in the array.
                contentArr = GetEvenItems(contentArr);
    
                // Remove 'lines:"even"' from the search string.
                searchString = RemoveFromString(searchString, filterString);
            }
    
            // Determine the results. 
            uint progress = 0;
            foreach (string line in contentArr)
            {
                if (matchCase == true)
                {
                    if (line.Contains(searchString))
                    {
                        sb.AppendLine(line);
                        resultCount++;
                    }
                }
                else
                {
                    if (line.ToLower().Contains(searchString.ToLower()))
                    {
                        sb.AppendLine(line);
                        resultCount++;
                    }
                }
    
                SearchCallback.ReportProgress(this, progress++, (uint)contentArr.GetLength(0));
    
                // Uncomment the following line to demonstrate the progress bar. 
                // System.Threading.Thread.Sleep(100);
            }
        }
        catch (Exception e)
        {
            this.ErrorCode = VSConstants.E_FAIL;
        }
        finally
        {
            ThreadHelper.Generic.Invoke(() =>
            { ((TextBox)((TestSearchControl)m_toolWindow.Content).SearchResultsTextBox).Text = sb.ToString(); });
    
            this.SearchResults = resultCount;
        }
    
        // Call the implementation of this method in the base class. 
        // This sets the task status to complete and reports task completion. 
        base.OnStartSearch();
    }
    
  4. Przetestuj kod.

  5. Skompiluj projekt i rozpocznij debugowanie. W eksperymentalnym wystąpieniu programu Visual Studio otwórz okno narzędzia, a następnie wybierz strzałkę w dół w kontrolce wyszukiwania.

    Pole wyboru Dopasuj wielkość liter i zostanie wyświetlony filtr Wyszukaj tylko wiersze.

  6. Wybierz filtr.

    Pole wyszukiwania zawiera wiersze:"parzysta", a wyświetlane są następujące wyniki:

    2 dobre

    4 Dobre

    6 Pożegnanie

  7. Usuń lines:"even" z pola wyszukiwania, zaznacz pole wyboru Dopasowywanie wielkości liter , a następnie wprowadź g w polu wyszukiwania.

    Pojawią się następujące wyniki:

    1 idź

    2 dobre

    5 pożegnanie

  8. Wybierz znak X po prawej stronie pola wyszukiwania.

    Wyszukiwanie zostanie wyczyszczone, a oryginalna zawartość zostanie wyświetlona. Jednak pole wyboru Dopasuj wielkość liter jest nadal zaznaczone.