[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

User Control input / output with dynamic graphic elements or better use form?

Posted on 2005-04-28
15
Medium Priority
?
388 Views
Last Modified: 2010-04-16
I want to create something like a tool to control light color and intensity along time axis.

There are waypoints (in this simple example two: a pure green and a pure blue one) with specified color and intensity (the height of the polygon). Between the waypoints light is smoothly blended (therefore the gradient story...)

As there can be hundreds of waypoints, I need to have a long polygonal shape that would look more or less like the teeth of a saw. Each waypoint should be moveable in x and / or y direction with something like a rectangle (or maybe a button) that can be clicked with the mouse and then should make the waypoint moveable.

I want to know, if I can create such dynamic editable polygon "thing", namely my graphical light timeline representation via a user control, or if this works only within the main form...

So I need a dynamic control that resizes / reshapes itself dependent on user editing.

If this is possible with the custom control, how can I send / receive data like polygon point coordinates to / from it?

I have the following code that was put to a custom control to avoid flickering! Flickering is an important issue and may not appear in my app, so if you answer, please keep the flickering issues in mind!

---

The code of the custom control with a simpel 2-waypoint-example with green-to-blue color gradient,
works really FLICKER-FREE:

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace Test
{
  /// <summary>
  /// Summary description for GradientPanel.
  /// </summary>
  public class GradientPanel : System.Windows.Forms.UserControl
  {
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.Container components = null;

    public GradientPanel()
    {
      // This call is required by the Windows.Forms Form Designer.
      InitializeComponent();

      // Add any initialization after the InitializeComponent call
      this.SetStyle( ControlStyles.UserPaint, true );
      this.SetStyle( ControlStyles.AllPaintingInWmPaint, true );
      this.SetStyle( ControlStyles.DoubleBuffer, true );
      this.SetStyle( ControlStyles.SupportsTransparentBackColor, true );

      this.BackColor = Color.Transparent;

    }

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    protected override void Dispose( bool disposing )
    {
      if( disposing )
      {
        if(components != null)
        {
          components.Dispose();
        }
      }
      base.Dispose( disposing );
    }

    #region Component Designer generated code
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
      //
      // GradientPanel
      //
      this.Name = "GradientPanel";
      this.Size = new System.Drawing.Size(240, 212);
      this.Resize += new System.EventHandler(this.GradientPanel_Resize);
      this.Paint += new System.Windows.Forms.PaintEventHandler(this.GradientPanel_Paint);

    }
    #endregion

    private void GradientPanel_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    {
   
      Graphics g = e.Graphics;

      Point[] PolyPoints = new Point[4];
      PolyPoints[0] = new Point(0, 0);
      PolyPoints[1] = new Point(this.Width, 0);
      PolyPoints[2] = new Point(this.Width, this.Height);
      PolyPoints[3] = new Point(0, this.Height / 2);

      LinearGradientBrush myBrush =
        new LinearGradientBrush(new Point(0,0), new Point(this.Width, 0),
        Color.Green, Color.Blue);

      g.FillPolygon(myBrush, PolyPoints, FillMode.Alternate);

      myBrush.Dispose();

    }

    private void GradientPanel_Resize(object sender, System.EventArgs e)
    {
      this.Invalidate();
    }

  }
}



*********************

The code of the simple demo app:


using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace Flicker1
{
       public class Form1 : System.Windows.Forms.Form
       {
                private Test.GradientPanel gradientPanel1;
                private System.ComponentModel.Container components = null;

                public Form1()
      {
             InitializeComponent();
           }

      protected override void Dispose( bool disposing )
      {
                  if( disposing )
              {
            if (components != null)
                         {
                  components.Dispose();
                       }
               }
                 base.Dispose( disposing );
        }

          #region
           private void InitializeComponent()
           {
            System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(Form1));
            this.gradientPanel1 = new Test.GradientPanel();
            this.SuspendLayout();
            //
            // gradientPanel1
            //
            this.gradientPanel1.BackColor = System.Drawing.Color.Transparent;
            this.gradientPanel1.Location = new System.Drawing.Point(40, 32);
            this.gradientPanel1.Name = "gradientPanel1";
            this.gradientPanel1.Size = new System.Drawing.Size(696, 176);
            this.gradientPanel1.TabIndex = 0;
            this.gradientPanel1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.gradientPanel1_MouseMove);
            //
            // Form1
            //
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));
            this.ClientSize = new System.Drawing.Size(800, 422);
            this.Controls.Add(this.gradientPanel1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);

        }
      #endregion
 
      [STAThread]
      static void Main()
       {
      Application.Run(new Form1());
       }

        private void gradientPanel1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            gradientPanel1.Width = Cursor.Position.X;
            gradientPanel1.Refresh();    
        }
    }
}


