Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Универсальные приложения Windows — это приложения, предназначенные для Windows 8.1 и Windows Phone 8.1, что позволяет разработчикам использовать код и другие ресурсы на обеих платформах. Общий код и ресурсы хранятся в общем проекте, а код и ресурсы для платформы хранятся в отдельных проектах, один для Windows и другой для Windows Phone. Дополнительные сведения о универсальных приложениях Windows см. в статье "Универсальные приложения Windows". Расширения Visual Studio, управляющие проектами, должны учитывать, что проекты универсальных приложений Windows имеют структуру, которая отличается от одноплатформенных приложений. В этом пошаговом руководстве показано, как перемещаться по общему проекту и управлять общими элементами.
Навигация по общему проекту
Создайте проект VSIX C# с именем TestUniversalProject. (Файл>Создать>Проект и затем C#>Расширяемость>Пакет Visual Studio). Добавьте шаблон элемента пользовательского командного проекта (в обозревателе решений щелкните правой кнопкой мыши узел проекта и выберите "Добавить>новый элемент", а затем перейдите к расширяемости). Назовите файл TestUniversalProject.
Добавьте ссылку на Microsoft.VisualStudio.Shell.Interop.12.1.DesignTime.dll и Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.dll (в разделе "Расширения ").
Откройте TestUniversalProject.cs и добавьте следующие
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поле, указывающее на окно вывода .public sealed class TestUniversalProject { IVsOutputWindowPane output; . . . }Задайте ссылку на область вывода в конструкторе 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)); }Удалите существующий
ShowMessageBoxкод из метода:private void ShowMessageBox(object sender, EventArgs e) { }Получите объект DTE, который мы будем использовать для нескольких различных целей в этом пошаговом руководстве. Кроме того, убедитесь, что решение загружается при нажатии кнопки меню.
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; } }Найдите общий проект. Общий проект — это лишь контейнер; он не компилируется и не производит выходные данные. Следующий метод находит первый общий проект в решении, ищет IVsHierarchy объект, имеющий общую возможность проекта.
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выводит заголовок (имя проекта, отображаемое в обозревателе решений) общего проекта.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; } }Получите активный проект платформы. Проекты платформы — это проекты, содержащие код и ресурсы для конкретной платформы. Следующий метод использует новое поле VSHPROPID_SharedItemContextHierarchy для получения активного проекта платформы.
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выведите заголовок активного проекта платформы.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"); } }Выполните итерацию по проектам платформы. Следующий метод получает все импортируемые платформенные проекты из совместного проекта.
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; } } }Это важно
Если пользователь открыл проект универсального приложения Windows C++ в экспериментальном экземпляре, приведенный выше код вызывает исключение. Это известная проблема. Чтобы избежать исключения, замените приведенный
foreachвыше блок следующим:var importingProjects = sharedAssetsProject.EnumImportingProjects(); for (int i = 0; i < importingProjects.Count; ++i) { yield return importingProjects[i]; }В методе
ShowMessageBoxвыводите подпись каждого проекта платформы. Вставьте следующий код после строки, которая выводит заголовок активного проекта платформы. В этом списке отображаются только загруженные проекты платформы.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)); }Изменение активного проекта платформы. Следующий метод задает активный проект с помощью SetProperty.
private int SetActiveProjectContext(IVsHierarchy hierarchy, IVsHierarchy activeProjectContext) { return hierarchy.SetProperty((uint)VSConstants.VSITEMID.Root, (int)__VSHPROPID7.VSHPROPID_SharedItemContextHierarchy, activeProjectContext); }Измените активный проект платформы в методе
ShowMessageBox. Вставьте этот код вforeachблок.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');Теперь попробуйте его. Нажмите клавишу F5, чтобы запустить экспериментальный экземпляр. Создайте проект приложения универсального Центра C# в экспериментальном экземпляре (в диалоговом окне "Новый проект", Visual C#>Windows>Windows 8>Универсальное>приложение Центра). После загрузки решения перейдите в меню "Сервис" и нажмите кнопку "Вызвать TestUniversalProject", а затем проверьте текст на панели вывода . Вы должны увидеть примерно следующее:
Found shared project: HubApp.Shared The active platform project: HubApp.Windows Platform projects: * HubApp.Windows * HubApp.WindowsPhone set active project: HubApp.WindowsPhone
Управление общими элементами в проекте платформы
Найдите общие элементы в проекте платформы. Элементы в общем проекте отображаются в проекте платформы в качестве общих элементов. Их нельзя увидеть в обозревателе решений, но их можно найти в иерархии проектов. Следующий метод проходит по иерархии и собирает все общие элементы. При необходимости он выводит заголовок каждого элемента. Общие элементы определяются новым свойством 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добавьте следующий код, чтобы пройти элементы иерархии проектов платформы. Вставьте его в блокforeach.output.OutputStringThreadSafe("Walk the active platform project:\n"); var sharedItemIds = new List<uint>(); this.InspectHierarchyItems(activePlatformHier, (uint)VSConstants.VSITEMID.Root, 1, sharedItemIds, true, true);Прочтите совместно используемые элементы. Общие элементы отображаются в проекте платформы как скрытые связанные файлы, и вы можете читать все свойства как обычные связанные файлы. Следующий код считывает полный путь первого общего элемента.
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));Теперь попробуйте его. Нажмите клавишу F5 , чтобы запустить экспериментальный экземпляр. Создайте проект универсального приложения-шаблона C# в экспериментальном экземпляре (в диалоговом окне «Новый проект», Visual C#>Windows>8>универсальное>приложение Hub), выберите в меню «Сервис» пункт «Вызвать TestUniversalProject», а затем проверьте текст на панели Output. Вы должны увидеть примерно следующее:
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
Обнаружение изменений в проектах платформы и в общих проектах
Вы можете использовать события иерархии и проекта для обнаружения изменений в общих проектах, как и для проектов платформы. Однако элементы проекта в общем проекте не видны, что означает, что некоторые события не запускаются при изменении общих элементов проекта.
Рассмотрим последовательность событий при переименовании файла в проекте:
Имя файла изменяется на диске.
Файл проекта обновляется, чтобы включить новое имя файла.
События иерархии (например, IVsHierarchyEvents) обычно отслеживают изменения, отображаемые в пользовательском интерфейсе, как в обозревателе решений. В рамках событий иерархии операция переименования файла рассматривается как удаление файла с последующим добавлением нового файла. Однако при изменении невидимых элементов система событий иерархии запускает OnItemDeleted событие, но не OnItemAdded событие. Таким образом, если вы переименовываете файл в проекте платформы, вы получите оба OnItemDeleted и OnItemAdded, но при переименовании файла в общем проекте вы получите только OnItemDeleted.
Чтобы отслеживать изменения в элементах проекта, можно обрабатывать события элементов проекта DTE (найденные в ProjectItemsEventsClass). Однако, если вы обрабатываете большое количество событий, можно повысить производительность, обрабатывая события в IVsTrackProjectDocuments2. В этом пошаговом руководстве показаны только события иерархии и события DTE. В этой процедуре вы добавите прослушиватель событий в общий проект и проект платформы. Затем при переименовании одного файла в общем проекте и другом файле в проекте платформы можно увидеть события, которые запускаются для каждой операции переименования.
В этой процедуре вы добавите прослушиватель событий в общий проект и проект платформы. Затем при переименовании одного файла в общем проекте и другом файле в проекте платформы можно увидеть события, которые запускаются для каждой операции переименования.
Добавьте прослушиватель событий. Добавьте новый файл класса в проект и вызовите его HierarchyEventListener.cs.
Откройте файл HierarchyEventListener.cs и добавьте следующие директивы using:
using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio; using System.IO;Сделайте так, чтобы класс
HierarchyEventListenerреализовал IVsHierarchyEvents:class HierarchyEventListener : IVsHierarchyEvents { }Реализуйте элементы IVsHierarchyEvents, как в приведенном ниже коде.
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; } }В том же классе добавьте другой обработчик событий для события ItemRenamedDTE, который возникает при переименовании элемента проекта.
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)); }Зарегистрируйтесь на иерархические мероприятия. Необходимо зарегистрироваться отдельно для каждого проекта, который вы отслеживаете. Добавьте следующий код в
ShowMessageBox, один для общего проекта и другой для одного из проектов платформы.// 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);Зарегистрируйтесь на событие элемента проекта DTE ItemRenamed. Добавьте следующий код после подключения второго прослушивателя.
// hook up DTE events for project items Events2 dteEvents = (Events2)dte.Events; dteEvents.ProjectItemsEvents.ItemRenamed += listener1.OnItemRenamed;Измените общий элемент. Нельзя изменять общие элементы в проекте платформы; Вместо этого необходимо изменить их в общем проекте, который является фактическим владельцем этих элементов. Вы можете получить соответствующий идентификатор элемента в общем проекте IsDocumentInProject, предоставив ему полный путь к общему элементу. Затем можно изменить общий элемент. Изменение распространяется на проекты платформы.
Это важно
Перед изменением элемента проекта необходимо выяснить, является ли элемент проекта общим.
Следующий метод изменяет имя файла элемента проекта.
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)); } }Вызовите этот метод после всего остального кода в
ShowMessageBox, чтобы изменить имя файла элемента в общем проекте. Вставьте его после кода, который получает полный путь к элементу в общем проекте.// 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);Соберите проект и запустите его. Создайте универсальное приложение C# в экспериментальном экземпляре, перейдите в меню "Сервис " и нажмите кнопку "Вызвать TestUniversalProject" и проверьте текст в общей области выходных данных. Имя первого элемента в общем проекте (мы ожидаем, что это файл App.xaml ) должно быть изменено, и вы увидите, что ItemRenamed событие сработало. В этом случае, так как переименование App.xaml приводит к тому, что будет переименован и App.xaml.cs, вы увидите четыре события (два для каждого проекта платформы). (События DTE не отслеживают элементы в общем проекте.) Вы должны увидеть два OnItemDeleted события (по одному для каждого из платформенных проектов), но ни одного OnItemAdded события.
Теперь попробуйте переименовать файл в проекте платформы, и вы можете увидеть разницу в событиях, которые будут запущены. Добавьте следующий код в
ShowMessageBoxпосле вызоваModifyFileName.// 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);Соберите проект и запустите его. Создайте универсальный проект C# в экспериментальном экземпляре, перейдите в меню "Сервис " и нажмите кнопку "Вызвать TestUniversalProject" и проверьте текст в общей области вывода. После переименования файла в проекте платформы, вы увидите и событие OnItemAdded, и событие OnItemDeleted. Так как изменение файла не привело к изменению других файлов, и поскольку изменения элементов в проекте платформы не распространяются нигде, существует только один из этих событий.