Gerenciar projetos universais do Windows
Os aplicativos universais do Windows são aplicativos destinados ao Windows 8.1 e ao Windows Phone 8.1, permitindo que os desenvolvedores usem código e outros ativos em ambas as plataformas. O código e os recursos compartilhados são mantidos em um projeto compartilhado, enquanto o código e os recursos específicos da plataforma são mantidos em projetos separados, um para o Windows e outro para o Windows Phone. Para obter mais informações sobre aplicativos universais do Windows, consulte Aplicativos universais do Windows. As extensões do Visual Studio que gerenciam projetos devem estar cientes de que os projetos de aplicativos universais do Windows têm uma estrutura diferente dos aplicativos de plataforma única. Este passo a passo mostra como navegar no projeto compartilhado e gerenciar os itens compartilhados.
Navegar no projeto compartilhado
Crie um projeto VSIX C# chamado TestUniversalProject. (Arquivo>Novo>Projeto e, em seguida, C#>Extensibility>Visual Studio Package). Adicione um modelo de item de projeto Comando Personalizado (no Gerenciador de Soluções, clique com o botão direito do mouse no nó do projeto e selecione Adicionar>Novo Item e vá para Extensibilidade). Nomeie o arquivo TestUniversalProject.
Adicione uma referência a Microsoft.VisualStudio.Shell.Interop.12.1.DesignTime.dll e Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.dll (na seção Extensões).
Abra TestUniversalProject.cs e adicione as seguintes
using
diretrizes:using EnvDTE; using EnvDTE80; using Microsoft.VisualStudio; using Microsoft.VisualStudio.PlatformUI; using Microsoft.Internal.VisualStudio.PlatformUI; using System.Collections.Generic; using System.IO; using System.Windows.Forms;
Na classe,
TestUniversalProject
adicione um campo privado apontando para a janela Saída .public sealed class TestUniversalProject { IVsOutputWindowPane output; . . . }
Defina a referência para o painel de saída dentro do construtor TestUniversalProject:
private TestUniversalProject(Package package) { if (package == null) { throw new ArgumentNullException("package"); } this.package = package; OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService; if (commandService != null) { CommandID menuCommandID = new CommandID(MenuGroup, CommandId); EventHandler eventHandler = this.ShowMessageBox; MenuCommand menuItem = new MenuCommand(eventHandler, menuCommandID); commandService.AddCommand(menuItem); } // get a reference to the Output window output = (IVsOutputWindowPane)ServiceProvider.GetService(typeof(SVsGeneralOutputWindowPane)); }
Remova o código existente do
ShowMessageBox
método:private void ShowMessageBox(object sender, EventArgs e) { }
Obtenha o objeto DTE, que usaremos para várias finalidades diferentes neste passo a passo. Além disso, certifique-se de que uma solução é carregada quando o botão de menu é clicado.
private void ShowMessageBox(object sender, EventArgs e) { var dte = (EnvDTE.DTE)this.ServiceProvider.GetService(typeof(EnvDTE.DTE)); if (dte.Solution != null) { . . . } else { MessageBox.Show("No solution is open"); return; } }
Encontre o projeto compartilhado. O projeto compartilhado é um contêiner puro; ele não constrói ou produz saídas. O método a seguir localiza o primeiro projeto compartilhado na solução procurando o objeto que tem o IVsHierarchy recurso de projeto compartilhado.
private IVsHierarchy FindSharedProject() { var sln = (IVsSolution)this.ServiceProvider.GetService(typeof(SVsSolution)); Guid empty = Guid.Empty; IEnumHierarchies enumHiers; //get all the projects in the solution ErrorHandler.ThrowOnFailure(sln.GetProjectEnum((uint)__VSENUMPROJFLAGS.EPF_LOADEDINSOLUTION, ref empty, out enumHiers)); foreach (IVsHierarchy hier in ComUtilities.EnumerableFrom(enumHiers)) { if (PackageUtilities.IsCapabilityMatch(hier, "SharedAssetsProject")) { return hier; } } return null; }
ShowMessageBox
No método, produza a legenda (o nome do projeto que aparece no Gerenciador de Soluções) do projeto compartilhado.private void ShowMessageBox(object sender, EventArgs e) { var dte = (DTE)this.ServiceProvider.GetService(typeof(DTE)); if (dte.Solution != null) { var sharedHier = this.FindSharedProject(); if (sharedHier != null) { string sharedCaption = HierarchyUtilities.GetHierarchyProperty<string>(sharedHier, (uint)VSConstants.VSITEMID.Root, (int)__VSHPROPID.VSHPROPID_Caption); output.OutputStringThreadSafe(string.Format("Found shared project: {0}\n", sharedCaption)); } else { MessageBox.Show("Solution has no shared project"); return; } } else { MessageBox.Show("No solution is open"); return; } }
Obtenha o projeto de plataforma ativa. Projetos de plataforma são os projetos que contêm código e recursos específicos da plataforma. O método a seguir usa o novo campo VSHPROPID_SharedItemContextHierarchy para obter o projeto de plataforma ativo.
private IVsHierarchy GetActiveProjectContext(IVsHierarchy hierarchy) { IVsHierarchy activeProjectContext; if (HierarchyUtilities.TryGetHierarchyProperty(hierarchy, (uint)VSConstants.VSITEMID.Root, (int)__VSHPROPID7.VSHPROPID_SharedItemContextHierarchy, out activeProjectContext)) { return activeProjectContext; } else { return null; } }
ShowMessageBox
No método, saída da legenda do projeto de plataforma ativo.private void ShowMessageBox(object sender, EventArgs e) { var dte = (DTE)this.ServiceProvider.GetService(typeof(DTE)); if (dte.Solution != null) { var sharedHier = this.FindSharedProject(); if (sharedHier != null) { string sharedCaption = HierarchyUtilities.GetHierarchyProperty<string>(sharedHier, (uint)VSConstants.VSITEMID.Root, (int)__VSHPROPID.VSHPROPID_Caption); output.OutputStringThreadSafe(string.Format("Shared project: {0}\n", sharedCaption)); var activePlatformHier = this.GetActiveProjectContext(sharedHier); if (activePlatformHier != null) { string activeCaption = HierarchyUtilities.GetHierarchyProperty<string>(activePlatformHier, (uint)VSConstants.VSITEMID.Root, (int)__VSHPROPID.VSHPROPID_Caption); output.OutputStringThreadSafe(string.Format("Active platform project: {0}\n", activeCaption)); } else { MessageBox.Show("Shared project has no active platform project"); } } else { MessageBox.Show("Solution has no shared project"); } } else { MessageBox.Show("No solution is open"); } }
Itere através dos projetos da plataforma. O método a seguir obtém todos os projetos de importação (plataforma) do projeto compartilhado.
private IEnumerable<IVsHierarchy> EnumImportingProjects(IVsHierarchy hierarchy) { IVsSharedAssetsProject sharedAssetsProject; if (HierarchyUtilities.TryGetHierarchyProperty(hierarchy, (uint)VSConstants.VSITEMID.Root, (int)__VSHPROPID7.VSHPROPID_SharedAssetsProject, out sharedAssetsProject) && sharedAssetsProject != null) { foreach (IVsHierarchy importingProject in sharedAssetsProject.EnumImportingProjects()) { yield return importingProject; } } }
Importante
Se o usuário tiver aberto um projeto de aplicativo universal do Windows C++ na instância experimental, o código acima lançará uma exceção. Esse é um problema conhecido. Para evitar a exceção, substitua o
foreach
bloco acima pelo seguinte:var importingProjects = sharedAssetsProject.EnumImportingProjects(); for (int i = 0; i < importingProjects.Count; ++i) { yield return importingProjects[i]; }
ShowMessageBox
No método, saída da legenda de cada projeto de plataforma. Insira o código a seguir após a linha que gera a legenda do projeto de plataforma ativo. Somente os projetos de plataforma que são carregados aparecem nesta lista.output.OutputStringThreadSafe("Platform projects:\n"); IEnumerable<IVsHierarchy> projects = this.EnumImportingProjects(sharedHier); bool isActiveProjectSet = false; foreach (IVsHierarchy platformHier in projects) { string platformCaption = HierarchyUtilities.GetHierarchyProperty<string>(platformHier, (uint)VSConstants.VSITEMID.Root, (int)__VSHPROPID.VSHPROPID_Caption); output.OutputStringThreadSafe(string.Format(" * {0}\n", platformCaption)); }
Altere o projeto de plataforma ativo. O método a seguir define o projeto ativo usando SetProperty.
private int SetActiveProjectContext(IVsHierarchy hierarchy, IVsHierarchy activeProjectContext) { return hierarchy.SetProperty((uint)VSConstants.VSITEMID.Root, (int)__VSHPROPID7.VSHPROPID_SharedItemContextHierarchy, activeProjectContext); }
No método, altere
ShowMessageBox
o projeto de plataforma ativa. Insira este código dentro doforeach
bloco.bool isActiveProjectSet = false; string platformCaption = null; foreach (IVsHierarchy platformHier in projects) { platformCaption = HierarchyUtilities.GetHierarchyProperty<string>(platformHier, (uint)VSConstants.VSITEMID.Root, (int)__VSHPROPID.VSHPROPID_Caption); output.OutputStringThreadSafe(string.Format(" * {0}\n", platformCaption)); // if this project is neither the shared project nor the current active platform project, // set it to be the active project if (!isActiveProjectSet && platformHier != activePlatformHier) { this.SetActiveProjectContext(sharedHier, platformHier); activePlatformHier = platformHier; isActiveProjectSet = true; } } output.OutputStringThreadSafe("set active project: " + platformCaption +'\n');
Agora experimente. Pressione F5 para iniciar a instância experimental. Crie um projeto de aplicativo de hub universal C# na instância experimental (na caixa de diálogo Novo Projeto, Visual C#>Windows Windows>8>Universal>Hub App). Depois que a solução for carregada, vá para o menu Ferramentas e clique em Invocar TestUniversalProject e, em seguida, verifique o texto no painel Saída. Você verá algo semelhante ao que se segue:
Found shared project: HubApp.Shared The active platform project: HubApp.Windows Platform projects: * HubApp.Windows * HubApp.WindowsPhone set active project: HubApp.WindowsPhone
Gerenciar os itens compartilhados no projeto de plataforma
Encontre os itens compartilhados no projeto da plataforma. Os itens no projeto compartilhado aparecem no projeto de plataforma como itens compartilhados. Você não pode vê-los no Gerenciador de Soluções, mas pode percorrer a hierarquia do projeto para localizá-los. O método a seguir percorre a hierarquia e coleta todos os itens compartilhados. Opcionalmente, ele emite a legenda de cada item,. Os itens compartilhados são identificados pela nova propriedade VSHPROPID_IsSharedItem.
private void InspectHierarchyItems(IVsHierarchy hier, uint itemid, int level, List<uint> itemIds, bool getSharedItems, bool printItems) { string caption = HierarchyUtilities.GetHierarchyProperty<string>(hier, itemid, (int)__VSHPROPID.VSHPROPID_Caption); if (printItems) output.OutputStringThreadSafe(string.Format("{0}{1}\n", new string('\t', level), caption)); // if getSharedItems is true, inspect only shared items; if it's false, inspect only unshared items bool isSharedItem; if (HierarchyUtilities.TryGetHierarchyProperty(hier, itemid, (int)__VSHPROPID7.VSHPROPID_IsSharedItem, out isSharedItem) && (isSharedItem == getSharedItems)) { itemIds.Add(itemid); } uint child; if (HierarchyUtilities.TryGetHierarchyProperty(hier, itemid, (int)__VSHPROPID.VSHPROPID_FirstChild, Unbox.AsUInt32, out child) && child != (uint)VSConstants.VSITEMID.Nil) { this.InspectHierarchyItems(hier, child, level + 1, itemIds, isSharedItem, printItems); while (HierarchyUtilities.TryGetHierarchyProperty(hier, child, (int)__VSHPROPID.VSHPROPID_NextSibling, Unbox.AsUInt32, out child) && child != (uint)VSConstants.VSITEMID.Nil) { this.InspectHierarchyItems(hier, child, level + 1, itemIds, isSharedItem, printItems); } } }
ShowMessageBox
No método, adicione o código a seguir para percorrer os itens de hierarquia do projeto de plataforma. Insira-o dentro doforeach
bloco.output.OutputStringThreadSafe("Walk the active platform project:\n"); var sharedItemIds = new List<uint>(); this.InspectHierarchyItems(activePlatformHier, (uint)VSConstants.VSITEMID.Root, 1, sharedItemIds, true, true);
Leia os itens compartilhados. Os itens compartilhados aparecem no projeto de plataforma como arquivos vinculados ocultos e você pode ler todas as propriedades como arquivos vinculados comuns. O código a seguir lê o caminho completo do primeiro item compartilhado.
var sharedItemId = sharedItemIds[0]; string fullPath; ErrorHandler.ThrowOnFailure(((IVsProject)activePlatformHier).GetMkDocument(sharedItemId, out fullPath)); output.OutputStringThreadSafe(string.Format("Shared item full path: {0}\n", fullPath));
Agora experimente. Pressione F5 para iniciar a instância experimental. Crie um projeto de aplicativo de hub universal C# na instância experimental (na caixa de diálogo Novo Projeto, Visual C#>Windows Windows>8>Universal>Hub App) vá para o menu Ferramentas e clique em Invocar TestUniversalProject e, em seguida, verifique o texto no painel Saída. Você verá algo semelhante ao que se segue:
Found shared project: HubApp.Shared The active platform project: HubApp.Windows Platform projects: * HubApp.Windows * HubApp.WindowsPhone set active project: HubApp.WindowsPhone Walk the active platform project: HubApp.WindowsPhone <HubApp.Shared> App.xaml App.xaml.cs Assets DarkGray.png LightGray.png MediumGray.png Common NavigationHelper.cs ObservableDictionary.cs RelayCommand.cs SuspensionManager.cs DataModel SampleData.json SampleDataSource.cs HubApp.Shared.projitems Strings en-US Resources.resw Assets HubBackground.theme-dark.png HubBackground.theme-light.png Logo.scale-240.png SmallLogo.scale-240.png SplashScreen.scale-240.png Square71x71Logo.scale-240.png StoreLogo.scale-240.png WideLogo.scale-240.png HubPage.xaml HubPage.xaml.cs ItemPage.xaml ItemPage.xaml.cs Package.appxmanifest Properties AssemblyInfo.cs References .NET for Windows Store apps HubApp.Shared Windows Phone 8.1 SectionPage.xaml SectionPage.xaml.cs
Detectar alterações em projetos de plataforma e projetos compartilhados
Você pode usar a hierarquia e os eventos de projeto para detectar alterações em projetos compartilhados, assim como para projetos de plataforma. No entanto, os itens de projeto no projeto compartilhado não são visíveis, o que significa que determinados eventos não são acionados quando os itens de projeto compartilhados são alterados.
Considere a sequência de eventos quando um arquivo em um projeto é renomeado:
O nome do arquivo é alterado no disco.
O arquivo de projeto é atualizado para incluir o novo nome do arquivo.
Os eventos de hierarquia (por exemplo, ) geralmente controlam as alterações exibidas na interface do usuário, IVsHierarchyEventscomo no Gerenciador de Soluções. Os eventos de hierarquia consideram uma operação de renomeação de arquivo como consistindo em uma exclusão de arquivo e, em seguida, uma adição de arquivo. No entanto, quando itens invisíveis são alterados, o sistema de eventos de hierarquia dispara um evento, mas não um OnItemDeleted OnItemAdded evento. Portanto, se você renomear um arquivo em um projeto de plataforma, obterá ambos OnItemDeleted e OnItemAdded, mas se você renomear um arquivo em um projeto compartilhado, obterá apenas OnItemDeleted.
Para controlar alterações em itens de projeto, você pode manipular eventos de item de projeto DTE (os encontrados em ProjectItemsEventsClass). No entanto, se você estiver manipulando um grande número de eventos, poderá obter melhor desempenho ao lidar com os eventos no IVsTrackProjectDocuments2. Neste passo a passo, mostramos apenas os eventos de hierarquia e os eventos DTE. Neste procedimento, você adiciona um ouvinte de eventos a um projeto compartilhado e a um projeto de plataforma. Em seguida, quando você renomeia um arquivo em um projeto compartilhado e outro arquivo em um projeto de plataforma, você pode ver os eventos que são disparados para cada operação de renomeação.
Neste procedimento, você adiciona um ouvinte de eventos a um projeto compartilhado e a um projeto de plataforma. Em seguida, quando você renomeia um arquivo em um projeto compartilhado e outro arquivo em um projeto de plataforma, você pode ver os eventos que são disparados para cada operação de renomeação.
Adicione um ouvinte de eventos. Adicione um novo arquivo de classe ao projeto e chame-o de HierarchyEventListener.cs.
Abra o arquivo HierarchyEventListener.cs e adicione o seguinte usando diretivas:
using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio; using System.IO;
Faça com que a classe implemente
HierarchyEventListener
IVsHierarchyEvents:class HierarchyEventListener : IVsHierarchyEvents { }
Implemente os membros do IVsHierarchyEvents, como no código abaixo.
class HierarchyEventListener : IVsHierarchyEvents { private IVsHierarchy hierarchy; IVsOutputWindowPane output; internal HierarchyEventListener(IVsHierarchy hierarchy, IVsOutputWindowPane outputWindow) { this.hierarchy = hierarchy; this.output = outputWindow; } int IVsHierarchyEvents.OnInvalidateIcon(IntPtr hIcon) { return VSConstants.S_OK; } int IVsHierarchyEvents.OnInvalidateItems(uint itemIDParent) { return VSConstants.S_OK; } int IVsHierarchyEvents.OnItemAdded(uint itemIDParent, uint itemIDSiblingPrev, uint itemIDAdded) { output.OutputStringThreadSafe("IVsHierarchyEvents.OnItemAdded: " + itemIDAdded + "\n"); return VSConstants.S_OK; } int IVsHierarchyEvents.OnItemDeleted(uint itemID) { output.OutputStringThreadSafe("IVsHierarchyEvents.OnItemDeleted: " + itemID + "\n"); return VSConstants.S_OK; } int IVsHierarchyEvents.OnItemsAppended(uint itemIDParent) { output.OutputStringThreadSafe("IVsHierarchyEvents.OnItemsAppended\n"); return VSConstants.S_OK; } int IVsHierarchyEvents.OnPropertyChanged(uint itemID, int propID, uint flags) { output.OutputStringThreadSafe("IVsHierarchyEvents.OnPropertyChanged: item ID " + itemID + "\n"); return VSConstants.S_OK; } }
Na mesma classe, adicione outro manipulador de eventos para o evento ItemRenamedDTE, que ocorre sempre que um item de projeto é renomeado.
public void OnItemRenamed(EnvDTE.ProjectItem projItem, string oldName) { output.OutputStringThreadSafe(string.Format("[Event] Renamed {0} to {1} in project {2}\n", oldName, Path.GetFileName(projItem.get_FileNames(1)), projItem.ContainingProject.Name)); }
Inscreva-se nos eventos de hierarquia. Você precisa se inscrever separadamente para cada projeto que você está acompanhando. Adicione o seguinte código em
ShowMessageBox
, um para o projeto compartilhado e o outro para um dos projetos de plataforma.// hook up the event listener for hierarchy events on the shared project HierarchyEventListener listener1 = new HierarchyEventListener(sharedHier, output); uint cookie1; sharedHier.AdviseHierarchyEvents(listener1, out cookie1); // hook up the event listener for hierarchy events on the active project HierarchyEventListener listener2 = new HierarchyEventListener(activePlatformHier, output); uint cookie2; activePlatformHier.AdviseHierarchyEvents(listener2, out cookie2);
Inscreva-se no evento ItemRenamedde item de projeto DTE . Adicione o seguinte código depois de conectar o segundo ouvinte.
// hook up DTE events for project items Events2 dteEvents = (Events2)dte.Events; dteEvents.ProjectItemsEvents.ItemRenamed += listener1.OnItemRenamed;
Modifique o item compartilhado. Não é possível modificar itens compartilhados em um projeto de plataforma; em vez disso, você deve modificá-los no projeto compartilhado que é o proprietário real desses itens. Você pode obter a ID do item correspondente no projeto compartilhado com IsDocumentInProjecto , dando-lhe o caminho completo do item compartilhado. Em seguida, você pode modificar o item compartilhado. A mudança é propagada para os projetos da plataforma.
Importante
Você deve descobrir se um item de projeto é ou não um item compartilhado antes de modificá-lo.
O método a seguir modifica o nome de um arquivo de item de projeto.
private void ModifyFileNameInProject(IVsHierarchy project, string path) { int found; uint projectItemID; VSDOCUMENTPRIORITY[] priority = new VSDOCUMENTPRIORITY[1]; if (ErrorHandler.Succeeded(((IVsProject)project).IsDocumentInProject(path, out found, priority, out projectItemID)) && found != 0) { var name = DateTime.Now.Ticks.ToString() + Path.GetExtension(path); project.SetProperty(projectItemID, (int)__VSHPROPID.VSHPROPID_EditLabel, name); output.OutputStringThreadSafe(string.Format("Renamed {0} to {1}\n", path,name)); } }
Chame esse método depois de todos os outros códigos para
ShowMessageBox
modificar o nome do arquivo o item no projeto compartilhado. Insira isso após o código que obtém o caminho completo do item no projeto compartilhado.// change the file name of an item in a shared project this.InspectHierarchyItems(activePlatformHier, (uint)VSConstants.VSITEMID.Root, 1, sharedItemIds, true, true); ErrorHandler.ThrowOnFailure(((IVsProject)activePlatformHier).GetMkDocument(sharedItemId, out fullPath)); output.OutputStringThreadSafe(string.Format("Shared project item ID = {0}, full path = {1}\n", sharedItemId, fullPath)); this.ModifyFileNameInProject(sharedHier, fullPath);
Compile e execute o projeto. Crie um aplicativo de hub universal C# na instância experimental, vá para o menu Ferramentas e clique em Invocar TestUniversalProject e verifique o texto no painel de saída geral. O nome do primeiro item no projeto compartilhado (esperamos que seja o arquivo App.xaml) deve ser alterado e você deve ver que o ItemRenamed evento foi acionado. Nesse caso, como renomear App.xaml faz com que App.xaml.cs também seja renomeado, você deve ver quatro eventos (dois para cada projeto de plataforma). (Os eventos DTE não controlam os itens no projeto compartilhado.) Você deve ver dois OnItemDeleted eventos (um para cada um dos projetos da plataforma), mas nenhum OnItemAdded evento.
Agora tente renomear um arquivo em um projeto de plataforma e você pode ver a diferença nos eventos que são disparados. Adicione o seguinte código após
ShowMessageBox
a chamada paraModifyFileName
.// change the file name of an item in a platform project var unsharedItemIds = new List<uint>(); this.InspectHierarchyItems(activePlatformHier, (uint)VSConstants.VSITEMID.Root, 1, unsharedItemIds, false, false); var unsharedItemId = unsharedItemIds[0]; string unsharedPath; ErrorHandler.ThrowOnFailure(((IVsProject)activePlatformHier).GetMkDocument(unsharedItemId, out unsharedPath)); output.OutputStringThreadSafe(string.Format("Platform project item ID = {0}, full path = {1}\n", unsharedItemId, unsharedPath)); this.ModifyFileNameInProject(activePlatformHier, unsharedPath);
Compile e execute o projeto. Crie um projeto universal C# na instância experimental, vá para o menu Ferramentas e clique em Invocar TestUniversalProject e verifique o texto no painel de saída geral. Depois que o arquivo no projeto de plataforma for renomeado, você verá um evento e um OnItemAdded OnItemDeleted evento. Como a alteração do arquivo fez com que nenhum outro arquivo fosse alterado, e como as alterações nos itens em um projeto de plataforma não são propagadas em nenhum lugar, há apenas um em cada um desses eventos.