ArcObjects (ArcGIS for Desktop e C #): Como converter entre a interface COM do ArcMap e meus objetos .Net UserControl personalizados?

8

Estou criando um utilitário para executar no ArcGIS for Desktop usando o ArcObjects (9.3.1 SDK) e o C # .Net. Meu protótipo envolve uma barra de ferramentas com duas caixas de combinação e uma ferramenta. O primeiro combo seleciona uma camada no sumário e o segundo seleciona um campo da camada selecionada. A ferramenta será usada para interagir com o mapa.

Basicamente, quero selecionar uma camada, selecionar um campo válido, clicar em um recurso no mapa e obter seu valor para o campo escolhido. Aqui está uma imagem da barra de ferramentas, se ajudar:

insira a descrição da imagem aqui

[pergunta quase inteiramente reformulada daqui em diante]

O problema que estou tendo é passar o estado entre as partes da interface do usuário COM nativa e meus controles .Net personalizados. Por exemplo, quero capturar o evento DropDownClosed na caixa de combinação Layer, montar uma lista válida de colunas em relação a essa camada e aplicar a lista de nomes de campos (via IFields) à caixa de combinação Fields.

Após aplicar alguns comentários iniciais de RagiYaserBurham e blah238 e mesclá-los aos detalhes desta página , o seguinte manipulador de eventos DropDownClosed vai da caixa de combinação de volta à barra de ferramentas (ICommandBar), mas não entendo como transmitir a partir de ICommandItem para minha implementação da caixa de combinação Fields em um 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);

}

Então .. agora que estou aqui .. como faço para converter fieldsItem para FieldSelectUC?

[ A solução ]

Como blah238 sugeriu, tentei converter ICommandItem.Command para minha implementação personalizada UserControl e isso foi o que fez.

Primeiro, tive que adicionar um acessador público ao meu FieldSelectUCUserControl para retornar uma referência à sua ComboBox; esse acessador simples se parece com isso:

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

Com essa modificação, aqui está um manipulador de eventos DropDownClosed que preencherá a caixa de combinação Fields com todos os campos da camada selecionada:

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!");
    }
}

Este ainda é um teste sujo (daí o AngryToolbar). Mas a solução mostra como iniciar a partir de um UserControl estendido que implementa ICommand e IToolControl e fazer drill down de volta para um componente .Net. Eu realmente aprecio a assistência de todos que ofereceram sugestões. Muito obrigado. :)

elrobis
fonte
Que tal tornar seu escopo estático público da variável layerName?
artwork21
@ artwork21, isso parece bom, mas ainda não sei como acessar a instância de qualquer uma das caixas de combinação a partir de sua contraparte na barra de ferramentas? Sabe o que eu quero dizer? Eu suspeito que é uma coisa simples e fundamental que eu simplesmente não estou ciente.
elrobis
Isso soa como uma pergunta diferente. Não estou claro por que você precisa fazer isso. Parece-me que a caixa de combinação Fields deve ser limpa e preenchida novamente com base na combinação de camadas. A caixa de combinação Camada deve ser preenchida com base no ouvinte de evento do documento.
Rich Wawrzonek 31/03
@RichWawrzonek está exatamente certo. Mas não sei ao certo como chegar à instância existente do combo Campos a partir do combo Camadas. Por esse motivo, a ferramenta precisa ler os dois valores.
elrobis

Respostas:

4

Pelo que entendi, você tem duas .NET ComboBoxes em um UserControl que implementa ICommand e IToolControl e deseja obter uma referência a uma das caixas de combinação da outra. Desde que estejam no mesmo escopo, você deve ser capaz de se referir a eles pelos nomes de variáveis ​​(verifique o designer do UserControl para obter os nomes dos seus controles).

Se as duas caixas de combinação estiverem em UserControls separados, tente transmitir ICommandItem.Commandpara seu outro UserControl.

Veja este exemplo na ajuda da 9.3 para obter alguns exemplos: Arquivos usados ​​recentemente - Command, MultiItem e ToolControl

Também aqui está uma postagem no fórum da ESRI que discute esse problema: http://forums.esri.com/Thread.asp?c=93&f=993&t=170088

