Compartir a través de


Agregar búsqueda a una ventana de herramientas

Al crear o actualizar una ventana de herramientas en la extensión, puede agregar la misma funcionalidad de búsqueda que aparece en otra parte de Visual Studio. Esta funcionalidad incluye las siguientes características:

  • Cuadro de búsqueda que siempre se encuentra en un área personalizada de la barra de herramientas.

  • Indicador de progreso que se superpone en el propio cuadro de búsqueda.

  • La capacidad de mostrar los resultados tan pronto como escriba cada carácter (búsqueda instantánea) o solo después de elegir la tecla Entrar (buscar a petición).

  • Lista que muestra los términos para los que ha buscado más recientemente.

  • La capacidad de filtrar las búsquedas por campos específicos o aspectos de los destinos de búsqueda.

Siguiendo este tutorial, aprenderá a realizar las siguientes tareas:

  1. Cree un proyecto de VSPackage.

  2. Cree una ventana de herramientas que contenga un UserControl con un TextBox de solo lectura.

  3. Agregue un cuadro de búsqueda a la ventana de herramientas.

  4. Agregue la implementación de búsqueda.

  5. Habilite la búsqueda instantánea y la presentación de una barra de progreso.

  6. Agregue una opción De mayúsculas y minúsculas .

  7. Agregue un filtro de solo líneas de búsqueda.

Para crear un proyecto de VSIX

  1. Cree un proyecto VSIX denominado TestToolWindowSearch con una ventana de herramientas denominada TestSearch. Si necesita ayuda para ello, consulte Creación de una extensión con una ventana de herramientas.

Para crear una ventana de herramientas

  1. En el TestToolWindowSearch proyecto, abra el archivo TestSearchControl.xaml .

  2. Reemplace el bloque existente <StackPanel> por el siguiente bloque, que agrega un valor de solo lectura TextBox a en UserControl la ventana de herramientas.

    <StackPanel Orientation="Vertical">
        <TextBox Name="resultsTextBox" Height="800.0"
            Width="800.0"
            IsReadOnly="True">
        </TextBox>
    </StackPanel>
    
  3. En el archivo TestSearchControl.xaml.cs , agregue la siguiente directiva using:

    using System.Text;
    
  4. Quite el button1_Click() método .

    En la clase TestSearchControl , agregue el código siguiente.

    Este código agrega una propiedad pública TextBox denominada SearchResultsTextBox y una propiedad de cadena pública denominada SearchContent. En el constructor, SearchResultsTextBox se establece en el cuadro de texto y SearchContent se inicializa en un nuevo conjunto de cadenas delimitado por líneas. El contenido del cuadro de texto también se inicializa en el conjunto de cadenas.

    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 la solución y comience la depuración. Aparece la instancia experimental de Visual Studio.

  6. En la barra de menús, elija Ver>otros testSearch de Windows.>

    Aparece la ventana de herramientas, pero el control de búsqueda aún no aparece.

Para agregar un cuadro de búsqueda a la ventana de herramientas

  1. En el archivo TestSearch.cs , agregue el código siguiente a la TestSearch clase . El código invalida la SearchEnabled propiedad para que el descriptor de acceso get devuelva true.

    Para habilitar la búsqueda, debe invalidar la SearchEnabled propiedad . La ToolWindowPane clase implementa IVsWindowSearch y proporciona una implementación predeterminada que no habilita la búsqueda.

    public override bool SearchEnabled
    {
        get { return true; }
    }
    
  2. Compile la solución y comience la depuración. Aparece la instancia experimental.

  3. En la instancia experimental de Visual Studio, abra TestSearch.

    En la parte superior de la ventana de herramientas, aparece un control de búsqueda con una marca de agua de búsqueda y un icono de lupa. Sin embargo, la búsqueda aún no funciona porque no se ha implementado el proceso de búsqueda.

Para agregar la implementación de búsqueda

