Best node choice for a label + optional image

Hi there,

For my app, I want a node that always has a label and sometimes (upon user action) shows a small image (from disk) above the label. If there’s no image, I want the node to look like a plain GoBasicNode (no images, no ports, just the label). If there’s an image, I want the image to show up above the label. I’ve tried the GoIconicNode, but the issues I’ve run into are:

  1. if there’s no image (i.e. the node is initialized with a “null” image), I get a 20x20 black, empty outline (with a dot inside) above the label. I don’t want any of this if there’s no image. I just want the label.
  2. The label is always on a transparent background (setting Label.BackgroundColor to Color.White doesn’t do anything). I want the label to always have a rectangular background behind the text.

My question: what’s the best approach for this?

  1. Pursue the GoIconicNode route, assuming there are fixes for the two issues above?
  2. Create my own GoGroup node, which consists of a GoBasicNode and a GoImage?
  3. Use a GoBasicNode and add a GoImage as a child to it?

Thanks for any suggestions.

Set TransparentBackground to false on the label to get the BackgroundColor.

It sounds like GoIconicNode is pretty close, you just want better behavior when there is no icon.

Let me investigate a bit…

OK, I had a class laying around… this adds LabelSpot from GoBasicNode to GoIconicNode.

And GoIconicNode uses a GoShape if you don’t initialize the icon, so you can cheat a bit on that…

See if this is pretty close to what you want. The selection highlight of this node is still just a box centered over the text, and we can fix that if that’s your only remaining issue…

Updated… I fixed this with a change to LayoutChildren.

initialize thusly:

      GoIconicNode2 noiconnode = new GoIconicNode2();
      noiconnode.Initialize(null, null, "noiconnode GoIconicNode2");
      noiconnode.Location = new PointF(200, 500);
      noiconnode.LabelSpot = GoObject.Middle;
      GoShape ico = noiconnode.Icon as GoShape;
      if (ico != null) ico.Pen = Pens.Transparent;
      noiconnode.Figure = GoFigure.None;
      doc.Add(noiconnode);
  [Serializable]
  public class GoIconicNode2 : GoIconicNode
  {
    public override void LayoutChildren(GoObject childchanged)
    {
      base.LayoutChildren(childchanged);
      if (this.Initializing) return;

      GoObject icon = this.Icon;
      if (icon == null) return;

      GoText label = this.Label;
      if (label != null)
      {
        label.SetSpotLocation(SpotOpposite(this.LabelSpot), icon, this.LabelSpot);
        icon.Bounds = new RectangleF(label.Left, icon.Top, label.Width, icon.Height);
      }
    }
    /// <summary>
    /// Gets or sets the spot at which the <see cref="Label"/>, if any, should be positioned
    /// relative to the <see cref="Shape"/>.
    /// </summary>
    /// <remarks>
    /// This calls the virtual method <see cref="OnLabelSpotChanged"/> to determine
    /// how to change all of the parts of the node appropriately.
    /// </remarks>
    [Category("Appearance"), DefaultValue(MiddleTop)]
    [Description("The spot at which any label is positioned relative to the shape")]
    public virtual int LabelSpot
    {
      get { return myLabelSpot; }
      set
      {
        int old = myLabelSpot;
        if (old != value)
        {
          myLabelSpot = value;
          Changed(ChangedLabelSpot, old, null, NullRect, value, null, NullRect);
          if (!this.Initializing)
          {
            this.Label.Alignment = SpotOpposite(this.LabelSpot);
          }
          LayoutChildren(this.Label);
        }
      }
    }

    public const int ChangedLabelSpot = 2101;
    private int myLabelSpot = MiddleTop;
  }

Thanks, Jake, this got rid of the empty black square. I was getting a black dot centered on the label, obscuring the text. I fixed it by making the port invisible. Let me know if this sounds OK:

[Serializable]
internal class MyNodePort : GoPort {
    public MyNodePort() {
        this.Visible = false;
    } ...

3 remaining issues:

a) In a GoBasicNode, with LabelSpot = Middle, I can set the “padding” around the text with:

MiddleLabelMargin = new SizeF(10F,10F);

This doesn’t work for GoIconicMode: the label background is tight around the text. I supose I can fix it by measuring the label text (TextRenderer.MeasureText) and forcing the Height and Width of the label to be bigger, unless you have a simpler workaround.

b) The label doesn’t have an outline, i.e. no black rectangle around a white background, as in a typical GoBasicNode. I looked for “Outline” or “Pen” on the Label but I don’t see anything like that.

Note: if all else fails, I can fix 1) and 2) by overriding Paint.

c) The selection highlight (with no image) is a box centered on the label, but taller than the label background. Can I make it so that the selection highlight is the size of the label (with no image) and the size of the entire node (label + image) if an image is present?

Thanks for your help.

Yes, that’s a fine way to handle the Port.

a) Add the margin into the setting of the icon.Bounds. (e.g. subtract 5 from .x and add 5 to .width) The height of the icon is already giving you a vertical margin.

b) You can set the Pen of the Icon (which is really a GoShape). … change the initialization code from above to:

  GoShape ico = noiconnode.Icon as GoShape;
  if (ico != null) ico.Pen = Pens.Black;

don’t override Paint. There is (almost, like 99%) always a better, easier way.

GoText doesn’t have a Pen. Just a BackgroundColor.

c) I was just keeping the 20x20 default of the Icon and making it wider… again, you can change the icon.Bounds= how you like.

and the size of the entire node (label + image) if an image is present?

GoIconicNode.SelectionObject defaults to the Icon. You could override it to be the “this” of the node. But that might break some other assumption the code makes. You’d have to try it.

Thanks, Jake. Everything looks good without an image. One question: how should I make the icon visible upon user action? In the code below “node” is an instance of the custom GoIconicNode, “File” (a property of “node”) is an image file. I tried re-initializing the node with an ImageList with the image file, but the node still shows the text only:

ImageList il = new ImageList();
il.Images.Add(Bitmap.FromFile(node.File));
node.Initialize(il, 0, node.Label.Text);
//node.Icon = Bitmap.FromFile(node.File); // just testing

Right now, when creating a node, I Initialize it to just show the label (no image):

internal class CustomNode : GoIconicNode {
    public CustomNode() {
        Initialize(null, null, "Test");

Any help with this? Can’t get this to work…

I think this will work… haven’t tried it.

GoObject img = node,CreateIcon(imgList, index);
node.Icon = img;