C# Eto Form or Panel from Grasshopper?

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.

// Rolf

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
    }
}

Haven’t had any strange behaviour from it before.

1 Like