Thursday, August 26, 2010

How To Get an ArcGIS Add-in Dockable Window


It wasn't as easy as it first appeared. Here is a quote from an ESRI staff member that replied to my plea for help in the forums:
DockableWindow is a bit tricky here since it is implemented by two classes - 1) the UI part is the designer surface derived from controls and 2) the non-UI part is the class actually inherits from DockableWindow base class. 
FromID can only handle types derived from base classes declared in ESRI.ArcGIS.Desktop.AddIns.dll. You have to pass in the type that inherits DockableWindow, which is the inner class of the designer window itself if you are using the Add-in Wizard.
If you go to the xml declaration of the dockable window, you will see the class attribute is set to “DockableWindow1+AddinImpl”. You have to pass this type to get the dockablewindow implementation class first, then you have to expose a property there get a hold of the UI window.
While I won't pretend to understand stand it perfectly yet, I did come up with a solution that works well.

First, I added added a property called "UI" to the innerclass "AddinImpl" within my dockable window class that points to the dockable window class (why isn't this part of the template?):

/// <summary> /// Implementation class of the dockable window add-in. It is responsible for /// creating and disposing the user interface class of the dockable window. /// </summary> public class AddinImpl : ESRI.ArcGIS.Desktop.AddIns.DockableWindow { private DockableWindow1 m_windowUI; // this property is what I added - the rest was generated by the template internal DockableWindow1 UI { get { return m_windowUI; } } public AddinImpl() { } protected override IntPtr OnCreateChild() { m_windowUI = new DockableWindow1(this.Hook); return m_windowUI.Handle; } protected override void Dispose(bool disposing) { if (m_windowUI != null) m_windowUI.Dispose(disposing); base.Dispose(disposing); } }


Then to get the dockable window from another class it was just a few lines:

// get reference to dockable window DockableWindow1.AddinImpl winImpl = AddIn.FromID<DockableWindow1.AddinImpl>(ThisAddIn.IDs.DockableWindow1); DockableWindow1 dockWin = winImpl.UI; // or you could just do this DockableWindow1 dockWin = AddIn.FromID<DockableWindow1.AddinImpl>(ThisAddIn.IDs.DockableWindow1).UI;


I spent hours trying to figure this out. Hopefully this will save you the head-ache. Anyone else out there using Add-ins yet?

25 comments:

  1. Thanks so much, that just saved my day !! :)

    ReplyDelete
  2. Thank you very much ... I spent the whole day with this problem. I tried to find the way to access UserData as in ArcGIS 9.3. You saved the timeline of my project.

    ReplyDelete
  3. Let me add my thanks to the list.

    ReplyDelete
  4. thanks for this, but I could could not add items to my list box in Docwindow from any other class !!!! why this is not possible?


    regards
    Rajesh
    rrajeshh.blogspot.com

    ReplyDelete
  5. Exactly what I was looking for! Thanks for posting it.

    ReplyDelete
  6. I owe you one. WHY IS THIS NOT IN THE DOCUMENTATION ESRI!????

    ReplyDelete
  7. Thank You!!!

    http://i695.photobucket.com/albums/vv311/JVPB1/icecoldbeer344.jpg

    ReplyDelete
  8. Thanks. I then ran into the problem that once closed I could never bring the Dockable Window back again!!

    Anyway, I did this....

    I added the following to my UserControl (in between the Hook and the internal class).

    private UIDClass _uid = null;
    public UID UID
    {
    get
    {
    if (_uid == null)
    {
    _uid = new UIDClass();
    _uid.Value = ThisAddIn.IDs.RoadCATMainDockableWindow;
    }

    return _uid;
    }
    }

    public IDockableWindow DockableWindow
    {
    get
    {
    return ArcMap.DockableWindowManager.GetDockableWindow(UID);
    }
    }


    Then you have all the IDockableWindow stuff available to you - very useful (like your_win_dock_obj.DockableWindow.Dock(esriDockFlags.esriDockShow) ).

    Took me a while to work out how to get a docked window to show again! Or did I miss something obvious here??

    ReplyDelete
  9. I have dockable window in ArcCatalog Add-Ins. Do you know how would I make that dockable window available to ArcMap Add-Ins ?

    ReplyDelete
  10. Big Super Thank You here! I had been fighting with this one, so far, this is the only straight forward answer to this problem.

    You rock!

    ReplyDelete
  11. Have you tried this with WPF? In WPF, m_windowUI is of type ElementHost and DocableWindow1 is a child created off of it. I am struggling with getting hold of DockableWindow there.

    ReplyDelete
    Replies
    1. No, I haven't tried it in WPF. I've gotten away from .NET development in the last few years. Maybe someone else has ideas?

      Delete
  12. Very helpful, thanks for sharing!

    ReplyDelete
  13. Many Thanks! This was not obvious and would have taken lots of time to figure out.

    ReplyDelete
  14. Great post Scott.

    Anon@Sept 4th I am trying to work with support to determine how to do this correctly in WPF; I cannot figure it out.

    I have resorted in the past to finding the dockWin from... inside the dockWin class:

    'run from a button inside the dockWin to save and close it. save code not included.
    'where my dock win is called attributeWindow
    If attributeWindow Is Nothing Then
    Dim attributeWindowID As UID = New UIDClass()
    attributeWindowID.Value = My.ThisAddIn.IDs.attributeWindow
    attributeWindow = My.ArcMap.DockableWindowManager.GetDockableWindow(attributeWindowID)

    End If

    attributeWindow.Show(False)

    'end code


    but it seems ridiculous I have to find by name, the instance of the class I am in. I was hoping Me.Show(False) would close the window :)

    I also tried a DirectCast(Me, IDockableWindow).Show(False) but again it's not a dockWin it's an ElementHost.

    Very annoying, this should be dirt easy.

    Cheers
    Tom

    ReplyDelete
    Replies
    1. Good luck with that. I do almost exclusively javascript development now so I'm afraid I'm not much help.

      Delete
  15. Thank you very much. You saved my day.

    ReplyDelete
  16. Thank you very much!

    ReplyDelete
  17. please help, how can i make this dockable window non resizable.
    here is my complete post
    https://geonet.esri.com/thread/166202

    ReplyDelete
  18. Hi Scott. Thank you very much for this post. I got a couple of headaches trying to figure this out using ESRI's resources. Really shaking my head about why your method is not how ESRI implemented it in the first place, as it lets you access the dockable window like a windows form. My ArcGIS addins are MUCH more robust now with far fewer headaches. I had been using timers and user settings to exchange data.

    For anyone still stuck, I followed Scott's instructions to the letter. Where I deviated is that I first declared (but did not initialize) the dockable window as an instance variable (accessible everywhere) in the tool I was calling the window from:
    public class Tool_AddProject : ESRI.ArcGIS.Desktop.AddIns.Tool
    {
    DockableWindow_Projects dockWin;
    DockableWindow_Projects.AddinImpl winImpl;

    , and then initialized it as he indicated in the tool's constructor (sorry about the formatting):
    public Tool_AddPoint()
    {
    winImpl = AddIn.FromID (ThisAddIn.IDs.DockableWindow_Projects);
    dockWin = winImpl.UI;
    dockWin.Show();

    By declaring it first, you can access the dockable window from any method in your tool class. Scott's method is a significant improvement over using the UserData object (which, admittedly, I never figured out).

    ReplyDelete
  19. please help, how can i make this dockable window non resizable.
    here is my complete post
    https://geonet.esri.com/thread/166202

    ReplyDelete
  20. Probably too late to help anybody but just in case I was able to do this using wpf dockable window. I accomplished this by changing "private System.Windows.Forms.Integration.ElementHost m_windowUI; ' to 'public System.Windows.Forms.Integration.ElementHost m_windowUI; ' in the user control declaration.

    I then was able to get the dockable user control from another class by using:

    var winImp = ESRI.ArcGIS.Desktop.AddIns.AddIn.FromID(ThisAddIn.IDs.CurrentSheet);
    CurrentSheet cs = (AssessorEditTools.CurrentSheet)winImp.m_windowUI.Child;

    ReplyDelete
  21. This comment has been removed by the author.

    ReplyDelete