Thank you very much for helping me with this issue!
0
Comment
Question by:i-Thomas
  • 9
  • 4
14 Comments
 
LVL 21

Expert Comment

by:Yurich
ID: 13891475
probably it's not exactly (or completely) what you want but have you thought of using a trackbar?

regards
0
 

Author Comment

by:i-Thomas
ID: 13892349
Hello Yurich!

No, a trackbar does not meet it. Did you check my sample code and what it does? Imagine this, but n times copied side-by-side and mirrored on the horizontal axis, of course.


I will make a graphical design and post a link here in a few hours. Obviously it is a bit difficult to understand what I want to do when I just use words to describe.
0
 

Author Comment

by:i-Thomas
ID: 13892899
Ok... here we go:

http://www.ingot.de/ee/usercontrol/DynamicUserControl.jpg


The diagram will be "very" dynamic, that means the height, width will be resizable as well as number of waypoints. Each waypoint can be adjusted with the handle as illustrated in the image with the arrows, waypoints can be added / deleted / inserted. Moreover it should support zooming in / out.

I do not expect the full code for such complex control, I would like to get advice

a) if I can / should / cannot use a UserControl for this ->

if yes: how to get parameters in / out of this UserControl and how to add / remove waypoints

if no: how to realize it on a panel on the main form without flickering?


If there are further questions, please let me know, I will answer everything as detailed as possible to avoid guessing on your side!


Thanks a lot in advance for caring!


0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
LVL 96

Accepted Solution

by:
Bob Learned earned 2000 total points
ID: 13894820
A picture is worth 1000 words :)

I was bored, with nothing to do, so here you go:

