Properly Sizing Custom-Drawn Controls - c#

I'm attempting to make a custom control that properly draws itself to fill its current size. I was under the assumption that I should use the ClientRectangle property for sizing, but the right and bottom of the client rectangle seem to be getting clipped.
Filling the draw event handler with
Rectangle smaller = new Rectangle(5, 5, ClientRectangle.Width - 10, ClientRectangle.Height - 10);
e.Graphics.DrawRectangle(System.Drawing.Pens.Black, smaller);
e.Graphics.DrawRectangle(System.Drawing.Pens.Red, ClientRectangle);
yields this:
What should I be using to get the drawable area of the control?

You can either use:
ControlPaint.DrawBorder(g, this.ClientRectangle, _
Color.Red, ButtonBorderStyle.Solid);
where Graphics g = e.Graphics;.
Or draw it as you did but subtracting 1 from width and height (1 because width and height are inclusive but draw rectangle needs the size exclusive the last pixel - internally it calculates x + w/y + h which then ends up at the position for the next pixel after the last, hence we need to subtract one to get the position for the last pixel).
rectangle r = this.ClientRectangle;
r.Width -= 1;
r.Height -= 1;
g.DrawRectangle(System.Drawing.Pens.Red, r);
And of course this from within the OnPaint event handler.

Related

C# Rounded Rectangle Corner Inconsistencies

Using C# WinForms, I have a normal label which I fill with rounded corners. The issue I am having is that the resulting corners are not consistently rounded.
For ease of understanding, I have created 4 labels with rounded corners, ranging from a radius of 1 to 5. I have added a single outline border to the label for ease of visibility, but even without the single outline the results are the same. I have also zoomed the label images so we can see the corners more clearly.
Using a radius of 1, you can clearly see that the top corners are rounded accordingly, but the bottom borders remain square.
Using a radius of 2 and 3 respectively, produces the same output at the bottom.
Using a radius of 4 and also 5 respectively, you can clearly see the inconsistencies on all the corners.
Generally it wouldn't be too much of a problem if I had a label of a small size, but the user needs to be able to zoom, and then it just looks plain ugly as the radius increases, as the inconsistencies are clearly visible.
The code I am using to create the filled rectangle is supposedly very basic as per example, taken from How To Draw a Rounded Rectangle
public static GraphicsPath draw_rectangle(Rectangle bounds, int radius)
{
int diameter = radius * 2;
Size size = new Size(diameter, diameter);
Rectangle arc = new Rectangle(bounds.Location, size);
GraphicsPath path = new GraphicsPath();
if (radius == 0)
{
path.AddRectangle(bounds);
return path;
}
// top left arc
path.AddArc(arc, 180, 90);
// top right arc
arc.X = bounds.Right - diameter;
path.AddArc(arc, 270, 90);
// bottom right arc
arc.Y = bounds.Bottom - diameter;
path.AddArc(arc, 0, 90);
// bottom left arc
arc.X = bounds.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
return path;
}
I have studied and understand what the code does and how it works, I get the angles and the sweep, but I am by no means a seasoned graphics manipulator so I don't know if I am doing something wrong or if there is a way to better the code for all corner consistency.
Your help and\or explanations are appreciated.

Using graphics to draw rectangle at top left corner of WinForms control cuts off one pixel of top and left of rectangle

I have a WinForms project and I'm trying to draw a rectangle at (0,0), the top, left corner of the form. For some reason it's cutting off one pixel of height and width of the rectangle. Here's the code:
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(new Pen(Color.Red, 5), new Rectangle(0, 0, 50, 50));
}
Here is the result, blown up for clarity:
I understand I could correct for this by drawing the rectangle at (1,1), but that's not how it should work based on what I've seen from controls that are placed at (0,0) and (1,1). For instance, here's what a panel looks like at (1,1), and it clearly has a one-pixel gap:
So my question is: why does drawing a rectangle at (0,0) not behave like placing a control at (0,0)? Why does the rectangle get cut off by one pixel on the top and left?
The default value for Alignment property of Pen is PenAlignment.Center which means the drawn line will be centered over the line. So what you see is expected.
You may want to set the Alignment to PenAlignment.Inset:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawRectangle(
new Pen(Color.Red, 5) { Alignment = PenAlignment.Inset },
new Rectangle(0, 0, 50, 50));
}
You may also want to read Pen.Alignment remarks:
Center is the default value for this property and specifies that the
width of the pen is centered on the outline of the curve or polygon. A
value of Inset for this property specifies that the width of the pen
is inside the outline of the curve or polygon. The other three values,
Right, Left, and Outset, will result in a pen that is centered.