Al habilitar la búsqueda en , ToolWindowPanecomo en el procedimiento anterior, la ventana de herramientas crea un host de búsqueda. Este host configura y administra los procesos de búsqueda, que siempre se producen en un subproceso en segundo plano. Dado que la ToolWindowPane clase administra la creación del host de búsqueda y la configuración de la búsqueda, solo necesita crear una tarea de búsqueda y proporcionar el método de búsqueda. El proceso de búsqueda se produce en un subproceso en segundo plano y las llamadas al control de ventana de herramientas se producen en el subproceso de la interfaz de usuario. Por lo tanto, debe usar el método ThreadHelper.Invoke* para administrar las llamadas que realice en relación con el control.

  1. En el archivo TestSearch.cs , agregue las siguientes using directivas:

    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. En la TestSearch clase , agregue el código siguiente, que realiza las siguientes acciones:

    • Invalida el CreateSearch método para crear una tarea de búsqueda.

    • Invalida el ClearSearch método para restaurar el estado del cuadro de texto. Se llama a este método cuando un usuario cancela una tarea de búsqueda y cuando un usuario establece o anula opciones o filtros. Se llama a y CreateSearchClearSearch en el subproceso de la interfaz de usuario. Por lo tanto, no es necesario tener acceso al cuadro de texto mediante el método ThreadHelper.Invoke* .

    • Crea una clase denominada TestSearchTask que hereda de VsSearchTask, que proporciona una implementación predeterminada de IVsSearchTask.

      En TestSearchTask, el constructor establece un campo privado que hace referencia a la ventana de herramientas. Para proporcionar el método de búsqueda, invalide los OnStartSearch métodos y OnStopSearch . El OnStartSearch método es donde se implementa el proceso de búsqueda. Este proceso incluye realizar la búsqueda, mostrar los resultados de la búsqueda en el cuadro de texto y llamar a la implementación de clase base de este método para informar de que la búsqueda se ha completado.

    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. Pruebe la implementación de búsqueda mediante los pasos siguientes:

    1. Recompile el proyecto e inicie la depuración.

    2. En la instancia experimental de Visual Studio, vuelva a abrir la ventana de herramientas, escriba texto de búsqueda en la ventana de búsqueda y haga clic en ENTRAR.

      Deben aparecer los resultados correctos.

Para personalizar el comportamiento de búsqueda

Al cambiar la configuración de búsqueda, puede realizar una variedad de cambios en cómo aparece el control de búsqueda y cómo se lleva a cabo la búsqueda. Por ejemplo, puede cambiar la marca de agua (el texto predeterminado que aparece en el cuadro de búsqueda), el ancho mínimo y máximo del control de búsqueda y si se va a mostrar una barra de progreso. También puede cambiar el punto en el que los resultados de búsqueda comienzan a aparecer (a petición o búsqueda instantánea) y si se muestra una lista de términos para los que ha buscado recientemente. Puede encontrar la lista completa de opciones de configuración en la SearchSettingsDataSource clase .

  1. En el archivo* TestSearch.cs*, agregue el código siguiente a la TestSearch clase . Este código permite la búsqueda instantánea en lugar de la búsqueda a petición (lo que significa que el usuario no tiene que hacer clic en ENTRAR). El código invalida el ProvideSearchSettings método de la TestSearch clase , que es necesario para cambiar la configuración predeterminada.

    public override void ProvideSearchSettings(IVsUIDataSource pSearchSettings)
    {
        Utilities.SetValue(pSearchSettings,
            SearchSettingsDataSource.SearchStartTypeProperty.Name,
            (uint)VSSEARCHSTARTTYPE.SST_INSTANT);}
    
  2. Pruebe la nueva configuración recompilando la solución y reiniciando el depurador.

    Los resultados de la búsqueda aparecen cada vez que se escribe un carácter en el cuadro de búsqueda.

  3. En el ProvideSearchSettings método , agregue la línea siguiente, que habilita la presentación de una barra de progreso.

    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 aparezca la barra de progreso, se debe notificar el progreso. Para notificar el progreso, quite la marca de comentario del código siguiente en el OnStartSearch método de la TestSearchTask clase :

    SearchCallback.ReportProgress(this, progress++, (uint)contentArr.GetLength(0));
    
  4. Para ralentizar el procesamiento suficiente como para que la barra de progreso esté visible, quite la marca de comentario de la siguiente línea en el OnStartSearch método de la TestSearchTask clase :

    System.Threading.Thread.Sleep(100);
    
  5. Pruebe la nueva configuración recompilando la solución y empezando a depurar.

    La barra de progreso aparece en la ventana de búsqueda (como una línea azul debajo del cuadro de texto de búsqueda) cada vez que se realiza una búsqueda.

