ArcObjects (ArcGIS für Desktop und C #): Wie kann ich zwischen der ArcMap COM-Benutzeroberfläche und meinen benutzerdefinierten .Net UserControl-Objekten wechseln?

8

Ich erstelle ein Dienstprogramm zur Ausführung in ArcGIS for Desktop mit ArcObjects (9.3.1 SDK) und C # .Net. Mein Prototyp enthält eine Symbolleiste mit zwei Kombinationsfeldern und einem Werkzeug. Die erste Kombination wählt eine Ebene im Inhaltsverzeichnis aus, und die zweite wählt ein Feld aus der ausgewählten Ebene aus. Das Tool wird verwendet, um mit der Karte zu interagieren.

Grundsätzlich möchte ich einen Layer auswählen, ein gültiges Feld auswählen, dann auf ein Feature in der Karte klicken und dessen Wert für das ausgewählte Feld abrufen. Hier ist ein Bild der Symbolleiste, wenn es hilft:

Geben Sie hier die Bildbeschreibung ein

[Frage von hier unten fast vollständig umformuliert]

Das Problem besteht darin, den Status zwischen den nativen COM-UI-Teilen und meinen benutzerdefinierten .NET-Steuerelementen zu übergeben. Ich möchte beispielsweise das DropDownClosed-Ereignis in der Layer-Combobox abfangen, eine gültige Liste von Spalten relativ zu dieser Ebene zusammenstellen und dann die Liste der Feldnamen (über IFields) auf die Fields-Combobox anwenden.

Nachdem einige der ersten Kommentare von RagiYaserBurham und blah238 angewendet und mit Details auf dieser Seite zusammengeführt wurden , wechselt der folgende DropDownClosed-Ereignishandler von der Combobox zurück zur Symbolleiste (ICommandBar), aber ich verstehe nicht, wie ich ICommandItem zurücksetzen kann zu meiner Implementierung der Fields-Combobox in einem 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);

}

Also ... jetzt wo ich hier bin ... wie kann ich fieldsItem in FieldSelectUC umwandeln?

[ Die Lösung ]

Wie von blah238 vorgeschlagen, habe ich versucht, ICommandItem.Command in meine benutzerdefinierte UserControl-Implementierung zu übertragen, und das hat den Trick getan.

Zuerst musste ich meinem FieldSelectUCUserControl einen öffentlichen Accessor hinzufügen , um einen Verweis auf seine ComboBox zurückzugeben. Dieser einfache Accessor sieht folgendermaßen aus:

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

Mit dieser Änderung finden Sie hier einen DropDownClosed-Ereignishandler, der die Combobox "Felder" mit allen Feldern der ausgewählten Ebene füllt:

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

Dies ist immer noch eine schmutzige Testumgebung (daher AngryToolbar). Die Lösung zeigt jedoch, wie Sie von einem erweiterten UserControl ausgehen, das ICommand und IToolControl implementiert, und einen Drilldown zu einer .NET-Komponente durchführen. Ich bin sehr dankbar für die Unterstützung aller, die Vorschläge gemacht haben. Vielen Dank. :) :)

Elrobis
quelle
Wie wäre es, wenn Sie Ihre LayerName-Variable als öffentlichen statischen Bereich festlegen?
Artwork21
@ artist21, das hört sich gut an, aber ich bin mir immer noch nicht sicher, wie ich über das Gegenstück in der Symbolleiste auf die Instanz einer der beiden Comboboxen zugreifen soll. Weißt Du, was ich meine? Ich vermute, es ist eine einfache grundlegende Sache, die mir einfach nicht bewusst ist.
Elrobis
Das klingt nach einer anderen Frage. Mir ist nicht klar, warum Sie das tun müssen. Es scheint mir, dass die Feld-Combobox basierend auf der Layer-Kombination gelöscht und neu gefüllt werden sollte. Das Layer-Kombinationsfeld sollte basierend auf dem Listener für Dokumentereignisse ausgefüllt werden.
Rich Wawrzonek
@RichWawrzonek das ist genau richtig. Ich bin mir jedoch nicht sicher, wie ich über die Ebenen-Kombination zur vorhandenen Instanz der Feldkombination gelangen soll. In diesem Fall muss das Tool beide Werte lesen.
Elrobis

