Why I cannot move a custom component?

I am doing a custom component with custom attribute
I add a mouse down method to handle mouse down event(just change the text and color of the button):

but when i try this component in grasshopper,I found that it cannot be drag and move…
do some one know where is the problem,
Thank you!

this is all code:

    using System;
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Forms;
    using Rhino;
    using Grasshopper.Kernel;
    using Rhino.Geometry;
    using System.Drawing;
    using Grasshopper.Kernel.Attributes;
    using Grasshopper.GUI.Canvas;


    namespace FabUnionRobot
    {
    public class AttributesCustom1 : GH_ComponentAttributes

    {


        public AttributesCustom1(GH_Component owner) : base(owner)
        {
        }

        protected override void Layout()
        {
            base.Layout();
            //  Bounds = new RectangleF(100, 200, 30, 60);

            Rectangle r = GH_Convert.ToRectangle(this.Bounds);
            r.Height += 40;
            // this.Bounds = new RectangleF(Pivot, new SizeF(100, 50));
            this.Bounds = r;
        }
        //public override void ExpireLayout()
        //{
        //    base.ExpireLayout();
        //}
        private RectangleF rec2 { get; set; }
        public override Grasshopper.GUI.Canvas.GH_ObjectResponse RespondToMouseDoubleClick(GH_Canvas sender, Grasshopper.GUI.GH_CanvasMouseEvent e)
        {

            if (this.rec2.Contains(e.CanvasLocation))
            {
                System.Windows.Forms.MessageBox.Show("hello");
            }
            Owner.ExpireSolution(true);
            return Grasshopper.GUI.Canvas.GH_ObjectResponse.Handled;

        }
        public override Grasshopper.GUI.Canvas.GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, Grasshopper.GUI.GH_CanvasMouseEvent e)
        {
            if(((e.Button == MouseButtons.Left) && this.rec2.Contains(e.CanvasLocation)) && (this.Owner != null))
            // if (this.rec2.Contains(e.CanvasLocation))
            {
                (Owner as ComponentTest1).IsBtnDown = true;
                Owner.ExpireSolution(true);
                return GH_ObjectResponse.Capture;
            }
            return Grasshopper.GUI.Canvas.GH_ObjectResponse.Handled;
        }
        protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel channel)
        {
            rec2 = new RectangleF(Pivot.X - ((this.Bounds.Width - 4) / 2)+2, Pivot.Y+25, this.Bounds.Width - 4, 25);
            //var rec3 = new RectangleF();
            base.Render(canvas, graphics, channel);
            SolidBrush brush = new SolidBrush(Color.Pink);
            Pen pen = new Pen(brush, 1);
            Font font = new Font("MS UI Gothic", 3, FontStyle.Italic);
            if (channel == GH_CanvasChannel.Objects)
            {
                bool isbd = (Owner as ComponentTest1).IsBtnDown;
                GH_Capsule gH_Capsule;
                if (isbd)
                {
                    gH_Capsule = GH_Capsule.CreateTextCapsule(rec2, rec2, GH_Palette.Black, "btndown", 2, 0);
                    gH_Capsule.Render(graphics, this.Selected, base.Owner.Locked, false);
                    
                }
                else
                {
                    gH_Capsule = GH_Capsule.CreateTextCapsule(rec2, rec2, GH_Palette.Grey, "button", 2, 0);
                    gH_Capsule.Render(graphics, this.Selected, base.Owner.Locked, false);
                    
                }
                gH_Capsule.Dispose();
            }
        }
    }
    public class ComponentTest1 : GH_Component
    {
        /// <summary>
        /// Initializes a new instance of the ComponentTest class.
        /// </summary>
        /// 
        public int V { get; set; }
        public bool IsBtnDown { get; set; }

        public ComponentTest1()
          : base("ComponentTest", "CT",
              "This is the Description",
              CommonValues.Test, CommonValues.subCatalogOthers)
        {
            V = 0;
        }
        public override void CreateAttributes()
        {
            base.m_attributes = new AttributesCustom1(this);
        }


        /// <summary>
        /// Registers all the input parameters for this component.
        /// </summary>
        protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
        {
            pManager.AddNumberParameter("input", "v", "adfadf", GH_ParamAccess.item);
        }

        /// <summary>
        /// Registers all the output parameters for this component.
        /// </summary>
        protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
        {
            pManager.AddNumberParameter("output", "v", "adfadf", GH_ParamAccess.item);
        }

        /// <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)
        {
            
            //int n =  this.Params.Input[0].SourceCount;

            //Message = $"{n}";
            //DA.SetData("output",n);

        }

        /// <summary>
        /// Provides an Icon for the component.
        /// </summary>
        protected override System.Drawing.Bitmap Icon
        {
            get
            {
                //You can add image files to your project resources and access them like this:
                // return Resources.IconForThisComponent;
                return Resource1.CustomCmd;
            }
        }

        /// <summary>
        /// Gets the unique ID for this component. Do not change this ID after release.
        /// </summary>
        public override Guid ComponentGuid
        {
            get { return new Guid("8412de8e-6d7b-4ac7-a1e9-1235320f71a2"); }
        }
    }
}

