471,347 Members | 1,700 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 471,347 software developers and data experts.

Pens are tricky, hard to assign (Colors too) inside of paint handler

Just an observation: pens for drawing lines in Win Forms are tricky
when assignment is inside the paint handler.

inside of the Paint handler, but not inside a "using" brace (that is,
outside of "using { Pen mypen = new Pen(Color.Black, 1)) {}), which I
think makes a difference:

I find the following assignment does not work:

//myPenTest instantiated in the normal constructor, as was baseline,
both of Pen class

myPenTest = myobject.tPen; //does not work to assign
properties of myobject to myPenTest
myPenTest.Color = myobject.tPen.Color; //does not work
either

both Pens retain their own Color property. Perhaps Color is hard to
set, or perhaps since it's outside a 'using' bracket the myPenTest is
somehow local. But adding this line (in Paint) did not help:
myPenTest = new Pen(Color.Salmon); //does not help

All is not lost, and in fact this 'failure' is trivial, since color
property is maintained by the pens (albeit not assignable), hence the
myobject.tPen.Color property was successfully used in Paint
handler:

using (Pen pen = new Pen(myobject.tPen.Color, 1))
{
g.DrawLine(pen, XY0, XY1); //works fine, uses
tPen.Color
}

Just an observation. If anybody has seen this before I'd be curious as
to why, but not a big deal.

RL
Aug 19 '08 #1
9 2195
raylopez99 <ra********@yahoo.comwrote:
Just an observation: pens for drawing lines in Win Forms are tricky
when assignment is inside the paint handler.

inside of the Paint handler, but not inside a "using" brace (that is,
outside of "using { Pen mypen = new Pen(Color.Black, 1)) {}), which I
think makes a difference:

I find the following assignment does not work:

//myPenTest instantiated in the normal constructor, as was baseline,
both of Pen class

myPenTest = myobject.tPen; //does not work to assign
properties of myobject to myPenTest
That should be fine.
myPenTest.Color = myobject.tPen.Color; //does not work
either
This will work in some cases, but as the docs say, it will throw an
ArgumentException if you're using one of the predefined pens.
both Pens retain their own Color property. Perhaps Color is hard to
set, or perhaps since it's outside a 'using' bracket the myPenTest is
somehow local. But adding this line (in Paint) did not help:
myPenTest = new Pen(Color.Salmon); //does not help
Could you show a short but complete program which demonstrates the
problem? Here's a very short app which demonstrates simple assignment,
changing the colour of a pen (p2 changes colour from black to red) and
the form which you've had working:

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

class PenTest : Form
{
Pen pen;

public PenTest(Pen pen)
{
this.pen = pen;
Size = new Size(200, 200);
}

static void Main()
{
Application.Run(new PenTest(Pens.Red));
}

protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;

Pen p1 = pen;
g.DrawLine(p1, 0, 20, 100, 20);

Pen p2 = new Pen(Color.Black);
p2.Color = Color.Green;
g.DrawLine(p2, 0, 40, 100, 40);

using (Pen p3 = new Pen(Color.Blue, 1))
{
g.DrawLine(p3, 0, 60, 100, 60);
}
}
}

The result (on my box at least!) is a form with three lines - red, then
green, then blue.

I'm sure with an appropriate failing example, we can sort out the
problem you're having.

--
Jon Skeet - <sk***@pobox.com>
Web site: http://www.pobox.com/~skeet
Blog: http://www.msmvps.com/jon.skeet
C# in Depth: http://csharpindepth.com
Aug 19 '08 #2
OK, I tried various things and I think I figured it out.

Sorry no code, too complicated to try and cut and paste, but here's
what I think is happening:

the runtime error is clearly with the 'using pen {}' code. It only
happens when I resize the form (it works fine to show lines if the
form is never resized, and it's not null since I checked for the
condition: != null, which never occurs for myobject.tPen), because
when I resize the form I run three functions, part of a node manager
(the node manager has a list of lines), the three functions do not use
'new', but only take a scalar (the size of the client screen,
this.ClientSize). However, the myobject.tPen member variable is part
of the node manager. Perhaps, though I can't be sure, when you run
these three functions twice in a row (once in the constructor for the
form, and once upon resizing--this has to occur twice at least once,
upon startup of the form; and these three functions are necessary when
resizing the window because I want to redraw the lines so proportion
is maintained between the lines--I trust you know this trick) somehow
in the background the member myobject.tPen is instantiated again. In
which case, during OnPaint, you will clearly get (for a millisecond,
but enough) garbage collection and the old Pen myobject.tPen will go
out of scope, hence the exception.

Another possibility: Pen is somehow int while myobject.tPen uses
float, but I don't think so (the documentation for Pen is sketchy
IMO).

