Windows Forms - Track bar - live update to Grasshopper component

Hi,

I have a window form once I double click on the component.
On the windows form I have a track bar - “slider”.

I want to pass value to component from that track bar instantly without pressing update each time.

Is it possible to do so?

I understand that to pass a value from windows form to grasshopper component, I need to write if statement for checking if dialogResult is ok or other state , that requires pressing a windows form button. But is it possible to pass a value without this if statement, or update information live? For instance once I release mouse button or when the track bar value is changed? I attached component and windows form .cs files.

Thank you very much,
Petras

    public void DisplayForm()
    {
        Utilities.CurvatureAnalysis frm = new Utilities.CurvatureAnalysis();
        Grasshopper.GUI.GH_WindowsFormUtil.CenterFormOnCursor(frm,true);

        if (frm.ShowDialog() == DialogResult.OK)
        {
            //Harvest values from form and assing them to local variables
            mPetras = frm.messageForPetras;
        }
            this.ExpireSolution(true);
    }

Component and windows forms visual studio files

2 Likes

There are two (sensible) approaches you can take (more insensible ones).

  1. Have the window update the component when the trackbar value changes.
  2. Have the component respond to the trackbar events directly.

I think I’d use the latter approach, as it keeps the code inside the form to a minimum. The process, irregardless of which approach you take, is somewhat complicated by the Grasshopper solution api. You cannot just set the value of a component, you must make sure the new value is stored somewhere accessible to the component, then you expire the component, then you start a new solution. Finally, inside the SolveInstance method of your component (or inside RunScript if this is a script component) you get the accessible value and do whatever you need to do.

I attached an example about handling winform control events using a C# component.
trackbarevents.gh (4.9 KB)

Thank you:)

When you say “accessible to the component” do you mean that I cannot write this code inside partial windows form class. And the form must be constructed within component?

private void TrackBarValueChanged(object sender, EventArgs e)
{
TrackBar bar = _form.Controls[0] as TrackBar;
if (bar == null)
return;

_value = bar.Value;
Component.ExpireSolution(true);

}

here:

I just meant it has to be in a place where the component can read it. This is not typically the case unless you design the form to be that way. If the component has a reference to the form, the value can be stored there either as a public property or the TrackBar object can be made public. Or maybe you store the value in some shared/static field somewhere.

Do you know what should I do when I want:

  1. Change the slider position within your C# code?
  2. Add additional elements to the windows form?

When I try to add a label and change the position of slider I get the form:

public void InitializeComponent()
{

        form = new Form();

        form.Width = 600;
        form.Height = 200;
        form.FormBorderStyle = FormBorderStyle.FixedToolWindow;
        form.ShowInTaskbar = false;


        //form.SuspendLayout();

        //TrackBar
        TrackBar bar = new TrackBar();
        bar.Location = new System.Drawing.Point(0, 150);
        bar.Size = new System.Drawing.Size(490, 45);
        bar.Minimum = 0;
        bar.Maximum = 100;
        bar.Value = 0;
        bar.Dock = DockStyle.Fill;
        bar.ValueChanged += TrackBarValueChanged;

        //Label
        Label label1 = new Label();
        label1.Location = new System.Drawing.Point(490, 100);
        label1.Size = new  Size(0, 13);
        label1.Text = value.ToString();


        form.Controls.Add(bar);
        form.Controls.Add(label1);
        form.Show(Grasshopper.Instances.DocumentEditor);

        //form.ResumeLayout(false);
        //form.PerformLayout();
    }

Thanks,
Petras

You should never set the location and size of controls, only ever use a Dock type and add them in the correct order. Better yet, add a container control to your form first (TableLayout for example) and populate the cells in the table with your controls (always use control.Dock = DockStyle.Fill in that case).

Really complicated controls become harder and harder to design in code, so this is where the Visual Studio form designer comes in really handy. If you plan on doing stuff like this a lot, consider creating GHA files using VS2015 instead.

Sorry for asking again. I spent today and I could not solve it.
I understand that I need to “update” the component by this.ExpireSolution(true);. But I do not know how to do this correctly, when I make the windows form in Visual Studio. I also tried to compile your code from c# component, and it worked, but due to my programming skills, I do not know how to do the same thing when designing visual studio windows form.

I have a windows form in visual studio and component that opens a windows form already. And I get the value after pressing a button.

How to do the same thing that you did with c# with track bar?

Also I uploaded .cs files, as they are not readable here.CurvatureAnalysis.Designer.txt (5.5 KB)
CurvatureAnalysis.txt (2.0 KB)
Mesh_Cuvartue_Component.txt (10.0 KB)

using System;

namespace Bob.Mesh_Analysis
{
public class Mesh_Cuvartue_Component : GH_Component
{
Utilities.CurvatureAnalysis frm;
string mPetras;

    public override Guid ComponentGuid
    {
        get { return new Guid("{6a3c515d-7217-466e-b11c-bee5e8e03f3a}"); }
    }

    public Mesh_Cuvartue_Component() : base("Mesh Curvature", "Mesh Curvature", "Mesh Curvature", "Bob", "Mesh Analysis")
    {
    }

    public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
    {
        base.AppendAdditionalMenuItems(menu);
        Menu_AppendSeparator(menu);
        Menu_AppendItem(menu, "Learn more", Menu_LearnMore);
    }

