What property defines a controls paint bounds?

T

Tom P.

I am creating a custom control and I'm trying to get the painting of
it correct. I'd like to simply use:
e.Graphics.FillRectangle(Brushes.White, DisplayRectangle);
.... or ...
e.Graphics.FillRectangle(Brushes.White, ClientRectangle);
.... or ...
e.Graphics.FillRectangle(Brushes.White, Bounds);

But each of those fails to draw a rectangle all the way around (most
are 1 pixel off). Instead I find myself having to use:
e.Graphics.FillRectangle(Brushes.White, DisplayRectangle.X,
DisplayRectangle.Y, DisplayRectangle.Width - 1,
DisplayRectangle.Height - 1);

What am I really supposed to use? How do I get that last pixel
correct?

Tom P.
 
M

Matt

I am creating a custom control and I'm trying to get the painting of
it correct. I'd like to simply use:
e.Graphics.FillRectangle(Brushes.White, DisplayRectangle);
... or ...
e.Graphics.FillRectangle(Brushes.White, ClientRectangle);
... or ...
e.Graphics.FillRectangle(Brushes.White, Bounds);

But each of those fails to draw a rectangle all the way around (most
are 1 pixel off). Instead I find myself having to use:
e.Graphics.FillRectangle(Brushes.White, DisplayRectangle.X,
DisplayRectangle.Y, DisplayRectangle.Width - 1,
DisplayRectangle.Height - 1);

What am I really supposed to use? How do I get that last pixel
correct?

Tom P.

What is wrong with e.Graphics.FillRectangle( Brushes.White,
e.ClipRectangle);

?
Matt
 
P

Peter Duniho

I am creating a custom control and I'm trying to get the painting of
it correct. I'd like to simply use:
e.Graphics.FillRectangle(Brushes.White, DisplayRectangle);
... or ...
e.Graphics.FillRectangle(Brushes.White, ClientRectangle);
... or ...
e.Graphics.FillRectangle(Brushes.White, Bounds);

But each of those fails to draw a rectangle all the way around (most
are 1 pixel off). Instead I find myself having to use:
e.Graphics.FillRectangle(Brushes.White, DisplayRectangle.X,
DisplayRectangle.Y, DisplayRectangle.Width - 1,
DisplayRectangle.Height - 1);

What am I really supposed to use? How do I get that last pixel
correct?

Just what you're doing. Either DisplayRectangle or ClientRectangle are
appropriate, depending on your specific need (by default, they are the
same). And you need to subtract one from the width and height to get the
whole rectangle to draw.

This may seem arbitrary, but there's actually a good reason for it.
Graphical coordinates are treated as being the intersection of 0-width
lines in a grid. The pixels are _between_ these lines. A line drawn from
(0,0) to (0,100) for example will fill all of the pixels just to the right
of that 0-width vertical line between those coordinates, inclusive.

When you try to draw a rectangle a specific width and height, all of the
pixels are being drawn to the right and below of any coordinate that
describes the rectangle. This means that on the right and bottom of the
rectangle, if you've specified coordinates that are actually the absolute
outer bounds of the area in which the control can draw, those pixels fall
outside of the control and aren't drawn.

The fix is to reduce the width and height of the rectangle you're trying
to draw by the width of one pixel, so that the pixels drawn for that
rectangle fall within the control's drawable area.

Pete
 
F

Family Tree Mike

Tom P. said:
I am creating a custom control and I'm trying to get the painting of
it correct. I'd like to simply use:
e.Graphics.FillRectangle(Brushes.White, DisplayRectangle);
.... or ...
e.Graphics.FillRectangle(Brushes.White, ClientRectangle);
.... or ...
e.Graphics.FillRectangle(Brushes.White, Bounds);

But each of those fails to draw a rectangle all the way around (most
are 1 pixel off). Instead I find myself having to use:
e.Graphics.FillRectangle(Brushes.White, DisplayRectangle.X,
DisplayRectangle.Y, DisplayRectangle.Width - 1,
DisplayRectangle.Height - 1);

What am I really supposed to use? How do I get that last pixel
correct?

Tom P.

It looks like you really could do it with e.Graphics.Clear(Color.White);
 
T

Tom P.

It looks like you really could do it with e.Graphics.Clear(Color.White);

That's fine for clearing the control to white but what would I use to
draw the black outline?

Tom P.
 
T

Tom P.

What is wrong with e.Graphics.FillRectangle( Brushes.White,
e.ClipRectangle);

?
Matt

The same thing that's wrong with everything else - it's off by one
pixel.

Tom P.
 
T

Tom P.

