Forms, Dialogs and Rhino Documents
Ensuring a Form or Dialog has an appropriate parent host, and is correctly tied to the calling Rhino Document is very important to avoid dialogs hidden behind Rhino, and issues with multiple open documents.
Showing a Form
It is worth noting that at the time of writing when using the script editor, a form may show behind the editor in C# and python. A dialog will temporarily hide the editor until it is closed.
using Rhino.UI; using Eto.Forms; var myForm = new Form(); myForm.Show(__rhino_doc__);
Showing a Dialog
using Rhino.UI; using Eto.Forms; var dialog = new Dialog() { Width = 200, Height = 200 }; var parent = RhinoEtoApp.MainWindowForDocument(__rhino_doc__); dialog.ShowModal(parent); // or // DefaultButton and AbortButton is required for SemiModal dialog.DefaultButton = new Button(); dialog.AbortButton = new Button(); dialog.ShowSemiModal(__rhino_doc__, parent);
Avoid using RhinoEtoApp.MainWindow, it will not work correctly on Mac.
Prefer RhinoEtoApp.MainWindowForDocument(doc)
.
It is also advisable to avoid using RhinoEtoApp.MainWindowForDocument(RhinoDoc.ActiveDoc)
, as this may cause unexpected issues.
RhinoStyle
To ensure your form follows the same style as Rhino, and responds correctly to Dark/Light mode changes from within Rhino or the OS, there is a very handy extension method that can help you. It only needs to be called once at the top level of your Dialog or Form. It can safely be called inside or outside the constructor.
using Rhino.UI; using Eto.Forms; var form = new Form(); form.UseRhinoStyle(); form.Show();
PushPickButton
Push Pick Button can be very helpful for hiding and showing your form whilst the user is performing an action, giving them the full use of their screen real estate.
using Rhino.UI; using Eto.Forms; using Rhino; using Rhino.Input; var parent = RhinoEtoApp.MainWindowForDocument(__rhino_doc__); var label = new Label() { Text = "None" }; var button = new Button() { Text = "Click me!" }; var dialog = new Dialog() { Width = 120, Height = 120, Content = new TableLayout() { Rows = { new TableRow(label), new TableRow(button), } }, DefaultButton = new Button(), AbortButton = new Button() }; button.Click += (s, e) => { dialog.PushPickButton((s, e) => { RhinoGet.GetCircle(out var circle); label.Text = ((int)circle.Radius).ToString(); }); }; EtoExtensions.ShowSemiModal(dialog, __rhino_doc__, parent);
GetRhinoDoc
It’s important to avoid using RhinoDoc.ActiveDoc
especially when programming for mac, or else multiple documents will likely crash your code. This can get tricky when using events such as RhinoApp.Idle
which don’t/can’t provide a Rhino Doc instance.
As long as you use form.Show(doc);
to create your form, form.GetRhinoDoc();
can be called anytime to retrieve the document. Of course, in the script editor, the document can be retried via the script context, but this is not the case elsewhere.
using Rhino.UI; using Eto.Forms; var myForm = new Form(); myForm.Show(__rhino_doc__); var doc = myForm.GetRhinoDoc();
WindowsFromDocument
Returns all the associated windows with the current Document, this can be useful in the inverse scenario where you have access to the RhinoDoc, but not the instance of the form.
using Rhino; using Rhino.UI; using Rhino.DocObjects; using System.Linq; using Eto.Forms; class MyForm : Form {} var myForm = new MyForm() { Width = 100, Height = 100 }; myForm.Content = new Label() { Text = "0" }; void IncrementLabel(object sender, RhinoObjectEventArgs e) { var doc = e.TheObject.Document; var form2 = EtoExtensions.WindowsFromDocument<MyForm>(doc).FirstOrDefault(); var label = form2.FindChild<Label>(); if (int.TryParse(label.Text, out var count)) label.Text = $"{++count}"; } myForm.Shown += (a, b) => { RhinoDoc.AddRhinoObject -= IncrementLabel; RhinoDoc.AddRhinoObject += IncrementLabel; }; myForm.Closed += (s, e) => { RhinoDoc.AddRhinoObject -= IncrementLabel; }; myForm.Show(__rhino_doc__);
LocalizeAndRestore
After constructing a window calling this will store the location of the window so when it is closed it reopens in the same place. This information is stored permanently, so the window will keep its position between Rhino sessions.
using Rhino.UI; using Eto.Forms; class MyDialog : Dialog {} var myDialog = new MyDialog(); myDialog.LoadComplete += (s, e) => myDialog.LocalizeAndRestore(); myDialog.ShowModal();
To and from Eto
Moving between System.Drawing
and Eto is often required when using Rhino SDKs or working with Non-Eto UIs on Windows. Eto provides lots of handy dandy ToEto(...)
methods that can be called on most Eto types. See below for just a few. Note that some are available in Eto, but many additions are included within Rhino.
public static Font ToEto(...); public static Bitmap ToEto(...); public static Icon ToEto(...); public static Color ToEto(...); public static Image ToEto(...);
Eto also includes many corresponding ToSD or ToSystemDrawing methods as well should you need to move from Eto.Forms
types to System.Drawing
types.
public static SizeF ToSD(...); public static RectangleF ToSD(...); public static Point ToSD(...); public static PointF ToSD(...); public static Color ToSystemDrawing(...);
Relative Positioning
Eto.Forms.Control
is always positioned at the Top Left.Locations of Eto Controls are relative. If you call myControl.Location
the position returned is not the absolute position on the screen.
Let’s take the example of the Table Layout from the Containers Page and add a some code to get the positions of the buttons.
dialog.LoadComplete += (s, e) => { foreach(var button in dialog.Children.OfType<Button>()) { button.Text = button.Location.ToString(); } }; dialog.ShowModal(parent);

Obviously this is useful in the context of the Dynamic Layout, but outside of the context it’s not useful. Enter PointToScreen(...)
which returns the coordinates of a control relative to another control, such as the ParentWindow or even the Screen. Make sure to move the Dialog once its loaded.
dialog.LocationChanged += (s, e) => { foreach(var button in dialog.Children.OfType<Button>()) { var location = button.PointToScreen(button.Location); button.Text = location.ToString(); } }; dialog.ShowModal(parent);
