Compartilhar via


Adicionar pesquisa a uma janela de ferramenta

Quando você cria ou atualiza uma janela de ferramenta em sua extensão, você pode adicionar a mesma funcionalidade de pesquisa que aparece em outro lugar no Visual Studio. Essa funcionalidade inclui os seguintes recursos:

  • Uma caixa de pesquisa que está sempre localizada em uma área personalizada da barra de ferramentas.

  • Um indicador de progresso que é sobreposto na própria caixa de pesquisa.

  • A capacidade de mostrar resultados assim que você inserir cada caractere (pesquisa instantânea) ou somente depois de escolher a tecla Enter (pesquisa sob demanda).

  • Uma lista que mostra os termos pelos quais você pesquisou mais recentemente.

  • A capacidade de filtrar pesquisas por campos específicos ou aspectos dos destinos de pesquisa.

Seguindo este passo a passo, você aprenderá a executar as seguintes tarefas:

  1. Crie um projeto VSPackage.

  2. Crie uma janela de ferramenta que contém um UserControl com um TextBox somente leitura.

  3. Adicione uma caixa de pesquisa à janela da ferramenta.

  4. Adicione a implementação de pesquisa.

  5. Habilite a pesquisa instantânea e a exibição de uma barra de progresso.

  6. Adicione uma opção de correspondência de maiúsculas e minúsculas .

  7. Adicione um filtro Pesquisar somente linhas pares.

Para criar um projeto VSIX

  1. Crie um projeto VSIX nomeado TestToolWindowSearch com uma janela de ferramenta chamada TestSearch. Se precisar de ajuda para fazer isso, consulte Criando uma extensão com uma janela de ferramenta.

Para criar uma janela de ferramenta

  1. TestToolWindowSearch No projeto, abra o arquivo TestSearchControl.xaml.

  2. Substitua o bloco existente <StackPanel> pelo bloco a seguir, que adiciona um somente TextBox leitura ao UserControl na janela da ferramenta.

    <StackPanel Orientation="Vertical">
        <TextBox Name="resultsTextBox" Height="800.0"
            Width="800.0"
            IsReadOnly="True">
        </TextBox>
    </StackPanel>
    
  3. No arquivo TestSearchControl.xaml.cs adicione a seguinte diretiva using:

    using System.Text;
    
  4. Remova o button1_Click() método.

    Na classe TestSearchControl, adicione o código a seguir.

    Esse código adiciona uma propriedade pública chamada SearchResultsTextBox e uma propriedade de cadeia de caracteres pública TextBox chamada SearchContent. No construtor, SearchResultsTextBox é definido como a caixa de texto e SearchContent é inicializado para um conjunto de cadeias de caracteres delimitado por nova linha. O conteúdo da caixa de texto também é inicializado para o conjunto de cadeias de caracteres.

    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. Compile o projeto e comece a depuração. A instância experimental do Visual Studio aparece.

  6. Na barra de menus, escolha Exibir>outro Windows>TestSearch.

    A janela da ferramenta é exibida, mas o controle de pesquisa ainda não aparece.

Para adicionar uma caixa de pesquisa à janela da ferramenta

  1. No arquivo TestSearch.cs , adicione o seguinte código à TestSearch classe. O código substitui a SearchEnabled propriedade para que o acessador get retorne true.

    Para habilitar a pesquisa, você deve substituir a SearchEnabled propriedade. A ToolWindowPane classe implementa IVsWindowSearch e fornece uma implementação padrão que não habilita a pesquisa.

    public override bool SearchEnabled
    {
        get { return true; }
    }
    
  2. Compile o projeto e comece a depuração. A instância experimental é exibida.

  3. Na instância experimental do Visual Studio, abra TestSearch.

    Na parte superior da janela da ferramenta, um controle de pesquisa aparece com uma marca d'água Pesquisar e um ícone de lupa. No entanto, a pesquisa ainda não funciona porque o processo de pesquisa não foi implementado.

Para adicionar a implementação de pesquisa