using System;
using System.Collections;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace Test
{

  /// <summary>
  /// Summary description for MultiGradientPanel.
  /// </summary>
  public class MultiGradientPanel : System.Windows.Forms.UserControl
  {

    public class PathPoint
    {

      public Point RegionPoint;
      public Color RegionColor;

      public PathPoint(int x, int y, Color color)
      {
        RegionPoint = new Point(x, y);
        RegionColor = color;
      }

    }

    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.Container components = null;

    /// <summary>
    /// All the points for the region.
    /// </summary>
    private ArrayList _pointsXAxis = new ArrayList();

    private int _boundsX = Int32.MinValue;
    private int _boundsY = Int32.MinValue;

    // The start of the path is left-bottom.
    Point _origin;

    public MultiGradientPanel()
    {
      // This call is required by the Windows.Forms Form Designer.
      InitializeComponent();

      // Add any initialization after the InitializeComponent call
      this.SetStyle( ControlStyles.UserPaint, true );
      this.SetStyle( ControlStyles.AllPaintingInWmPaint, true );
      this.SetStyle( ControlStyles.DoubleBuffer, true );
      this.SetStyle( ControlStyles.SupportsTransparentBackColor, true );

      this.BackColor = Color.Transparent;

    }

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    protected override void Dispose( bool disposing )
    {
      if( disposing )
      {
        if(components != null)
        {
          components.Dispose();
        }
      }
      base.Dispose( disposing );
    }

    #region Component Designer generated code
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
      //
      // MultiGradientPanel
      //
      this.Name = "MultiGradientPanel";
      this.Size = new System.Drawing.Size(240, 212);
      this.Resize += new System.EventHandler(this.MultiGradientPanel_Resize);
      this.Paint += new System.Windows.Forms.PaintEventHandler(this.MultiGradientPanel_Paint);

    }
    #endregion

    private void MultiGradientPanel_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    {

      if (this.Points.Count  > 0)
      {
        this.DrawPanel(e.Graphics);
        this.DrawSeparatorSegments(e.Graphics);
        this.DrawUpperLine(e.Graphics);
        this.DrawHandles(e.Graphics);
      }

    }


    public void AddPoint(int x, int y, Color color)
    {

      // Allow enough space for the left handle to be drawn.
      // Push all the points to the right by 10 pixels.
      x += 10;

      this.Points.Add(new PathPoint(x, y, color));

      _boundsX = Math.Max(_boundsX, x);
      _boundsY = Math.Max(_boundsY, this.Height - y);

    }


    public ArrayList Points
    {
      get {return _pointsXAxis;}
      set {_pointsXAxis = value;}
    }


    private void DrawPanel(Graphics g)
    {

      try
      {

        // How many segments needed to be drawn.
        int count = _pointsXAxis.Count;

        // Build color blends using the colors specified.
        // The factors are percentages from 0 to 1.
        Color[] colorList = new Color[count];
        float[] factorList = new float[count];

        // Instantiate a new path.
        GraphicsPath path = new GraphicsPath();

        // Start at the origin.
        Point start = _origin;

        // Add all the lines for the polygon to the path.
        int index = 0;
        foreach (PathPoint current in _pointsXAxis)
        {

          // Add a line segment.
          path.AddLine(start, current.RegionPoint);

          // Move to the next point.
          start = current.RegionPoint;

          // Add each color to the blend.
          colorList[index] = current.RegionColor;

          // Calculate the factor percentage to spread the colors.
          factorList[index] = (float)(current.RegionPoint.X - 10) /
            (float)(_boundsX - 10);

          // Increment the point counter.
          index +=1;
        }

        // Create a color blend, so that each gradient color will
        // transition across the control.
        ColorBlend blend = new ColorBlend(count);
        blend.Colors = colorList;
        blend.Positions = factorList;

        // Close the figure.
        Point pt = new Point(start.X, this.Height - 1);
        path.AddLine(start, pt);
        path.AddLine(pt, _origin);
        path.CloseFigure();

        // Create a rectangle region to define the gradient brush.
        PathPoint firstPoint = (PathPoint)this.Points[0];
        Rectangle rect = new Rectangle(firstPoint.RegionPoint, new Size(_boundsX, _boundsY));

        // Define the gradient brush, that will transition gradient
        // colors across the control.
        LinearGradientBrush myBrush = new LinearGradientBrush(rect, this.BackColor,
          this.BackColor, LinearGradientMode.Horizontal );

        // Tell the brush that there will be multiple colors.
        myBrush.InterpolationColors = blend;

        // Fill the path with gradient colors.
        g.FillPath(myBrush, path);

        // Draw the figure.
        g.DrawPath(Pens.Black, path);

        // Release the resources for the brush.
        myBrush.Dispose();

      }

      catch(Exception ex)
      {
        MessageBox.Show(ex.ToString());
      }

    }


    private void DrawSeparatorSegments(Graphics g)
    {

      for (int index = 0; index < this.Points.Count; index++ )
      {

        PathPoint pt = (PathPoint)this.Points[index];
        Point start = pt.RegionPoint;
        Point end = new Point(start.X, this.Height - 1);

        g.DrawLine(Pens.Black, start, end);

      }

    }


    private void DrawUpperLine(Graphics g)
    {

      PathPoint pt = (PathPoint)this.Points[0];
      Point start = pt.RegionPoint;

      Pen thick = new Pen(Brushes.Black, 3);

      for (int index = 1; index < this.Points.Count; index++ )
      {

        pt = (PathPoint)this.Points[index];

        Point end = pt.RegionPoint;

        g.DrawLine(thick, start, end);

        start = end;

      }

      thick.Dispose();

    }

   

    private void DrawHandles(Graphics g)
    {

      foreach (PathPoint pt in this.Points )
      {

        Rectangle rect = new Rectangle(pt.RegionPoint.X - 5, pt.RegionPoint.Y - 5,
          10, 10);

        g.DrawEllipse(Pens.DarkGray, rect);
        g.FillEllipse(Brushes.DarkGray, rect);

      }

    }

   
    private void MultiGradientPanel_Resize(object sender, System.EventArgs e)
    {
   
      this.Invalidate();

      // Define the figure origin.
      _origin = new Point(10, this.Height - 1);

    }

  }
}


