Gestire i progetti di Windows universale
Le app di Windows universali sono app destinate sia a Windows 8.1 che a Windows Telefono 8.1, consentendo agli sviluppatori di usare codice e altri asset in entrambe le piattaforme. Il codice condiviso e le risorse vengono mantenuti in un progetto condiviso, mentre il codice e le risorse specifiche della piattaforma vengono mantenuti in progetti separati, uno per Windows e l'altro per Windows Telefono. Per altre informazioni sulle app di Windows universali, vedi App di Windows universali. Le estensioni di Visual Studio che gestiscono i progetti devono essere consapevoli che i progetti di app di Windows universali hanno una struttura diversa dalle app a piattaforma singola. Questa procedura dettagliata illustra come esplorare il progetto condiviso e gestire gli elementi condivisi.
Esplorare il progetto condiviso
Creare un progetto VSIX C# denominato TestUniversalProject. (File>Nuovo>progetto e quindi pacchetto C#>Extensibility>di Visual Studio). Aggiungere un modello di elemento di progetto Comando personalizzato (nel Esplora soluzioni fare clic con il pulsante destro del mouse sul nodo del progetto e scegliere Aggiungi>nuovo elemento, quindi passare a Estendibilità). Denominare il file TestUniversalProject.
Aggiungere un riferimento a Microsoft.VisualStudio.Shell.Interop.12.1.DesignTime.dll e Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.dll (nella sezione Extensions).
Aprire TestUniversalProject.cs e aggiungere le direttive seguenti
using
: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;
TestUniversalProject
Nella classe aggiungere un campo privato che punta alla finestra Output.public sealed class TestUniversalProject { IVsOutputWindowPane output; . . . }
Impostare il riferimento al riquadro di output all'interno del costruttore 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)); }
Rimuovere il codice esistente dal
ShowMessageBox
metodo :private void ShowMessageBox(object sender, EventArgs e) { }
Ottenere l'oggetto DTE, che verrà usato per diversi scopi in questa procedura dettagliata. Assicurarsi inoltre che una soluzione venga caricata quando si fa clic sul pulsante di menu.
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; } }
Trovare il progetto condiviso. Il progetto condiviso è un contenitore puro; non compila o produce output. Il metodo seguente trova il primo progetto condiviso nella soluzione cercando l'oggetto IVsHierarchy con la funzionalità del progetto condiviso.
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
Nel metodo restituire il didascalia (il nome del progetto visualizzato nella Esplora soluzioni) del progetto condiviso.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; } }
Ottenere il progetto della piattaforma attiva. I progetti della piattaforma sono i progetti che contengono risorse e codice specifici della piattaforma. Il metodo seguente usa il nuovo campo VSHPROPID_SharedItemContextHierarchy per ottenere il progetto della piattaforma attiva.
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
Nel metodo restituire il didascalia del progetto di piattaforma attivo.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"); } }
Scorrere i progetti della piattaforma. Il metodo seguente ottiene tutti i progetti di importazione (piattaforma) dal progetto condiviso.
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 l'utente ha aperto un progetto di app di Windows universale C++ nell'istanza sperimentale, il codice precedente genera un'eccezione. Questo è un problema noto Per evitare l'eccezione, sostituire il
foreach
blocco precedente con quanto segue:var importingProjects = sharedAssetsProject.EnumImportingProjects(); for (int i = 0; i < importingProjects.Count; ++i) { yield return importingProjects[i]; }
ShowMessageBox
Nel metodo restituire il didascalia di ogni progetto di piattaforma. Inserire il codice seguente dopo la riga che restituisce il didascalia del progetto di piattaforma attivo. In questo elenco vengono visualizzati solo i progetti di piattaforma caricati.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)); }
Modificare il progetto della piattaforma attiva. Il metodo seguente imposta il progetto attivo usando SetProperty.
private int SetActiveProjectContext(IVsHierarchy hierarchy, IVsHierarchy activeProjectContext) { return hierarchy.SetProperty((uint)VSConstants.VSITEMID.Root, (int)__VSHPROPID7.VSHPROPID_SharedItemContextHierarchy, activeProjectContext); }
ShowMessageBox
Nel metodo modificare il progetto di piattaforma attivo. Inserire questo codice all'interno delforeach
blocco.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');
Ora provalo. Premere F5 per avviare l'istanza sperimentale. Creare un progetto di app hub universale C# nell'istanza sperimentale (nella finestra di dialogo Nuovo progetto, Visual C#>Windows Windows>8>Universal>Hub App). Dopo aver caricato la soluzione, passare al menu Strumenti e fare clic su Richiama TestUniversalProject e quindi controllare il testo nel riquadro Output. Verrà visualizzata una schermata simile alla seguente:
Found shared project: HubApp.Shared The active platform project: HubApp.Windows Platform projects: * HubApp.Windows * HubApp.WindowsPhone set active project: HubApp.WindowsPhone
Gestire gli elementi condivisi nel progetto della piattaforma
Trovare gli elementi condivisi nel progetto della piattaforma. Gli elementi del progetto condiviso vengono visualizzati nel progetto di piattaforma come elementi condivisi. Non è possibile visualizzarli nella Esplora soluzioni, ma è possibile esaminare la gerarchia del progetto per trovarle. Il metodo seguente illustra la gerarchia e raccoglie tutti gli elementi condivisi. Restituisce facoltativamente il didascalia di ogni elemento. Gli elementi condivisi sono identificati dalla nuova proprietà 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
Nel metodo aggiungere il codice seguente per esaminare gli elementi della gerarchia del progetto della piattaforma. Inserirlo all'interno delforeach
blocco.output.OutputStringThreadSafe("Walk the active platform project:\n"); var sharedItemIds = new List<uint>(); this.InspectHierarchyItems(activePlatformHier, (uint)VSConstants.VSITEMID.Root, 1, sharedItemIds, true, true);
Leggere gli elementi condivisi. Gli elementi condivisi vengono visualizzati nel progetto della piattaforma come file collegati nascosti ed è possibile leggere tutte le proprietà come file collegati ordinari. Il codice seguente legge il percorso completo del primo elemento condiviso.
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));
Ora provalo. Premere F5 per avviare l'istanza sperimentale. Creare un progetto di app hub universale C# nell'istanza sperimentale (nella finestra di dialogo Nuovo progetto, Visual C#>Windows Windows>8>Universal>Hub App) passare al menu Strumenti e fare clic su Richiama TestUniversalProject, quindi selezionare il testo nel riquadro Output. Verrà visualizzata una schermata simile alla seguente:
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
Rilevare le modifiche nei progetti della piattaforma e nei progetti condivisi
È possibile usare eventi di gerarchia ed eventi di progetto per rilevare le modifiche nei progetti condivisi, proprio come per i progetti della piattaforma. Tuttavia, gli elementi del progetto nel progetto condiviso non sono visibili, il che significa che determinati eventi non vengono attivati quando vengono modificati gli elementi del progetto condiviso.
Si consideri la sequenza di eventi quando un file in un progetto viene rinominato:
Il nome del file viene modificato sul disco.
Il file di progetto viene aggiornato per includere il nuovo nome del file.
Gli eventi della gerarchia ( ad esempio , IVsHierarchyEvents) tengono in genere traccia delle modifiche visualizzate nell'interfaccia utente, come nel Esplora soluzioni. Gli eventi della gerarchia considerano un'operazione di ridenominazione di file in modo che sia costituita da un'eliminazione di file e quindi da un'aggiunta di file. Tuttavia, quando vengono modificati elementi invisibili, il sistema di eventi della gerarchia genera un OnItemDeleted evento ma non un OnItemAdded evento. Pertanto, se si rinomina un file in un progetto di piattaforma, si ottengono entrambi OnItemDeleted e OnItemAdded, ma se si rinomina un file in un progetto condiviso, si ottiene solo OnItemDeleted.
Per tenere traccia delle modifiche apportate agli elementi del progetto, è possibile gestire gli eventi degli elementi del progetto DTE (quelli disponibili in ProjectItemsEventsClass). Tuttavia, se si gestiscono un numero elevato di eventi, è possibile ottenere prestazioni migliori per la gestione degli eventi in IVsTrackProjectDocuments2. In questa procedura dettagliata vengono illustrati solo gli eventi della gerarchia e gli eventi DTE. In questa procedura si aggiunge un listener di eventi a un progetto condiviso e a un progetto di piattaforma. Quindi, quando si rinomina un file in un progetto condiviso e un altro file in un progetto di piattaforma, è possibile visualizzare gli eventi generati per ogni operazione di ridenominazione.
In questa procedura si aggiunge un listener di eventi a un progetto condiviso e a un progetto di piattaforma. Quindi, quando si rinomina un file in un progetto condiviso e un altro file in un progetto di piattaforma, è possibile visualizzare gli eventi generati per ogni operazione di ridenominazione.
Aggiungere un listener di eventi. Aggiungere un nuovo file di classe al progetto e chiamarlo HierarchyEventListener.cs.
Aprire il file HierarchyEventListener.cs e aggiungere le direttive using seguenti:
using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio; using System.IO;
Chiedere alla
HierarchyEventListener
classe di implementare IVsHierarchyEvents:class HierarchyEventListener : IVsHierarchyEvents { }
Implementare i membri di IVsHierarchyEvents, come nel codice seguente.
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; } }
Nella stessa classe aggiungere un altro gestore eventi per l'evento ItemRenamedDTE , che si verifica ogni volta che viene rinominato un elemento di progetto.
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)); }
Iscriversi agli eventi della gerarchia. È necessario iscriversi separatamente per ogni progetto monitorato. Aggiungere il codice seguente in
ShowMessageBox
, uno per il progetto condiviso e l'altro per uno dei progetti di piattaforma.// 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);
Iscriversi all'evento dell'elemento ItemRenameddel progetto DTE . Aggiungere il codice seguente dopo aver collegato il secondo listener.
// hook up DTE events for project items Events2 dteEvents = (Events2)dte.Events; dteEvents.ProjectItemsEvents.ItemRenamed += listener1.OnItemRenamed;
Modificare l'elemento condiviso. Non è possibile modificare gli elementi condivisi in un progetto di piattaforma; È invece necessario modificarli nel progetto condiviso che è il proprietario effettivo di questi elementi. È possibile ottenere l'ID elemento corrispondente nel progetto condiviso con IsDocumentInProject, assegnandogli il percorso completo dell'elemento condiviso. È quindi possibile modificare l'elemento condiviso. La modifica viene propagata ai progetti della piattaforma.
Importante
È necessario determinare se un elemento di progetto è un elemento condiviso prima di modificarlo.
Il metodo seguente modifica il nome di un file di elemento del progetto.
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)); } }
Chiamare questo metodo dopo tutto l'altro codice in
ShowMessageBox
per modificare il nome file dell'elemento nel progetto condiviso. Inserire questo elemento dopo il codice che ottiene il percorso completo dell'elemento nel progetto condiviso.// 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);
Compilare ed eseguire il progetto. Creare un'app hub universale C# nell'istanza sperimentale, passare al menu Strumenti e fare clic su Invoke TestUniversalProject e controllare il testo nel riquadro di output generale. Il nome del primo elemento nel progetto condiviso (si prevede che sia il file App.xaml ) deve essere modificato e si noterà che l'evento ItemRenamed è stato generato. In questo caso, poiché la ridenominazione di App.xaml causa anche la ridenominazione di App.xaml.cs, dovrebbero essere visualizzati quattro eventi (due per ogni progetto di piattaforma). Gli eventi DTE non tengono traccia degli elementi nel progetto condiviso. Dovrebbero essere visualizzati due OnItemDeleted eventi (uno per ogni progetto di piattaforma), ma nessun OnItemAdded evento.
Provare ora a rinominare un file in un progetto di piattaforma ed è possibile visualizzare la differenza negli eventi generati. Aggiungere il codice seguente in
ShowMessageBox
dopo la chiamata aModifyFileName
.// 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);
Compilare ed eseguire il progetto. Creare un progetto universale C# nell'istanza sperimentale, passare al menu Strumenti e fare clic su Richiama TestUniversalProject e controllare il testo nel riquadro di output generale. Dopo aver rinominato il file nel progetto della piattaforma, verrà visualizzato sia un OnItemAdded evento che un OnItemDeleted evento. Poiché la modifica del file non ha causato la modifica di altri file e poiché le modifiche apportate agli elementi di un progetto di piattaforma non vengono propagate da nessuna parte, è presente solo uno di questi eventi.