C# Winforms. Drawing text accurately centrally within a rectangle

What I'm attempting to do is draw a number inside a circle so that it's positioned centrally both vertically and horizontally. I'm drawing both the circle and text string using the same rectangle structure and using center for both horizontal and vertical alignments. But, as you can see from the image where I drew a horizontal line across the white circle, the text is aligning its base line centrally and my eye says it's horizontally aligning slightly to the left.
I've tried fudging by adding string.height/2 to the top of the rectangle to shift it down 1/2 its height but it's still not correct. (I adjust the font size so the number will fit within the box according to the number of digits in the counter)
How can I do this properly please?
var rect = new Rectangle(bmpWidth - maxCircleDiameter, bmpHeight - maxCircleDiameter, maxCircleDiameter, maxCircleDiameter);
g.FillEllipse(Brushes.White, rect);
using (var format = NewClassFactory.GetFormat(StringAlignment.Center, StringAlignment.Center, StringTrimming.Character))
g.DrawString(toDisplay, newFont, Brushes.Black, rect, format);
You can use TextRenderer.DrawText to draw a text in a rectangle bound, using a color and font and specifying different text format flag:
var rect = new Rectangle(10, 10, 32, 32);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.FillEllipse(Brushes.White, rect);
TextRenderer.DrawText(e.Graphics, "100", this.Font, rect, Color.Black,
TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);

Cropping a cross rectangle from image using c#

What I want to do is basically cropping a rectangle from an image. However, it should satisfy some special cases:
I want to crop an angled rectangle on image.
I don't want to rotate the image and crop a rectangle :)
If cropping exceeds the image size, I don't want to crop an empty background color.
I want to crop from back of the starting point, that will end at starting point when rectangle size completed. I know I couldn't explain well so if I show what I want visually:
The blue dot is the starting point there, and the arrow shows cropping direction. When cropping exceeds image borders, it will go back to the back of the starting point as much as, when the rectangle width and height finished the end of the rectangle will be at starting point.
Besides this is the previous question I asked:
How to crop a cross rectangle from an image using c#?
In this question, I couldn't predict that a problem can occur about image dimensions so I didn't ask for it. But now there is case 3. Except case three, this is exactly same question. How can I do this, any suggestions?
What needs to be done is to add offsets to the matrix alignment. In this case I am taking one extra length of the rectangle from each side (total 9 rectangles) and offsetting the matrix each time.
Notice that it is necessary to place offset 0 (the original crop) last, otherwise you will get the wrong result.
Also note that if you specify a rectangle that is bigger than the rotated picture you will still get empty areas.
public static Bitmap CropRotatedRect(Bitmap source, Rectangle rect, float angle, bool HighQuality)
{
int[] offsets = { -1, 1, 0 }; //place 0 last!
Bitmap result = new Bitmap(rect.Width, rect.Height);
using (Graphics g = Graphics.FromImage(result))
{
g.InterpolationMode = HighQuality ? InterpolationMode.HighQualityBicubic : InterpolationMode.Default;
foreach (int x in offsets)
{
foreach (int y in offsets)
{
using (Matrix mat = new Matrix())
{
//create the appropriate filler offset according to x,y
//resulting in offsets (-1,-1), (-1, 0), (-1,1) ... (0,0)
mat.Translate(-rect.Location.X - rect.Width * x, -rect.Location.Y - rect.Height * y);
mat.RotateAt(angle, rect.Location);
g.Transform = mat;
g.DrawImage(source, new Point(0, 0));
}
}
}
}
return result;
}
To recreate your example:
Bitmap source = new Bitmap("C:\\mjexample.jpg");
Bitmap dest = CropRotatedRect(source, new Rectangle(86, 182, 87, 228), -45, true);

