Are there any working C# examples of Eto.Forms (modal or modeless, or Panels, starting from Grasshopper?
I tried to throw out a simple Hello World form but encountered strange behavior, like when moving the mouse from GH canvas over to Rhino (the dialog disappeared etc).
Searching the forum I found this thread which doesn’t sound too promising for Eto on GH, or has something changed since?
If there are any working examples out there I’d appreciate a link.
I have created my own grasshopper components that implement and show a modal Eto.Forms.
I think the only way to get the scrip to update at the same time as the form would be to have something that writes the output and schedules a solution. However I have no experience with this as all mine only update after completed input.
An example of an input box is as follows:
using System;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Special;
using GH_IO.Serialization;
using Rhino.UI;
using Eto.Drawing;
using Eto.Forms;
namespace Testing
{
public class TestForm : Dialog<Boolean>
{
public double val = 0;
private NumericMaskedTextBox<double> input = new NumericMaskedTextBox<double>();
public TestForm(double num)
{
Title = "Test Input Form";
Padding = new Padding(10);
Resizable = false;
Content = Generate(num);
}
private Control Generate(double num)
{
input.Value = num;
Button Run = new Button();
Run.Text = "Done";
Run.Click += Run_Click;
DynamicLayout Layout = new DynamicLayout();
Layout.Padding = new Padding(10);
Layout.Spacing = new Size(5, 5);
Layout.AddRow(input);
Layout.AddRow(Run);
return Layout;
}
private void Run_Click(object sender, EventArgs e)
{
val = input.Value;
Close(true);
}
}
public class TestInput : GH_Component
{
#region Construction
public double num = 0;
private bool UpdateOut { get; set; }
/// <summary>
/// Initializes a new instance of the PortalFrameStructureInput class.
/// </summary>
public TestInput()
: base("Test", "Test",
"Test Input",
"Category", "SubCategory")
{
UpdateOut = true;
}
/// <summary>
/// Registers all the input parameters for this component.
/// </summary>
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddBooleanParameter("Run", "R", "Run the Stript", GH_ParamAccess.item, false);
}
/// <summary>
/// Registers all the output parameters for this component.
/// </summary>
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddNumberParameter("Input", "I", "Input data type", GH_ParamAccess.item);
}
protected override void ExpireDownStreamObjects()
{
if (UpdateOut)
{
Params.Output[0].ExpireSolution(false);
}
}
public override void AddedToDocument(GH_Document document)
{
base.AddedToDocument(document);
if (Params.Input[0].SourceCount == 0)
{
GH_ButtonObject Button = new GH_ButtonObject();
Button.CreateAttributes();
Button.Name = "Run";
Button.NickName = "Run";
Button.Attributes.Pivot = new System.Drawing.PointF(Attributes.Pivot.X - Button.Attributes.Bounds.Width - 50, Attributes.Pivot.Y - Attributes.Bounds.Height / 2 + 3);
document.AddObject(Button, false);
Params.Input[0].AddSource(Button);
}
}
#endregion
#region Protected Override
/// <summary>
/// This is the method that actually does the work.
/// </summary>
/// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param>
protected override void SolveInstance(IGH_DataAccess DA)
{
DA.DisableGapLogic();
Boolean run = false;
DA.GetData(0, ref run);
bool res = false;
if (UpdateOut)
{
DA.SetData(0, num);
UpdateOut = false;
return;
}
if (run)
{
TestForm Form = new TestForm(num);
res = Form.ShowModal(RhinoEtoApp.MainWindow);
if (res)
{
num = Form.val;
}
}
DA.SetData(0, num);
if (res)
{
UpdateOut = true;
var doc = OnPingDocument();
doc?.ScheduleSolution(5, Callback);
}
}
private void Callback(GH_Document doc)
{
if (UpdateOut)
{
ExpireSolution(false);
}
}
protected override System.Drawing.Bitmap Icon
{
get
{
return null;
}
}
public override Guid ComponentGuid
{
get { return new Guid("abc7e47a-d424-44c3-90cf-5f1fda376ef3"); }
}
#endregion
}
}