Para permitir a los usuarios refinar sus búsquedas

Puede permitir a los usuarios refinar sus búsquedas mediante opciones como Match Case o Match whole word. Las opciones pueden ser booleanas, que aparecen como casillas o comandos, que aparecen como botones. En este tutorial, creará una opción booleana.

  1. En el archivo TestSearch.cs , agregue el código siguiente a la TestSearch clase . El código invalida el SearchOptionsEnum método , que permite a la implementación de búsqueda detectar si una opción determinada está activada o desactivada. El código de SearchOptionsEnum agrega una opción para buscar coincidencias entre mayúsculas y minúsculas en un IVsEnumWindowSearchOptions enumerador. La opción para buscar coincidencias de mayúsculas y minúsculas también está disponible como la MatchCaseOption propiedad .

    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. En la TestSearchTask clase , quite la marca de comentario de la línea siguiente en el OnStartSearch método :

    matchCase = m_toolWindow.MatchCaseOption.Value;
    
  3. Pruebe la opción:

    1. Compile la solución y comience la depuración. Aparece la instancia experimental.

    2. En la ventana de herramientas, elija la flecha abajo en el lado derecho del cuadro de texto.

      Aparece la casilla Coincidir mayúsculas y minúsculas .

    3. Active la casilla Coincidir mayúsculas y minúsculas y, a continuación, realice algunas búsquedas.

Para agregar un filtro de búsqueda

Puede agregar filtros de búsqueda que permitan a los usuarios refinar el conjunto de destinos de búsqueda. Por ejemplo, puede filtrar archivos en Explorador de archivos por las fechas en las que se modificaron más recientemente y sus extensiones de nombre de archivo. En este tutorial, agregará un filtro solo para líneas pares. Cuando el usuario elige ese filtro, el host de búsqueda agrega las cadenas que especifique a la consulta de búsqueda. A continuación, puede identificar estas cadenas dentro del método de búsqueda y filtrar los destinos de búsqueda en consecuencia.

  1. En el archivo TestSearch.cs , agregue el código siguiente a la TestSearch clase . El código se SearchFiltersEnum implementa agregando un WindowSearchSimpleFilter que especifica filtrar los resultados de la búsqueda para que solo aparezcan líneas pares.

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

    Ahora el control de búsqueda muestra el filtro Search even lines onlyde búsqueda . Cuando el usuario elige el filtro, la cadena lines:"even" aparece en el cuadro de búsqueda. Otros criterios de búsqueda pueden aparecer al mismo tiempo que el filtro. Las cadenas de búsqueda pueden aparecer antes del filtro, después del filtro o ambos.

  2. En el archivo TestSearch.cs , agregue los métodos siguientes a la TestSearchTask clase , que se encuentra en la TestSearch clase . Estos métodos admiten el OnStartSearch método , que modificará en el paso siguiente.

    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. En la TestSearchTask clase , actualice el OnStartSearch método con el código siguiente. Este cambio actualiza el código para admitir el 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. Pruebe el código.

  5. Compile la solución y comience la depuración. En la instancia experimental de Visual Studio, abra la ventana de herramientas y elija la flecha Abajo en el control de búsqueda.

    La casilla Buscar mayúsculas y minúsculas y solo aparecen las líneas de búsqueda.

  6. Elija el filtro.

    El cuadro de búsqueda contiene líneas:"even" y aparecen los siguientes resultados:

    2 buenas

    4 Bueno

    6 Adiós

  7. Elimine lines:"even" del cuadro de búsqueda, active la casilla Coincidir mayúsculas y minúsculas y, a continuación, escriba g en el cuadro de búsqueda.

    Aparecen los siguientes resultados:

    1 ir

    2 buenas

    5 adiós

  8. Elija la X en el lado derecho del cuadro de búsqueda.

    La búsqueda se borra y aparece el contenido original. Sin embargo, la casilla Coincidir mayúsculas y minúsculas sigue activada.