Поделиться через


Добавление поиска в окно инструментов

При создании или обновлении окна инструментов в расширении можно добавить ту же функцию поиска, которая отображается в другом месте Visual Studio. Эта функция включает следующие функции:

  • Поле поиска, которое всегда находится в пользовательской области панели инструментов.

  • Индикатор хода выполнения, наложенный на само поле поиска.

  • Возможность отображать результаты сразу после ввода каждого символа (мгновенного поиска) или только после выбора клавиши ВВОД (поиск по запросу).

  • Список, в котором показаны термины, для которых вы выполнили поиск в последнее время.

  • Возможность фильтрации поиска по определенным полям или аспектам целевых объектов поиска.

Следуя этому пошаговому руководству, вы узнаете, как выполнять следующие задачи:

  1. Создайте проект VSPackage.

  2. Создайте окно средства, содержащее UserControl с текстовым полем только для чтения.

  3. Добавьте поле поиска в окно инструментов.

  4. Добавьте реализацию поиска.

  5. Включите мгновенный поиск и отображение индикатора выполнения.

  6. Добавьте параметр "Вариант сопоставления".

  7. Добавьте только фильтр только строк поиска.

Создание проекта VSIX

  1. Создайте проект VSIX с именем TestToolWindowSearch окна инструментов с именем TestSearch. Если вам нужна помощь в этом, см. статью "Создание расширения с помощью окна инструментов".

Создание окна средства

  1. TestToolWindowSearch В проекте откройте файл TestSearchControl.xaml.

  2. Замените существующий <StackPanel> блок следующим блоком, который добавляет доступ только для TextBox UserControl чтения в окно инструментов.

    <StackPanel Orientation="Vertical">
        <TextBox Name="resultsTextBox" Height="800.0"
            Width="800.0"
            IsReadOnly="True">
        </TextBox>
    </StackPanel>
    
  3. В файле TestSearchControl.xaml.cs добавьте следующую директиву using:

    using System.Text;
    
  4. button1_Click() Удалите метод.

    В классе TestSearchControl добавьте следующий код.

    Этот код добавляет общедоступное TextBox свойство с именем SearchResultsTextBox и открытое строковое свойство с именем SearchContent. В конструкторе searchResultsTextBox задано текстовое поле, а SearchContent инициализирован в новый набор строк с разделителями. Содержимое текстового поля также инициализируется набором строк.

    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. Выполните сборку решения и запустите отладку. Появится экспериментальный экземпляр Visual Studio.

  6. В строке меню выберите "Просмотреть>другие запросы Windows>TestSearch".

    Откроется окно инструментов, но элемент управления поиском еще не отображается.

Добавление поля поиска в окно инструментов

  1. В файле TestSearch.cs добавьте следующий код в TestSearch класс. Код переопределяет SearchEnabled свойство, чтобы метод доступа возвращал true.

    Чтобы включить поиск, необходимо переопределить SearchEnabled свойство. Класс ToolWindowPane реализует IVsWindowSearch и предоставляет реализацию по умолчанию, которая не включает поиск.

    public override bool SearchEnabled
    {
        get { return true; }
    }
    
  2. Выполните сборку решения и запустите отладку. Откроется экспериментальный экземпляр.

  3. В экспериментальном экземпляре Visual Studio откройте TestSearch.

    В верхней части окна инструментов элемент управления поиском отображается с подложкой поиска и значком с увеличением стекла. Однако поиск еще не работает, так как процесс поиска не реализован.

Добавление реализации поиска

При включении поиска по объекту ToolWindowPane,как и в предыдущей процедуре, окно инструментов создает узел поиска. Этот узел настраивает и управляет процессами поиска, которые всегда происходят в фоновом потоке. ToolWindowPane Так как класс управляет созданием узла поиска и настройкой поиска, необходимо создать только задачу поиска и предоставить метод поиска. Процесс поиска происходит в фоновом потоке и вызовы элемента управления окном инструментов происходят в потоке пользовательского интерфейса. Поэтому необходимо использовать метод ThreadHelper.Invoke* для управления вызовами, которые вы выполняете в работе с элементом управления.

  1. В файле TestSearch.cs добавьте следующие using директивы:

    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 В классе добавьте следующий код, который выполняет следующие действия:

    • Переопределяет CreateSearch метод для создания задачи поиска.

    • Переопределяет ClearSearch метод для восстановления состояния текстового поля. Этот метод вызывается, когда пользователь отменяет задачу поиска, а также когда пользователь задает или отменяет параметры или фильтры. ClearSearch Оба CreateSearch и вызываются в потоке пользовательского интерфейса. Поэтому доступ к текстовому поле не требуется с помощью метода ThreadHelper.Invoke* .

    • Создает класс, который TestSearchTask наследует от VsSearchTask, который предоставляет реализацию IVsSearchTaskпо умолчанию.

      В TestSearchTaskконструкторе устанавливается частное поле, которое ссылается на окно инструментов. Чтобы предоставить метод поиска, переопределите OnStartSearch методы и OnStopSearch методы. Метод заключается в OnStartSearch реализации процесса поиска. Этот процесс включает выполнение поиска, отображение результатов поиска в текстовом поле и вызов реализации базового класса этого метода, чтобы сообщить о завершении поиска.

    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. Протестируйте реализацию поиска, выполнив следующие действия.

    1. Перестройте проект и запустите отладку.

    2. В экспериментальном экземпляре Visual Studio снова откройте окно инструментов, введите текст поиска в окне поиска и нажмите клавишу ВВОД.

      Должны отображаться правильные результаты.