Displaying rectangles in game window with XNA

I want to divide my game grid into an array of rectangles. Each rectangle is 40x40 and there are 14 rectangles in every column, with a total of 25 columns. This covers a game area of 560x1000.
This is the code I have set up to make the first column of rectangles on the game grid:
Rectangle[] gameTiles = new Rectangle[15];
for (int i = 0; i <= 15; i++)
{
gameTiles[i] = new Rectangle(0, i * 40, 40, 40);
}
I'm pretty sure this works, but of course I cannot confirm it because rectangles do not render on the screen for me to physically see them. What I would like to do for debugging purposes is to render a border, or fill the rectangle with color so I can see it on the game itself, just to make sure this works.
Is there a way to make this happen? Or any relatively simple way I can just make sure that this works?
Thank you very much.
First, make a 1x1 pixel texture of white for the rectangle:
var t = new Texture2D(GraphicsDevice, 1, 1);
t.SetData(new[] { Color.White });
Now, you need to render the rectangle - assume the Rectangle is called rectangle. For a rendering a filled block, it is very simple - make sure to set the tint Color to be the colour you want. Just use this code:
spriteBatch.Draw(t, rectangle, Color.Black);
For a border, is it more complex. You have to draw the 4 lines that make up the outline (the rectangle here is r):
int bw = 2; // Border width
spriteBatch.Draw(t, new Rectangle(r.Left, r.Top, bw, r.Height), Color.Black); // Left
spriteBatch.Draw(t, new Rectangle(r.Right, r.Top, bw, r.Height), Color.Black); // Right
spriteBatch.Draw(t, new Rectangle(r.Left, r.Top, r.Width , bw), Color.Black); // Top
spriteBatch.Draw(t, new Rectangle(r.Left, r.Bottom, r.Width, bw), Color.Black); // Bottom
Hope it helps!
This worked perfect if you want to draw rectangles over your existing textures. Great when you want to test/see for collisions
http://bluelinegamestudios.com/blog/posts/drawing-a-hollow-rectangle-border-in-xna-4-0/
-----From Site-----
The basic trick to drawing shapes is to make a single-pixel texture which is White, which you can then mix with other colors and display in solid shapes.
// At the top of your class:
Texture2D pixel;
// Somewhere in your LoadContent() method:
pixel = new Texture2D(GameBase.GraphicsDevice, 1, 1, false, SurfaceFormat.Color);
pixel.SetData(new[] { Color.White }); // so that we can draw whatever color we want on top of it
Then in your Draw() method do something like:
spriteBatch.Begin();
// Create any rectangle you want. Here we'll use the TitleSafeArea for fun.
Rectangle titleSafeRectangle = GraphicsDevice.Viewport.TitleSafeArea;
// Call our method (also defined in this blog-post)
DrawBorder(titleSafeRectangle, 5, Color.Red);
spriteBatch.End();
And the actual method that does the drawing:
private void DrawBorder(Rectangle rectangleToDraw, int thicknessOfBorder, Color borderColor)
{
// Draw top line
spriteBatch.Draw(pixel, new Rectangle(rectangleToDraw.X, rectangleToDraw.Y, rectangleToDraw.Width, thicknessOfBorder), borderColor);
// Draw left line
spriteBatch.Draw(pixel, new Rectangle(rectangleToDraw.X, rectangleToDraw.Y, thicknessOfBorder, rectangleToDraw.Height), borderColor);
// Draw right line
spriteBatch.Draw(pixel, new Rectangle((rectangleToDraw.X + rectangleToDraw.Width - thicknessOfBorder),
rectangleToDraw.Y,
thicknessOfBorder,
rectangleToDraw.Height), borderColor);
// Draw bottom line
spriteBatch.Draw(pixel, new Rectangle(rectangleToDraw.X,
rectangleToDraw.Y + rectangleToDraw.Height - thicknessOfBorder,
rectangleToDraw.Width,
thicknessOfBorder), borderColor);
}

Categories

Resources