I tried this below trick but it failed to compile ("Cannot assign to
'pen' because it is a 'using variable'") (had it compiled I'm
confident it would have solved the problem!)

using (Pen pen = new Pen(Color.AliceBlue)) //note: manually insert new
pen Info since seems member variable myobject.tPen from
LineNodeManager cause runtime exception
{
pen.Width = 1.0f;
//trick: //pen = myobject.tPen; //Cannot assign to 'pen'
because it is a 'using variable'
g.DrawLine(pen, myPoint.StartXY, myPoint.EndXY);
}
//BTW the above code works as a fix to the problem, since Pen pen is
being used, not myobject.tPen, but my proposed 'trick' was to see if I
could sneak in the latter member variable in the back door, but I
could not).

Another possibility: I use (and correct me if I'm wrong) a helper
function that is stored in "myobject" to draw these lines. In other
words: MyNodeManagerClass myobjectMgr = new MyNodeManagerClass();
then, a member function of this class:

public void DisplayLine(PaintEventArgs e)
{
Graphics g = e.Graphics;

// etc etc
}

then, from Form1 Paint event handler I call this as follows, without
any 'override' keyword:

private void Form3_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;

myobjectMgr.DisplayLine(e);

// other similar 'helper' graphics methods here
// done to cut down on visual clutter, and also prevent name collision
}

Is this bad style? Let me know if you think this is 'bad style'.
Note Graphics g = e.Graphics is displayed twice, but I don't think
that's a problem.

In short, I solved my problem simply by replacing myobject.tPen with a
newly instantiated Pen, as shown above. The program works fine and
everything is great, even though I don't understand my error. This is
what I was alluding to in an earlier post--you have to either know C#
from the 'compiler' or IL engine level or at a deep level (which may
explain why, upon resizing, perhaps behind the scenes somehow the
member variable pen 'myobject.tPen' is in fact going out of scope),
or, you can learn though experience certain entry points or
'templates' that work, and then use these templates/entry points to
write code that works. Below is an example of this from my personal
diary, to make this point*. (please keep in mind I'm explaining how a
noob like me thinks, not recommending you adopt this yourself, as
clearly you and others here prefer a deeper understanding of the
language).

Hope that make sense and thanks for your reply.

RL