You’re overriding the behaviour of mousedown, so at no point is the base class mousedown handled which is required for a drag operation.

You also shouldn’t capture the focus unless you plan to release it during a MouseUp. I’d recommend returning only Handled states from the UI response methods.

try this:

public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
{
  if (e.Button == MouseButtons.Left)
    if (rec2.Contains(e.CanvasLocation)
      if (Owner != null)
      {
        (Owner as ComponentTest1).IsBtnDown = true;
        Owner.ExpireSolution(true);
        return GH_ObjectResponse.Handled;
      }
  return base.RespondToMouseDown(sender, e);
}
1 Like

Thank you David,it works
but only one question:


if mouse down in the area of button,then release the mouse outside the component
the button cannot change into its initial state…
i see your button works very nice…image
i add the following code in the end of MouseUp method

 public override Grasshopper.GUI.Canvas.GH_ObjectResponse RespondToMouseUp(GH_Canvas sender,         Grasshopper.GUI.GH_CanvasMouseEvent e)
    {
      bool bd= (Owner as ComponentTest1).IsBtnDown ;
        if ((e.Button == MouseButtons.Left) && (this.Owner != null)&&(bd==true))
        {
            (Owner as ComponentTest1).IsBtnDown = false;
            Owner.ExpireSolution(true);
            return GH_ObjectResponse.Handled;
        }
        return base.RespondToMouseUp(sender, e);
       
    }

but it doesnot work…
ask for help @DavidRutten

In RespondToMouseUp(), return GH_ObjectResponse.Release when from RespondToMouseDown() or RespondToMouseMove() you have returned GH_ObjectResponse.Handled or GH_ObjectResponse.Captured.

Note that this should only happen when you want interaction, i.e. when it enters its if statement, in the other cases you should return GH_ObjectResponse.Ignore or call the base method (which will surely return this).

These methods do not work as an event handler, but as a checker that says there has been interaction (and it is the developer who takes care of this by returning Handled or Capture), the only thing GH does is to stop the search for possible interactions when it finds these results in some attribute. Please David, correct me if I’m wrong.

@Dani_Abalde’s advice is probably what you need.

Let me elaborate on the GH_ObjectResponse enum meanings. If you return Ignore, then the event (be it mousemove, mouseup, mousedoubleclick, whatever) will be passed on to the next object. If all objects ignore the event, then the canvas may choose to handle it. In the case of mouse-down, it may start a Drag interaction or a Window select, depending on where and what.

If you return Handled, it means you did something and since each UI event should only do one thing it will not be processed any further. Typically if you respond to an event you return Handled and that’s the end of it.

In certain rare cases you want to respond to a mousedown event, and then keep handling all subsequent events until something happens. For example the slider wants to handle a mousedown on the grip, and then handle all mousemove events until there’s a mouseup. For such tricky behaviour you have to use the Capture and Release states. Capture tells Grasshopper that you handled the event and that you want first dibs on all other events from now on. When you Release yourself from this state the events will be once available to all objects and the canvas. So if you capture but never release, you really mess with the default UI behaviour.

2 Likes