Usar o contexto de interface do usuário baseada em regras para extensões do Visual Studio
O Visual Studio permite o carregamento de VSPackages quando determinados UIContexts conhecidos são ativados. No entanto, esses contextos de interface do usuário não são refinados, o que deixa os autores de extensão sem escolha a não ser escolher um contexto de interface do usuário disponível que seja ativado antes do ponto em que eles realmente queriam que o VSPackage carregasse. Para obter uma lista de contextos de interface do usuário conhecidos, consulte KnownUIContexts.
Carregar pacotes pode afetar o desempenho e carregá-los mais cedo do que o necessário não é a melhor prática. O Visual Studio 2015 introduziu o conceito de Contextos de Interface do Usuário baseados em Regras, um mecanismo que permite aos autores de extensão definir as condições precisas sob as quais um Contexto de Interface do Usuário é ativado e os VSPackages associados são carregados.
Contexto da interface do usuário baseada em regras
Uma "Regra" consiste em um novo contexto de interface do usuário (um GUID) e uma expressão booleana que faz referência a um ou mais "Termos" combinados às operações lógicas "and", "or", "not". Os "Termos" são avaliados dinamicamente em tempo de execução e a expressão será reavaliada sempre que algum de seus termos mudar. Quando a expressão é avaliada como true, o contexto da interface do usuário associado é ativado. Caso contrário, o contexto da interface do usuário será desativado.
O contexto de interface do usuário baseada em regras pode ser usado de várias maneiras:
Especifique restrições de visibilidade para comandos e janelas de ferramentas. É possível ocultar as janelas de comandos/ferramentas até que a regra de contexto da interface do usuário seja atendida.
Como restrições de carregamento automático: carregar pacotes automaticamente somente quando a regra for atendida.
Como uma tarefa atrasada: atrase o carregamento até que um intervalo especificado tenha passado e a regra ainda seja atendida.
O mecanismo pode ser usado por qualquer extensão do Visual Studio.
Criar um contexto de interface do usuário baseada em regras
Suponha que você tenha uma extensão chamada TestPackage, que oferece um comando de menu que se aplica somente a arquivos com a extensão .config. Antes do VS2015, a melhor opção era carregar o TestPackage quando a UI Context SolutionExistsAndFullyLoadedContext era ativada. Carregar o TestPackage dessa maneira não é eficiente, já que a solução carregada pode nem mesmo conter um arquivo .config. Estas etapas mostram como o Contexto da Interface do Usuário baseado em regras pode ser usado para ativar um Contexto da Interface do Usuário somente quando um arquivo com a extensão .config for selecionado e carregar o TestPackage quando esse Contexto da Interface do Usuário for ativado.
Defina um novo GUID UIContext e adicione à classe VSPackage ProvideAutoLoadAttribute e ProvideUIContextRuleAttribute.
Por exemplo, vamos supor que um novo UIContext "UIContextGuid" deve ser adicionado. O GUID criado (é possível criar um GUID clicando em Ferramentas>Criar GUID) é "8B40D5E2-5626-42AE-99EF-3DD1EFF46E7B". Em seguida, adicione a seguinte declaração dentro da classe do pacote:
public const string UIContextGuid = "8B40D5E2-5626-42AE-99EF-3DD1EFF46E7B";
Para os atributos, adicione os seguintes valores: (os detalhes desses atributos serão explicados posteriormente)
[ProvideAutoLoad(TestPackage.UIContextGuid)] [ProvideUIContextRule(TestPackage.UIContextGuid, name: "Test auto load", expression: "DotConfig", termNames: new[] { "DotConfig" }, termValues: new[] { "HierSingleSelectionName:.config$" })]
Esses metadados definem o novo GUID UIContext (8B40D5E2-5626-42AE-99EF-3DD1EFF46E7B) e uma expressão referente a um único termo, "DotConfig". O termo "DotConfig" será avaliado como true sempre que a seleção atual na hierarquia ativa tiver um nome que corresponda ao padrão de expressão regular "\.config$" (termina com .config). O valor (Default) define um nome opcional para a regra útil para depuração.
Os valores do atributo são adicionados ao pkgdef gerado durante o tempo de compilação posterior.
No arquivo VSCT dos comandos do TestPackage, adicione o sinalizador "DynamicVisibility" aos comandos apropriados:
<CommandFlag>DynamicVisibility</CommandFlag>
Na seção VisibilityConstraints do VSCT, vincule os comandos apropriados ao novo GUID UIContext definido em #1:
<VisibilityConstraints> <VisibilityItem guid="guidTestPackageCmdSet" id="TestId" context="UIContextGuid"/> </VisibilityConstraints>
Na seção Símbolos, adicione a definição do UIContext:
<GuidSymbol name="UIContextGuid" value="{8B40D5E2-5626-42AE-99EF-3DD1EFF46E7B}" />
Agora, os comandos do menu de contexto dos arquivos *.config ficarão visíveis somente quando o item selecionado no gerenciador de soluções for um arquivo .config e o pacote não será carregado até que um desses comandos seja selecionado.
Em seguida, use um depurador para confirmar se o pacote será carregado somente quando você desejar. Para depurar o TestPackage:
Defina um ponto de interrupção no método Initialize.
Crie o TestPackage e inicie a depuração.
Crie ou abra um projeto.
Selecione qualquer arquivo com uma extensão diferente de .config. O ponto de interrupção não deve ser atingido.
Selecione o arquivo App.Config.
O TestPackage carrega e é interrompido no ponto de interrupção.
Adicionar mais regras para o contexto da interface do usuário
Como as regras de Contexto da Interface do Usuário são expressões booleanas, é possível adicionar regras mais restritas para um Contexto da Interface do Usuário. Por exemplo, no contexto da interface do usuário acima, é possível especificar que a regra se aplica somente quando uma solução com um projeto é carregada. Dessa forma, os comandos não aparecerão se você abrir um arquivo .config como um arquivo autônomo, não como parte de um projeto.
[ProvideAutoLoad(TestPackage.UIContextGuid)]
[ProvideUIContextRule(TestPackage.UIContextGuid,
name: "Test auto load",
expression: "(SingleProject | MultipleProjects) & DotConfig",
termNames: new[] { "SingleProject", "MultipleProjects","DotConfig" },
termValues: new[] { VSConstants.UICONTEXT.SolutionHasSingleProject_string , VSConstants.UICONTEXT.SolutionHasMultipleProjects_string , "HierSingleSelectionName:.config$" })]
Agora, a expressão faz referência a três termos. Os dois primeiros termos, "SingleProject" e "MultipleProjects", referem-se a outros contextos de interface do usuário bem conhecidos (por seus GUIDs). O terceiro termo, "DotConfig" é o contexto de interface do usuário baseada em regras definido anteriormente neste artigo.
Ativação atrasada
As regras podem ter um "Atraso" opcional. O tempo de atraso é especificado em milissegundos. Se presente, o atraso faz com que a ativação ou desativação do contexto da interface do usuário de uma regra seja atrasada por esse intervalo de tempo. Se a regra for alterada antes do intervalo de atraso, nada acontecerá. Esse mecanismo pode ser usado para "escalonar" as etapas de inicialização - especialmente a inicialização única sem depender de temporizadores ou registrar notificações ociosas.
Por exemplo, é possível especificar sua regra de carga de teste para ter um atraso de 100 milissegundos:
[ProvideAutoLoad(TestPackage.UIContextGuid)]
[ProvideUIContextRule(TestPackage.UIContextGuid,
name: "Test auto load",
expression: "DotConfig",
termNames: new[] { "DotConfig" },
termValues: new[] { "HierSingleSelectionName:.config$" },
delay: 100)]
Tipos de termos
Veja aqui os vários tipos de termos compatíveis:
Termo | Descrição |
---|---|
{nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn} | O GUID refere-se a um contexto de interface do usuário. O termo será true sempre que o contexto da interface do usuário estiver ativo, caso contrário, ele será false. |
HierSingleSelectionName:<pattern> | O termo será true sempre que a seleção na hierarquia ativa for um único item e o nome do item selecionado corresponder à expressão regular .NET oferecida por "padrão". |
UserSettingsStoreQuery:<query> | "query" representa um caminho completo para o armazenamento de configurações do usuário, que deve ser avaliado para um valor diferente de zero. A consulta é dividida em "coleção" e "propertyName" na última barra. |
ConfigSettingsStoreQuery:<query> | "query" representa um caminho completo para o armazenamento do conjunto de configurações, que deve ser avaliado para um valor diferente de zero. A consulta é dividida em "coleção" e "propertyName" na última barra. |
ActiveProjectFlavor:<projectTypeGuid> | O termo será true sempre que o projeto atualmente selecionado tiver uma variante (agregado) e essa variante corresponder ao GUID do tipo de projeto fornecido. |
ActiveEditorContentType:<contentType> | O termo será true quando o documento selecionado for um editor de texto com o tipo de conteúdo fornecido. Nota: quando o documento selecionado for renomeado, esse termo não será atualizado até que o arquivo seja fechado e reaberto. |
ActiveProjectCapability:<Expression> | O termo será true quando os recursos ativos do projeto corresponderem à expressão fornecida. Uma expressão pode ser algo como VB | CSharp. |
SolutionHasProjectCapability:<Expression> | Semelhante ao exemplo acima, mas o termo será true quando a solução tiver qualquer projeto carregado que corresponda à expressão. |
SolutionHasProjectFlavor:<projectTypeGuid> | O termo será true sempre que uma solução tiver um projeto com uma variante (agregado) e essa variante corresponder ao GUID do tipo de projeto fornecido. |
ProjectAddedItem:<pattern> | O termo será true quando um arquivo correspondente ao "padrão" for adicionado a um projeto na solução que foi aberta. |
ActiveProjectOutputType:<outputType> | O termo será true quando o tipo de saída para o projeto ativo corresponder exatamente. O outputType pode ser um inteiro ou um tipo __VSPROJOUTPUTTYPE. |
ActiveProjectBuildProperty:<buildProperty>=<regex> | O termo será true quando o projeto ativo tiver um uma propriedade de compilação especificada e o valor da propriedade corresponder ao filtro regex fornecido. Consulte Dados persistentes em arquivos de projeto do MSBuild para obter mais detalhes sobre as propriedades de compilação. |
SolutionHasProjectBuildProperty:<buildProperty>=<regex> | O termo será true quando a solução tiver um projeto carregado com a propriedade de compilação especificada e o valor da propriedade corresponde ao filtro regex fornecido. |
Compatibilidade com extensão de versão cruzada
Os contextos de interface do usuário baseada em regras é um novo recurso no Visual Studio 2015 e não tem portabilidade para versões anteriores. A não portabilidade para versões anteriores cria um problema com extensões/pacotes que visam várias versões do Visual Studio. Essas versões teriam que ser carregadas automaticamente no Visual Studio 2013 e em versões anteriores, mas podem se beneficiar de contextos de interface do usuário baseada em regras para evitar que sejam carregados automaticamente no Visual Studio 2015.
Para oferecer suporte a esses pacotes, as entradas AutoLoadPackages no Registro agora podem fornecer um sinalizador em seu campo de valor para indicar que a entrada deve ser ignorada no Visual Studio 2015 e em versões superiores. Isso pode ser feito adicionando uma opção de sinalizadores ao PackageAutoLoadFlags. Os VSPackages agora podem adicionar a opção SkipWhenUIContextRulesActive ao seu atributo ProvideAutoLoadAttribute para indicar que a entrada deve ser ignorada no Visual Studio 2015 e em versões superiores.
Regras de contexto da interface do usuário extensível
Às vezes, os pacotes não podem usar regras estáticas de contexto da interface do usuário. Por exemplo, suponha que você tenha um pacote com suporte à extensibilidade em que o estado do comando seja baseado em tipos de editor compatíveis com provedores MEF importados. O comando será habilitado se houver uma extensão que ofereça suporte ao tipo de edição atual. Nesses casos, o pacote em si não pode usar uma regra estática de contexto da interface do usuário, uma vez que os termos mudariam dependendo de quais extensões MEF estão disponíveis.
Para oferecer suporte a esses pacotes, os Contextos de Interface do Usuário baseada em regras oferecem suporte a uma expressão codificada "*" que indica que todos os termos abaixo serão unidos usando OR. Isso permite que o pacote mestre defina um contexto de interface do usuário baseada em regras conhecido e vincule seu estado de comando a esse contexto. Posteriormente, qualquer extensão MEF direcionada ao pacote mestre pode adicionar seus termos a editores compatíveis sem afetar outros termos ou a expressão mestre.
A documentação do construtor ProvideExtensibleUIContextRuleAttribute exibe a sintaxe para regras de contexto de interface do usuário extensíveis.