ArcObjects (ArcGIS for Desktop y C #): ¿Cómo transmitir entre la interfaz de usuario de ArcMap COM y mis objetos personalizados .Net UserControl?

8

Estoy creando una utilidad para ejecutar en ArcGIS for Desktop usando ArcObjects (9.3.1 SDK) y C # .Net. Mi prototipo implica una barra de herramientas con dos cuadros combinados y una herramienta. El primer combo selecciona una capa en la tabla de contenido, y el segundo selecciona un campo de la capa seleccionada. La herramienta se usará para interactuar con el mapa.

Básicamente quiero seleccionar una capa, seleccionar un campo válido, luego hacer clic en una entidad en el mapa y obtener su valor para el campo elegido. Aquí hay una imagen de la barra de herramientas, si ayuda:

ingrese la descripción de la imagen aquí

[pregunta redactada casi por completo de aquí abajo]

El problema que tengo es pasar el estado entre las partes nativas de la interfaz de usuario de COM y mis controles personalizados .Net. Por ejemplo, quiero capturar el evento DropDownClosed en el cuadro combinado Capa, armar una lista válida de columnas relativas a esa capa, luego aplicar la lista de nombres de campo (a través de IFields) al cuadro combinado Campos.

Después de aplicar algunos de los comentarios iniciales de RagiYaserBurham y blah238, y fusionarlos con los detalles de esta página , el siguiente controlador de eventos DropDownClosed regresa del cuadro combinado a la barra de herramientas (ICommandBar), pero no entiendo cómo transmitir desde ICommandItem a mi implementación del cuadro combinado Campos en un UserControl:

private void layerSelectCBO_DropDownClosed(object sender, EventArgs e)
{
    _completionNotify.SetComplete();

    string layerName = (sender as ComboBox).SelectedItem as string;

    // These two lines are a combination of initial commenter suggestions.
    ICommandItem toolbar = _iApp.Document.CommandBars.Find("ArcProject.UI.AngryToolbar", false, false);
    ICommandItem fieldsItem = (toolbar as ICommandBar).Find("ArcProject.UI.FieldSelectUC", false);

}

Entonces ... ahora que estoy aquí ... ¿cómo lanzo fieldsItem a FieldSelectUC?

[ La solución ]

Como sugirió blah238, intenté enviar ICommandItem.Command a mi implementación personalizada de UserControl y eso funcionó.

Primero, tuve que agregar un descriptor de acceso público a mi FieldSelectUCUserControl para devolver una referencia a su ComboBox; ese simple descriptor de acceso se ve así:

// fieldSelectCBO is the name of the combobox control in the design view..
public ComboBox FieldsComboBox { get { return fieldSelectCBO; } }

Con esa modificación en su lugar, aquí hay un controlador de eventos DropDownClosed que completará el cuadro combinado Campos con todos los campos de la capa seleccionada:

private void layerSelectCBO_DropDownClosed(object sender, EventArgs e)
{
    _completionNotify.SetComplete();

    string layerName = (sender as ComboBox).SelectedItem as string;

    // get the toolbar..
    ICommandItem toolbar = _iApp.Document.CommandBars.Find("ArcProject.UI.AngryToolbar", false, false);

    // get my fields combo by way of CommandItem.Command..
    ICommandItem fieldsCI = (toolbar as ICommandBar).Find("ArcProject.UI.FieldSelectUC", false);
    FieldSelectUC fieldsUC = fieldsCI.Command as FieldSelectUC;
    ComboBox fieldsComboBox = fieldsUC.FieldsComboBox;

    // get the fields for the selected layer..
    IFields fields = null;
    int layerCount = _iDoc.FocusMap.LayerCount;
    int i;
    for (i = 0; i < layerCount; i++)
    {
        if (_iDoc.FocusMap.get_Layer(i).Name == layerName)
        {
            if (_iDoc.FocusMap.get_Layer(i) is FeatureLayer)
            {
                fields = (_iDoc.FocusMap.get_Layer(i) as FeatureLayer).FeatureClass.Fields;
            }
        }
    }

    // Build a list of field names for the combobox items..
    List<string> fieldNameList = new List<string>();
    if (fields != null)
    {
        int fieldCount = fields.FieldCount;
        int j;
        for (j = 0; j < fieldCount; j++)
        {
            string oneFieldName = fields.get_Field(j).Name;
            fieldNameList.Add(oneFieldName);
        }
    }

    // Populate the combobox items..  
    if (fieldNameList.Count > 0)
    {
        fieldsComboBox.Items.Clear();

        foreach (string fieldName in fieldNameList)
        {
            fieldsComboBox.Items.Add(fieldName);
        }

        fieldsComboBox.SelectedItem = fieldsComboBox.Items[0];
    }
    else
    {
        fieldsComboBox.Items.Add("Error: No fields!");
    }
}

Esto sigue siendo un banco de pruebas sucio (de ahí AngryToolbar). Pero la solución muestra cómo comenzar desde un UserControl extendido que implementa ICommand e IToolControl y volver a un componente .Net. Realmente aprecio la ayuda de todos los que ofrecieron sugerencias. Muchas gracias. :)