* saw common error in String.ToUpper() function (cannot use by itself,
since C# strings are immutable, but rather must use with another
instantiated string; e.g. string mystring = “sss”;
mystring.ToUpper(); //will not work to convert 'sss' to uppercase,
but : string upperString = new string(); string upperString =
mystring.ToUpper(); //will work, as will: mystring =
mystring.ToUpper(); // in this last case, the ‘old’ string is
technically discarded, with the ‘new’ uppercase string substituted, so
string still immutable.
Aug 19 '08 #3
raylopez99 <ra********@yahoo.comwrote:
OK, I tried various things and I think I figured it out.

Sorry no code, too complicated to try and cut and paste, but here's
what I think is happening:
It would really, *really* help to see some full code, I'm afraid. It
can (and should be!) a very cut down version, with only enough code to
trigger the problem - but without that I can't be confident in what's
wrong.
the runtime error is clearly with the 'using pen {}' code.
What runtime error were you getting?
It only
happens when I resize the form (it works fine to show lines if the
form is never resized, and it's not null since I checked for the
condition: != null, which never occurs for myobject.tPen), because
when I resize the form I run three functions, part of a node manager
(the node manager has a list of lines), the three functions do not use
'new', but only take a scalar (the size of the client screen,
this.ClientSize). However, the myobject.tPen member variable is part
of the node manager. Perhaps, though I can't be sure, when you run
these three functions twice in a row (once in the constructor for the
form, and once upon resizing--this has to occur twice at least once,
upon startup of the form; and these three functions are necessary when
resizing the window because I want to redraw the lines so proportion
is maintained between the lines--I trust you know this trick) somehow
in the background the member myobject.tPen is instantiated again. In
which case, during OnPaint, you will clearly get (for a millisecond,
but enough) garbage collection and the old Pen myobject.tPen will go
out of scope, hence the exception.
Without knowing what exception you're getting, it's hard to comment on
how feasible that is. However, if you've got a reference to the pen
within OnPaint, it shouldn't be garbage collected.
Another possibility: Pen is somehow int while myobject.tPen uses
float, but I don't think so (the documentation for Pen is sketchy
IMO).
Which part of the pen? How would they be different?
I tried this below trick but it failed to compile ("Cannot assign to
'pen' because it is a 'using variable'") (had it compiled I'm
confident it would have solved the problem!)
I don't see why you'd want to initialize a variable and then ignore its
value. I see no reason to think that would be any better than any other
assignment.
using (Pen pen = new Pen(Color.AliceBlue)) //note: manually insert new
pen Info since seems member variable myobject.tPen from
LineNodeManager cause runtime exception
{
pen.Width = 1.0f;
//trick: //pen = myobject.tPen; //Cannot assign to 'pen'
because it is a 'using variable'
g.DrawLine(pen, myPoint.StartXY, myPoint.EndXY);
}
//BTW the above code works as a fix to the problem, since Pen pen is
being used, not myobject.tPen, but my proposed 'trick' was to see if I
could sneak in the latter member variable in the back door, but I
could not).
You really shouldn't need to. Unless you're disposing of the pen
somewhere else, you should be fine to just copy the reference.
Another possibility: I use (and correct me if I'm wrong) a helper
function that is stored in "myobject" to draw these lines. In other
words: MyNodeManagerClass myobjectMgr = new MyNodeManagerClass();
then, a member function of this class:

public void DisplayLine(PaintEventArgs e)
{
Graphics g = e.Graphics;

// etc etc
}

then, from Form1 Paint event handler I call this as follows, without
any 'override' keyword:

private void Form3_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;

myobjectMgr.DisplayLine(e);

// other similar 'helper' graphics methods here
// done to cut down on visual clutter, and also prevent name collision
}

Is this bad style? Let me know if you think this is 'bad style'.
No, I don't think so - although I'd make DisplayLine take a Graphics as
the parameter rather than PaintEventArgs.
Note Graphics g = e.Graphics is displayed twice, but I don't think
that's a problem.
It's pointless, but not a problem.
In short, I solved my problem simply by replacing myobject.tPen with a
newly instantiated Pen, as shown above. The program works fine and
everything is great, even though I don't understand my error. This is
what I was alluding to in an earlier post--you have to either know C#
from the 'compiler' or IL engine level or at a deep level (which may
explain why, upon resizing, perhaps behind the scenes somehow the
member variable pen 'myobject.tPen' is in fact going out of scope),
or, you can learn though experience certain entry points or
'templates' that work, and then use these templates/entry points to
write code that works.
The trouble is that without understanding why it doesn't work, you
won't know what's wrong next time. It could well be that you'll be in a
*similar* situation, but your workaround may not work because the exact
cause is different. In addition, you're quite possibly bending your
design out of shape to work around a problem which can be solved in a
*much* simpler way.

I very much doubt that it really requires a particularly deep knowledge
of C# (and certainly not IL) - but without seeing the code which is
failing, we can't tell.
Below is an example of this from my personal
diary, to make this point*. (please keep in mind I'm explaining how a
noob like me thinks, not recommending you adopt this yourself, as
clearly you and others here prefer a deeper understanding of the
language).

Hope that make sense and thanks for your reply.

