Panel does not close

Hi all,

I simply want to close panel when document is closing. But its not closing, I really don’t know why. Anyone has any idea?

Simple code example :arrow_down:

[Guid("ff4bd166-b1f7-419b-9af4-53daede5e1fb")]
public class MainPanel : Panel
{
    public static System.Guid PanelId => typeof(MainPanel).GUID;

    public MainPanel(uint documentRuntimeSerialNumber)
    {
        RhinoDoc.CloseDocument += OnCloseDocument;
    }

    private void OnCloseDocument(object sender, DocumentEventArgs e)
    {
        Panels.ClosePanel(PanelId );
    }
}

Thanks in advance.
-Oğuzhan

@JohnM - is this something you can help with?

Hi @oguzhankoral,

I haven’t tested this. But this might be a case where you want to, from your RhinoDoc.CloseDocument handler, hookup a RhinoApp.Idle and call Panels.ClosePanel from there.

– Dale

Hi @dale,

I want to make sure that Panel is closed when user closed to RhinoApp. Otherwise it will be annoying for user in some aspects.

If below code that is what you suggest, it is not working.

private void OnCloseDocument(object sender, DocumentEventArgs e)
{
    RhinoApp.Idle += OnPanelClosingIdle;
}

private void OnPanelClosingIdle(object sender, EventArgs e)
{
    RhinoApp.Idle -= OnPanelClosingIdle;
    Panels.ClosePanel(PanelId);
}

-Oğuzhan

I tested the following and I think it does what you want:

  [CommandStyle(Style.Hidden)]
  public class TestAutoClosePanel : Rhino.Commands.Command
  {
    public TestAutoClosePanel()
    {
      TestPanel.RegisterPanel();
    }
    public override string EnglishName => "TestAutoClosePanel";

    protected override Result RunCommand(RhinoDoc doc, RunMode mode)
    {
      Panels.OpenPanel(TestPanel.PanelId);
      return Result.Success;
    }
  }

  [Guid("B0EB1C1C-A61B-4EA3-80A9-7F9CC9A67BDD")]
  public class TestPanel : Panel
  {
    public static Guid PanelId => typeof(TestPanel).GUID;
    public static void RegisterPanel()
    {
      if (!_registered)
        Panels.RegisterPanel(
          CommandsPlugIn.Instance,
          typeof(TestPanel),
          "CloseWithDoc",
          System.Drawing.SystemIcons.Exclamation,
          PanelType.System);
      _registered = false;
    }
    static bool _registered;

    public TestPanel()
    {
      Content = new Button { Text = "Close with doc" };
      if (_OnCloseDoc == null)
        RhinoDoc.CloseDocument += (_OnCloseDoc = RhinoDocCloseDocument);
    }
    static EventHandler<DocumentEventArgs> _OnCloseDoc;

    private static void RhinoDocCloseDocument(object sender, DocumentEventArgs e)
    {
      RhinoApp.Idle += RhinoAppIdle;
    }

    private static void RhinoAppIdle(object sender, EventArgs e)
    {
      // Wait until there is a new active document
      if (RhinoDoc.ActiveDoc != null)
      {
        // Remove idle hook
        RhinoApp.Idle -= RhinoAppIdle;
        // Remove doc closed hook;
        if (_OnCloseDoc != null)
          RhinoDoc.CloseDocument -= _OnCloseDoc;
        _OnCloseDoc = null;
        Panels.ClosePanel(PanelId);
      }
    }
  }
1 Like

I tested this using a per document panel and panel constructor with a uint argument representing a document serial number. Please note that the RhinoDoc and RhinoApp event hooks reference static functions. It is important to avoid situations where a static event hook points to a method that may be garbage collected later and then called again when a document closes which will result in Rhino crashing.

1 Like

Hi @JohnM,

Thanks for your example, I just tested code now sorry for late. It did work for document switching but it is not catching for RhinoApp.Closing event. And also when I try to exit rhino without saving anything, it doesn’t catch RhinoAppIdle.

This is my MainPanel which register panel as PerDoc

static EventHandler<DocumentEventArgs> OnCloseDoc;
static EventHandler OnCloseRhino;

// constructor
public MainPanel(uint documentRuntimeSerialNumber)
{
    var doc = RhinoDoc.FromRuntimeSerialNumber(documentRuntimeSerialNumber);
    ViewModel = doc?.RuntimeData.GetValue(typeof(MyPluginViewModel), rhinoDoc => new MyPluginrViewModel(rhinoDoc.RuntimeSerialNumber));
    DataContext = ViewModel;
    this.Bind(c => c.Content, ViewModel, m => m.Content);
    if (OnCloseDoc == null)
        RhinoDoc.CloseDocument += (OnCloseDoc = OnCloseDocument);
    if(OnCloseRhino == null)
        RhinoApp.Closing += OnCloseRhino = OnRhinoClosing;
}