Antworten:

4

Soweit ich weiß, haben Sie zwei .NET-ComboBoxen auf einem UserControl, die ICommand und IToolControl implementieren, und Sie möchten einen Verweis auf eines der Kombinationsfelder vom anderen erhalten. Solange sie sich im selben Bereich befinden, sollten Sie nur mit ihren Variablennamen auf sie verweisen können (überprüfen Sie die Namen Ihrer Steuerelemente in Ihrem UserControl-Designer).

Wenn sich die beiden Kombinationsfelder in separaten UserControls befinden, versuchen Sie, das Casting ICommandItem.Commandauf Ihr anderes UserControl durchzuführen.

In diesem Beispiel in der 9.3-Hilfe finden Sie einige Beispiele: Zuletzt verwendete Dateien - Command, MultiItem und ToolControl

Hier ist auch ein Beitrag des ESRI-Forums, in dem dieses Problem behandelt wird: http://forums.esri.com/Thread.asp?c=93&f=993&t=170088

blah238
quelle
Bingo. Diese Zeile hat den Trick gemacht, um von der Instanz von ICommandItemzurück in die von UserControlmir implementierte Klasse zu gelangen : FieldSelectUC fs = fieldsItem.Command as FieldSelectUC;Ich kann jetzt alle Requisiten im Debugger sehen. Vielen Dank an Sie.
Elrobis
Hurra! Ich habe Add-Ins für eine Weile exklusiv verwendet, also musste ich einige alte Sachen durchforsten, um mich daran zu erinnern, wie alles funktioniert hat :) So etwas ist mit Add-Ins in 10 seitdem viel einfacher (wenn auch zugegebenermaßen weniger flexibel) ist ein bestimmter ComboBox-Typ und Sie können einfach auf andere Add-In-Komponenten mit statischen Variablen und Methoden verweisen.
blah238
1
Ja, durch die neuen Add-Ins schien es definitiv einfacher zu sein. Bei der Recherche habe ich Add-Ins und Add-Ins gefunden, die meiner Implementierung jedoch nicht zur Verfügung standen. Das GraphicsLayerToolControl-Beispiel in der .Net-Hilfe (auf meinem System lautet diese Adresse C:\Program Files (x86)\ArcGIS\DeveloperKit\SamplesNET\Desktop\GraphicsLayerToolControl\CSharp\GraphicsLayerToolControl2008.sln), das mir geholfen hat, mit dem UserControl- und Ereignismaterial umzugehen, aber ich konnte einfach nicht herausfinden, wie ich vom COM-Steuerelement aus ein Loch in .Net durchstoßen kann. Ich kann nicht übertreiben, wie dankbar ich bin. Mit freundlichen Grüßen.
Elrobis
2

Immer wenn ich so etwas mache, speichere ich die Ebenen- und Feldnamen in einem statischen Eigenschaftssatz, der in der Symbolleiste enthalten ist. Dann verwende ich einen Dokumentereignishandler, um zu überprüfen, ob Ebenen zu ArcMap hinzugefügt / daraus entfernt wurden oder ob das Dokument geändert wurde. Die Ebenen- und Feldeigenschaften werden aktualisiert, wenn sie vom Benutzer in der Dropdown-Liste geändert werden. Wenn die Ebene aus ArcMap entfernt oder das Dokument geschlossen wird, werden sie auf Null zurückgesetzt. Dann können Sie einfach nach Nullwerten suchen, bevor Ihr Programm ausgeführt wird.

Rufen Sie über die ICommandItem-Schnittstelle einen Verweis auf die Combobox ab:

ICommandItem toolbar = _iApp.Document.CommandBars.Find ("ArcProject.UI.AngryToolbar", false, false);
ICommandItem fieldsItem = (Symbolleiste als ICommandBar) .Find ("ArcProject.UI.FieldSelectUC", false); IComboBox cbo = (IComboBox) fieldsItem; // Benötigt einen Verweis auf ESRI.ArcGIS.SystemUI;