Quando você habilita a pesquisa em um , como no procedimento anterior, a janela de ferramenta cria um ToolWindowPanehost de pesquisa. Esse host configura e gerencia processos de pesquisa, que sempre ocorrem em um thread em segundo plano. Como a classe gerencia a criação do host de pesquisa e a ToolWindowPane configuração da pesquisa, você só precisa criar uma tarefa de pesquisa e fornecer o método de pesquisa. O processo de pesquisa ocorre em um thread em segundo plano e as chamadas para o controle de janela de ferramenta ocorrem no thread da interface do usuário. Portanto, você deve usar o método ThreadHelper.Invoke* para gerenciar todas as chamadas que você faz ao lidar com o controle.

  1. No arquivo TestSearch.cs adicione as seguintes using diretivas:

    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 Na classe, adicione o seguinte código, que executa as seguintes ações:

    • Substitui o CreateSearch método para criar uma tarefa de pesquisa.

    • Substitui o método para restaurar o ClearSearch estado da caixa de texto. Esse método é chamado quando um usuário cancela uma tarefa de pesquisa e quando um usuário define ou desdefine opções ou filtros. Ambos CreateSearch e ClearSearch são chamados no thread da interface do usuário. Portanto, você não precisa acessar a caixa de texto por meio do método ThreadHelper.Invoke* .

    • Cria uma classe chamada TestSearchTask que herda de , que fornece uma implementação padrão de VsSearchTaskIVsSearchTask.

      No TestSearchTask, o construtor define um campo privado que faz referência à janela da ferramenta. Para fornecer o método de pesquisa, substitua os OnStartSearch métodos e OnStopSearch . O OnStartSearch método é onde você implementa o processo de pesquisa. Esse processo inclui executar a pesquisa, exibir os resultados da pesquisa na caixa de texto e chamar a implementação de classe base desse método para relatar que a pesquisa foi concluída.

    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. Teste sua implementação de pesquisa executando as seguintes etapas:

    1. Recrie o projeto e inicie a depuração.

    2. Na instância experimental do Visual Studio, abra a janela de ferramenta novamente, insira algum texto de pesquisa na janela de pesquisa e clique em ENTER.

      Os resultados corretos devem aparecer.

Para personalizar o comportamento de pesquisa

Ao alterar as configurações de pesquisa, você pode fazer várias alterações em como o controle de pesquisa aparece e como a pesquisa é realizada. Por exemplo, você pode alterar a marca d'água (o texto padrão que aparece na caixa de pesquisa), a largura mínima e máxima do controle de pesquisa e se deseja mostrar uma barra de progresso. Você também pode alterar o ponto em que os resultados da pesquisa começam a aparecer (sob demanda ou pesquisa instantânea) e se deseja mostrar uma lista de termos para os quais você pesquisou recentemente. Você pode encontrar a lista completa de configurações na SearchSettingsDataSource classe.

  1. No arquivo * TestSearch.cs*, adicione o seguinte código à TestSearch classe. Esse código habilita a pesquisa instantânea em vez da pesquisa sob demanda (o que significa que o usuário não precisa clicar em ENTER). O código substitui o ProvideSearchSettings método na TestSearch classe, que é necessário para alterar as configurações padrão.

    public override void ProvideSearchSettings(IVsUIDataSource pSearchSettings)
    {
        Utilities.SetValue(pSearchSettings,
            SearchSettingsDataSource.SearchStartTypeProperty.Name,
            (uint)VSSEARCHSTARTTYPE.SST_INSTANT);}
    
  2. Teste a nova configuração recriando a solução e reiniciando o depurador.

    Os resultados da pesquisa aparecem sempre que você insere um caractere na caixa de pesquisa.

  3. ProvideSearchSettings No método, adicione a seguinte linha, que permite a exibição de uma barra de progresso.

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

    Para que a barra de progresso apareça, o progresso deve ser relatado. Para relatar o progresso, remova o comentário do OnStartSearch seguinte código no método da TestSearchTask classe:

    SearchCallback.ReportProgress(this, progress++, (uint)contentArr.GetLength(0));
    
  4. Para retardar o processamento o suficiente para que a barra de progresso esteja visível, remova o comentário da OnStartSearch seguinte linha no método da TestSearchTask classe:

    System.Threading.Thread.Sleep(100);
    
  5. Teste as novas configurações recriando a solução e começando a depurar.

    A barra de progresso aparece na janela de pesquisa (como uma linha azul abaixo da caixa de texto de pesquisa) sempre que você executa uma pesquisa.

