Este artículo proviene de un motor de traducción automática.
Team System
Creación de una extensión de Visual Studio Team Explorer
Brian a. Randell y Marcel de Vries
Descargar el ejemplo de código
La interfaz de usuario principal que se utilizar para interactuar con Team Foundation Server (TFS) es el cliente de Team Explorer. Team Explorer proporciona una forma de acceso y ver información en uno o más servidores TFS. De forma predeterminada, Team Explorer proporciona acceso a las características TFS como funcionen elementos, bibliotecas de documentos de SharePoint, informes, generaciones y control de código fuente.
Los equipos de desarrollo de Marcel utilizan un número de soluciones de Visual Studio independientes, cada uno de los que consta de cinco y diez proyectos. En cada una de estas soluciones, hay un único punto de implementación, mantenimiento y diseño. A veces hay dependencias entre-solución. El equipo administra esas dependencias a través de una carpeta especial dentro el repositorio de control de versiones TFS. Cada solución escribe su salida en una ubicación conocida y comprueba el equipo en el resultado después de cada generación. Una solución que depende el resultado de otra solución puede hacer referencia al ensamblado cuando se recuperan de TFS.
Desgraciadamente, equipo de Marcel que enfrenta un problema: Visual Studio almacena las referencias de archivo utilizando una ruta de acceso relativa a la solución actual, pero desean que los desarrolladores la flexibilidad de utilizar sus propias estructuras de directorios y las asignaciones del área de trabajo. Esta flexibilidad deseable produce frustración cuando Visual Studio no puede crear soluciones porque no puede encontrar archivos a los que se hace referencia.
Una forma de resolver este problema es asignar la ubicación que contiene las referencias binarias como una unidad sustituida mediante el comando subst.exe. Al hacer referencia a los ensamblados en la unidad sustituida, Visual Studio es capaz de encontrar los archivos en una ubicación coherente. Los desarrolladores pueden poner los archivos en las ubicaciones que prefieran y, a continuación, utilice subst.exe asignar sus ubicaciones a la asignación estándar. Puesto que los archivos residen en una unidad diferente, Visual Studio almacena una ruta de acceso completa en lugar de una ruta de acceso relativa. Una ventaja adicional es que un desarrollador puede probar una versión diferente cambiando simplemente la asignación.
Aunque esta técnica funciona, incluso mejor sería una extensión de Team Explorer que permite a un desarrollador definir una asignación entre la ubicación de control de versión y una unidad asignada. Equipo de Marcel implementó esta funcionalidad en una extensión de Team Explorer denominada sustitutas Explorer. Puede ver el menú para la extensión de explorador sustitutas en consulte de figura 1.
Figura 1 del Explorer sustitutas cargado en la ventana de Team Explorer
Introducción
Para generar su propio complemento de Team Explorer, podrá necesita Visual Studio 2008 Standard Edition o superior, equipo Explorer y el SDK de Visual Studio 2008, que puede obtener desde el Centro de desarrolladores de extensibilidad de Visual Studio (msdn.microsoft.com/vsx de ).
Antes de crear un Team Explorer complemento, deberá crear un VSPackage. El paquete proporciona una clase que implementa la interfaz de Microsoft.TeamFoundation.Common.ITeamExplorerPlugin definida en Microsoft.VisualStudio.TeamFoundation.Client.dll.
En el lenguaje de Visual Studio, el complemento se convierte en parte de la jerarquía. Una característica que diferencia de la jerarquía de Team Explorer de otras implementaciones de jerarquía en Visual Studio es que admite la carga asincrónica de la jerarquía de Team Explorer, esencialmente porque, cargando la jerarquía para cosas como elementos de trabajo y documentos a menudo requiere consultas remotas a TFS. De lo contrario, estas llamadas bloquearía Visual Studio durante esas consultas, lo que una experiencia de usuario pobre. La interfaz de ITeamExplorerPlugin implementada por su VSPackage proporciona el mecanismo que Team Explorer se utiliza para cargar el contenido de cada nodo de forma asincrónica.
Crear un VSPackage seleccionando archivo | nuevo | proyecto. En el cuadro de diálogo nuevo proyecto, expanda el nodo otros tipos de proyectos y seleccione el nodo de extensibilidad. Sobre en el panel Plantillas, seleccione la plantilla Visual Studio Integration Package.
Después de rellenar el cuadro de diálogo nuevo proyecto y haga clic en Aceptar, Visual Studio inicia al Asistente de paquete de integración de Visual Studio. En primer lugar, elija su idioma preferido, C++, C# o Visual Basic (C# se utiliza en este artículo). Cuando elija Visual Basic o C#, debe seleccionar la opción para generar un archivo de clave de nombre seguro nuevo o especificar un archivo existente. En la siguiente página, rellene la información para su empresa y algunos detalles acerca del paquete, incluidos el Package Name, icono y así sucesivamente. Gran parte de esta información se muestra en la Ayuda | acerca de cuadro de diálogo en Visual Studio.
En la siguiente página, seleccione cómo desea que Visual Studio para exponer su paquete. En este ejemplo concreto, desea tener sólo un MenuCommand. El complemento utilizará para controlar los menús contextuales. En la siguiente página, deberá proporcionar un nombre de comando y un comando. Simplemente acepte los valores predeterminados desde que se va a cambiar más adelante. En la siguiente página, puede agregar un soporte técnico para los proyectos de prueba. Nos no ser que cubren a ellos en este artículo, así que no dude en quite la selección y, a continuación, finalice al asistente. El asistente generará las clases básicas y recursos que necesita para implementar el VSPackage.
A continuación, deberá agregar las referencias siguientes, que proporcionan acceso a las clases de base de Team Explorer utiliza para crear complemento Team Explorer:
Microsoft.VisualStudio.TeamFoundation.Client.(9.0.0)
Microsoft.VisualStudio.TeamFoundation (9.0.0)
Microsoft.VisualStudio.Shell (2.0.0)
También necesita quitar la referencia predeterminada a Microsoft.VisualStudio.Shell.9.0, puesto que Microsoft creó los ensamblados de Team Foundation contra la versión 2.0 de los ensamblados en lugar de la versión 9.0. Además, como generado, el proyecto supone puede utilizar la herramienta regpkg.exe para registrar el paquete en el registro después de compilación. Sin embargo, regpkg.exe depende del ensamblado Shell.9.0. Para que el proyecto de generación en Visual Studio 2008, debe cambiar el archivo del proyecto .proj. Debe descargar el archivo de proyecto y, a continuación, agregue las siguientes propiedades para el archivo de la propiedad RegisterOutputPackage:
<!-- We are 2005 compatible, and don't rely on RegPkg.exe
of VS2008 which uses Microsoft.VisualStudio.Shell.9.0 -->
<UseVS2005MPF>true</UseVS2005MPF>
<!-- Don't try to run as a normal user (RANA),
create experimental hive in HKEY_LOCAL_MACHINE -->
<RegisterWithRanu>false</RegisterWithRanu>.
El ensamblado Microsoft.VisualStudio.TeamFoundation.Client proporciona un espacio de nombres de Microsoft.TeamFoundation.Common que contiene una clase base denominada PluginHostPackage. Use esto como clase base para el paquete. También contiene una clase base denominada BasicAsyncPlugin que implementa la interfaz de ITeamExplorerPlugin necesaria. Deberá eliminar la implementación predeterminada de la clase generada del paquete y, a continuación, heredar de PluginHostPackage en lugar de la clase de paquete predeterminado.
Dado que ahora la clase se hereda de PluginHostPackage, sólo necesita reemplazar el método OnCreateService. Este método devuelve una nueva instancia de una clase derivada de BasicAsyncPlugin que administra la implementación real de complemento. Puede ver la implementación de la HostPackage para el Explorador de sustitutas en de figura 2. También tendrá que registrar manualmente el complemento de Team Explorer, una tarea se volverá a más adelante en este artículo.
Figura 2 de implementación de SubstExplorerPackage
...
[ProvideService(typeof(SubstExplorer))]
[PluginRegistration(Catalogs.TeamProject, "Subst explorer", typeof(SubstExplorer))]
public sealed class SubstExplorerPackage: PluginHostPackage, IVsInstalledProduct {
private static SubstExplorerPackage _instance;
public static SubstExplorerPackage Instance {
get { return _instance; }
}
public SubstExplorerPackage () : base() {
_instance = this;
}
protected override object OnCreateService(
IServiceContainer container, Type serviceType) {
if (serviceType == typeof(SubstExplorer)) {
return new SubstExplorer();
}
throw new ArgumentException(serviceType.ToString());
}
}
En la figura 2, hay dos atributos que son de especial interés para el complemento de Team Explorer. ProvideService indica este paquete proporciona un servicio y el tipoServicio es SubstExplorer. PluginRegistration indica que el paquete proporciona un complemento de Team Explorer y que el registro adicional es necesario. Este atributo se deriva de RegistrationAttribute y regpkg.exe normalmente lo procesa.
Los nodos y jerarquías
Como puede ver en la figura 2, la implementación de OnCreateService es sencilla. Devuelve una nueva instancia de la clase SubstExplorer que proporciona la implementación de la clase BasicAsyncPlugin. La clase SubstExplorer es responsable de administrar una parte de la jerarquía de Team Explorer. Una jerarquía en Visual Studio es un árbol de nodos donde cada nodo tiene un conjunto de propiedades asociadas. Ejemplos de otras jerarquías en Visual Studio son el Explorador de soluciones, el Explorador de servidores y el Explorador de rendimiento.
El SubstExplorer administra la jerarquía de complemento reemplazando dos métodos denominados CreateNewTree y GetNewUIHierarchy . En de figura 3, puede ver la implementación de la clase SubstExplorer que se deriva de BasicAsyncPlugin.
Figura 3 deimplementación de SubstExplorer
[Guid("97CE787C-DE2D-4b5c-AF6D-79E254D83111")]
public class SubstExplorer : BasicAsyncPlugin {
public SubstExplorer() :
base(MSDNMagazine.TFSPlugins.SubstExplorerHostPackage.Instance) {}
public override String Name
{ get { return "Subst drive mappings"; } }
public override int DisplayPriority {
get {
// After team explorer build, but before any installed power tools
// power tools start at 450
return 400;
}
}
public override IntPtr OpenFolderIconHandle
{ get { return IconHandle; }}
public override IntPtr IconHandle
{ get { return new Bitmap(
SubstConfigurationFile.GetCommandImages().Images[2]).GetHicon(); } }
protected override BaseUIHierarchy GetNewUIHierarchy(
IVsUIHierarchy parentHierarchy, uint itemId) {
SubstExplorerUIHierarchy uiHierarchy =
new SubstExplorerUIHierarchy(parentHierarchy, itemId, this);
return uiHierarchy;
}
protected override BaseHierarchyNode CreateNewTree(
BaseUIHierarchy hierarchy) {
SubstExplorerRoot root =
new SubstExplorerRoot(hierarchy.ProjectName +
'/' + "SubstExplorerRoot");
PopulateTree(root);
// add the tree to the UIHierarchy so it can handle the commands
if (hierarchy.HierarchyNode == null)
{ hierarchy.AddTreeToHierarchy(root, true); }
return root;
}
public static void PopulateTree(BaseHierarchyNode teNode) {
string projectName =
teNode.CanonicalName.Substring(0,
teNode.CanonicalName.IndexOf("/"));
var substNodes =
SubstConfigurationFile.GetMappingsForProject(projectName);
if (substNodes != null) {
foreach (var substNode in substNodes) {
SubstExplorerLeaf leafNode =
new SubstExplorerLeaf(substNode.name, substNode.drive,
substNode.versionControlPath);
teNode.AddChild(leafNode);
}
// (bug workaround) force refresh of icon that changed
// during add, to force icon refresh
if (teNode.IsExpanded) {
teNode.Expand(false);
teNode.Expand(true);
}
}
}
}
La clase SubstExplorer administra la creación de un conjunto de nodos de jerarquía. Para el paquete SubstExplorer, estos nodos representan ubicaciones de carpeta virtual que puede asignar el complemento como una unidad. Cada nodo contiene las propiedades necesarias para asignar una unidad con el comando subst.exe. El paquete realizará el seguimiento de nombre, letraDeUnidad y ubicación (en el repositorio de control de versión).
El paquete crea el árbol en dos pasos. En primer lugar, crea la clase de controlador de comando de todos los nodos de jerarquía, conocido como UIHierarchy. El método GetNewUIHierarchy inicia este paso. En segundo lugar, el método CreateNewTree controla la creación del árbol de nodos que representan las asignaciones de unidad virtual.
GetNewUIHierarchy se llama desde el subproceso de interfaz de usuario y devuelve una instancia de una clase que deriva de la clase base BaseUIHierarchy. Encontrará la implementación del paquete en la clase SubstExplorerUIHierarchy. SubstExplorerUIHierarchy necesita controlar todos los agregar, eliminar y editar comandos ejecutado desde cualquiera de los nodos que agrega el paquete a Team Explorer. La clase de método ExecCommand controla estos comandos. Pero primero debe crear los menús y comandos en Visual Studio.
En la clase SubstExplorer, reemplazar el método CreateNewTree que se llama desde un subproceso que no son de interfaz de usuario y devuelve el árbol de nodos que representan todas las sustituciones de unidad configuradas para un proyecto de equipo. El árbol comienza siempre con un nodo raíz, que se deriva de la clase RootNode. Para cada definición, agregará un nodo secundario a la raíz. El nodo hoja contiene las propiedades que necesita para asignar una unidad.
Propiedades y comandos
Ahora que ha visto los requisitos básicos para configurar un Team Explorer complemento, deberá agregar alguna funcionalidad a él. La clase SubstExplorerRoot se deriva de la clase RootNode que se encuentra en el ensamblado Microsoft.TeamFoundation.Common. Aquí se pueden reemplazar los iconos, PropertiesClassName y ContexMenu propiedades.
La propiedad iconos devuelve una ImageList que contiene los iconos que desea utilizar para mostrar los nodos. En el constructor de la RootNode, deberá establecer la ImageIndex para que señale a la imagen en ImageList derecha.
El PropertiesClassName devuelve una cadena que representa el nombre que Visual Studio se muestra en la ventana de la cuadrícula de propiedades cuando selecciona un nodo. Cualquier cadena que se cree que es adecuado aquí será suficiente.
La propiedad ContextMenu devuelve un que representa el menú contextual que desea mostrar CommandID. Para que el nodo raíz, es necesario un menú contextual con una opción, llamada Add. figura 4 muestra la implementación de la SubstExplorerRoot.
Figura 4 de SubstExplorerRoot
public class SubstExplorerRoot : RootNode {
static private readonly CommandID command =
new CommandID(GuidList.guidPackageCmdSet,
CommandList.mnuAdd);
public SubstExplorerRoot(string path) : base(path) {
this.ImageIndex = 2;
NodePriority = (int)TeamExplorerNodePriority.Folder;
}
public override System.Windows.Forms.ImageList Icons
{ get { return SubstConfigurationFile.GetCommandImages(); } }
public override string PropertiesClassName {
//Name of the node to show in the properties window
get { return "Subst Explorer Root"; }
}
public override
System.ComponentModel.Design.CommandID ContextMenu
{ get { return command; } }
}
La clase de nodo de hoja que SubstExplorerLeaf (consulte la figura 5 de ) se deriva de BaseHierarchyNode y aquí tiene que reemplazar las propiedades ContextMenu, PropertiesClassName y PropertiesObject. Youl también debe proporcionar una implementación personalizada de DoDefaultAction. Visual Studio llama a este método cuando se hace doble clic en un nodo hoja. DoDefaultAction ejecuta el código que realiza el comando sustitutas. Si previamente ha ejecutado el comando sustitutas, quita la asignación.
Figura 5 de SubstExplorerLeaf
public class SubstExplorerLeaf : BaseHierarchyNode {
private enum SubstIconId {
unsubsted = 1,
substed = 2
}
CommandID command =
new CommandID(GuidList.guidPackageCmdSet,
CommandList.mnuDelete);
bool IsDriveSubsted { get; set; }
public string VersionControlPath { get; set; }
public string SubstDriveLetter { get; set; }
public SubstExplorerLeaf(string path,
string substDriveLetter, string versionControlPath)
: base(path, path + " (" + substDriveLetter + ":)") {
this.ImageIndex = (int)SubstIconId.unsubsted;
this.NodePriority = (int)TeamExplorerNodePriority.Leaf;
this.VersionControlPath = versionControlPath;
this.SubstDriveLetter = substDriveLetter;
this.IsDriveSubsted = false;
}
public override void DoDefaultAction() {
if (!IsDriveSubsted) {
SubstDrive();
}
else {
UnsubstDrive(SubstDriveLetter);
}
}
public override CommandID ContextMenu
{ get { return command; } }
public override string PropertiesClassName
{ get { return "Subst Leaf Node"; }}
public override ICustomTypeDescriptor PropertiesObject {
get {
return new SubstExplorerProperties(this);
}
}
private void SubstDrive() {
if (IsDriveAlreadySubsted(SubstDriveLetter)) {
UnsubstDrive(SubstDriveLetter);
}
string substresponse =
SubstHelper.Subst(SubstDriveLetter, GetLocalFolder());
if (string.IsNullOrEmpty(substresponse)) {
IsDriveSubsted = true;
this.ImageIndex = (int)SubstIconId.substed;
}
else {
MessageBox.Show(string.Format(
"Unable to make subst mapping. Message:\n {0}",
substresponse));
}
}
private bool IsDriveAlreadySubsted(string driveLetter) {
bool IsdrivePhysicalyMaped =
SubstHelper.SubstedDrives().Where(
d => d.Contains(driveLetter + ":\\")).Count() != 0;
bool IsdriveKnownToBeMaped =
(from substedNode in _substedNodes
where substedNode.SubstDriveLetter == driveLetter
select substedNode).ToArray<SubstExplorerLeaf>().Length > 0;
return IsdriveKnownToBeMaped || IsdrivePhysicalyMaped;
}
public void UnsubstDrive(string substDriveLetter) {
string substResponse = SubstHelper.DeleteSubst(substDriveLetter);
IsDriveSubsted = false;
this.ImageIndex = (int)SubstIconId.unsubsted;
}
public string localPath {
get { return VersionControlPath; }
}
}
La propiedad ContextMenu representa el menú contextual que desee mostrar en el nodo hoja. El menú contextual expone dos comandos: Las propiedades y eliminar. En la clase, el PropertiesClassName tiene el mismo propósito como en el nodo raíz. Utilice la propiedad PropertiesObject para volver a un objeto que puede utilizar para mostrar las propiedades del nodo seleccionado en la ventana Propiedades. Para el nodo hoja, las propiedades expuestas será Name, DriveLetter y VersionControlPath.
Devolver una nueva instancia del tipo SubstExplorerProperties (consulte de figura 6). Utilice este objeto para mostrar las propiedades del nodo hoja. SubstExplorerProperties proporciona una implementación de la interfaz de ICustomTypeDescriptor que devuelve la información en las propiedades que desea mostrar y cómo desea mostrarlos. BaseHierarchyNode tiene un objeto de propiedades predeterminado muestra aspectos como la dirección URL, ServerName y ProjectName, pero que no parece útil para nuestro nodo hoja.
Figura 6 de SubstExplorerProperties
public class SubstExplorerProperties
: ICustomTypeDescriptor, IVsProvideUserContext {
private BaseHierarchyNode m_node = null;
public SubstExplorerProperties(BaseHierarchyNode node)
{ m_node = node; }
public string GetClassName()
{ return m_node.PropertiesClassName;}
public string GetComponentName()
{ return m_node.Name; }
public PropertyDescriptorCollection
GetProperties(Attribute[] attributes) {
// create for each of our properties the
// appropriate PropertyDescriptor
List<PropertyDescriptor> list = new List<PropertyDescriptor>();
PropertyDescriptorCollection descriptors =
TypeDescriptor.GetProperties(this, attributes, true);
for (int i = 0; i < descriptors.Count; i++) {
list.Add(new DesignPropertyDescriptor(descriptors[i]));
}
return new PropertyDescriptorCollection(list.ToArray());
}
public object GetPropertyOwner(PropertyDescriptor pd) {
// return the object implementing the properties
return this;
}
// rest of ICustomTypeDescriptor methods are not
// shown since they are returning defaults
// actual properties start here
[Category("Drive mapping")]
[Description("...")]
[DisplayName("Version Control Path")]
public string VersionControlPath
{ get { return ((SubstExplorerLeaf)m_node).VersionControlPath; } }
[Category("Drive mapping")]
[Description("...")]
[DisplayName("Subst drive letter")]
public SubstDriveEnum SubstDriveLetter {
get { return
(SubstDriveEnum)Enum.Parse(typeof(SubstDriveEnum),
((SubstExplorerLeaf)m_node).SubstDriveLetter); }
}
[Category("Drive mapping")]
[Description("...")]
[DisplayName("Mapping name")]
public string MappingName
{ get { return ((SubstExplorerLeaf)m_node).Name; } }
}
Menús y comandos
Si examina la raíz y las implementaciones de nodo de hoja, verá que ambos necesitan mostrar un menú contextual. El nodo raíz debe tener un elemento de menú Agregar. Un nodo hoja necesita propiedades y eliminar elementos de menú. Ambas implementaciones de nodo devuelven una instancia CommandID como la implementación de sus respectivas propiedades ContextMenu. En pedido para la clase CommandID funcione correctamente, deberá definir los menús y comandos en la solución.
Para agregar un menú y un comando a Visual Studio, deberá definir los comandos en una tabla de comando. Agregar tablas de comando para el ensamblado como recurso incrustado. Además, deberá registrar la tabla de comandos y el registro del sistema durante el registro del paquete. Cuando ejecute devenv /setup, Visual Studio recopila todos los recursos de comando de todos los paquetes registrados y genera una representación interna de todos los comandos en el entorno de desarrollo.
A partir de Visual Studio 2005, puede definir las tablas de comando en un archivo XML con la extensión .vsct. En este archivo, defina los menús, los grupos de comandos y los botones que desee mostrar en el menú. Un comando de Visual Studio es parte de un grupo de comandos. Colocar grupos de comandos en menús.
Para que el nodo raíz, es necesario un comando de agregar, colocar en un grupo contenido en un menú. El nodo hoja necesita comandos eliminar y propiedades. Deberá definir un segundo menú que contiene un grupo diferente que contiene estos dos comandos. (Consulte la descarga que acompaña este artículo para un archivo de .vsct de ejemplo.)
El archivo .vsct necesita un tratamiento especial en el proyecto de Visual Studio. Debe compilar en un recurso y, a continuación, incrustar el recurso en el ensamblado. Después de instalar el SDK de Visual Studio, puede seleccionar una acción de compilación especial para el archivo de comando denominado VSCTCompile. Esta acción se ocupa de compilar e incrustar el recurso en el ensamblado.
En la tabla de comandos XML, algunos símbolos se utilizan en la definición de los menús y comandos. Agregar todos los menús, comandos y botones a la misma commandSet denominado GuidPackageCmdSet:
<Symbols>
<!-- This is the package guid. -->
<GuidSymbol name="GuidPackage" value=
"{9B024C14-2F6F-4e38-AA67-3791524A807E}"/>
<GuidSymbol name="GuidPackageCmdSet" value=
"{D0C59149-AC1D-4257-A68E-789592381830}"/>
<IDSymbol name="mnuAdd" value="0x1001" />
<IDSymbol name="mnuDelete" value="0x1002" />
En todas partes que debe proporcionar información de contexto de menú, hacen referencia a este símbolo como el contenedor del menú. Por lo tanto, en las implementaciones de SubstExplorerRootNode y SubstExplorerLeafNode, cree una instancia del tipo CommandID y utilizar GuidPackageCommandSet como primer argumento y el menú real que desea mostrar como segundo argumento:
CommandID command = new CommandID(
GuidList.guidPackageCmdSet,
CommandList.mnuDelete);
En el archivo .vsct, hay tres comandos que necesita el UIHierarchy para responder a. Al hacer clic en uno de los elementos de menú, se llama al método ExecCommand. El método necesita seleccionar la acción que se va a ejecutar basándose en el que se le ha pasado de nCmdId. Se muestra la implementación básica de la SubstExplorerUIHierarchy en de figura 7.
Figura 7 de SubstExplorerUIHierarchy
public class SubstExplorerUIHierarchy : BaseUIHierarchy,
IVsHierarchyDeleteHandler, IVsHierarchyDeleteHandler2 {
public SubstExplorerUIHierarchy(IVsUIHierarchy parentHierarchy,
uint itemId, BasicAsyncPlugin plugin)
: base(parentHierarchy, itemId, plugin,
MSDNMagazine.TFSPlugins.SubstExplorerHostPackage.Instance) {
}
public override int ExecCommand(uint itemId,
ref Guid guidCmdGroup, uint nCmdId,
uint nCmdExecOpt, IntPtr pvain, IntPtr p) {
if (guidCmdGroup == GuidList.guidPackageCmdSet) {
switch (nCmdId) {
case (uint)CommandList.cmdAdd:
AddNewDefinition(this.ProjectName);
return VSConstants.S_OK;
case (uint)CommandList.cmdDelete:
RemoveDefinition(itemId);
return VSConstants.S_OK;
case (uint)CommandList.cmdEdit:
EditDefinition(itemId);
return VSConstants.S_OK;
default: return VSConstants.E_FAIL;
}
}
return base.ExecCommand(itemId, ref guidCmdGroup, nCmdId, nCmdExecOpt, pvain, p);
}
...
}
Agregar, editar y eliminar
Ahora necesita para proporcionar una forma para el usuario agregar, eliminar o editar asignaciones en los nodos raíz o de hoja. El código está en el lugar para controlar las llamadas para agregar en el nodo raíz y para los comandos Edit y DELETE en los nodos hoja. Agregar una nueva asignación requiere la intervención del usuario y necesita almacenar los datos de asignación en una ubicación conocida. Esta ubicación es preferiblemente en el perfil de usuario móvil. Así que vamos a eche un vistazo en cómo se puede responder al comando Agregar.
El método AddNewDefinition en la clase SubstExplorerUIHierarchy controla el comando Agregar. AddNewDefinition muestra un cuadro de diálogo que permite a los usuarios especificar la asignación que deseen crear. Una asignación necesita tener un nombre y una letra de unidad para el comando sustitutas. Además, la asignación debe señalar a una ruta de acceso en el repositorio de control de versiones. Desea permitir al usuario elegir la ubicación de control de versiones en lugar de tener que escribir manualmente un trazado complejo. Puede habilitar utilizando el modelo de objetos TFS, específicamente el método GetServer desde la clase TeamFoundationServerFactory. GetServer acepta una dirección URL que representa el servidor que desee utilizar y un credentialsProvider en caso de que el usuario no está en el mismo dominio que el servidor y la conexión de servidor requiere autenticación nuevo. Después de tener acceso a una instancia TeamFoundationServer válida, tiene acceso a los diversos servicios proporcionados por TFS.
Necesitará el servicio VersionControlServer para obtener información acerca de la estructura de carpetas de proyecto de equipo actual. En enero de Brian 2007 Team System columna (msdn.microsoft.com/magazine/cc163498), le mostramos cómo podría utilizar este servicio para crear su propio cuadro de diálogo de explorador de carpeta control de versión. Nos hemos volver a utilizar el cuadro de diálogo descrita en ese artículo aquí (consulte de figura 8). El cuadro de diálogo devuelve la carpeta seleccionada por el usuario en el repositorio de control de versiones como se muestra en la figura 9 de. Almacenar la ruta de acceso devuelto en un archivo de configuración.
Cuando el usuario hace clic en Aceptar, puede agregar un nuevo nodo al archivo de configuración y un nuevo nodo secundario a la jerarquía. Agregar un nuevo nodo en la instancia de HierarchyNode llamando al método AddChild.
Figura 8 de Agregar una nueva definición de asignación
Figura 9 de Elegir una ubicación en el control de versiones
Ejecutar el comando predeterminado
La clase SubstExplorerUIHierarchy es responsable de controlar todos los comandos desencadenados por las opciones de menú que ofrece el complemento. Uno de los comandos que necesite controlar es cuando un usuario hace doble clic en un nodo. El método DoDefaultAction procesa este evento. Para el nodo raíz, la acción predeterminada de contraer o expandir los nodos en la jerarquía es aceptable. Sin embargo, para nodos de hoja, especificará una implementación personalizada.
Desea sustituir la unidad en función de las propiedades establecidas para ese nodo. Para una unidad subst, puede emitir una acción de la línea de comandos y proporcionar los parámetros requeridos. Para ello, creamos un SubstHelperClass que llama en el espacio de nombres System.Diagnostics para crear un nuevo proceso llamado subst.exe y proporcionar con los parámetros necesarios. Los parámetros necesarios son la letra de unidad y la carpeta local que desea asignar como la unidad. Tiene la letra de unidad disponible. Sin embargo, deberá asignar la ruta de acceso de control de versión a la carpeta local. Una vez más, se utilizará el modelo de objetos TFS y obtener una referencia al objeto VersionControlServer. Puede consultar este objeto de todas las áreas de trabajo disponibles y trate de conseguir una asignación a una carpeta local basándose en la ruta de acceso de control de versión que tiene. figura 10 proporciona una implementación.
Figura 10 de asignación de una control de versión ruta a una ubicación en disco
private string GetLocalFolder() {
VersionControlServer vcs =
(VersionControlServer)((
SubstExplorerUIHierarchy)ParentHierarchy).
tfs.GetService(typeof(VersionControlServer));
Workspace[] workspaces =
vcs.QueryWorkspaces(null, vcs.AuthenticatedUser,
Environment.MachineName);
foreach (Workspace ws in workspaces) {
WorkingFolder wf =
ws.TryGetWorkingFolderForServerItem(VersionControlPath);
if (wf != null) {
// We found a workspace that contains this versioncontrolled item
// get the local location to map the drive to this location....
return wf.LocalItem;
}
}
return null;
}
Toques finales
Ahora dispone de toda la lógica en lugar de mostrar un árbol de nodos y controlar la asignación de unidad. Sin embargo, desea que su Team Explorer complemento destaquen. Quizás desee agregar que algunas características adicionales en términos de eliminar el control de nodo y otros toques profesionales, como agregar un icono en la pantalla de inicio de Visual Studio.
Agregar la funcionalidad de eliminación requiere implementar una interfaz adicional en la clase SubstExplorerUIHierarchy. Visual Studio tiene una interfaz específica denominada IVsHierarchyDeleteHandler que implementar para mostrar un cuadro de diálogo predeterminado al presionar la tecla SUPR. Para este complemento, deseará proporcionar un cuadro de diálogo personalizado que se pregunta al usuario que confirme la eliminación del nodo que está seleccionado. Para hacer que el trabajo, también deberá implementar la interfaz IVsHierarchyDeleteHandler2 para el tratamiento de eliminación trabajar desde el teclado. Puesto que ya se ha implementado la funcionalidad de eliminación real, sólo necesita implementar esta interfaz y llamar a las funciones existentes. Puede ver la implementación de las interfaces en de figura 11.
Figura 11 de implementación de controlador de IVsHierarchyDelete
#region IVsHierarchyDeleteHandler2 Members
public int ShowMultiSelDeleteOrRemoveMessage(
uint dwDelItemOp, uint cDelItems,
uint[] rgDelItems, out int pfCancelOperation) {
pfCancelOperation = Convert.ToInt32(true);
return VSConstants.S_OK;
}
public int ShowSpecificDeleteRemoveMessage(
uint dwDelItemOps, uint cDelItems, uint[] rgDelItems,
out int pfShowStandardMessage, out uint pdwDelItemOp) {
SubstExplorerLeaf nodeToDelete =
NodeFromItemId(rgDelItems[0]) as SubstExplorerLeaf;
if (AreYouSureToDelete(nodeToDelete.Name)) {
pdwDelItemOp = 1; // == DELITEMOP_DeleteFromStorage;
// DELITEMOP_RemoveFromProject==2;
}
else {
pdwDelItemOp = 0; // NO delete, user selected NO option }
pfShowStandardMessage = Convert.ToInt32(false);
return VSConstants.S_OK;
}
#endregion
#region IVsHierarchyDeleteHandler Members
public int DeleteItem(uint dwDelItemOp, uint itemid) {
SubstExplorerLeaf nodeToDelete =
NodeFromItemId(itemid) as SubstExplorerLeaf;
if (nodeToDelete != null) {
// remove from storage
RemoveDefinitionFromFile(nodeToDelete);
// remove from UI
nodeToDelete.Remove();
}
return VSConstants.S_OK;
}
public int QueryDeleteItem(uint dwDelItemOp, uint itemid,
out int pfCanDelete) {
pfCanDelete = Convert.ToInt32(NodeFromItemId(itemid) is SubstExplorerLeaf);
return VSConstants.S_OK;
}
#endregion
Es importante observar que el complemento no es compatible con varios nodos seleccionados a la vez que se va a eliminar, por lo tanto pfCancelOperation se establece en true en el método ShowMultiSelDeleteOrRemoveMessage. En la implementación del método ShowSpecificDeleteRemoveMessage, deberá devolver el valor correcto de lo que desea eliminar. Devolver un valor de 1 para indicar que se quitó de almacenamiento. Estos indicadores se utilizan normalmente en el sistema de proyectos de Visual Studio y sólo un valor de 1 genera los resultados correctos.
Quizás también desee agregar compatibilidad para integración de la pantalla de bienvenida. De forma predeterminada, cada vez que inicie Visual Studio, verá una pantalla de presentación listado de los productos registrados. Para ello, implementando la interfaz IVsInstalledProduct en la clase de implementación de SubstExplorerHostPackage. Los métodos requieren que registrar los identificadores para el icono para utilizar en la pantalla de presentación y el icono para utilizar en el cuadro acerca de recursos.
La implementación no es algo más que establecer el parámetro out en el valor entero correcto e incrustar un icono 32 x 32 píxeles como un recurso en el ensamblado nada. Para incrustar correctamente el recurso de su ensamblado, debe abrir el archivo resources.resx en el editor XML y agregue las líneas siguientes al archivo de recursos:
<data name="500"
type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\SplashIcon.bmp;System.Drawing.Bitmap,
System.Drawing, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
Esto agrega el mapa de bits de recurso ubicado en la carpeta de recursos en el proyecto para el recurso y lo incrusta en la referencia 500. En el método IdBmpSplash, ahora puede establecer pIdBmp a 500 y devolver la constante S_OK, como un valor devuelto. Para obtener el icono en la pantalla de presentación, deberá generar el ensamblado y, a continuación, ejecute devenv /setup desde la línea de comandos. Esto obtendrá la información del paquete que ha creado y lo almacenará en caché los datos. Esto garantiza que el paquete no es necesario que se cargarán cuando Visual Studio muestra la pantalla de presentación. Para ello, para los mismos motivos que era necesario hacerlo para las opciones de menú que ha agregado: Para acelerar el tiempo de carga de Visual Studio.
Paquete de registro
Ahora que ha terminado la extensión de Team Explorer, es el momento para empaquetar el producto y obtenerlo ejecuta en sistemas de otros desarrolladores ’s. ¿Así, cómo se puede distribuir los resultados?
En primer lugar, Visual Studio funciona de manera diferente cuando hayas instalado el SDK. De forma predeterminada se acepte o cargar cualquier VSPackage. Esto no será el caso en equipos donde no ha instalado el SDK.
Para que un paquete cargar correctamente, es necesario incrustar una clave de carga de paquete, que se puede obtener de Microsoft (consulte de msdn.microsoft.com/vsx/cc655795 ). La parte más importante de este proceso es asegurarse de que proporciona la misma información exacta al registrar para su clave como la información proporcionada en los atributos para la clase hostPackage (en este caso, la clase SubstExplorerHostPackage). Además, cuando el sitio Web le pide que escriba el nombre de paquete, debe proporcionar el nombre del producto que se utiliza en el atributo ProvideLoadKey.
Una vez obtener su clave de carga, puede pegarlo en el archivo de recursos con el identificador de recursos que proporcionó como último argumento del atributo ProvideLoadKey. Asegúrese de que quitar las fuentes de retorno de línea del carro de la cadena cuando se copia desde el sitio, por lo que es una cadena consecutivo antes de pegarlo en el archivo de recursos.
Ahora puede probar si el complemento funciona especificando un parámetro de depuración adicional: / NoVsip. Este parámetro garantiza que Visual Studio utiliza el comportamiento de carga normal. Si no se acepta la clave, Visual Studio mostrará un cuadro de diálogo de error de carga. Con el SDK instalado, puede encontrar en el menú Herramientas de Visual Studio el Analizador de la carga de paquete. Puede señalar en el ensamblado para ayudar a depurar ¿cuál es el problema. Si es sólo la clave de la carga del paquete, a continuación, asegurarse de que ha escrito exactamente los mismos parámetros en el sitio Web como en el atributo.
El último paso que permanece es el registro del paquete para un equipo de producción. Lamentablemente, porque los ensamblados de Team System utilizan una versión diferente de los ensamblados de shell, no se puede utilizar regpkg.exe para registrar su paquete. En su lugar, deberá hacerlo manualmente mediante un archivo de registro. En este archivo, deberá publicar el paquete en la ubicación correcta del registro. De figura 12 muestra la secuencia de comandos de registro necesario.
Figura 12 de script de registro del paquete
REGEDIT4
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\TeamSystemPlugins\Team Explorer Project Plugins\SubstExplorer]
@="97CE787C-DE2D-4b5c-AF6D-79E254D83111"
"Enabled"=dword:00000001
[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Services\{97ce787c-de2d-4b5c-af6d-79e254d83111}]
@="{9b024c14-2f6f-4e38-aa67-3791524a807e}"
"Name"="SubstExplorer"
[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Packages\{9b024c14-2f6f-4e38-aa67-3791524a807e}]
@="MSDNMagazine.TFSPlugins.SubstExplorerHostPackage, TFSSubstExplorer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=324c86b3b5813447"
"InprocServer32"="C:\\Windows\\system32\\mscoree.dll"
"Class"="MSDNMagazine.TFSPlugins.SubstExplorerHostPackage"
"CodeBase"="c:\\program files\\msdnsamples\\TFSSubstExplorer.dll"
"ID"=dword:00000065
"MinEdition"="Professional"
"ProductVersion"="1.0"
"ProductName"="SubstExplorer"
"CompanyName"="vriesmarcel@hotmail.com"
[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\InstalledProducts\SubstExplorerHostPackage]
"Package"="{9b024c14-2f6f-4e38-aa67-3791524a807e}"
"UseInterface"=dword:00000001
[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Menus]
"{9b024c14-2f6f-4e38-aa67-3791524a807e}"=", 1000, 1"
En la secuencia de comandos de registro, verá un número de entradas.La primera entrada registra una nueva extensión de Team Explorer Team Explorer se debe cargar en cuanto se carga.Aquí es proporcionar un valor del registro que hace referencia al identificador de servicio que proporciona una implementación de ITeamExplorerPlugin.La entrada siguiente proporciona el registro de servicio donde verá el identificador de servicio que se hace referencia a previamente, así como un valor del registro que señala al paquete que proporciona el complemento.
La entrada siguiente es el propio registro de paquetes.Existe utiliza el identificador del paquete como una nueva clave y proporcione la información donde se puede encontrar el ensamblado, puede ser cargado utilizando la infraestructura de COM, y qué versión de Visual Studio admite el paquete.Las dos últimas entradas son el registro de los productos instalados, utilizado para la pantalla de presentación.Aquí la clave de UseInterface indica a Visual Studio debe llamar a la interfaz IVsInstalledProduct en lugar de confiar en el atributo InstalledProductRegistration para proporcionar una descripción de producto y de icono que debe mostrarse en el inicio.
La última entrada es el registro de los menús contextuales.Aquí hacer referencia a su paquete, pero también proporcionan información sobre donde ha incrustado los recursos en el ensamblado.Estos son los recursos incrustados que creó antes de utilizar los archivos de .vsct y acción en el archivo de generación de la personalizada.Con esta secuencia de comandos y el ensamblado generado, se puede implementar en otros equipos.Simplemente coloque el ensamblado en el sistema de archivos, ajustar la secuencia de comandos del registro para reflejar la ubicación correcta del ensamblado y combinar en el registro.A continuación, el paso final es ejecutar devenv /setup en ese equipo.Al iniciar Visual Studio, verá el icono en la pantalla de presentación y al cargar el Team Explorer, verá el nodo raíz del complemento que ha creado.
Brian A.Randelles consultor senior de MCW Technologies LLC.Brian ofrece charlas, enseña y escribe sobre tecnologías de Microsoft.Es autor del curso Applied Team System de Pluralsight y es MVP de Microsoft.Puede ponerse en contacto con Brian a través de su blog en mcwtech.com/blogs/brianr de.
Marcel de Vrieses un arquitecto de TI en Info Support en los países bajos, donde el administrador crea soluciones para grande banco y seguros las empresas de todo el país y enseña a los cursos de Team System y Windows Workflow.Marcel es ponente habitual en conferencias de desarrolladores en Europa, un MVP de Team System desde 2006 y un director regional de Microsoft desde enero de 2009.
Gracias a los siguientes expertos técnicos para revisar este artículo: Dennis Habib y Buck Hodges