elrobis
fuente
¿Qué tal hacer que su nombre de capa variable sea público, estático?
artwork21
@ artwork21, eso suena bien, pero todavía no estoy seguro de cómo acceder a la instancia de cualquier cuadro combinado desde su contraparte en la barra de herramientas. ¿Ya tu sabes? Sospecho que es una cosa fundamental simple que simplemente no conozco.
elrobis
Esto suena como una pregunta diferente. No estoy claro por qué necesitas hacer eso. Me parece que el cuadro combinado Campos debería borrarse y repoblarse en función del cuadro combinado Capa. El cuadro combinado de capa debe rellenarse en función del detector de eventos del documento.
Rich Wawrzonek
@RichWawrzonek eso es exactamente correcto. Pero no estoy seguro de cómo llegar a la instancia existente del combo Fields desde el combo Layers. De hecho, la herramienta necesita leer ambos valores.
elrobis

Respuestas:

4

Según tengo entendido, tiene dos .NET ComboBoxes en un UserControl que implementa ICommand e IToolControl, y desea obtener una referencia a uno de los cuadros combinados del otro. Siempre que estén en el mismo ámbito, solo debería poder referirse a ellos por sus nombres de variables (consulte a su diseñador de UserControl para conocer los nombres de sus controles).

Si los dos cuadros combinados están en UserControls separados, intente transmitir ICommandItem.Commanda su otro UserControl.

Consulte este ejemplo en la ayuda 9.3 para ver algunos ejemplos: Archivos usados ​​recientemente: Command, MultiItem y ToolControl

También aquí hay una publicación en el foro de ESRI sobre este tema: http://forums.esri.com/Thread.asp?c=93&f=993&t=170088

blah238
fuente
Bingo. Esta línea hizo el truco para volver de la instancia de ICommandItema la UserControlclase que implementé: FieldSelectUC fs = fieldsItem.Command as FieldSelectUC;ahora puedo ver todos sus accesorios en el depurador. Enormes gracias a ti.
elrobis
¡Hurra! He estado usando complementos exclusivamente durante un tiempo, así que tuve que investigar algunas cosas viejas para recordar cómo funcionó :) Este tipo de cosas es mucho más fácil (aunque ciertamente menos flexible) con complementos en 10 desde entonces es un tipo de ComboBox específico y puede referirse a otros componentes adicionales con variables y métodos estáticos.
blah238
1
Sí, definitivamente parecía más fácil a través de los nuevos complementos. Al investigar esto, encontré complementos y complementos que, pero simplemente no estaban disponibles para mi implementación. El ejemplo GraphicsLayerToolControl en la ayuda .Net (en mi sistema esa dirección es C:\Program Files (x86)\ArcGIS\DeveloperKit\SamplesNET\Desktop\GraphicsLayerToolControl\CSharp\GraphicsLayerToolControl2008.sln) que me ayudó a ponerme en marcha con UserControl y los eventos, pero no pude encontrar la manera de hacer un agujero en .Net desde el control COM. No puedo exagerar lo agradecido que estoy. Sinceramente.
elrobis
2

Cada vez que hago este tipo de cosas, almaceno los nombres de capa y campo en un conjunto de propiedades estáticas contenido en la barra de herramientas. Luego uso un controlador de eventos de documentos para verificar si las capas se agregan / eliminan de ArcMap o si se cambia el documento. Las propiedades de capa y campo se actualizan cada vez que el usuario las modifica en el menú desplegable. Si la capa se elimina de ArcMap o el documento se cierra, se restablecen a nulo. Luego, puede verificar los valores nulos antes de que se ejecute su programa.

