How can I create Rectangles in WPF dynamically? - c#

I am trying to create rectangles and the number of rectangles is depend on data passed from database.
For example, if number = 5, the program will generate 5 rectangles. Also, these rectangles must be able to follow my rectangle property settings, like height, width,color...put them in one line at the end.
Is there a way to do that?
I am using WPF and C#.
Thank you.

To create the rectangle in code dynamically:
int number = 5;
int width = 10;
int height = 10;
int top = 20;
int left = 20;
for (int i = 0; i < number; i++)
{
// Create the rectangle
Rectangle rec = new Rectangle()
{
Width = width,
Height = height,
Fill = Brushes.Green,
Stroke = Brushes.Red,
StrokeThickness = 2,
};
// Add to a canvas for example
canvas.Children.Add(rec);
Canvas.SetTop(rec, top);
Canvas.SetLeft(rec, left);
}

Related

Custom Measure String in C# without Graphics.MeasureString

I am trying to create an Image with a Caption/Text on it for Youtube-Thumbnails.
Following Rules are defined:
The Text is the Title of the Video and always changes from Thumbnail to Thumbnail.
The Porgram uses a pre-defined Text-Width which must not be touched by the Text on the Image.
The Text should be as close to the pre-defined with as possible.
So my thoughts were that I would use Graphics.MeasureString to be able to track the Width of the String on the Image and increase the Font-Size and repeat this until the pre-defined Width is closely reached but not touched.
But I have tested it with MeasureString and found out that it isn't that accurate. And also found confirmation here: Graphics.MeasureCharacterRanges giving wrong size calculations
I have tried the things they have recommended but with no success as the final width of my string always overflooded the image borders. Even if my pre-defined Width was way smaller than the Image Width. (Image Width: 1920; Pre-Defined Width: 1600)
So I came up with the Idea to create a custom Measurement Method and to write the String as I want it on a new Bitmap and to count the maximum Pixels of the String in Height and Width. (The Height is just for future stuff)
My current Code is:
public static SizeF MeasuredStringSize(string text, Bitmap originBitmap, FontFamily fontFamily, StringFormat strformat)
{
int currentFontSize = 10;
SizeF measuredSize = new();
var highestWidth = 0;
var highestHeight = 0;
while (highestWidth < maximumTextWidth)
{
Bitmap bitmap = new(originBitmap);
Bitmap _bitmap = new(bitmap.Width, bitmap.Height);
using Graphics graphics = Graphics.FromImage(bitmap);
if (graphics != null)
{
graphics.TranslateTransform(bitmap.Width / 2, bitmap.Height / 2);
currentFontSize++;
graphics.Clear(Color.White);
using GraphicsPath path = new();
using SolidBrush brush = new(Color.Red);
using Pen pen = new(Color.Red, 6)
{
LineJoin = LineJoin.Round
};
path.AddString(text, fontFamily, (int)fontStyle, currentFontSize, new Point(0, 0), strformat);
graphics.DrawPath(pen, path);
graphics.FillPath(brush, path);
Dictionary<int, List<int>> redPixelMatrix = new();
for (int i = 0; i < bitmap.Width; i++)
{
for (int j = 0; j < bitmap.Height; j++)
{
var currentPixelColor = bitmap.GetPixel(i, j);
if (currentPixelColor.B != 255 && currentPixelColor.G != 255 && currentPixelColor.R == 255)
{
if (!redPixelMatrix.ContainsKey(i))
{
redPixelMatrix.Add(i, new());
}
redPixelMatrix[i].Add(j);
}
}
}
highestWidth = redPixelMatrix.Keys.Count;
highestHeight = redPixelMatrix.Aggregate((l, r) => l.Value.Count > r.Value.Count ? l : r).Value.Count;
Console.WriteLine($"X:{highestWidth};Y:{highestHeight}");
//Debugging the final Image with Text to see the Result
bitmap.Save(ResultPath);
}
}
measuredSize = new SizeF(highestWidth, highestHeight);
return measuredSize;
}
The Resulting Image from bitmap.Save(ResultPath); as the String reaches the Image borders looks like this:
But the exact String width is 1742 instead of the width of my Image 1920 which should be more or less the same at this moment.
So, why is the Text nearly as wide as the Image but doesn't have the same width?
highestWidth = redPixelMatrix.Keys.Count; This will just count the number of columns containing red pixels, excluding any spaces in the text. You presumably want the minimum and maximum indices.
I.e.
var minX = int.MaxValue;
var maxX = int.MinValue;
// Loops over rows & columns
// Check if pixel is red
if(i > maxX) maxX = i;
if(i < minX) minX = i;
If you only want the text width and not the bounds you can just do maxX - minX.

How to inflate the size of drawn rectangle throught a for-loop in c#?

