Drawing lines - another noob question

G

Guest

I am not a programmer, I'm an engineer trying to make an interface to a
product I'm designing. I have used C# to make a form that interrogates the
unit via the serial port and receives the data. I want to be able to draw
lines in a picturebox based upon certain data points I have received. I
dragged a picturebox from the toolbar onto my form, but after having gone
through the help files, looking online and trying a variety of things, I
absolutely cannot seem to draw lines on the picturebox. The sample code in
the help files seems to assume that I knwo more about the process than I do,
as I can find nothing that says, "How to draw lines in a picturebox". I was
able to successfully add a background image, but I'm also wondering if this
graphic data has to accompany the application, or if it gets "embedded" into
the actual application?
At any rate, is there a tutorial that gives some explicit instructions as to
how to use a method (not part of the picturebox click event handler) to write
to the picturebox. If I can't do this, then how can I easily do it? The lines
have to be able to be easily updated when new data are read in.
 
A

Andy Bates

Probably not the most appropriate control to use; the picture box is for
displaying images.

To use the picture box you would have to create an image that contained the
changes you wanted and give it to the picture box. Could be useful if you
wanted to save the image (say for a web page) but not the most efficient way
to go, especially if the data is dynamic (as it sounds yours is).

I'd suggest using a user control. This is a visual control which can be used
to group common controls and functionality or provide custom drawing etc.
User controls can contain the common toolbox elements and also other user
controls so they are very powerful. The other advantage is that if you put
them into their own assembly and ensure that the encapsulation is correct
you can share the control betwen multiple projects very easily (if you need
the same functionality in multiple places).

To do this:

1. Open or create a Windows Forms application and build it.
2. Right click on the project in Solution Explorer and select Add->User
Control. Enter a name i.e. MyUserControl.cs and click OK.
3. A blank control will open up.
4. In the properties, click on the lightning bolt (for events). Scroll down
to Paint and double click on it. This will create a Paint event.
5. In this event place the following code (method signature etc. is shown
but just paste the line in the middle):

private void MyUserControl_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.DrawLine(new Pen(Brushes.Red), new Point(0, 0), new Point(this.Width,
this.Height));
}

6. Build your project.
7. Open the applications main form.
8. On the toolbox on the left hand side (enable via View.Toolbox if not
present) look at the top and you should see a section called "<<AppName>>
Components" and in here should be the component (MyUserComponent) that you
created in step 2 above. Select this and drop it onto the form.
9. The control can now be docked or positioned on the form as you require.
10. Build and run the application. The control will appear with a red line
running through it.

This is just an example, you obviously need to revise the painting above and
add controls etc. to the user control as you application requires but it
should get you started.

HTH

- Andy
 
B

Bob Powell [MVP]

I strongly recommend the Beginners Guide to GDI+ available on my site
followed by constant referral to the GDI+ FAQ.

--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.
 
G

Guest

Andy,
Thanks for the detailed reply. I have to admit much of it does not make
sense to me just reading through it, but I'll give it a try when I have the
chance. I was finally able to get the picturebox writing method to work. The
problem was that I was not "triggering" the call to the line drawing function
properly, so it never ran. I finally was able to step through and find that I
never reached that portion of code, and now (so far!) I think I can do what I
want to do.
As a side note - how long does it normally take to get used to this OOP
thing? I have dabbled in C for several years, and I find it confusing and
frustrating to not be able to do things (such as easily declare global
variables, which I use quite a bit in my microcontroller programs) in C#. I'm
hoping that, as time goes on, the process will become less painful!
Thanks again!
 
G

Guest

Bob,
I did take a look at your webpage, and tried some of the code there, but
unfortunately, it doesn't do what I want. If I understand correctly, the
graphics object just draws within the form you already have generated. In my
forma I have a bunch of butttons, text boxes, etc. on which I don't want to
draw. The background color is red, and I want a space at the bottom that
would be the actual graphics "window". If I make a new form in my project, I
can't seem to find a way to add it to the form I already have so that I can
write to it.
I realize that I'm not paying you (or anyone else) for advice, so I can't
ask for a whole lot, but can you "embed" this graphic object into an existing
form such that it is locked into a particular position and can be written to
without worrying about anything else on the form?
Thanks,
Dave .
 
G

Guest