    private void Menu_LearnMore(Object sender, EventArgs e)
    {
        DisplayForm();
    }

    public override void CreateAttributes()
    {
        m_attributes = new MySpecialComponentAttributes(this);
    }

    public void DisplayForm()
    {
         frm = new Utilities.CurvatureAnalysis();
        Grasshopper.GUI.GH_WindowsFormUtil.CenterFormOnCursor(frm, true);

        if (frm.ShowDialog() == DialogResult.Retry)
        {
            //Harvest values from form and assing them to local variables
            mPetras = frm.messageForPetras;
        }
        this.ExpireSolution(true);
    }

    public class MySpecialComponentAttributes : GH_ComponentAttributes
    {
        public MySpecialComponentAttributes(IGH_Component Mesh_Cuvartue_Component) : base(Mesh_Cuvartue_Component) { }

        public override GH_ObjectResponse RespondToMouseDoubleClick(GH_Canvas sender, GH_CanvasMouseEvent e)
        {
            ((Mesh_Cuvartue_Component)Owner).DisplayForm();
            return GH_ObjectResponse.Handled;
        }
    }



    protected override Bitmap Icon { get { return null; } }

    protected override void RegisterInputParams(GH_InputParamManager pManager)
    {
         ........
    }

    protected override void RegisterOutputParams(GH_OutputParamManager pManager)
    {
        .....
    }

    protected override void SolveInstance(IGH_DataAccess DA)
    {


        base.Message = "Double Click Me" + mPetras;
        DA.SetData(3, mPetras);
        this.ExpireSolution(true);

}
///////////////////////////////////////////////////////////////////Windows form://///////////////////////////////////////////////////////////////////////////

namespace Bob.Utilities
{
public partial class CurvatureAnalysis : Form
{

    public string messageForPetras;
    public int _value;

    public CurvatureAnalysis()
    {
        InitializeComponent();
        textBox1.Text = "0";
        messageForPetras = "Error";
    }

    private void CurvatureAnalysis_Load(object sender, EventArgs e)
    {
        trackBar1.Minimum = 0;
        trackBar1.Maximum = 100;
        trackBar1.TickStyle = TickStyle.BottomRight;
        trackBar1.TickFrequency = 10;
    }

    public void trackBar1_Scroll(object sender, EventArgs e)
    {
        label1.Text = trackBar1.Value.ToString();
    }

// Here I get the value from Windows form slider:

    private void OnShowAboutBox(object sender, EventArgs e)
    {
        MessageBox.Show("Bob - Grasshopper Add-On by Petras Vestartas 2016");
    }

    private void button1_Click(object sender, EventArgs e)
    {
        string message2 = " Ok";
        messageForPetras = trackBar1.Value.ToString();
    }

    private void button2_Click(object sender, EventArgs e)
    {
        textBox1.Text = DateTime.Now.ToString();
    }
}

}

Can you zip the project and upload it? I’ll need a *.csproj file and the necessary *.cs files. There’s no need to post *.sln or *.suo files, or anything from the bin or obj folder.

Hi,

Bob_csproj and cs files.rar (5.7 KB)
Bob_Whole_Project.rar (4.9 MB)

I am sending:

1 rar file with .csproj and .cs files.
1 rar file with whole project.

In your c# I saw that you referenced Component Class Component.ExpireSolution(true); to update information once the track bar is changed. I do not know how to do the same thing using Visual Studio.
I tried:
Mesh_Analysis.Mesh_Cuvartue_Component.ExpireSolution(true); but I get error “An object reference is required for non-static field, method or property ‘GH_ActiveObject.ExpireSolution(bool)’”

Thank you for your time,
Petras

If your code runs inside a GH_Component, you can just directly call ExpireSolution(true);

But then how to call function from windows form once the track bar is changed properly? I tried to expire solution in solve instance but it results in timer like behavior and updates component constantly.

Yup, you should never expire during solutions. You need to expire your component in response to a UI event like a button click or a trackbar move. I’ll have a look at your project today.

See attached. All code is inside the component, none in the form.

MeshCurvature.zip (9.9 KB)

2 Likes

Dear David Rutten,

Thank you very much, I learned a lot from you:)

Video

1 Like

Hi,

Maybe there is a small function to show windows form if grasshopper window is minimized?
When I minimize the grasshopper window the windows form is minimized too.

Thanks,
Petras

That’s because the form is a child window of the Grasshopper window. The benefit of this is that is will always be on top of the Grasshopper window, but it also means it’ll be minimized along with it. You can make your form be dependent on the Rhino window instead, or even nor make it a subsidiary to any existing form. Just change the Show() method.

Hi, I am very interested in your work. I am curious about how can you know the controls’ names when writing a c# script in grasshopper. Is there any reference? Thank you.

Do you mean communication between windows form and grasshopper component class inputs?

yes, how can I design the winform that can import the parameters into gh file. The winform is not complex, only contains several textboxes and buttons

You can take a look this project file, how windows forms communicate with ghMeshCurvature.zip (9.9 KB)

When you double click component, the windows form pops up, and then inputs are retrieved from it.

You should also considering using eto forms, if you are interested for cross platform development.

1 Like