Just what you're doing. Either DisplayRectangle or ClientRectangle are
appropriate, depending on your specific need (by default, they are the
same). And you need to subtract one from the width and height to get the
whole rectangle to draw.

This may seem arbitrary, but there's actually a good reason for it.
Graphical coordinates are treated as being the intersection of 0-width
lines in a grid. The pixels are _between_ these lines. A line drawn from
(0,0) to (0,100) for example will fill all of the pixels just to the right
of that 0-width vertical line between those coordinates, inclusive.

When you try to draw a rectangle a specific width and height, all of the
pixels are being drawn to the right and below of any coordinate that
describes the rectangle. This means that on the right and bottom of the
rectangle, if you've specified coordinates that are actually the absolute
outer bounds of the area in which the control can draw, those pixels fall
outside of the control and aren't drawn.

The fix is to reduce the width and height of the rectangle you're trying
to draw by the width of one pixel, so that the pixels drawn for that
rectangle fall within the control's drawable area.

Pete

I guess you're right. There's nothing else I can find to change this.
There should be a "DrawRectangle" property for this type of
situation. Oh, well... thanks.

Tom P.
 
P

Peter Duniho

I guess you're right. There's nothing else I can find to change this.
There should be a "DrawRectangle" property for this type of
situation. Oh, well... thanks.

I don't understand what you mean. There is a DrawRectangle() method, but
it just outlines the rectangle rather than filling it.

There's not an actual problem here with .NET. The only issue is that you
need to comprehend the API differently. Once you understand that
coordinates you provide don't refer to pixels, but rather to the grid
between pixels, everything makes sense.

If you don't understand that, then yes...you'll continue to believe a
problem exists, even though none does.

Pete
 
T

Tom P.

I don't understand what you mean. There is a DrawRectangle() method, but
it just outlines the rectangle rather than filling it.

There's not an actual problem here with .NET. The only issue is that you
need to comprehend the API differently. Once you understand that
coordinates you provide don't refer to pixels, but rather to the grid
between pixels, everything makes sense.

If you don't understand that, then yes...you'll continue to believe a
problem exists, even though none does.

Pete

I know there is a method DrawRectangle(), but I was talking about a
property that describe the ACTUAL painting region. In the same units,
or relationship, as the method is going to use it. What use have I for
a description of an area I have to adjust before using? Why provide me
with measurements that are close to what I need, but don't finally
describe the area I want? There are no fewer than four rectangles that
describe areas one pixel larger than the Displayed area of a control.
You mean to tell me they couldn't find it in their hearts to provide
one single property that depicts the ACTUAL area that methods like
DrawRectangle() are expecting? It doesn't seem like that big a deal.

Tom P.
 
P

Peter Duniho

[...]
There's not an actual problem here with .NET. The only issue is that
you
need to comprehend the API differently. Once you understand that
coordinates you provide don't refer to pixels, but rather to the grid
between pixels, everything makes sense.

If you don't understand that, then yes...you'll continue to believe a
problem exists, even though none does.

Pete

I know there is a method DrawRectangle(), but I was talking about a
property that describe the ACTUAL painting region.

The relationship between ClientRectangle, DisplayRectangle, etc. and the
visible area of the control is very well defined. Because of the way the
_graphical_ API works, it's true that those rectangles can't be passed
directly to the graphical API if you want to draw a line that is contained
within that area. But they do exactly describe the actual painting region
itself.
In the same units,
or relationship, as the method is going to use it. What use have I for
a description of an area I have to adjust before using?

I'm sorry you don't see the use for ClientRectangle, etc. Suffice to say,
those properties are in fact very useful, and if they behaved differently,
they wouldn't be.
Why provide me
with measurements that are close to what I need, but don't finally
describe the area I want?

Because the rectangle you get _is_ an exact description of the Control
instance. It would be far worse to have a ClientRectangle return (for
example) a rectangle that's only 639 pixels wide by 479 pixels high when
the control is actually 640 pixels wide by 480 pixels high.

As things are now, the rectangle you get is exactly correct. You would
prefer that it be wrong just for your convenience in one specific scenario?
There are no fewer than four rectangles that
describe areas one pixel larger than the Displayed area of a control.

You're incorrect that they "describe areas one pixel larger than the
displayed area of a control". They describe an area _exactly_ the same
size as the displayed area of the control. It's just that the graphical
API always fills pixels to the right and left of whatever grid coordinates
you've specified.

If your control tells you that the ClientRectangle is 100 pixels wide,
then if you count the pixels on the screen you will find there are exactly
100 pixels of width there. Not 101. And definitely not 99. Exactly 100
pixels.
You mean to tell me they couldn't find it in their hearts to provide
one single property that depicts the ACTUAL area that methods like
DrawRectangle() are expecting? It doesn't seem like that big a deal.