Andy,
I tried what you suggested, and I did end up with a control on my form that
had a red line through it. However, I now can't find a way to draw graphics
from within my existing form. How do I reference this control from (and I
struggle to know how to say this in OOP context) my main function/method? For
example, I have a method that would look at data in the various text boxes,
do some math, then draw lines in the graphics control based upon those data.
I got this to work using a picturebox control, using the line:
Graphics g = pictueBox1.CreateGraphics(); but if I substitute the name of my
new control for my picturebox, the application won't build. I keep getting
error messages that say that the control name (MyUserControl1) does not exist
in the current context.
Thanks for any light you can add to this!
Dave T.
P.S.
Here's the code for the buttonclick event that is supposed to do the drawing:
private void sendButton_Click(object sender, EventArgs e)
{
if (rpmIn.Text != "")
{
System.Drawing.Pen myPen;
myPen = new System.Drawing.Pen(System.Drawing.Color.Black);
Graphics g = pictureBox1.CreateGraphics();
float line_start = ((float.Parse(seg1startIn.Text) / 1000) *
40) + 35;
float line_end = (float.Parse(seg1endIn.Text) / 1000 * 40) +
35;
float line_height = (float)167 -
(float.Parse(seg1advIn.Text) * (float)3.2);
g.DrawLine(myPen, line_start, 167, line_end, line_height);
myPen.Dispose();
g.Dispose();


}
else
{
MessageBox.Show("No curve data to send. Read distributor
first.");
}
}
 
A

Andy Bates

It's a difficult question to answer regarding OOP. Yes you can still write
simple procedural C type applications but it makes it difficult to use the
..NET framework effectively (you'll effectively be working against it).

OOP provides a lot of power and initially can seem quite daunting but once
you appreciate the way it allows you to work then it makes life a lot
easier.

There are obviously various elements to OOP, including encapsulation and
polymorphism.

In C you would declare structures and global variables to be your data. You
would then write functions to manipulate the data. There is no control so
any function can modify any data so the two are seperate; bugs can be a real
nightmare to track down due to this.

At the simplest level OOP combines the two so data and functionality exist
together. You create a class and the methods it exposes allow you to work
with the class and manipulate the data (which generally should be private to
the class)..

Advantages that this offers is i) the functionality can be re-implemented
without impacting the users (if the interface doesn't change), ii) access to
the data is through a controlled mechanism (the classes interface).

Polymorphism says okay you can now define an abstract class with methods
that have to be implemented (say a shape) with a Draw method. You then
derive shapes from this base class and because the root template stipulates
that the class must have a Draw method you have to implement it. Then when
you have a Shape reference you can always ask it to draw itself; you don't
need to care what the shape is. So it aids extensibility. In this instance a
Shape class wouldn't make sense to instantiate on its own so you'd probably
make it abstract (i.e. so you cannot do new Shape() as it doesn't make
sense).

Interfaces allow you to control polymorphism so that you don't create huge
classes that have the same disparate functionality listed over and over. You
inherit your class from an interface and you then have to implement the
methods that the interface requires (this is the same interface for all
classes that derive from it, so you can query whether an object implements
an interface via if (o is IComparable)...).

You mention that you can't do globals; several solutions to this:

1. Create a singleton.
2. Add a public static field to your own class.
3. Create a class called Globals (name can be changed) and only add static
fields to it (Math class is a prime example but this just contains static
methods).

If you get the model of your application right then all of the pieces fall
into place and the coding becomes a pleasure; however if you get the model
wrong then it can also become a nightmare to work with.

It probably makes sense to start out slowly and try some simple modelling
using OO and then build on it. Once you've tried it I'm 100% confident that
you will not go back to procedural coding which C enforces. It's a case of
biting the bullet and trying it...

- Andy
 
A

Andy Bates

This is an example of the previous post.

You essentially have two classes, the Form and the UserControl The Form
contains the user control so it will have a variable being the user control
it contains. This is an instantiation of the control as the class must be
created to appear on the form.

This will be something like myUserControl1 (look in Form1.Designer.cs) or
click on the control in the form and the properties will show the name that
the control has (in the (Name) property).

This is how the Form talks to the user control and unless exposed it is
private to the Form; so nothing else can change it!

The code below isn't ideal. Click your button and then bring another
application over the form and bring the form back. The line you've drawn
will disppear; until you click the button again.

What you want is to extend the functionality of the UserControl so that when
the button is clicked you call methods/set properties on the user control to
set data within it so that it can display the data on screen.

So in you're example you would possible pass in the line start and line end;
this is the difficult bit as I don't understand what you're trying to do
with the control.

For instance if my user control was a traffic light then I may add a
property called State which could be Red, Amber or Green. When I set this
property the control would update to display the respective state; so I
would hold the state in the control and then Redraw the control to show the
new state (if it had changed).

What you want is for the user control to be given the data it needs to
display the information that you require and no more. The form can then call
the interface and the control can take care of displaying the information on
it's client area. That way you build a useful control that will have a
defined interface (for your purpose) that can be re-used in other
applications.

So you talk to the control via the interface, it takes care of displaying
the data (in it's Paint method) and everyone is happy. The Form doesn't care
how the user control processes the data, it just uses the controls interface
to tell it that something has changed.

The form can only talk to the control it contains from methods within the
Form (you can make the control visible but you're then breaking the
encapsulation) but only using methods/properties that the control exposes
publicly.

- Andy
 
G

Guest

Andy,
I realized from your reply that I didn't explain my app clearly enough. I
have a form with a number of text boxes, etc. This form reads data from the
serial port, and formats it and puts it into the text boxes. At the bottom of
the form I want a graphics display that will show lines representing the
values of these textbox items. Also, I want to be able to change the values
in the text boxes, and have those changes show up on the graph. I did make a
new user control, but so far, I have not been able to get the data from other
methods in my form to display using this user control. Typically, I get a
"variable xxx does not exists in this context or something equally cryptic. I
tried changing the user control .cs so that the part that draws the line was
public instead of private, but that didn't help. I just don't know what to do
at this point. I can make it work using the pictureBox control (which you &
everyone else say I shouldn't do) but no other way. I am stumped. If you know
of a book that has a "this is how you do this" format for simple graphics in
C# (that doesn't assume an intimate knowledge of OOP's incredibly confusing
structure) I'd appreciate a recommendation. I'll buy it and see if I can
figure this out.
Thanks,
Dave T.
 
A

Andy Bates

Dave -

It can be frustrating when you start working with OO but persevere the
results are worth the pain; honest!

So if it's a serial port then does it need to be a scrolling display where
you essentially add a new value, it gets displayed and then when you add
another the same thing happens (like the Task Manager performance monitor)
or does it just display a single line? Essentially what do you want the
control to display.

The Paint method on the control should be prvate. You're trusting the
control that it knows best with regards to how to handle the paint request
and others shouldn't need to interfere with this (i.e. call it directly;
there are indirect ways to do this (i.e. control.Update()).

In the traffic light example it would just be potentially a state. In your
example you need to give it a sensible interface that provides the control
with enough information to support itself.

So if I was writing a BankAccount class I would add a public decimal Balance
property and void Credit/Debit(decimal amount) methods. I could then use the
class. It would have an initial balance and I could work with it. Would
probably need extending to add statement display but I have a basic object I
can ask to do things; as in:

BankAccount myAccount = new BankAccount();
myAccount.Credit(100.0);
myAccount.Debit(50.0);

Your control is exactly the same. You need to provide an interface (not
necessaily a .NET interface but it could be; more like a contract) on the
control that is public and whose methods/properties enable for the control
to render itself on screen. You need to make the control self-sufficient in
the sense that you give it data and it takes the data and does something
sensible with it.

So if I wanted to be able to draw a bar in my control; say a progress bar
which displayed a particular percentage then I would write:

public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
}

int progress = 25;
public int Progress
{
get { return this.progress; }

set
{
// Ensure that we don't get an invalid progress value.
if (value > 100)
throw new ArgumentOutOfRangeException("Progress", "Invalid progress
value specified!");

this.progress = value;

// Refresh our display.
this.Invalidate();
}
}

private void MyUserControl_Paint(object sender, PaintEventArgs e)
{
// Get the graphics object.
Graphics g = e.Graphics;

// Work out the width across we are and display a blue filled rectangle.
int width = this.Width * this.progress / 100;
g.FillRectangle(Brushes.Blue, new Rectangle(1,1, width, this.Height -
1));

// Draw a border around the control
g.DrawRectangle(Pens.Black, new Rectangle(0, 0, this.Width - 1,
this.Height - 1));
}
}

The interface to this control is just a single property called Progress. All
the control needs to display a progress bar is a percentage. So in my
hosting form if I set:

this.myUserControl1.Progress = 50;

The Progress method on the object will be called and set the internal
progress field to 50. It will then update the display to represent 50%.

Use intellisense where you can to avoid the problems that you see below;
type "this." and a list of available properties, fields and methods will
appear. Select myUserControl1 and then type . and again it's available list
of properties, fields and methods will display.

I'm not aware of any good books but I'm sure there will be a few out there;
probably worth going along to a decent book shop and seeing what they have
or perhaps someone else can recommend a book on OO and GDI+?

- Andy
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

Deleted lines 5
How to get pixel in picturebox?? 1
How to draw a one-pixel line?? 33
Image Viewer application 8
Drawing question 4
Drawing Lines in a Picturebox 3
PictureBox problem 1
VB 2005 graphics concepts 12

Top