Im in winforms and trying to make a tileengine in it for school reasons. I have a trouble with drawing the tileset in the Panel. It's instead drawing in the top left of the Form and behind the Panel.
This is the code:
private void gfxPanel_Paint(object sender, PaintEventArgs e)
{
using (Bitmap sourceBmp = new Bitmap("../../assets/art/Tileset5.png"))
{
Size s = new Size(level.TileWidth, level.TileHeight);
Rectangle destRect = new Rectangle(Point.Empty, s);
for (int row = 0; row <= level.MapHeight; row++)
for (int col = 0; col < level.MapWidth; col++)
{
PictureBox p = new PictureBox();
p.Size = s;
Point loc = new Point(level.TileWidth * col, level.TileHeight * row);
Rectangle srcRect = new Rectangle(loc, s);
Bitmap tile = new Bitmap(level.TileWidth, level.TileHeight);
Graphics G = Graphics.FromImage(tile);
G.DrawImage(sourceBmp, destRect, srcRect, GraphicsUnit.Pixel);
p.Image = tile;
p.Location = loc;
this.Controls.Add(p);
}
}
}
Whats going on here and what am I doing wrong?
You are adding the picture boxes to the form (this). Add them to the panel instead:
gfxPanel.Controls.Add(p);
Note: You are adding the controls in the paint event, which means that you will be adding multiple sets of the same control. The paint even is called whenever the control needs to be redrawn on the screen. You should only add the controls once, perhaps in the form load event. If you want to use the paint event you should use the graphics object that is send in the event arguments to draw directly on the screen, not add controls that contain what you want to draw.
If you really just want to draw the tiles without interaction you can change the Paint event like this:
private void gfxPanel_Paint(object sender, PaintEventArgs e)
{
using (Bitmap sourceBmp = new Bitmap("../../assets/art/Tileset5.png"))
{
Size s = new Size(levelTile.Width, levelTile.Height);
for (int row = 0; row <= levelMap.Height; row++)
for (int col = 0; col < levelMap.Width; col++)
{
Rectangle destRect = new Rectangle(
col * levelTile.Width, row * levelTile.Height,
levelTile.Width, levelTile.Height);
Point loc = new Point(levelTile.Width * col, levelTile.Height * row);
Rectangle srcRect = new Rectangle(loc, s);
e.Graphics.DrawImage(sourceBmp, destRect, srcRect, GraphicsUnit.Pixel);
}
}
}
Note that I didn't fully rewrite the code. If the tiles are meant to be drawn in the same order and size as they appear in the TileSet the whole TileSet could be drawn directly..
Related
I'm triying to cover an image with another in order to provide a watermark but it has to cover the entire source image. The problem is that the watermark provided is of 600x600 and the source image can have any size and aspect ratio. So far it does not cover the source image entirely.
I solved it like this (in a very straightforward way).
private void button1_Click(object sender, EventArgs e)
{
var image = new Bitmap( this.pictureBox1.Image.Width, this.pictureBox1.Image.Height);
var rect = new Rectangle(0, 0, this.pictureBox1.Image.Width, this.pictureBox1.Image.Height);
Graphics graphics = Graphics.FromImage(image);
graphics.DrawImage(this.pictureBox1.Image, 0, 0);
var waterMarkImage = new Bitmap(this.pictureBox2.Image.Width, this.pictureBox2.Image.Height);
for (int y = 0; y < waterMarkImage.Height; y++)
{
for (int x = 0; x < waterMarkImage.Width; x++)
{
var color = (this.pictureBox2.Image as Bitmap).GetPixel(x, y);
color = Color.FromArgb(50, color.R, color.G, color.B);
waterMarkImage.SetPixel(x, y, color);
}
}
graphics.DrawImage(waterMarkImage, rect);
this.pictureBox3.Image = image;
}
In pictureBox1 I loaded main image. In pictureBox2 I loaded "water mark". In the event handler I created resulting image (first main image then the second) and loaded it into pictureBox3. To get water mark affect I reduced alpha component of color (I set it to 50).
I have a windows forms project written in C#. The main form has a TabControl on it and there is a requirement for one of the users to be able to print one of the TabPages. The form is very long and I use a vertical scroll bar. The whole of the form needs to be able to be printed.
I have tried using the DrawToBitmap method to convert to a bitmap first, but this will only include the portion of the form that the user can see. Some other solutions I have tried involve screen capturing, which has the same issue.
How can I print out, or get an image, of the whole of the tab page, including the parts the user only sees when they scroll down?
This is rather simple for any control including TabControls and TabPages but not Forms.
All you need to do is enlarge the relevant controls enough to show all their content. (They don't have to be actually visible on screen.)
Here is an example:
tabControl1.Height = 10080;
tabPage2.Height = 10050;
dataGridView1.Height = 10000;
dataGridView1.Rows.Add(3000);
for (int i = 0; i < dataGridView1.Rows.Count; i++) dataGridView1[0, i].Value = i;
using (Bitmap bmp = new Bitmap(tabControl1.Width , tabControl1.Height ))
{
tabControl1.DrawToBitmap(bmp, tabControl1.ClientRectangle);
bmp.Save("D:\\xxxx.png", ImageFormat.Png);
}
This saves the full content of the DataGridView, the TabPage and the TabControl..
Note: that this will not work with forms, which can't much exceed the screen dimensions..
Update: Here is code that saves a form with vertical scrolling by patching several bitmaps together. It can, of course be expanded to include horizontal scrolling as well. I have coded a similar solution for larger Panels here.
static void saveLargeForm(Form form, string fileName)
{
// yes it may take a while
form.Cursor = Cursors.WaitCursor;
// allocate target bitmap and a buffer bitmap
Bitmap target = new Bitmap(form.DisplayRectangle.Width, form.DisplayRectangle.Height);
Bitmap buffer = new Bitmap(form.Width, form.Height);
// the vertical pointer
int y = 0;
var vsc = form.VerticalScroll;
vsc.Value = 0;
form.AutoScrollPosition = new Point(0, 0);
// the scroll amount
int l = vsc.LargeChange;
Rectangle srcRect = ClientBounds(form);
Rectangle destRect = Rectangle.Empty;
bool done = false;
// we'll draw onto the large bitmap with G
using (Graphics G = Graphics.FromImage(target))
{
while (!done)
{
destRect = new Rectangle(0, y, srcRect.Width, srcRect.Height);
form.DrawToBitmap(buffer, new Rectangle(0, 0, form.Width, form.Height));
G.DrawImage(buffer, destRect, srcRect, GraphicsUnit.Pixel);
int v = vsc.Value;
vsc.Value = vsc.Value + l;
form.AutoScrollPosition = new Point(form.AutoScrollPosition.X, vsc.Value + l);
int delta = vsc.Value - v;
done = delta < l;
y += delta;
}
destRect = new Rectangle(0, y, srcRect.Width, srcRect.Height);
form.DrawToBitmap(buffer, new Rectangle(0, 0, form.Width, form.Height));
G.DrawImage(buffer, destRect, srcRect, GraphicsUnit.Pixel);
}
// write result to disc and clean up
target.Save(fileName, System.Drawing.Imaging.ImageFormat.Png);
target.Dispose();
buffer.Dispose();
GC.Collect(); // not sure why, but it helped
form.Cursor = Cursors.Default;
}
It makes use of a helper function to determine the the net size of the virtual client rectangle, ie excluding borders, title and scrollbar:
static Rectangle ClientBounds(Form f)
{
Rectangle rc = f.ClientRectangle;
Rectangle rb = f.Bounds;
int sw = SystemInformation.VerticalScrollBarWidth;
var vsc = f.VerticalScroll;
int bw = (rb.Width - rc.Width - (vsc.Visible ? sw : 0) ) / 2;
int th = (rb.Height - rc.Height) - bw * 2;
return new Rectangle(bw, th + bw, rc.Width, rc.Height );
}
I have created application where user can drag and drop the controls during run-time. I use my ss function to align the controls in a table like fashion using 2D-array. I want the application to show/highlight the position where the control will be placed when its brought near certain coordinate.
struct IconPanel
{
public int left;
public int top;
}
static void ss(Control control)
{
int row, col, nearestCol = 0, nearestRow = 0, rowDist = 100, diff, colDist = 100;
for (row = 0; row < iconPanels.GetLength(0); row++)
{
for (col = 0; col < iconPanels.GetLength(1); col++)
{
diff = Math.Abs(control.Left - iconPanels[row, col].left);
if (diff < colDist)
{
colDist = diff;
nearestCol = col;
}
diff = Math.Abs(control.Top - iconPanels[row, col].top);
if (diff < rowDist)
{
rowDist = diff;
nearestRow = row;
}
}
}
control.Left = iconPanels[nearestRow, nearestCol].left;
control.Top = iconPanels[nearestRow, nearestCol].top;
}
You probably don't want these position markers to persist, so this is one of the rare cases where I would use
using ( Graphics G = theParentContainer.CreateGraphics())
foreach(Rectangle rect in yourPositions) G.DrawRectangle(Pens.Red, rect);
Clean them up after the drop or after leaving the control etc.. by calling theParentContainer.Invalidate();
This assumes that you know the possible position and can create a
List<Rectangle> yourPositions = new List<Rectangle>();
holding them.
As we all know ;-) graphics created with a Graphics object that was instantiated with Control.CeateGraphics won't persist and will dissappear with the next Paint event. But for such an interactive helper graphics this is perfect. Other examples are rubberband-drawing or cursor tracking..
On control that you would like to be highlited subscribe to Paint event.
private void highlightControl_Paint(object sender, System.Windows.Forms.PaintEventArgs e) {
if(mouse is over or other control is over) {
Graphics g = e.Graphics;
g.FillRectangle(Brushes.Red, 0, 0, this.Width, this.Height);
}
}
I have a windows forms project written in C#. The main form has a TabControl on it and there is a requirement for one of the users to be able to print one of the TabPages. The form is very long and I use a vertical scroll bar. The whole of the form needs to be able to be printed.
I have tried using the DrawToBitmap method to convert to a bitmap first, but this will only include the portion of the form that the user can see. Some other solutions I have tried involve screen capturing, which has the same issue.
How can I print out, or get an image, of the whole of the tab page, including the parts the user only sees when they scroll down?
This is rather simple for any control including TabControls and TabPages but not Forms.
All you need to do is enlarge the relevant controls enough to show all their content. (They don't have to be actually visible on screen.)
Here is an example:
tabControl1.Height = 10080;
tabPage2.Height = 10050;
dataGridView1.Height = 10000;
dataGridView1.Rows.Add(3000);
for (int i = 0; i < dataGridView1.Rows.Count; i++) dataGridView1[0, i].Value = i;
using (Bitmap bmp = new Bitmap(tabControl1.Width , tabControl1.Height ))
{
tabControl1.DrawToBitmap(bmp, tabControl1.ClientRectangle);
bmp.Save("D:\\xxxx.png", ImageFormat.Png);
}
This saves the full content of the DataGridView, the TabPage and the TabControl..
Note: that this will not work with forms, which can't much exceed the screen dimensions..
Update: Here is code that saves a form with vertical scrolling by patching several bitmaps together. It can, of course be expanded to include horizontal scrolling as well. I have coded a similar solution for larger Panels here.
static void saveLargeForm(Form form, string fileName)
{
// yes it may take a while
form.Cursor = Cursors.WaitCursor;
// allocate target bitmap and a buffer bitmap
Bitmap target = new Bitmap(form.DisplayRectangle.Width, form.DisplayRectangle.Height);
Bitmap buffer = new Bitmap(form.Width, form.Height);
// the vertical pointer
int y = 0;
var vsc = form.VerticalScroll;
vsc.Value = 0;
form.AutoScrollPosition = new Point(0, 0);
// the scroll amount
int l = vsc.LargeChange;
Rectangle srcRect = ClientBounds(form);
Rectangle destRect = Rectangle.Empty;
bool done = false;
// we'll draw onto the large bitmap with G
using (Graphics G = Graphics.FromImage(target))
{
while (!done)
{
destRect = new Rectangle(0, y, srcRect.Width, srcRect.Height);
form.DrawToBitmap(buffer, new Rectangle(0, 0, form.Width, form.Height));
G.DrawImage(buffer, destRect, srcRect, GraphicsUnit.Pixel);
int v = vsc.Value;
vsc.Value = vsc.Value + l;
form.AutoScrollPosition = new Point(form.AutoScrollPosition.X, vsc.Value + l);
int delta = vsc.Value - v;
done = delta < l;
y += delta;
}
destRect = new Rectangle(0, y, srcRect.Width, srcRect.Height);
form.DrawToBitmap(buffer, new Rectangle(0, 0, form.Width, form.Height));
G.DrawImage(buffer, destRect, srcRect, GraphicsUnit.Pixel);
}
// write result to disc and clean up
target.Save(fileName, System.Drawing.Imaging.ImageFormat.Png);
target.Dispose();
buffer.Dispose();
GC.Collect(); // not sure why, but it helped
form.Cursor = Cursors.Default;
}
It makes use of a helper function to determine the the net size of the virtual client rectangle, ie excluding borders, title and scrollbar:
static Rectangle ClientBounds(Form f)
{
Rectangle rc = f.ClientRectangle;
Rectangle rb = f.Bounds;
int sw = SystemInformation.VerticalScrollBarWidth;
var vsc = f.VerticalScroll;
int bw = (rb.Width - rc.Width - (vsc.Visible ? sw : 0) ) / 2;
int th = (rb.Height - rc.Height) - bw * 2;
return new Rectangle(bw, th + bw, rc.Width, rc.Height );
}
I need to display a persistent grid in a TabPage. My problems would be instantly solved if I could draw to the entire non-visible portion of the TabPage and prevent graphics from being erased when scrolling.
The only other solution I can think of is tracking the scroll position in the tab and basing the grid drawn from that.
To get this to draw in the first place, I had to create an EventHandler for TabPage.Paint.
//Code removed
This method draws vertical and horizontal lines to create a grid within the visible tab, but it continues to draw whenever a Paint event occurs (i.e. scrolling), so it creates overlapping lines and aren't aligned to anything but the size of the current visible area of the tab.
Maybe something like this will work for you:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
const int gridSpacing = 20;
const int lineThickness = 1;
Bitmap bmp = new Bitmap(gridSpacing, gridSpacing);
using (System.Drawing.Pen pen = new System.Drawing.Pen(Color.Blue, lineThickness))
{
using (Graphics G = Graphics.FromImage(bmp))
{
G.Clear(this.BackColor);
G.DrawLine(pen, 0, bmp.Height - pen.Width, bmp.Width, bmp.Height - pen.Width); // horizontal
G.DrawLine(pen, bmp.Width - pen.Width, 0, bmp.Width - pen.Width, bmp.Height); // vertical
}
}
foreach (TabPage TP in tabControl1.TabPages)
{
TP.BackgroundImage = bmp;
TP.BackgroundImageLayout = ImageLayout.Tile;
}
}
}
Keep in mind that this solution is just pseudo. You also have to respond to scrolling.
void form_draw()
{
spacingX = offsetX % scale * -1;
spacingY = offsetY % scale * -1;
if (form.HorizontalPosition != lastXPosition && form.VerticalPosition == lastYPosition)
lastStartX += spacingX;
else if (tab.HorizontalScroll.Value == lastXPosition && form.VerticalPosition != lastYPosition)
lastStartY += spacingY;
lastYPosition = form.VerticalPosition;
lastXPosition = form.HorizontalPosition;
for (int i = lastStartY; i < formHeight; i += scale)
form.draw(0, i, formWidth, i);
for (int i = lastStartX; i < formWidth; i += scale)
form.draw(i, 0, i, formWidth);
}