Well, I'm sorry you aren't able to see that it's a big deal, but it is.
Those properties must describe the actual size of the Control precisely.
It would be wrong for them to return a rectangle that's one pixel smaller
in width and height than the control actually is.

At the same time, it's inappropriate for the graphical API to sometimes
fill pixels inside the description of a shape and sometimes fill pixels
outside the description of a shape. That API needs to be consistent, and
if it did what you apparently would prefer it did, it wouldn't be. For
example, you can think of a rectangle as four lines. But if a rectangle
got drawn inside the boundary specified by the rectangle, rather than just
following the lines defined by the rectangle and drawing it the same way
as any other line, you wouldn't be able to get the same results drawing
the individual lines as you get drawing the rectangle itself.

This would be really bad. It's critical that a graphical API not have
inconsistencies like this. There's a very good reason that all of the
major graphical APIs, including all of Apple's Quickdraw iterations along
with Carbon and Cocoa, all of the Windows graphical APIs, Java, etc.
behave exactly this way. It's a mathematically pure, consistent API that
allows a developer to produce reliable results.

Is it really that big of a deal to have to subtract your pen size (one
pixel by default, of course) from the width and height of the rectangle
you want to be visible? It seems to me that you could easily write a
short method that handles this for you, if you really find it that hard to
just do it explicitly when needed.

Pete
 
T

Tom P.

[...]
There's not an actual problem here with .NET. The only issue is that
you
need to comprehend the API differently. Once you understand that
coordinates you provide don't refer to pixels, but rather to the grid
between pixels, everything makes sense.
If you don't understand that, then yes...you'll continue to believe a
problem exists, even though none does.
Pete
I know there is a method DrawRectangle(), but I was talking about a
property that describe the ACTUAL painting region.

The relationship between ClientRectangle, DisplayRectangle, etc. and the
visible area of the control is very well defined. Because of the way the
_graphical_ API works, it's true that those rectangles can't be passed
directly to the graphical API if you want to draw a line that is contained
within that area. But they do exactly describe the actual painting region
itself.
In the same units,
or relationship, as the method is going to use it. What use have I for
a description of an area I have to adjust before using?

I'm sorry you don't see the use for ClientRectangle, etc. Suffice to say,
those properties are in fact very useful, and if they behaved differently,
they wouldn't be.
Why provide me
with measurements that are close to what I need, but don't finally
describe the area I want?

Because the rectangle you get _is_ an exact description of the Control
instance. It would be far worse to have a ClientRectangle return (for
example) a rectangle that's only 639 pixels wide by 479 pixels high when
the control is actually 640 pixels wide by 480 pixels high.

As things are now, the rectangle you get is exactly correct. You would
prefer that it be wrong just for your convenience in one specific scenario?
There are no fewer than four rectangles that
describe areas one pixel larger than the Displayed area of a control.

You're incorrect that they "describe areas one pixel larger than the
displayed area of a control". They describe an area _exactly_ the same
size as the displayed area of the control. It's just that the graphical
API always fills pixels to the right and left of whatever grid coordinates
you've specified.

If your control tells you that the ClientRectangle is 100 pixels wide,
then if you count the pixels on the screen you will find there are exactly
100 pixels of width there. Not 101. And definitely not 99. Exactly 100
pixels.
You mean to tell me they couldn't find it in their hearts to provide
one single property that depicts the ACTUAL area that methods like
DrawRectangle() are expecting? It doesn't seem like that big a deal.

Well, I'm sorry you aren't able to see that it's a big deal, but it is.
Those properties must describe the actual size of the Control precisely.
It would be wrong for them to return a rectangle that's one pixel smaller
in width and height than the control actually is.

At the same time, it's inappropriate for the graphical API to sometimes
fill pixels inside the description of a shape and sometimes fill pixels
outside the description of a shape. That API needs to be consistent, and
if it did what you apparently would prefer it did, it wouldn't be. For
example, you can think of a rectangle as four lines. But if a rectangle
got drawn inside the boundary specified by the rectangle, rather than just
following the lines defined by the rectangle and drawing it the same way
as any other line, you wouldn't be able to get the same results drawing
the individual lines as you get drawing the rectangle itself.

This would be really bad. It's critical that a graphical API not have
inconsistencies like this. There's a very good reason that all of the
major graphical APIs, including all of Apple's Quickdraw iterations along
with Carbon and Cocoa, all of the Windows graphical APIs, Java, etc.
behave exactly this way. It's a mathematically pure, consistent API that
allows a developer to produce reliable results.