Obtenga una referencia al cuadro combinado a través de la interfaz ICommandItem:

Barra de herramientas ICommandItem = _iApp.Document.CommandBars.Find ("ArcProject.UI.AngryToolbar", falso, falso);
ICommandItem fieldsItem = (barra de herramientas como ICommandBar) .Find ("ArcProject.UI.FieldSelectUC", falso); IComboBox cbo = (IComboBox) fieldsItem; // Necesita una referencia a ESRI.ArcGIS.SystemUI;

Rich Wawrzonek
fuente
+1 lo haces sonar tan fácil. :) Pero el problema que tengo es acceder a las propiedades de un control ya instanciado desde el punto de vista de cualquier otro control. Me gusta su idea de poner sus propiedades compartidas en la barra de herramientas, ¿podría actualizar su respuesta para mostrar cómo puedo leer realmente las propiedades de la barra de herramientas que contiene desde el punto de vista de uno de los cuadros combinados? Porque eso es básicamente lo que busco. Ya sé cómo usar eventos de documentos para escuchar los cambios en la tabla de contenido, por lo que no necesito ayuda con ese aspecto. Gracias por su respuesta.
elrobis
@elrobis También puede poner el estado compartido en el propio ICommand (ya que siempre habrá una sola instancia) y luego hacer que los controles manejen el comando a través de help.arcgis.com/en/sdk/10.0/arcobjects_net/componenthelp / ...
Ragi Yaser Burhum
Ragi tiene razón. Como está utilizando la barra de herramientas COM, puede pasar el uid de su cuadro combinado al método ICommandBar.Find para obtener una referencia. Su enlace lo explica todo.
Rich Wawrzonek
También me gusta la idea de Ragi. Sin embargo, hay algo fundamental que me falta. Por ejemplo, esta línea devuelve una barra de herramientas nula (donde thises una UserControlcon etiqueta y cuadro combinado): ICommandBar toolbar = this.Parent as ICommandBar;es este tipo de recorrido fundamental de objetos de IU lo que me está matando. No sé cómo volver a la barra de herramientas para implementar cualquiera de sus sugerencias. (Y en realidad me gusta un poco mejor la idea de estos vars en la barra de herramientas. Probablemente lo haría agregando un captador público a la barra de herramientas que aplique la idea de Ragi). Gracias por tu ayuda continua.
elrobis
2
No creo que this.Parentsea ​​válido para las interfaces COM, ese es un concepto de .NET / Windows Forms. No desea "atravesar la interfaz de usuario", desea acceder a sus comandos IC por sus ID.
blah238
1

Tuve un problema similar con una herramienta personalizada. Tengo un formulario personalizado que se abre con un botón en una barra de herramientas de AddIn en ArcGis 10.x. En este formulario hay un botón que debe recuperar las coordenadas de un clic en el mapa, incluido el ajuste. Pude iniciar la herramienta y manejar el clic en el mapa, pero no pude recuperar el valor de mi formulario, porque la conversión a la herramienta personalizada siempre fallaba. La solución fue usar AddIn de ESRI.ArcGIS.Desktop.AddIns. Con esto fue fácil acceder a todas las propiedades y métodos de mi herramienta personalizada. La documentación de ESRI reside aquí: http://resources.arcgis.com/en/help/arcobjects-net/conceptualhelp/index.html#/Add_in_coding_patterns/0001000000zz000000/

Aquí está el fragmento de código del evento OnClick del botón en el formulario personalizado:

//DESCRIPTION:
//Connect a tool embedded in a Windows Form with the ArcGIS Application Framework.

ESRI.ArcGIS.esriSystem.IUID UIDCls = new ESRI.ArcGIS.esriSystem.UIDClass();
UIDCls.Value = "MyNamespace_MyCustomTool";
IDocument actDoc = (IDocument)ArcMap.Document;
//Finding the customTool
ESRI.ArcGIS.Framework.ICommandItem commandItem = actDoc.CommandBars.Find(UIDCls, false, false); 

if (commandItem == null) 
{ 
   return; 
}

//This cast would fail:
//MyCustomTool_Class actCustomTool2 = (MyCustomTool_Class)commandItem.Command;

MyCustomTool_Class actCustomTool = AddIn.FromID<MyCustomTool_Class (ThisAddIn.IDs.MyCustomTool);
actCustomTool.actFrm = this;

ArcMap.Application.CurrentTool = commandItem;
dpalmetz
fuente