Настройка поведения поиска

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

  1. В файле TestSearch.cs* добавьте следующий код в TestSearch класс. Этот код включает мгновенный поиск вместо поиска по запросу (это означает, что пользователю не нужно щелкнуть ВВОД). Код переопределяет ProvideSearchSettings метод в TestSearch классе, который необходим для изменения параметров по умолчанию.

    public override void ProvideSearchSettings(IVsUIDataSource pSearchSettings)
    {
        Utilities.SetValue(pSearchSettings,
            SearchSettingsDataSource.SearchStartTypeProperty.Name,
            (uint)VSSEARCHSTARTTYPE.SST_INSTANT);}
    
  2. Проверьте новый параметр, перестроив решение и перезапустите отладчик.

    Результаты поиска отображаются каждый раз, когда вы вводите символ в поле поиска.

  3. В методе ProvideSearchSettings добавьте следующую строку, которая позволяет отображать индикатор хода выполнения.

    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);
    }
    

    Чтобы появилась панель выполнения, необходимо сообщить о ходе выполнения. Чтобы сообщить о ходе выполнения, раскомментируйте следующий код в OnStartSearch методе TestSearchTask класса:

    SearchCallback.ReportProgress(this, progress++, (uint)contentArr.GetLength(0));
    
  4. Чтобы замедлить обработку, чтобы индикатор выполнения был видимым, раскомментируйте следующую строку в OnStartSearch методе TestSearchTask класса:

    System.Threading.Thread.Sleep(100);
    
  5. Проверьте новые параметры, перестроив решение и начав отладку.

    Индикатор выполнения отображается в окне поиска (в виде синей строки под текстовым полем поиска) каждый раз при выполнении поиска.

Чтобы пользователи могли уточнить свои поисковые запросы

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

  1. В файле TestSearch.cs добавьте следующий код в TestSearch класс. Код переопределяет SearchOptionsEnum метод, который позволяет реализации поиска определить, включен или отключен заданный параметр. В коде SearchOptionsEnum добавляется параметр сопоставления регистра IVsEnumWindowSearchOptions перечислителю. Вариант сопоставления регистра также доступен в качестве MatchCaseOption свойства.

    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 В классе раскомментируйте следующую строку в методеOnStartSearch:

    matchCase = m_toolWindow.MatchCaseOption.Value;
    
  3. Проверьте параметр:

    1. Выполните сборку решения и запустите отладку. Откроется экспериментальный экземпляр.

    2. В окне инструментов щелкните стрелку ВНИЗ в правой части текстового поля.

      Появится поле "Совпадение " проверка.

    3. Выберите поле "Совпадение" проверка поле, а затем выполните некоторые поиски.

Добавление фильтра поиска

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

  1. В файле TestSearch.cs добавьте следующий код в TestSearch класс. Код реализуется SearchFiltersEnum путем добавления, WindowSearchSimpleFilter указывающего фильтрацию результатов поиска таким образом, чтобы отображались только даже строки.

    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;
        }
    }
    
    

    Теперь элемент управления поиском отображает фильтр Search even lines onlyпоиска. Когда пользователь выбирает фильтр, строка lines:"even" появится в поле поиска. Другие критерии поиска могут отображаться одновременно с фильтром. Строки поиска могут отображаться перед фильтром, после фильтра или обоих.

  2. В файле TestSearch.cs добавьте следующие методы в TestSearchTask класс, который находится в TestSearch классе. Эти методы поддерживают OnStartSearch метод, который вы измените на следующем шаге.

    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 В классе обновите OnStartSearch метод следующим кодом. Это изменение обновляет код для поддержки фильтра.

    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. Проверьте код.

  5. Выполните сборку решения и запустите отладку. В экспериментальном экземпляре Visual Studio откройте окно инструментов, а затем щелкните стрелку ВНИЗ в элементе управления поиска.

    Поле "Совпадение" проверка поле и отображается только фильтр "Поиск даже строк".

  6. Выберите фильтр.

    Поле поиска содержит строки:"даже", и отображаются следующие результаты:

    2 хорошие

    4 Хороший

    6 Гудби

  7. Удалите lines:"even" из поля поиска, выберите поле "Совпадение" проверка поле, а затем введите g в поле поиска.

    Отображаются следующие результаты:

    1 go

    2 хорошие

    5 прощание

  8. Выберите X в правой части поля поиска.

    Поиск очищается и отображается исходное содержимое. Однако поле "Совпадение " проверка по-прежнему выбрано.