blah238
fonte
Bingo. Essa linha fez o truque para ICommandItemretornar da instância de volta à UserControlclasse que eu implementei: FieldSelectUC fs = fieldsItem.Command as FieldSelectUC;agora posso ver todos os seus props no depurador. Um tremendo agradecimento a você.
elrobis
Viva! Eu tenho usado suplementos exclusivamente por um tempo, então tive que vasculhar algumas coisas antigas para lembrar como tudo funcionava :) Esse tipo de coisa é muito mais fácil (embora seja menos flexível) com suplementos em 10, já que existem é um tipo específico de ComboBox e você pode apenas consultar outros componentes de suplemento com variáveis ​​e métodos estáticos.
blah238
1
Sim, definitivamente parecia mais fácil através dos novos suplementos. Ao pesquisar isso, encontrei suplementos isso e suplementos que, mas eles simplesmente não estavam disponíveis para minha implementação. O exemplo GraphicsLayerToolControl na ajuda .Net (no meu sistema é o endereço C:\Program Files (x86)\ArcGIS\DeveloperKit\SamplesNET\Desktop\GraphicsLayerToolControl\CSharp\GraphicsLayerToolControl2008.sln) que me ajudou a seguir o UserControl e as coisas de eventos, mas eu simplesmente não conseguia descobrir como fazer um furo no .NET com o controle COM. Não posso exagerar o quanto sou grato. Atenciosamente.
elrobis
2

Sempre que faço esse tipo de coisa, armazeno os nomes de camada e campo em um conjunto de propriedades estáticas contido na barra de ferramentas. Então eu uso um manipulador de eventos de documento para verificar se as camadas foram adicionadas / removidas do ArcMap ou se o documento foi alterado. As propriedades da camada e do campo são atualizadas sempre que são alteradas no menu suspenso pelo usuário. Se a camada for removida do ArcMap ou o documento for fechado, elas serão redefinidas para nulas. Então você pode apenas verificar valores nulos antes da execução do programa.

Obtenha uma referência à caixa de combinação através da interface ICommandItem:

Barra de ferramentas ICommandItem = _iApp.Document.CommandBars.Find ("ArcProject.UI.AngryToolbar", false, false);
ICommandItem fieldsItem = (barra de ferramentas como ICommandBar) .Find ("ArcProject.UI.FieldSelectUC", false); IComboBox cbo = (IComboBox) fieldsItem; // Precisa de uma referência para ESRI.ArcGIS.SystemUI;

Rich Wawrzonek
fonte
+1, você faz parecer tão fácil. :) Mas o problema que tenho é acessar as propriedades de um controle já instanciado do ponto de vista de qualquer outro controle. Gosto da sua ideia de colocar as propriedades compartilhadas na barra de ferramentas. Você poderia atualizar sua resposta para mostrar como eu posso realmente ler as propriedades da barra de ferramentas que contém o ponto de vista de uma das caixas de combinação? Porque é basicamente isso que eu estou procurando. Eu já sei como usar eventos de documentos para ouvir alterações no sumário, por isso não preciso de ajuda com esse aspecto. Obrigado pela sua resposta.
elrobis
@elrobis Você também pode colocar o estado compartilhado no próprio ICommand (já que sempre haverá apenas uma instância) e fazer com que os controles identifiquem o comando por meio de help.arcgis.com/en/sdk/10.0/arcobjects_net/componenthelp /…
Ragi Yaser Burhum
Ragi está certo. Como você está usando a barra de ferramentas COM, basta passar o uid da sua caixa de combinação para o método ICommandBar.Find para obter uma referência. O link dele explica tudo.
Rich Wawrzonek 31/03
Eu também gosto da ideia de Ragi. Há algo fundamental que estou perdendo, no entanto. Por exemplo, esta linha retorna uma barra de ferramentas nula (onde thisestá um UserControlrótulo e uma caixa de combinação): ICommandBar toolbar = this.Parent as ICommandBar;é esse tipo de passagem fundamental de objeto da interface do usuário que está me matando. Não sei como voltar para a barra de ferramentas para implementar uma de suas sugestões. (E, na verdade, eu gosto um pouco melhor da ideia desses vars na barra de ferramentas. Eu provavelmente faria isso adicionando um getter público à barra de ferramentas que aplica a ideia de Ragi). Obrigado pela sua ajuda contínua.
elrobis
2
Eu não acho que this.Parentseja válido para interfaces COM - esse é um conceito do .NET / Windows Forms. Você não deseja "atravessar a interface do usuário", deseja acessar seus ICommands pelos respectivos IDs.
blah238
1

Eu tive um problema semelhante com uma ferramenta personalizada. Eu tenho um formulário personalizado que é aberto por um botão em uma barra de ferramentas AddIn no ArcGis 10.x. Neste formulário, há um botão que deve recuperar as coordenadas de um clique no mapa, incluindo os encaixes. Eu poderia iniciar a ferramenta e manipular o clique no mapa, mas não consegui recuperar o valor no meu formulário, porque a conversão para a ferramenta personalizada sempre falhava. A solução foi usar o AddIn do ESRI.ArcGIS.Desktop.AddIns. Com isso, foi fácil obter acesso a todas as propriedades e métodos da minha ferramenta personalizada. A documentação das ESRIs está aqui: http://resources.arcgis.com/en/help/arcobjects-net/conceptualhelp/index.html#/Add_in_coding_patterns/0001000000zz000000/

Aqui está o snippet de código do OnClick-Event do botão no formulário 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
fonte