Is it really that big of a deal to have to subtract your pen size (one
pixel by default, of course) from the width and height of the rectangle
you want to be visible? It seems to me that you could easily write a
short method that handles this for you, if you really find it that hard to
just do it explicitly when needed.

Pete

I think I am being misunderstood (and over something this picayune, -1
isn't really that hard). I never advocated the current methods or
properties be changed, just that an additional one be provided that
bridged the apparent discrepancy between what is provided and what is
drawn. I understand that there are reasons to describe the contents of
a control as inclusive. Much like you posited, it would not do for a
control to say it was 101 pixels wide when it wasn't.

I understand it's the same concept as a List object, there's a
difference between the Count and the index of an item. If a List has
100 items they are 0 through 99.

Understanding all that, what is wrong with saying the PaintingArea is
from (x, y) 0, 0 to (width, height) 99, 99?

But I think your last statement gives me some insight into my issue
(and a better way to paint the object, so thank you) The particular
rectangles provided are only 1 pixel too small because the pen I am
using is 1 pixel. They are, in point of fact, "Pen.Width" smaller. I
should not be subtracting 1, I should be subtracting the Pen.Width to
ensure the entire pen falls within the paintable area regardless of
what size the pen is. To that end I thank you for helping me
understand exactly what is being presented and how to use it.

Tom P.
 
P

Peter Duniho

[...]
Understanding all that, what is wrong with saying the PaintingArea is
from (x, y) 0, 0 to (width, height) 99, 99?

Because that would describe an area only 99 pixels wide and 99 pixels high
(9801 pixels total), when in fact the "painting area" (that is, the area
that the control is responsible for painting) is 100 pixels wide by 100
pixels high (10000 pixels total).
But I think your last statement gives me some insight into my issue
(and a better way to paint the object, so thank you) The particular
rectangles provided are only 1 pixel too small because the pen I am
using is 1 pixel. They are, in point of fact, "Pen.Width" smaller. I
should not be subtracting 1, I should be subtracting the Pen.Width to
ensure the entire pen falls within the paintable area regardless of
what size the pen is.

Yes, that's exactly right.
To that end I thank you for helping me
understand exactly what is being presented and how to use it.

You're welcome.

Pete
 
T

Tom P.

Crap, now I'm back to not understanding. I tried putting Pen.Width in
my calculations and it worked, for pens with a width of 1. If the user
changes the border size the X, Y, gets messed up. And the great thing
about this is, it's messed up by "half" the pen width. Is there a good
tutorial that presents all these little control drawing issues so
people can understand them? I just know there's some scale variable
that I'm missing that would make everything fall in line. At this
point I think I need a tutorial and some instruction.

Tom P.
 
P

Peter Duniho

Crap, now I'm back to not understanding. I tried putting Pen.Width in
my calculations and it worked, for pens with a width of 1. If the user
changes the border size the X, Y, gets messed up. And the great thing
about this is, it's messed up by "half" the pen width. Is there a good
tutorial that presents all these little control drawing issues so
people can understand them?

Bob Powell's web site is the one I see referenced often. It's not
perfect, but I've seen lots of good advice there. It's the closest I've
seen to what you're asking for.
http://www.bobpowell.net/

I didn't see anything on his site that specifically addresses the GDI/.NET
coordinate system, but I only took a quick look. You might browse around
and see if there's something there that's useful to you.

It seems I've misremembered the effect of pen width as it relates to the
coordinates. This discussion in the GDI+ docs suggests so anyway:
http://msdn2.microsoft.com/en-us/library/ms533855(VS.85).aspx. "When the
rectangle is drawn, the pen is centered on the rectangle's boundary".

In other words, in GDI+ you actually need to inset the whole rectangle by
half the pen width, rather than insetting the right and bottom by a whole
pen width (note that you still need to reduce the width and height of the
drawn rectangle by the pen width in total to get it to fit within the
displayable area...it's just where you make the reduction is different).

This actually simplifies things, as the Rectangle struct has an Inflate()
method that can be used to do just that, rather than having to adjust the
width and height explicitly. It will preserve the center of the rectangle
while adjusting the boundary of the rectangle in all four directions by
the values passed in. Just pass in half the pen width, and that method
will fix your rectangle so it draws the way you want.

Sorry for any confusion...the perils of working with a wide variety of
graphical APIs, I guess. It's hard to keep the exact behaviors of each
straight. :(

Pete
 
F

Family Tree Mike

It looks like you really could do it with e.Graphics.Clear(Color.White);
That's fine for clearing the control to white but what would I use to
draw the black outline?

Tom P.

I just let the control's border property take care of that.
 

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

Top