Rich Wawrzonek
quelle
+1 Sie lassen es so einfach klingen. :) Aber das Problem, das ich habe, ist der Zugriff auf die Eigenschaften eines bereits instanziierten Steuerelements aus der Sicht eines anderen Steuerelements. Ich mag Ihre Idee, ihre gemeinsamen Eigenschaften in die Symbolleiste aufzunehmen. Können Sie Ihre Antwort aktualisieren, um zu zeigen, wie ich die Eigenschaften der enthaltenen Symbolleiste aus der Sicht einer der Comboboxen tatsächlich lesen kann? Denn das ist im Grunde das, wonach ich suche. Ich weiß bereits, wie man Dokumentereignisse verwendet, um auf Änderungen am Inhaltsverzeichnis zu warten, daher benötige ich keine Hilfe bei diesem Aspekt. Vielen Dank für Ihre Antwort.
Elrobis
@elrobis Sie können den freigegebenen Status auch in den ICommand selbst einfügen (da es immer nur eine Instanz gibt) und dann die Steuerelemente über help.arcgis.com/en/sdk/10.0/arcobjects_net/componenthelp
Ragi Yaser Burhum
Ragi hat recht. Da Sie die COM-Symbolleiste verwenden, können Sie die UID Ihrer Combobox einfach an die ICommandBar.Find-Methode übergeben, um eine Referenz abzurufen. Sein Link erklärt alles.
Rich Wawrzonek
Ich mag auch Ragis Idee. Es fehlt mir jedoch etwas Grundlegendes. Zum Beispiel gibt diese Zeile eine Null-Symbolleiste zurück (wo thisist eine UserControlmit Beschriftung und Combobox): ICommandBar toolbar = this.Parent as ICommandBar;Es ist diese Art von grundlegender UI-Objektdurchquerung, die mich umbringt. Ich weiß nicht, wie ich zur Symbolleiste zurückkehren soll, um einen Ihrer Vorschläge umzusetzen. (Und tatsächlich gefällt mir die Idee dieser Variablen in der Symbolleiste etwas besser. Ich würde es wahrscheinlich tun, indem ich der Symbolleiste einen öffentlichen Getter hinzufüge, der Ragis Idee anwendet.) Vielen Dank für Ihre weitere Hilfe.
Elrobis
2
Ich denke nicht, dass dies this.Parentfür COM-Schnittstellen gültig ist - das ist ein .NET / Windows Forms-Konzept. Sie möchten die Benutzeroberfläche nicht "durchlaufen", sondern über ihre IDs auf Ihre ICommands zugreifen.
blah238
1

Ich hatte ein ähnliches Problem mit einem benutzerdefinierten Tool. Ich habe ein benutzerdefiniertes Formular, das über eine Schaltfläche in einer AddIn-Symbolleiste in ArcGis 10.x geöffnet wird. Auf diesem Formular befindet sich eine Schaltfläche, mit der die Koordinaten eines Klicks in der Karte abgerufen werden können, einschließlich des Einrastens. Ich konnte das Werkzeug starten und den Klick in der Karte verarbeiten, aber ich konnte den Wert nicht in mein Formular zurückholen, da die Umwandlung in das benutzerdefinierte Werkzeug immer fehlschlug. Die Lösung bestand darin, AddIn von ESRI.ArcGIS.Desktop.AddIns zu verwenden. Auf diese Weise war es einfach, auf alle Eigenschaften und Methoden meines benutzerdefinierten Tools zuzugreifen. Die Dokumentation zu ESRIs finden Sie hier: http://resources.arcgis.com/de/help/arcobjects-net/conceptualhelp/index.html#/Add_in_coding_patterns/0001000000zz000000/

Hier ist das Code-Snippet aus dem OnClick-Event der Schaltfläche im benutzerdefinierten Formular:

//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
quelle