* saw common error in String.ToUpper() function (cannot use by itself,
since C# strings are immutable, but rather must use with another
instantiated string; e.g. string mystring = =3Fsss=3F;
mystring.ToUpper(); //will not work to convert 'sss' to uppercase,
More accurately, it will convert 'sss' to upper case as a new string -
which you then ignore.
but : string upperString = new string(); string upperString =
mystring.ToUpper(); //will work
I don't think you meant the first part of that, but the rest is fine.
as will: mystring = mystring.ToUpper();
Yup.
// in this last case, the =3Fold=3F string is
technically discarded, with the =3Fnew=3F uppercase string substituted, so
string still immutable.
It depends on exactly what you mean by "discarded" - the variable now
refers to a different string, certainly.

--
Jon Skeet - <sk***@pobox.com>
Web site: http://www.pobox.com/~skeet
Blog: http://www.msmvps.com/jon.skeet
C# in Depth: http://csharpindepth.com
Aug 19 '08 #4
Thanks for your help. I consider this thread closed since I found a
workaround. Pen is not a problem. I will save uploading code for some
real serious problem; this is not one of them. Programming is not
that well documented IMO--it's trial and error--I'm sure VS2008 is
full of bugs waiting to be discovered. This bug/error is not a
serious problem for me, and I've got bigger fish to fry. I'm happy
for example to have learned a few weeks ago that doing a 'foreach' is
faster in many situations than doing a delegate/event call to
subscriber classes/member variables from a publisher class/variable.
This is precisely the situation in this program, which again uses
"resize" to redraw lines. A line is two points, and if a screen is
resized these two points become different points (relative to the
larger or smaller screen), so, what to do? Use 'foreach' and go
through a collection, updating the points (which is what I'm working
on tonight) or use a publisher-subscriber model? Based on a
discussion a few weeks ago I concluded the former is faster (if you
have fewer than a couple of hundred lines/nodes, as is the case
here). That's why this forum is invaluable--for stuff like that, not
debugging trivial stuff like this thread--so I don't want to waste
your time, you've done enough for me. BTW your book is not that bad,
LOL, but it has to be taken in small doses IMO for it to sink in.

Good night,

RL

Jon Skeet [ C# MVP ] wrote:
raylopez99 <ra********@yahoo.comwrote:
OK, I tried various things and I think I figured it out.
Aug 19 '08 #5
Could not resist getting in one more Parthian shot: here is the
compiler error
System.ArgumentException: Parameter is not valid.
at System.Drawing.Graphics.CheckErrorStatus(Int32 status)
at System.Drawing.Graphics.DrawLine(Pen pen, Single x1, Single y1,
Single x2, Single y2)
at System.Drawing.Graphics.DrawLine(Pen pen, PointF pt1, PointF
pt2)
It's quite possible that somehow, despite my double checking (and
again this is a moot point now that I've solved the problem by
rewriting some code), that it's a simple 'cast' problem where a point
is float and another point is Single (int?), nothing more serious than
that.

But it's moot.

RL
Aug 19 '08 #6
raylopez99 <ra********@yahoo.comwrote:
Could not resist getting in one more Parthian shot: here is the
compiler error
Just to be pedantic, more for the sake of future posts than anything
else, that's an exception which occurred at execution time. Compiler
errors occur at compile time.
System.ArgumentException: Parameter is not valid.
at System.Drawing.Graphics.CheckErrorStatus(Int32 status)
at System.Drawing.Graphics.DrawLine(Pen pen, Single x1, Single y1,
Single x2, Single y2)
at System.Drawing.Graphics.DrawLine(Pen pen, PointF pt1, PointF
pt2)

It's quite possible that somehow, despite my double checking (and
again this is a moot point now that I've solved the problem by
rewriting some code), that it's a simple 'cast' problem where a point
is float and another point is Single (int?), nothing more serious than
that.

But it's moot.
I don't think it's anything like that, but without being able to
reproduce it I guess we'll never know :)