Test usage on a form:
      this.multiGradientPanel1.AddPoint(0, 20, Color.LightCoral);
      this.multiGradientPanel1.AddPoint(100, 50, Color.LightGreen);
      this.multiGradientPanel1.AddPoint(200, 20, Color.LightYellow);


Bob
0
 

Author Comment

by:i-Thomas
ID: 13895934
Hi Bob!

Thank you very much !!! I am happy that my illustration helped.

I will spend my weekend digging into it and I am SURE I will LEARN a lot :-)

I will post my experiences here!
0
 

Author Comment

by:i-Thomas
ID: 13910784
Hi Bob!

This is so pretty nice what you've done for me... I don't want to exaggerate, but I ... really feel deeply thankful.

a question:

you used variable names like "_boundsX" ... what special mark is the underscore? for what kind of variables?
Is this your style or official .NET programming style?

And... don't misunderstand me:

When I change the width of the control, the rightmost segment changes in width / is cut away. If I want the whole control to be scaled evenly, do I have to re-calculate the points in the ArrayList? Or is there more elegant / automatic way?

In any case, I think I have understood how user controls work and that in principle they are complete "sub-programs" that can use own class methods etc.


Best regards!

i-Thomas



0
 
LVL 96

Expert Comment

by:Bob Learned
ID: 13910866
The underscore was a trick that I learned from a VB.NET trainer to indicate internal fields that are bound to external properties, which is where I started out going, and then changed my mind.  I didn't see any value to exposing the bounds to the outside world, but I didn't rename the variables.

Are these segments fixed in width?  If so, then you can make a simple change, and say that you want 5 points, and let the control recalculate the point coordinates.

Also, I didn't include anything to let the user click + drag the individual points.  I wanted to make sure that the rendering was appropriate before including anything that complicated.

Bob
0
 

Author Comment

by:i-Thomas
ID: 13958089
Hi Bob!

Yes, the rendering is absolutely adequate. The only thing is that the handles should have rectangular size instead of circular, but that is simply cosmetics. For me it is important to learn and understand the principles.

So if you would like to help with extended version I would be very happy, but on the other hand you did more than enough for me so that I don't really expect anything more.

I could also offer to start another thread with 500 points to reward you, if this is ok from your point of view and also according to ee rules!

Best regards,

i-Thomas
0
 
LVL 96

Expert Comment

by:Bob Learned
ID: 13959222
If you want to figure out the next steps, then I would suggest start a new question.  

Otherwise, is this question completed?

Bob
0
 

Author Comment

by:i-Thomas
ID: 13976209
Yes, it is completed. I missed your last comment somehow.

Should I accept now or tomorrow, when I start new question so that I can post the link here?

Or will you get notified about this thread also after I accepted it?

0
 
LVL 96

Expert Comment

by:Bob Learned
ID: 13976810
You can accept whenever you'd like.  Did you start another question?  If so, what is the URL?

Bob
0
 

Author Comment

by:i-Thomas
ID: 13977422
I will start new question after I managed the object sender issue with my custom control... so hopefully tomorrow.

I will post new link here!
0
 

Author Comment

by:i-Thomas
ID: 14151495
I wil come back soon. This week there is time to code again.
0
 

Author Comment

by:i-Thomas
ID: 14375699
Hi Bob!

Sorry for not coming back till now. Business was throwing me here and there...

I will post new thread ASAP.


Thank you very much again!


Thomas
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article is for Object-Oriented Programming (OOP) beginners. An Interface contains declarations of events, indexers, methods and/or properties. Any class which implements the Interface should provide the concrete implementation for each Inter…
Hello there! As a developer I have modified and refactored the unit tests which was written by fellow developers in the past. On the course, I have gone through various misconceptions and technical challenges when it comes to implementation. I would…
We’ve all felt that sense of false security before—locking down external access to a database or component and feeling like we’ve done all we need to do to secure company data. But that feeling is fleeting. Attacks these days can happen in many w…
Screencast - Getting to Know the Pipeline
Suggested Courses
Course of the Month19 days, 18 hours left to enroll

873 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question