Добавление поиска в окно инструментов
При создании или обновлении окна инструментов в расширении можно добавить ту же функцию поиска, которая отображается в другом месте Visual Studio. Эта функция включает следующие функции:
Поле поиска, которое всегда находится в пользовательской области панели инструментов.
Индикатор хода выполнения, наложенный на само поле поиска.
Возможность отображать результаты сразу после ввода каждого символа (мгновенного поиска) или только после выбора клавиши ВВОД (поиск по запросу).
Список, в котором показаны термины, для которых вы выполнили поиск в последнее время.
Возможность фильтрации поиска по определенным полям или аспектам целевых объектов поиска.
Следуя этому пошаговому руководству, вы узнаете, как выполнять следующие задачи:
Создайте проект VSPackage.
Создайте окно средства, содержащее UserControl с текстовым полем только для чтения.
Добавьте поле поиска в окно инструментов.
Добавьте реализацию поиска.
Включите мгновенный поиск и отображение индикатора выполнения.
Добавьте параметр "Вариант сопоставления".
Добавьте только фильтр только строк поиска.
Создание проекта VSIX
- Создайте проект VSIX с именем
TestToolWindowSearch
окна инструментов с именем TestSearch. Если вам нужна помощь в этом, см. статью "Создание расширения с помощью окна инструментов".
Создание окна средства
TestToolWindowSearch
В проекте откройте файл TestSearchControl.xaml.Замените существующий
<StackPanel>
блок следующим блоком, который добавляет доступ только для TextBox UserControl чтения в окно инструментов.<StackPanel Orientation="Vertical"> <TextBox Name="resultsTextBox" Height="800.0" Width="800.0" IsReadOnly="True"> </TextBox> </StackPanel>
В файле TestSearchControl.xaml.cs добавьте следующую директиву using:
using System.Text;
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(); } }
Выполните сборку решения и запустите отладку. Появится экспериментальный экземпляр Visual Studio.
В строке меню выберите "Просмотреть>другие запросы Windows>TestSearch".
Откроется окно инструментов, но элемент управления поиском еще не отображается.
Добавление поля поиска в окно инструментов
В файле TestSearch.cs добавьте следующий код в
TestSearch
класс. Код переопределяет SearchEnabled свойство, чтобы метод доступа возвращалtrue
.Чтобы включить поиск, необходимо переопределить SearchEnabled свойство. Класс ToolWindowPane реализует IVsWindowSearch и предоставляет реализацию по умолчанию, которая не включает поиск.
public override bool SearchEnabled { get { return true; } }
Выполните сборку решения и запустите отладку. Откроется экспериментальный экземпляр.
В экспериментальном экземпляре Visual Studio откройте TestSearch.
В верхней части окна инструментов элемент управления поиском отображается с подложкой поиска и значком с увеличением стекла. Однако поиск еще не работает, так как процесс поиска не реализован.
Добавление реализации поиска
При включении поиска по объекту ToolWindowPane,как и в предыдущей процедуре, окно инструментов создает узел поиска. Этот узел настраивает и управляет процессами поиска, которые всегда происходят в фоновом потоке. ToolWindowPane Так как класс управляет созданием узла поиска и настройкой поиска, необходимо создать только задачу поиска и предоставить метод поиска. Процесс поиска происходит в фоновом потоке и вызовы элемента управления окном инструментов происходят в потоке пользовательского интерфейса. Поэтому необходимо использовать метод ThreadHelper.Invoke* для управления вызовами, которые вы выполняете в работе с элементом управления.
В файле 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;
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; } }
Протестируйте реализацию поиска, выполнив следующие действия.
Перестройте проект и запустите отладку.
В экспериментальном экземпляре Visual Studio снова откройте окно инструментов, введите текст поиска в окне поиска и нажмите клавишу ВВОД.
Должны отображаться правильные результаты.
Настройка поведения поиска
Изменив параметры поиска, можно внести различные изменения в способе отображения элемента управления поиском и способах выполнения поиска. Например, можно изменить подложку (текст по умолчанию, отображаемый в поле поиска), минимальную и максимальную ширину элемента управления поиска и показать ли индикатор выполнения. Вы также можете изменить точку, в которой результаты поиска начинают отображаться (по запросу или мгновенному поиску) и отображать список терминов, для которых вы недавно искали. Полный список параметров можно найти в SearchSettingsDataSource классе.
В файле TestSearch.cs* добавьте следующий код в
TestSearch
класс. Этот код включает мгновенный поиск вместо поиска по запросу (это означает, что пользователю не нужно щелкнуть ВВОД). Код переопределяетProvideSearchSettings
метод вTestSearch
классе, который необходим для изменения параметров по умолчанию.public override void ProvideSearchSettings(IVsUIDataSource pSearchSettings) { Utilities.SetValue(pSearchSettings, SearchSettingsDataSource.SearchStartTypeProperty.Name, (uint)VSSEARCHSTARTTYPE.SST_INSTANT);}
Проверьте новый параметр, перестроив решение и перезапустите отладчик.
Результаты поиска отображаются каждый раз, когда вы вводите символ в поле поиска.
В методе
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));
Чтобы замедлить обработку, чтобы индикатор выполнения был видимым, раскомментируйте следующую строку в
OnStartSearch
методеTestSearchTask
класса:System.Threading.Thread.Sleep(100);
Проверьте новые параметры, перестроив решение и начав отладку.
Индикатор выполнения отображается в окне поиска (в виде синей строки под текстовым полем поиска) каждый раз при выполнении поиска.
Чтобы пользователи могли уточнить свои поисковые запросы
Вы можете разрешить пользователям уточнять поисковые запросы с помощью таких параметров, как "Совпадение" или "Совпадение всего слова". Параметры могут быть логическими, которые отображаются как проверка поля или команды, которые отображаются в виде кнопок. В этом пошаговом руководстве вы создадите логический параметр.
В файле 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; } }
TestSearchTask
В классе раскомментируйте следующую строку в методеOnStartSearch
:matchCase = m_toolWindow.MatchCaseOption.Value;
Проверьте параметр:
Выполните сборку решения и запустите отладку. Откроется экспериментальный экземпляр.
В окне инструментов щелкните стрелку ВНИЗ в правой части текстового поля.
Появится поле "Совпадение " проверка.
Выберите поле "Совпадение" проверка поле, а затем выполните некоторые поиски.
Добавление фильтра поиска
Можно добавить фильтры поиска, позволяющие пользователям уточнить набор целевых объектов поиска. Например, файлы можно фильтровать в проводник по датам, в которых они были изменены в последнее время, и их расширения имени файла. В этом пошаговом руководстве вы добавите фильтр только для даже строк. Когда пользователь выбирает этот фильтр, узел поиска добавляет строки, указанные в запросе поиска. Затем эти строки можно определить внутри метода поиска и отфильтровать целевые объекты поиска соответствующим образом.
В файле 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"
появится в поле поиска. Другие критерии поиска могут отображаться одновременно с фильтром. Строки поиска могут отображаться перед фильтром, после фильтра или обоих.В файле 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; }
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(); }
Проверьте код.
Выполните сборку решения и запустите отладку. В экспериментальном экземпляре Visual Studio откройте окно инструментов, а затем щелкните стрелку ВНИЗ в элементе управления поиска.
Поле "Совпадение" проверка поле и отображается только фильтр "Поиск даже строк".
Выберите фильтр.
Поле поиска содержит строки:"даже", и отображаются следующие результаты:
2 хорошие
4 Хороший
6 Гудби
Удалите
lines:"even"
из поля поиска, выберите поле "Совпадение" проверка поле, а затем введитеg
в поле поиска.Отображаются следующие результаты:
1 go
2 хорошие
5 прощание
Выберите X в правой части поля поиска.
Поиск очищается и отображается исходное содержимое. Однако поле "Совпадение " проверка по-прежнему выбрано.