I am using StretchImage because the box is resizable with splitters. It looks like the default is some kind of smooth bilinear filtering, causing my image to be blurry and have moire patterns.
I needed this functionality also. I made a class that inherits PictureBox, overrides OnPaint and adds a property to allow the interpolation mode to be set:
using System.Drawing.Drawing2D;
using System.Windows.Forms;
/// <summary>
/// Inherits from PictureBox; adds Interpolation Mode Setting
/// </summary>
public class PictureBoxWithInterpolationMode : PictureBox
{
public InterpolationMode InterpolationMode { get; set; }
protected override void OnPaint(PaintEventArgs paintEventArgs)
{
paintEventArgs.Graphics.InterpolationMode = InterpolationMode;
base.OnPaint(paintEventArgs);
}
}
I suspect you're going to have to do the resizing manually thru the Image class and DrawImage function and respond to the resize events on the PictureBox.
I did a MSDN search and turns out there's an article on this, which is not very detailed but outlines that you should use the paint event.
http://msdn.microsoft.com/en-us/library/k0fsyd4e.aspx
I edited a commonly available image zooming example to use this feature, see below
Edited from: http://www.dotnetcurry.com/ShowArticle.aspx?ID=196&AspxAutoDetectCookieSupport=1
Hope this helps
private void Form1_Load(object sender, EventArgs e)
{
// set image location
imgOriginal = new Bitmap(Image.FromFile(#"C:\images\TestImage.bmp"));
picBox.Image = imgOriginal;
// set Picture Box Attributes
picBox.SizeMode = PictureBoxSizeMode.StretchImage;
// set Slider Attributes
zoomSlider.Minimum = 1;
zoomSlider.Maximum = 5;
zoomSlider.SmallChange = 1;
zoomSlider.LargeChange = 1;
zoomSlider.UseWaitCursor = false;
SetPictureBoxSize();
// reduce flickering
this.DoubleBuffered = true;
}
// picturebox size changed triggers paint event
private void SetPictureBoxSize()
{
Size s = new Size(Convert.ToInt32(imgOriginal.Width * zoomSlider.Value), Convert.ToInt32(imgOriginal.Height * zoomSlider.Value));
picBox.Size = s;
}
// looks for user trackbar changes
private void trackBar1_Scroll(object sender, EventArgs e)
{
if (zoomSlider.Value > 0)
{
SetPictureBoxSize();
}
}
// redraws image using nearest neighbour resampling
private void picBox_Paint_1(object sender, PaintEventArgs e)
{
e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
e.Graphics.DrawImage(
imgOriginal,
new Rectangle(0, 0, picBox.Width, picBox.Height),
// destination rectangle
0,
0, // upper-left corner of source rectangle
imgOriginal.Width, // width of source rectangle
imgOriginal.Height, // height of source rectangle
GraphicsUnit.Pixel);
}
When resizing an image in .net, the System.Drawing.Drawing2D.InterpolationMode offers the following resize methods:
Bicubic
Bilinear
High
HighQualityBicubic
HighQualityBilinear
Low
NearestNeighbor
Default
Related
I created Panel class "GpanelBorder" which draw border in custom panel with code:
namespace GetterControlsLibary
{
public class GpanelBorder : Panel
{
private Color colorBorder;
public GpanelBorder()
{
SetStyle(ControlStyles.UserPaint | ControlStyles.ResizeRedraw | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawRectangle(
new Pen(
new SolidBrush(colorBorder), 8),
e.ClipRectangle);
}
public Color BorderColor
{
get
{
return colorBorder;
}
set
{
colorBorder = value;
}
}
}
}
Works fine but when i in design mode, mouse click inside panel and move mouse or drag other control over this panel, artifacts are created (picture below)
How to fix it?
The .ClipRectangle parameter does NOT necessarily represent the entire area of your control to be painted within. It may represent a SMALLER portion of your control indicating just that portion needs to be repainted. You can use the "clip rectangle" to redraw only a portion of your control in situations where it would be too costly to compute and repaint the entire control. If that situation doesn't apply to you, then use the ClientRectangle to get the bounds of your entire control and use that to draw your border. Also, you are LEAKING a PEN and a SOLIDBRUSH. You need to .Dispose() of those resources when you're done with them. This is best done with a using block:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (SolidBrush sb = new SolidBrush(colorBorder), 8))
{
using (Pen p = new Pen(sb))
{
e.Graphics.DrawRectangle(p, this.ClientRectangle);
}
}
}
You may need to create a new Rectangle based on ClientRectangle and adjust that to your liking before drawing.
Thanks, Works great!
but corectly code is
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (SolidBrush sb = new SolidBrush(colorBorder))
{
using (Pen p = new Pen(colorBorder, 2))
{
e.Graphics.DrawRectangle(p, this.ClientRectangle);
}
}
}
I'm trying to understand the graphics, and in the Graphics.FromImage documentation, it has this as an example:
private void FromImageImage(PaintEventArgs e)
{
// Create image.
Image imageFile = Image.FromFile("SampImag.jpg");
// Create graphics object for alteration.
Graphics newGraphics = Graphics.FromImage(imageFile);
// Alter image.
newGraphics.FillRectangle(new SolidBrush(Color.Black), 100, 50, 100, 100);
// Draw image to screen.
e.Graphics.DrawImage(imageFile, new PointF(0.0F, 0.0F));
// Dispose of graphics object.
newGraphics.Dispose();
}
I'm new to C# and Windows Forms and am struggling to understand how this all fits together. How is this code run, say when the form first loads or when a button is pressed?
Maybe this will help. I have an example of both drawing on Paint events but also drawing on top of an existing Image. I created a form with two picture boxes. One for each case. pictureBox1 has an event handler for the .Paint event, while pictureBox2 is only drawn when a button is pressed.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
pictureBox1.BackColor=Color.Black;
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
// The code below will draw on the surface of pictureBox1
// It gets triggered automatically by Windows, or by
// calling .Invalidate() or .Refresh() on the picture box.
DrawGraphics(e.Graphics, pictureBox1.ClientRectangle);
}
private void toolStripButton1_Click(object sender, EventArgs e)
{
// The code below will draw on an existing image shown in pictureBox2
var img = new Bitmap(pictureBox2.Image);
var g = Graphics.FromImage(img);
DrawGraphics(g, pictureBox2.ClientRectangle);
pictureBox2.Image=img;
}
void DrawGraphics(Graphics g, Rectangle target)
{
// draw a red rectangle around the moon
g.DrawRectangle(Pens.Red, 130, 69, 8, 8);
}
}
So when you launch the application a red rectangle appears on the left only, because the button hasn't been pressed yet.
and when the button is pressed, the red rectangle appears on top of the image displayed in pictureBox2.
Nothing dramatic, but it does the job. So depending on the mode of operation you need (user graphics, or image annotations) use the example code to understand how to do it.
I have one Flow Layout Panel with some User Controls in it.
I Want to Select these controls using rectangle selection using Mouse,like one used in windows file explorer .
I have tried these : https://support.microsoft.com/en-us/kb/314945
But it was very flickering and not useful (I might be wrong,please correct me).
Any good examples please.
Drawing the rubber-band rectangle is done like this:
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
panel1.Refresh();
using (Graphics g = panel1.CreateGraphics())
{
Rectangle rect = GetRectangle(mdown, e.Location);
g.DrawRectangle(Pens.Red, rect);
}
}
}
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
mdown = e.Location;
}
It uses a helper function:
static public Rectangle GetRectangle(Point p1, Point p2)
{
return new Rectangle(Math.Min(p1.X, p2.X), Math.Min(p1.Y, p2.Y),
Math.Abs(p1.X - p2.X), Math.Abs(p1.Y - p2.Y));
}
I don't get any flicker from it. If you do, maybe you have coded the Paint event, you may want to use a double-buffered Panel:
class DrawPanel : Panel
{
public DrawPanel()
{
DoubleBuffered = true;
}
}
Update: Instead of a Panel, which is a Container control and not really meant to draw onto, you can use a Picturebox or a Label (with Autosize=false); both have the DoubleBuffered property turned on out of the box and support drawing better than Panels do.
If the problem is the flickering only. You might want to set the Forms double buffer property to true.
I have a growing rectangle drawn on top of a TableLayoutPanel but when it grows it causes a horrible flicker even with Double Buffer.
I am using e.Graphics.FillRectangle and a global variable that increases the size of the rectangle. I set up a timer to make it grow every 1/10th of a second by 1 pixel. Why does it flicker so much and how can I stop the flicker?
int grow = 100;
private void tableLayoutPanel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.FillRectangle(Brushes.Red, (tableLayoutPanel1.Width-grow)/2, (tableLayoutPanel1.Height-grow)/2, grow,grow);
}
private void timer1_Tick(object sender, EventArgs e)
{
grow += 10;
tableLayoutPanel1.Refresh();
}
In order to rule out all other possibilities I removed everything from my program and started from scratch with just a growing rectangle and it still creates this horrible flicker.
Ok, here is the code. You first need to make background buffer Bitmap with the size of your control. After that, you will need to draw everything on the Bitmap, and than draw that bitmap onto the control.
Bitmap backBuffer = null;
int grow = 100;
private void tableLayoutPanel1_Paint(object sender, PaintEventArgs e)
{
if (backBuffer == null)
backBuffer = new Bitmap(tableLayoutPanel1.Width, tableLayoutPanel1.Height);
Graphics g = Graphics.FromImage(backBuffer);
g.Clear(tableLayoutPanel1.BackColor);
g.FillRectangle(Brushes.Red, (tableLayoutPanel1.Width - grow) / 2, (tableLayoutPanel1.Height - grow) / 2, grow, grow);
e.Graphics.DrawImage(backBuffer, 0, 0, backBuffer.Width, backBuffer.Height);
g.Dispose();
}
private void tableLayoutPanel1_Resize(object sender, EventArgs e)
{
backBuffer = null;
}
private void timer1_Tick(object sender, EventArgs e)
{
grow += 10;
tableLayoutPanel1.Invalidate();
}
Note that you will need to create new Bitmap each time you resize the TableLayoutPanel. In addition I suggest using Invalidate() instead of Refresh().
But this will still include some potential flickering. In order to completely avoid flicker, in addition to the previous code, you will need to subclass the TableLayoutPanel and override the OnPaintBackground() method in such a way that base.OnPaintBackground is never called. This way way won't have any flickering at all. The reason why you have the flickering is because the background is redrawn before your Rectangle, any time you draw it. Switch the original TableLayoutPanel class with this BackgroundlessTableLayoutPanel
public class BackgroundlessTableLayoutPanel : TableLayoutPanel
{
protected override void OnPaintBackground(PaintEventArgs e)
{
}
}
Most controls have a Paint event where you can implement any custom drawing you need to do. It is also possible to implement your own control where you override the OnPaint method. See the article here.
Both these should give ok results.
is there a way to add a drop shadow to controls?
are there any controls out there with this feature?
You have to overwrite the CreateParamsproperty like this:
private const int CS_DROPSHADOW = 0x00020000;
protected override CreateParams CreateParams
{
get
{
// add the drop shadow flag for automatically drawing
// a drop shadow around the form
CreateParams cp = base.CreateParams;
cp.ClassStyle |= CS_DROPSHADOW;
return cp;
}
}
This question has been around for 6 years and needs an answer. I hope that anyone who needs to do this can extrapolate an answer for any control set from my solution. I had a panel and wanted to draw a drop shadow underneath every child control - in this instance one or more panels (but the solution should hold good for other control types with some minor code changes).
As the drop shadow for a control has to be drawn on the surface of that control's container we start by adding a function to the container's Paint() event.
Container.Paint += dropShadow;
dropShadow() looks like this:
private void dropShadow(object sender, PaintEventArgs e)
{
Panel panel = (Panel)sender;
Color[] shadow = new Color[3];
shadow[0] = Color.FromArgb(181, 181, 181);
shadow[1] = Color.FromArgb(195, 195, 195);
shadow[2] = Color.FromArgb(211, 211, 211);
Pen pen = new Pen(shadow[0]);
using (pen)
{
foreach (Panel p in panel.Controls.OfType<Panel>())
{
Point pt = p.Location;
pt.Y += p.Height;
for (var sp = 0; sp < 3; sp++)
{
pen.Color = shadow[sp];
e.Graphics.DrawLine(pen, pt.X, pt.Y, pt.X + p.Width - 1, pt.Y);
pt.Y++;
}
}
}
}
Clearly you can pick a different control type from the container's collection and you can vary the colour and depth of the shadow with some minor tweaks.
The top answer does in fact generate a shadow, but I personally wasn't satisfied with it for a few reasons:
It only works for rectangles (granted, WinForms controls are all rectangles, but we might want to use this in other cases)
More importantly: It's not smooth. It doesn't look as natural as other shadows in other programs look.
Finally, it's slightly annoying to configure.
So, because of all these things, I ended up writing my own for my project and I thought I'd share it here:
public partial class Form1 : Form
{
List<Control> shadowControls = new List<Control>();
Bitmap shadowBmp = null;
public Form1()
{
InitializeComponent();
shadowControls.Add(panel1);
this.Refresh();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
if (shadowBmp == null || shadowBmp.Size != this.Size)
{
shadowBmp?.Dispose();
shadowBmp = new Bitmap(this.Width, this.Height, PixelFormat.Format32bppArgb);
}
foreach (Control control in shadowControls)
{
using (GraphicsPath gp = new GraphicsPath())
{
gp.AddRectangle(new Rectangle(control.Location.X, control.Location.Y, control.Size.Width, control.Size.Height));
DrawShadowSmooth(gp, 100, 60, shadowBmp);
}
e.Graphics.DrawImage(shadowBmp, new Point(0, 0));
}
}
private static void DrawShadowSmooth(GraphicsPath gp, int intensity, int radius, Bitmap dest)
{
using (Graphics g = Graphics.FromImage(dest))
{
g.Clear(Color.Transparent);
g.CompositingMode = CompositingMode.SourceCopy;
double alpha = 0;
double astep = 0;
double astepstep = (double)intensity / radius / (radius / 2D);
for (int thickness = radius; thickness > 0; thickness--)
{
using (Pen p = new Pen(Color.FromArgb((int)alpha, 0, 0, 0), thickness))
{
p.LineJoin = LineJoin.Round;
g.DrawPath(p, gp);
}
alpha += astep;
astep += astepstep;
}
}
}
}
In this implementation, all Controls added to the shadowControls will be painted with a smooth shadow. You should be able to implement this for non-rectangular shapes because the main function to generate the shadows takes a GraphicsPath. Please note that it's important you draw the shadow to another bitmap before drawing it to the form because the main function requires a compositing mode of SourceCopy to work, which means if you don't draw it to another surface first anything behind the shadow will be completely replaced and the transparency aspect is useless. I'm on a roll of answering 10-year-old questions, but hopefully, this helps someone!
There is in WPF if you can stretch to using that instead, I don't believe there is an alternative in Windows Forms due to the limited capabilities of GDI+.
Here's a controversial opinion, you do it without code.
Set your main panel Border Style to Fixed Single.
Create 3 panels below it, each 1 pixel larger in every direction.
Each of the 3 panels is of a lighter shade of gray.
Not perfect but cheap and easy.
panel with pseudo-shadow