SCADA Valve: Overriding SetBoundsCore and GetBoundsCore

Code samples and article discussion

Moderators: Frank Hileman, Anne Szyjan

SCADA Valve: Overriding SetBoundsCore and GetBoundsCore

Postby Frank Hileman » Tue Apr 10, 2007 4:51 pm

This post describes the purpose of the SetBoundsCore and GetBoundsCore methods in a Picture component.

SetBoundsCore is passed a rectangle. If you wish to leave the Picture bounds unchanged, you return false from the function.

So the first thing you might do is see if the passed in rectangle is equal to the current Picture Bounds. But what is the Picture Bounds? That depends on GetBoundsCore. SetBoundsCore is the Bounds property set function, and GetBoundsCore is the Bounds property get function.

The default behavior, when not overridden, is to put a ScalingTranslation on the Picture. This does not go into the Transformation property, but you can see the effect by the difference between the Picture Bounds and the Bounds of the children. See "Sub Pictures have an extra transformation" in the Users Guide for more information, and here: http://www.vgdotnet.org/forums/viewtopic.php?t=39

Override SetBoundsCore to change the default behavior. If you have only a single object in your Picture, you might simply change the Bounds of that object to match the passed in one. This is usually not the case.

GetBoundsCore is only needed if you want to return a "fake" bounds that is not equivalent to the union of the child bounds. For example, you may want some decorations to stick out of your Picture, yet not be considered as part of the Bounds. To do this, modify the "base object", perhaps a Rectangle, to be the same as the passed in bounds in SetBoundsCore. Reposition your decorating objects relative to that. In GetBoundsCore, return only the Bounds of the base object, ignoring the decorations.

You must be consistent between SetBoundsCore and GetBoundsCore. The Get must always return what the Set was passed, with these exceptions:
  • You may limit the width or height to a min or max. Do not try to set both the width and height of any children to 0.
  • You may force the rectangle to always maintain a fixed aspect ratio.
  • You may prohibit any resizing, and return false.

If there is not a logical relationship between SetBoundsCore and GetBoundsCore, the designer will go insane when trying to resize your object.

Here is a SCADA valve sample. Depending on the ifdef, it either enforces no resize at all, or a resize of the Valve ports only. The ports are two lines on each side of the Valve body. By overriding GetBoundsCore we make it appear as if the Valve Bounds does not include the text label.
Code: Select all
// Copyright © 2005 Prodige Software Corporation. All rights reserved.
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using Prodige.Drawing;
using Prodige.Drawing.Styles;

namespace ResizableComponents.UI
{
   /// <summary>
   /// A SCADA-style valve with a custom resizing algorithm.
   /// </summary>
   public class Valve : Picture
   {
      private Prodige.Drawing.Polyline body;
      private Prodige.Drawing.Polyline leftPort;
      private Prodige.Drawing.Polyline rightPort;
      private Prodige.Drawing.Group mainGroup;
      private Prodige.Drawing.Rectangle label;
      private bool open;
   
      public Valve()
      {
         InitializeComponent();   // required by the designer
      }

      /// <summary>
      /// The text to display in this valve.
      /// </summary>
      [DefaultValue("valve")]
      [Description("Text to display in this valve.")]
      public string Text
      {
         get { return label.Text; }
         set { label.Text = value; }
      }

      /// <summary>
      /// Specifies whether this valve is open or closed.
      /// </summary>
      [DefaultValue(false)]
      [Description("Specifies whether this valve is open or closed.")]
      public bool Open
      {
         get { return open; }
         set
         {
            if (value == open)
               return;
            open = value;
            body.Style = open ? "Open" : "Closed";
         }
      }


#if WIDEN_PORTS
      protected override bool SetBoundsCore(FRectangle bounds, BoundsChanges changes)
      {
         // a custom resizing algorithm, to change child objects, instead
         // of scaling
         // only ports get wider; height cannot change
         bounds.Height = mainGroup.Height;

         // don't allow new width to be smaller than body
         float extra = bounds.Width - body.Width;
         if (extra < 0)
         {
            bounds.Width = body.Width;
            extra = 0;
         }
         if (bounds == mainGroup.Bounds)
            return false;
         Vector change = bounds.Location - mainGroup.Location;
         mainGroup.Location += change;
         label.Location += change;

         // center label in x dimension
         label.Location = new Vector(bounds.Left + bounds.Width/2 - label.Width/2,
            label.Location.Y);
         // offset body by half extra
         float halfExtra = extra/2;
         // don't set width and height of polyline both to 0 -- throws exceptions during display
         if (halfExtra == 0)
            halfExtra = 1;
         body.Location = new Vector(mainGroup.Location.X + halfExtra, body.Location.Y);
         // change port lengths
         leftPort.Width = halfExtra;
         rightPort.Width = halfExtra;
         rightPort.Location = new Vector(bounds.Right - halfExtra, rightPort.Location.Y);
         return true;
      }
#else
      protected override bool SetBoundsCore(FRectangle bounds, BoundsChanges changes)
      {
         // a custom resizing algorithm, to change child objects, instead
         // of scaling
         // width and height cannot be changed, only location
         bounds.Width = mainGroup.Width;
         bounds.Height = mainGroup.Height;
         if (bounds == mainGroup.Bounds)
            return false;
         Vector change = bounds.Location - mainGroup.Location;
         mainGroup.Location += change;
         label.Location += change;
         return true;
      }
#endif