--
Jon Skeet - <sk***@pobox.com>
Web site: http://www.pobox.com/~skeet
Blog: http://www.msmvps.com/jon.skeet
C# in Depth: http://csharpindepth.com
Aug 19 '08 #7
Jon Skeet [C# MVP] wrote:
raylopez99 <ra********@yahoo.comwrote:
>OK, I tried various things and I think I figured it out.

Sorry no code, too complicated to try and cut and paste, but here's
what I think is happening:

It would really, *really* help to see some full code, I'm afraid. It
can (and should be!) a very cut down version, with only enough code to
trigger the problem - but without that I can't be confident in what's
wrong.
>the runtime error is clearly with the 'using pen {}' code.

What runtime error were you getting?
I'm guessing this is the failing code:

class F:Form
{
Pen p = new Pen(Colors.Red);

override void OnPaint(PaintEventArgs pea)
{
//...
using (p) {
}
// ...
}
}

The solution is of course to eliminate the using block, and Dispose the Pen
in F.Dispose(bool disposing) and only if disposing is set.
Aug 28 '08 #8
On Aug 28, 12:42*pm, "Ben Voigt [C++ MVP]" <r...@nospam.nospamwrote:
>
The solution is of course to eliminate the using block, and Dispose the Pen
in F.Dispose(bool disposing) and only if disposing is set.
Thanks, that's pretty slick detective work.

Truth be told, I don't know what I did wrong, nor will I ever find out
since I did a workaround, but what you just alerted me to is the fact
that 'using' is not 'fail safe'. I assumed that 'using' cannot do you
wrong, in that if it's not needed it's just not used, but you alert me
to the fact this might not be the case. For IO operations Albahari
says 'using (Filestream fs = new Filestream ("myfile.txt",
FileMode.Open) { //} is equivalent to:

FileStream fs = new FileStream ("myFile.txt", FileMode.Open);
try { //} finally { if (fs != null) fs.Dispose();}

As you probably know.

Strangely though, I find that even with using I often have to wrap it
with a try/catch block (probably because I'm doing something wrong).

RL

Aug 29 '08 #9
raylopez99 wrote:
On Aug 28, 12:42 pm, "Ben Voigt [C++ MVP]" <r...@nospam.nospamwrote:
>>
The solution is of course to eliminate the using block, and Dispose
the Pen in F.Dispose(bool disposing) and only if disposing is set.

Thanks, that's pretty slick detective work.

Truth be told, I don't know what I did wrong, nor will I ever find out
since I did a workaround, but what you just alerted me to is the fact
that 'using' is not 'fail safe'. I assumed that 'using' cannot do you
wrong, in that if it's not needed it's just not used, but you alert me
to the fact this might not be the case. For IO operations Albahari
says 'using (Filestream fs = new Filestream ("myfile.txt",
FileMode.Open) { //} is equivalent to:

FileStream fs = new FileStream ("myFile.txt", FileMode.Open);
try { //} finally { if (fs != null) fs.Dispose();}

As you probably know.
Yes, and it's the correct thing to do if and only if the result of the
initializer-expression in the using block is a new object. If there's any
way (caching, explicit copy of reference like I showed, etc) of multiple
references to the same object, you then get multiple calls to Dispose on the
same object, use after Dispose, and so forth.
>
Strangely though, I find that even with using I often have to wrap it
with a try/catch block (probably because I'm doing something wrong).
Well, it's because using adds a finally block, not a catch. So the resource
is disposed automatically, but the exception is still handed onward --
because disposing the resources didn't fix the exceptional condition, it
would be wrong to discard the exception.
>
RL

Sep 3 '08 #10

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

1 post views Thread by Schuntermann Joerg \(IFAT IT MFG COC MES MAC\) | last post: by
1 post views Thread by news.microsoft.com | last post: by
2 posts views Thread by Joep | last post: by
7 posts views Thread by Schorschi | last post: by
2 posts views Thread by Tyreec | last post: by
5 posts views Thread by =?Utf-8?B?SmVzcGVyLCBEZW5tYXJr?= | last post: by
14 posts views Thread by raylopez99 | last post: by
reply views Thread by Ronak mishra | last post: by

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.