Para permitir que os usuários refinem suas pesquisas

Você pode permitir que os usuários refinem suas pesquisas por meio de opções como Coincidir maiúsculas e minúsculas ou Corresponder palavra inteira. As opções podem ser booleanas, que aparecem como caixas de seleção, ou comandos, que aparecem como botões. Para este passo a passo, você criará uma opção booleana.

  1. No arquivo TestSearch.cs , adicione o seguinte código à TestSearch classe. O código substitui o método, o SearchOptionsEnum que permite que a implementação de pesquisa detecte se uma determinada opção está ativada ou desativada. O código em SearchOptionsEnum adiciona uma opção para corresponder maiúsculas e minúsculas a um IVsEnumWindowSearchOptions enumerador. A opção de combinar maiúsculas e minúsculas também é disponibilizada como propriedade 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. Na classe, remova o TestSearchTask comentário da seguinte linha no OnStartSearch método:

    matchCase = m_toolWindow.MatchCaseOption.Value;
    
  3. Teste a opção:

    1. Compile o projeto e comece a depuração. A instância experimental é exibida.

    2. Na janela da ferramenta, escolha a seta para baixo no lado direito da caixa de texto.

      A caixa de seleção Corresponder maiúsculas e minúsculas é exibida.

    3. Marque a caixa de seleção Corresponder maiúsculas e minúsculas e execute algumas pesquisas.

Para adicionar um filtro de pesquisa

Você pode adicionar filtros de pesquisa que permitem aos usuários refinar o conjunto de destinos de pesquisa. Por exemplo, você pode filtrar arquivos no Explorador de Arquivos pelas datas em que foram modificados mais recentemente e suas extensões de nome de arquivo. Neste passo a passo, você adicionará um filtro apenas para linhas pares. Quando o usuário escolhe esse filtro, o host de pesquisa adiciona as cadeias de caracteres especificadas à consulta de pesquisa. Em seguida, você pode identificar essas cadeias de caracteres dentro do seu método de pesquisa e filtrar os destinos de pesquisa de acordo.

  1. No arquivo TestSearch.cs , adicione o seguinte código à TestSearch classe. O código é SearchFiltersEnum implementado adicionando um WindowSearchSimpleFilter que especifica filtrar os resultados da pesquisa para que apenas linhas pares apareçam.

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

    Agora, o controle de pesquisa exibe o filtro Search even lines onlyde pesquisa . Quando o usuário escolhe o filtro, a cadeia de caracteres lines:"even" aparece na caixa de pesquisa. Outros critérios de pesquisa podem aparecer ao mesmo tempo que o filtro. As cadeias de caracteres de pesquisa podem aparecer antes do filtro, depois do filtro ou de ambos.

  2. No arquivo TestSearch.cs , adicione os seguintes métodos à TestSearchTask classe, que está na TestSearch classe. Esses métodos oferecem suporte ao OnStartSearch método, que você modificará na próxima etapa.

    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 Na classe, atualize o método com o OnStartSearch código a seguir. Essa alteração atualiza o código para oferecer suporte ao filtro.

    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. Teste seu código.

  5. Compile o projeto e comece a depuração. Na instância experimental do Visual Studio, abra a janela da ferramenta e, em seguida, escolha a seta para baixo no controle de pesquisa.

    A caixa de seleção Corresponder maiúsculas e minúsculas e o filtro Pesquisar somente linhas pares são exibidas.

  6. Escolha o filtro.

    A caixa de pesquisa contém linhas:"par" e os seguintes resultados aparecem:

    2 bom

    4 Bom

    6 Adeus

  7. Exclua lines:"even" da caixa de pesquisa, marque a caixa de seleção Corresponder maiúsculas e minúsculas e digite g na caixa de pesquisa.

    Os seguintes resultados aparecem:

    1 ir

    2 bom

    5 Adeus

  8. Escolha o X no lado direito da caixa de pesquisa.

    A pesquisa é limpa e o conteúdo original é exibido. No entanto, a caixa de seleção Corresponder maiúsculas e minúsculas ainda está marcada.