private void OnRhinoClosing(object sender, EventArgs e)
{
    RhinoApp.Idle += RhinoAppIdle;
}

private static void OnCloseDocument(object sender, DocumentEventArgs e)
{
    RhinoApp.Idle += RhinoAppIdle;
}

private static void RhinoAppIdle(object sender, EventArgs e)
{
    // Wait until there is a new active document
    if (RhinoDoc.ActiveDoc != null)
    {
          // Remove idle hook
           RhinoApp.Idle -= RhinoAppIdle;
           // Remove doc closed hook;
           if (OnCloseDoc != null)
                RhinoDoc.CloseDocument -= OnCloseDoc;
           if (OnCloseRhino != null)
                RhinoApp.Closing -= OnCloseRhino;
           OnCloseDoc = null;
           Panels.ClosePanel(PanelId);
     }
}

public static void RegisterPanel()
{
     var icon = DrawingUtilities.IconFromResource("Resources.myPlugin.ico");
     if (!registered)
           Panels.RegisterPanel(
              MyPlugin.Instance,
              typeof(MainPanel),
              "MyPlugin",
              icon,
              PanelType.PerDoc);
     registered = false;
}
  • I was wondering is this RhinoApp.Closing didn’t catch RhinoAppIdle event handler because of what you warn me.

  • Another thing is that why we don’t have Panels.Unregister(panelId) method. Panels.ClosePanel(panelId) actualy just hide panel. It s not so useful I guess. I need to destroy completely.

Am I missing something?

Thanks a million.

-Oğuzhan

The panel system is not designed to be used in this way. There is no current way to get exactly what you want. I can make a small change in V7 which will allow you to close the panel without having to use the idle event and will actually work when closing Rhino. You might consider using a modeless form instead of a panel to get what you want. Rhino 7 has an extension method for Eto forms that allows you to associate a form with a document, for example:

  [CommandStyle(Style.Hidden)]
  public class TestPerDocForm : Rhino.Commands.Command
  {
    public override string EnglishName => "TestPerDocForm";

    protected override Result RunCommand(RhinoDoc doc, RunMode mode)
    {
      var form = new Form
      {
        Content = new RhinoDialogTableLayout(false)
        {
          Rows =
          {
            new Button{ Text = LocalName }
          }
        },
        Owner = RhinoEtoApp.MainWindow
      };
      form.Show(doc);
      return Result.Success;
    }
  }

I went ahead and added a overloaded method Panels.ClosePanel(Guid,RhinoDoc) which you can call directly from the document closing hook passing the document you get in your hook. This will be available in the next 7.3 pre release.

1 Like

@JohnM, Thanks for considering. I will test it when it is shipped! And If I encounter something I’ll let you know.

Best

This is my test for 7.3 that works when changing the current doc or shutting down Rhino.

  [CommandStyle(Style.Hidden)]
  public class TestAutoClosePanel : Rhino.Commands.Command
  {
    public TestAutoClosePanel()
    {
      TestPanel.RegisterPanel();
    }
    public override string EnglishName => "TestAutoClosePanel";

    protected override Result RunCommand(RhinoDoc doc, RunMode mode)
    {
      Panels.OpenPanel(TestPanel.PanelId);
      return Result.Success;
    }
  }

  [Guid("B0EB1C1C-A61B-4EA3-80A9-7F9CC9A67BDD")]
  public class TestPanel : Panel
  {
    public static Guid PanelId => typeof(TestPanel).GUID;
    public static void RegisterPanel()
    {
      if (!_registered)
        Panels.RegisterPanel(
          CommandsPlugIn.Instance,
          typeof(TestPanel),
          "CloseWithDoc",
          System.Drawing.SystemIcons.Exclamation,
          PanelType.System);
      _registered = false;
    }
    static bool _registered;

    public TestPanel()
    {
      Content = new Button { Text = "Close with doc" };
      if (_OnCloseDoc == null)
        RhinoDoc.CloseDocument += (_OnCloseDoc = RhinoDocCloseDocument);
    }
    static EventHandler<DocumentEventArgs> _OnCloseDoc;

    private static void RhinoDocCloseDocument(object sender, DocumentEventArgs e)
    {
      // Wait until there is a new active document
      if (e.Document != null)
      {
        // Remove doc closed hook;
        RhinoDoc.CloseDocument -= _OnCloseDoc;
        _OnCloseDoc = null;
        Panels.ClosePanel(PanelId, e.Document);
      }
    }
  }

1 Like

@JohnM Thanks a million!