      protected override FRectangle GetBoundsCore()
      {
         // label bounds is ignored
         return mainGroup.Bounds;
      }


      #region Designer generated code
      /// <summary>
      /// Required for designer support - do not modify with the code editor.
      /// </summary>
      private void InitializeComponent()
      {
         this.body = new Prodige.Drawing.Polyline();
         this.leftPort = new Prodige.Drawing.Polyline();
         this.rightPort = new Prodige.Drawing.Polyline();
         this.label = new Prodige.Drawing.Rectangle();
         this.mainGroup = new Prodige.Drawing.Group();
         ((System.ComponentModel.ISupportInitialize)(this)).BeginInit();
         //
         // body
         //
         this.body.Closed = true;
         this.body.DrawAction = Prodige.Drawing.ShapeDrawAction.Fill;
         this.body.Points.AddRange(new Prodige.Drawing.Vector[] {
                                                      new Prodige.Drawing.Vector(20F, 100F),
                                                      new Prodige.Drawing.Vector(20F, 120F),
                                                      new Prodige.Drawing.Vector(62F, 100F),
                                                      new Prodige.Drawing.Vector(62F, 120F)});
         this.body.Style = "Closed";
         //
         // leftPort
         //
         this.leftPort.DrawAction = Prodige.Drawing.ShapeDrawAction.Edge;
         this.leftPort.Points.AddRange(new Prodige.Drawing.Vector[] {
                                                         new Prodige.Drawing.Vector(0F, 110F),
                                                         new Prodige.Drawing.Vector(20F, 110F)});
         //
         // rightPort
         //
         this.rightPort.DrawAction = Prodige.Drawing.ShapeDrawAction.Edge;
         this.rightPort.Points.AddRange(new Prodige.Drawing.Vector[] {
                                                         new Prodige.Drawing.Vector(62F, 110F),
                                                         new Prodige.Drawing.Vector(80F, 110F)});
         //
         // label
         //
         this.label.DrawAction = Prodige.Drawing.ShapeDrawAction.Fill;
         this.label.Location = new Prodige.Drawing.Vector(-10F, 120F);
         this.label.Size = new Prodige.Drawing.FSize(100F, 50F);
         this.label.Style = "Label";
         this.label.Text = "valve";
         //
         // mainGroup
         //
         this.mainGroup.Elements.AddRange(new Prodige.Drawing.Element[] {
                                                            this.leftPort,
                                                            this.body,
                                                            this.rightPort});
         //
         // Valve
         //
         this.Elements.AddRange(new Prodige.Drawing.Element[] {
                                                    this.label,
                                                    this.mainGroup});
         this.Styles.Add(new Prodige.Drawing.Styles.Style("Closed", null, new Prodige.Drawing.Styles.SolidFill(System.Drawing.Color.Red), null, null));
         this.Styles.Add(new Prodige.Drawing.Styles.Style("Open", null, new Prodige.Drawing.Styles.SolidFill(System.Drawing.Color.Lime), null, null));
         this.Styles.Add(new Prodige.Drawing.Styles.Style("Label", null, new Prodige.Drawing.Styles.SolidFill(0F), null, null));
         ((System.ComponentModel.ISupportInitialize)(this)).EndInit();

      }
      #endregion
   }
}


More examples:
http://www.vgdotnet.org/forums/viewtopic.php?t=96
Also look at the TransparentButton class in the TransparentButtons sample installed by VG.net.
User avatar
Frank Hileman
Site Admin
 
Posts: 1399
Joined: Sun Jul 25, 2004 8:16 pm
Location: California

Return to Samples

Who is online

Users browsing this forum: No registered users and 1 guest

cron