I cannot inflate the original drawn rectangle through for-loop.
I wanted to maybe store the original drawn rectangle into an array and from their loop it, but it did not work properly.
loop_txtbx.Text = 5
parameter_txtbx.Text = 20
int[] rec = new int[loops];
int xCenter = Convert.ToInt32(startX_coord_txtbx.Text);
int yCenter = Convert.ToInt32(startY_coord_txtbx.Text);
int width = Convert.ToInt32(width_txtbx.Text);
int height = Convert.ToInt32(height_txtbx.Text);
//Find the x-coordinate of the upper-left corner of the rectangle to draw.
int x = xCenter - width / 2;
//Find y-coordinate of the upper-left corner of the rectangle to draw.
int y = yCenter - height / 2;
int loops = Convert.ToInt32(loop_txtbx.Text);
int param = Convert.ToInt32(parameter_txtbx.Text);
// Create a rectangle.
Rectangle rec1 = new Rectangle(x, y, width, height);
// Draw the uninflated rectangle to screen.
gdrawArea.DrawRectangle(color_pen, rec1);
for (int i = 0; i < loops; i++)
{
// Call Inflate.
Rectangle rec2 = Rectangle.Inflate(rec1, param, param);
// Draw the inflated rectangle to screen.
gdrawArea.DrawRectangle(color_pen, rec2);
}
Only 2 drawn rectangles are shown, while it was supposed to be 5. I cannot manage to modify rec2
You are using the same rec1 as a base to inflate. So after the first loop you get always the same size for the new rectangle.
You need to use rec2
Rectangle rec2 = rec1;
for (int i = 0; i < loops; i++)
{
rec2 = Rectangle.Inflate(rec2, param, param);
....
}
But with this approach you should invert the order of the calls to draw the initial rectangle
Rectangle rec2 = rec1;
for (int i = 0; i < loops; i++)
{
// Draw the current rectangle to screen.
gdrawArea.DrawRectangle(color_pen, rec2);
// Call Inflate.
rec2 = Rectangle.Inflate(rec2, param, param);
}

C# How to nextline in e.Drawing when printing images

I'm generating a barcode depending on how many inputs that the user set in the numericUpDown control. The problem is when generating a lot of barcodes, the other barcodes cannot be seen in the printpreviewdialog because it I cannot apply a nextline or \n every 4-5 Images.
int x = 0, y = 10;
for (int i = 1; i <= int.Parse(txtCount.Text); i++)
{
idcount++;
connection.Close();
Zen.Barcode.Code128BarcodeDraw barcode = Zen.Barcode.BarcodeDrawFactory.Code128WithChecksum;
Random random = new Random();
string randomtext = "MLQ-";
int j;
for (j = 1; j <= 6; j++)
{
randomtext += random.Next(0, 9).ToString();
Image barcodeimg = barcode.Draw(randomtext, 50);
resultimage = new Bitmap(barcodeimg.Width, barcodeimg.Height + 20);
using (var graphics = Graphics.FromImage(resultimage))
using (var font = new Font("Arial", 11)) // Any font you want
using (var brush = new SolidBrush(Color.Black))
using (var format = new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Far}) // Also, horizontally centered text, as in your example of the expected output
{
graphics.Clear(Color.White);
graphics.DrawImage(barcodeimg, 0, 0);
graphics.DrawString(randomtext, font, brush, resultimage.Width / 2, resultimage.Height, format);
}
x += 25;
}
e.Graphics.DrawImage(resultimage, x, y);
}
There's no "new lines" in rasterized graphics. There's pixels. You've got the right idea, every n number of images, add a new line. But since you're working with pixels, let's say every 4 images you're going to need to add a vertical offset by modifying the y coordinate of all your graphics draw calls. This offset, combined with a row height in pixels could look something like this:
var rowHeight = 250; // pixels
var maxColumns = 4;
var verticalOffset = (i % maxColums) * rowHeight;
Then, when you can supply a y coordinate, starting at or near 0, add the vertical offset to it.

Filling a big rectangle with a variable quantity of little rectangles

I'm trying to fill a rectangle with a variable quantity of little rectangles but adjusting the distance between them depending on the number of units (more units->the lesser the distance between).
I'm a newbie programming WPF in C# and i don´t know how to advance from this point.
How can I do it?
The code so far:
int units = 20;
int width = 10;
int height = 20;
int top = 200;
int left = 200;
int rectangleWidth = 300;
int rectangleHeight = 100;
for (int i = 0; i < units; i++)
{
Rectangle rec = new Rectangle()
{
Width = width,
Height = height,
Fill = Brushes.Black,
Stroke = Brushes.White,
StrokeThickness = 1,
RadiusX = 3,
RadiusY = 3,
};
cuadernodibujo.Children.Add(rec);
Canvas.SetTop(rec, top);
Canvas.SetLeft(rec, left + (i*50));
}
I have updated the code, but doesn´t work.
I don´t know what am i doing wrong.
The piece of code so far:
int rectangleWidth = 500;
int rectangleHeight = 100;
int units = 60;
int unitsX = 10;
int unitsY = 6;
var childWidht = (rectangleWidth - 2*Left) / unitsX;
var childHeigth = (rectangleHeight - 2*Top ) / unitsY;
int width = 10;
int height = 20;
double top = 100;
double left = 100;
for (int i = 0; i < units; i++)
{
Rectangle rec = new Rectangle()
{
Width = width,
Height = height,
Fill = Brushes.Black,
Stroke = Brushes.White,
StrokeThickness = 1,
RadiusX = 3,
RadiusY = 3,
};
cuadernodibujo.Children.Add(rec);
for (int j = 0; j < unitsY; j++)
{
Rectangle rec2 = new Rectangle()
{
Width = width,
Height = height,
Fill = Brushes.Black,
Stroke = Brushes.White,
StrokeThickness = 1,
RadiusX = 3,
RadiusY = 3,
};
cuadernodibujo.Children.Add(rec2);
Canvas.SetTop(rec, top + (j * childHeigth));
for (int k = 0; k < unitsX; k++)
{
Rectangle rec3 = new Rectangle()
{
Width = width,
Height = height,
Fill = Brushes.Black,
Stroke = Brushes.White,
StrokeThickness = 1,
RadiusX = 3,
RadiusY = 3,
};
cuadernodibujo.Children.Add(rec3);
Canvas.SetLeft(rec, left + (k * childWidht));
}
}
}
If I understand correctly, you want to spread the little rectangles uniformly over the width of the parent rectangle.
This is less a programming problem, then a maths problem.
Given the parent rectangle's width parentWidht and the number of child rectangles units each child rectangle has a width of:
var childWidht = parentWidht / units;
If you want to add a left and right margin (given your left variable), you need to subtract the margin from parentWidht.
var childWidht = (parentWidht - 2 * left) / units; // 2 times left, to add the margin on both sides.
This gives you the width of each child, you now only have to move each child rectangle according to the previously calculated childWidht.
...
var childWidht = (parentWidht - 2 * left) / units;
for (int i = 0; i < units; i++)
{
...
Canvas.SetLeft(rec, left + (i*childWidht));
}
Update to question in the comments
With that I can fill a single line, but how can I fill the rest of the lines (to fill the parent height as well)?
We can apply the same logic as for the horizontal filling.
First calculate the child rectangles height (parentHeight - 2 * top)
Then wrap the horizontal rectangles into a loop and move each line according to the calculated height.
Here the listing with horizontal and vertical filling.
...
var childWidht = (parentWidht - 2 * left) / unitsX;
var childHeigth = (parentHeigth - 2 * top) / unitsY;
for (int j = 0; j < unitsY; i++) // first loop for vertical filling
{
for (int i = 0; i < unitsX; i++) // second loop for horizontal
{
var rect = new Rectangle { ... } ;
Canvas.Children.Add(rect); // Only add once in the inner loop.
Canvas.SetTop(rec, top + (j * childHeigth)); // here we use j, the vertical index
Canvas.SetLeft(rec, left + (i*childWidht)); // here we use i, the horizontal index
}
}

Matrix of photos

So I have a code that injects an image into my project through resources Image foodWorld = Resources.orange and I want to make a matrix out of this photo, so it can look like this:
I have this code but I don't know how to draw the matrix. Also, I don't know if this is the right way to draw it or not:
this.Width = 400;
this.Height = 300;
Bitmap b = new Bitmap(this.Width, this.Height);
for(int i = 0; i < this.Height; i++)
{
for(int j = 0; j < this.Width; j ++)
{
//fill the matrix
}
}
I am not too familiar with WinForms, but in WPF, I'd do it this way:
var columns = 15;
var rows = 10;
var imageWidth = 32;
var imageHeight = 32;
var grid = new Grid();
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
//Get the image in your project; I'm not sure how this is done in WinForms
var b = new Bitmap(imageWidth, imageHeight);
//Display it
var pictureBox = new PictureBox();
pictureBox.Image = b;
//Set the position
Grid.SetColumn(j, pictureBox);
Grid.SetRow(i, pictureBox);
//Insert into the "matrix"
grid.Children.Add(pictureBox);
}
}
For moving Pacman, repeat the above, but for only one image. Store a reference to the current position and when certain keys are pressed,
Animate it's margin until it appears to be in an adjacent cell (for instance, if each cell is 16 pixels wide and pacman should be in the center of any given cell, animate the right margin by 16 pixels to get it into the cell on the right and so forth).
Once it has moved to another cell, set the new row and column based on the direction in which it last moved.
If there is a fruit at the new position, get the fruit at that position and remove it from the Grid. You can get it by using myGrid.Children[currentRow * totalColumns + currentColumn] assuming currentRow and currentColumn are both zero-based.
Repeat for each cell it must move to.
This does mean the matrix will have a fixed size, but in WPF, there is a Viewbox, which is convenient for these types of scenarios. Also, set the z-index of pacman to be greater than the fruits so it's always on top.

Categories