Show Transparent Loading Spinner above other Controls - c#

I am working in a spinner control. I want the control to support transparent backcolor. When the arc is drawn, there is a blank space in the middle, I want that space to be truly transparent, so that I could put another control behind it and it would not be covered by the spinner.
I tried overriding the CreateParams void.
Also I set the style to support TransparentColor.
Tried overriding OnPaintBackground void, but I cannot achieve the real transparent backcolor.
So, what can you suggest me to do?

To make a transparent layer, you should override painting of the control and draw the control in this order, First draw all controls in the same container which are under your control (based on z-index) on a bitmap.
Then draw that bitmap on graphics of your control.
At last draw content of your control.
Also the BackColor of your control should be Color.Transparent.
Also as another option to make transparent layer, you can exclude some regions from your control when drawing.
In the following samples I used the first technique and created 2 controls. A spinning circles transparent control. and a transparent picturebox control.
In both samples I used a delay between loading rows to showing a spinner make sense.
Sample 1 - Using a SpinningCircles Control
SpinningCircles control draws circles and supports transparency. The control doesn't animate at design-time, but it animates at run-time. Also it doesn't consume resources when it is not visible.
Sample 2 - Using a TransparentPictureBox Control and a transparent animated gif
TransparentPictureBox control supports transparency, so I used an animated gif as its image and as you can see, the gif is showing correctly.
Sample 1 Code - SpinningCircles
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
public class SpinningCircles : Control
{
int increment = 1;
int radius = 4;
int n = 8;
int next = 0;
Timer timer;
public SpinningCircles()
{
timer = new Timer();
this.Size = new Size(100, 100);
timer.Tick += (s, e) => this.Invalidate();
if (!DesignMode)
timer.Enabled = true;
SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.ResizeRedraw | ControlStyles.UserPaint |
ControlStyles.SupportsTransparentBackColor, true);
BackColor = Color.Transparent;
}
protected override void OnPaint(PaintEventArgs e)
{
if (Parent != null && this.BackColor == Color.Transparent)
{
using (var bmp = new Bitmap(Parent.Width, Parent.Height))
{
Parent.Controls.Cast<Control>()
.Where(c => Parent.Controls.GetChildIndex(c) > Parent.Controls.GetChildIndex(this))
.Where(c => c.Bounds.IntersectsWith(this.Bounds))
.OrderByDescending(c => Parent.Controls.GetChildIndex(c))
.ToList()
.ForEach(c => c.DrawToBitmap(bmp, c.Bounds));
e.Graphics.DrawImage(bmp, -Left, -Top);
}
}
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
int length = Math.Min(Width, Height);
PointF center = new PointF(length / 2, length / 2);
int bigRadius = length / 2 - radius - (n - 1) * increment;
float unitAngle = 360 / n;
if (!DesignMode)
next++;
next = next >= n ? 0 : next;
int a = 0;
for (int i = next; i < next + n; i++)
{
int factor = i % n;
float c1X = center.X + (float)(bigRadius * Math.Cos(unitAngle * factor * Math.PI / 180));
float c1Y = center.Y + (float)(bigRadius * Math.Sin(unitAngle * factor * Math.PI / 180));
int currRad = radius + a * increment;
PointF c1 = new PointF(c1X - currRad, c1Y - currRad);
e.Graphics.FillEllipse(Brushes.Black, c1.X, c1.Y, 2 * currRad, 2 * currRad);
using (Pen pen = new Pen(Color.White, 2))
e.Graphics.DrawEllipse(pen, c1.X, c1.Y, 2 * currRad, 2 * currRad);
a++;
}
}
protected override void OnVisibleChanged(EventArgs e)
{
timer.Enabled = Visible;
base.OnVisibleChanged(e);
}
}
Sample 2 Code - TransparentPictureBox Code
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
class TransparentPictureBox : PictureBox
{
public TransparentPictureBox()
{
this.BackColor = Color.Transparent;
}
protected override void OnPaint(PaintEventArgs e)
{
if (Parent != null && this.BackColor == Color.Transparent)
{
using (var bmp = new Bitmap(Parent.Width, Parent.Height))
{
Parent.Controls.Cast<Control>()
.Where(c => Parent.Controls.GetChildIndex(c) > Parent.Controls.GetChildIndex(this))
.Where(c => c.Bounds.IntersectsWith(this.Bounds))
.OrderByDescending(c => Parent.Controls.GetChildIndex(c))
.ToList()
.ForEach(c => c.DrawToBitmap(bmp, c.Bounds));
e.Graphics.DrawImage(bmp, -Left, -Top);
}
}
base.OnPaint(e);
}
}

I slightly changed Reza's code (SpinningCircles) to add a semi-transparent background. I wanted to share it with you.
(NOTE: The spinner length was fixed to 100 and should be added as a component property)
public partial class WorkingPanel : UserControl
{
#region Constants
private static readonly Int32 kSpinnerLength = 100;
#endregion
#region Fields
private Int32 increment = 1;
private Int32 radius = 4;
private Int32 n = 8;
private Int32 next = 0;
private Timer timer = null;
#endregion
#region Constructor
public WorkingPanel()
{
this.Size = new Size(100, 100);
timer = new Timer();
timer.Tick += (s, e) => this.Invalidate();
if (!DesignMode)
timer.Enabled = true;
SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.ResizeRedraw | ControlStyles.UserPaint |
ControlStyles.SupportsTransparentBackColor, true);
BackColor = Color.Transparent;
}
#endregion
#region Methods (Protected - Override)
protected override void OnPaint(PaintEventArgs e)
{
if (null != Parent && (this.BackColor.A != 255 || this.BackColor == Color.Transparent))
{
using (var bmp = new Bitmap(Parent.Width, Parent.Height))
{
Parent.Controls.Cast<Control>()
.Where(c => Parent.Controls.GetChildIndex(c) > Parent.Controls.GetChildIndex(this))
.Where(c => c.Bounds.IntersectsWith(this.Bounds))
.OrderByDescending(c => Parent.Controls.GetChildIndex(c))
.ToList()
.ForEach(c => c.DrawToBitmap(bmp, c.Bounds));
e.Graphics.DrawImage(bmp, -Left, -Top);
if (this.BackColor != Color.Transparent)
e.Graphics.FillRectangle(new SolidBrush(this.BackColor), new Rectangle(0, 0, Width, Height));
}
}
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
Int32 length = kSpinnerLength;
PointF center = new PointF(Width / 2, Height / 2);
Int32 bigRadius = length / 2 - radius - (n - 1) * increment;
float unitAngle = 360 / n;
if (!DesignMode)
next++;
next = next >= n ? 0 : next;
Int32 a = 0;
for (Int32 i = next; i < next + n; i++)
{
Int32 factor = i % n;
float c1X = center.X + (float)(bigRadius * Math.Cos(unitAngle * factor * Math.PI / 180));
float c1Y = center.Y + (float)(bigRadius * Math.Sin(unitAngle * factor * Math.PI / 180));
Int32 currRad = radius + a * increment;
PointF c1 = new PointF(c1X - currRad, c1Y - currRad);
e.Graphics.FillEllipse(Brushes.White, c1.X, c1.Y, 2 * currRad, 2 * currRad);
using (Pen pen = new Pen(Color.White, 2))
e.Graphics.DrawEllipse(pen, c1.X, c1.Y, 2 * currRad, 2 * currRad);
a++;
}
}
protected override void OnVisibleChanged(EventArgs e)
{
timer.Enabled = Visible;
base.OnVisibleChanged(e);
}
#endregion
}

Modification from CrApHeR's code to add Designer Properties
How to use
Add this class to your project
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
[DesignerCategory("Code")]
public class Spinner : Control
{
private int next = 0;
private Timer timer = new Timer();
public Spinner()
{
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
Size = new Size(100, 100);
BackColor = Color.Transparent;
timer.Tick += (s, e) => Invalidate();
if (!DesignMode)
{
timer.Enabled = true;
}
}
[Browsable(true)]
[Category("Appearance")]
public int NodeCount { get; set; } = 8;
[Browsable(true)]
[Category("Appearance")]
public int NodeRadius { get; set; } = 4;
[Browsable(true)]
[Category("Appearance")]
public float NodeResizeRatio { get; set; } = 1.0f;
[Browsable(true)]
[Category("Appearance")]
public Color NodeFillColor { get; set; } = Color.Black;
[Browsable(true)]
[Category("Appearance")]
public Color NodeBorderColor { get; set; } = Color.White;
[Browsable(true)]
[Category("Appearance")]
public int NodeBorderSize { get; set; } = 2;
[Browsable(true)]
[Category("Appearance")]
public int SpinnerRadius { get; set; } = 100;
protected override void OnPaint(PaintEventArgs e)
{
if (null != Parent && (BackColor.A != 255 || BackColor == Color.Transparent))
{
using (Bitmap bmp = new Bitmap(Parent.Width, Parent.Height))
{
foreach (Control control in GetIntersectingControls(Parent))
{
control.DrawToBitmap(bmp, control.Bounds);
}
e.Graphics.DrawImage(bmp, -Left, -Top);
if (BackColor != Color.Transparent)
{
using (Brush brush = new SolidBrush(BackColor))
{
e.Graphics.FillRectangle(brush, 0, 0, Width, Height);
}
}
}
}
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
PointF center = new PointF(Width / 2, Height / 2);
int bigRadius = (int)(SpinnerRadius / 2 - NodeRadius - (NodeCount - 1) * NodeResizeRatio);
float unitAngle = 360 / NodeCount;
if (!DesignMode)
{
next++;
}
next = next >= NodeCount ? 0 : next;
for (int i = next, a = 0; i < next + NodeCount; i++, a++)
{
int factor = i % NodeCount;
float c1X = center.X + (float)(bigRadius * Math.Cos(unitAngle * factor * Math.PI / 180));
float c1Y = center.Y + (float)(bigRadius * Math.Sin(unitAngle * factor * Math.PI / 180));
int currRad = (int)(NodeRadius + a * NodeResizeRatio);
PointF c1 = new PointF(c1X - currRad, c1Y - currRad);
using (Brush brush = new SolidBrush(NodeFillColor))
{
e.Graphics.FillEllipse(brush, c1.X, c1.Y, 2 * currRad, 2 * currRad);
}
using (Pen pen = new Pen(Color.White, NodeBorderSize))
{
e.Graphics.DrawEllipse(pen, c1.X, c1.Y, 2 * currRad, 2 * currRad);
}
}
}
protected override void OnVisibleChanged(EventArgs e)
{
timer.Enabled = Visible;
base.OnVisibleChanged(e);
}
private IOrderedEnumerable<Control> GetIntersectingControls(Control parent)
{
return parent.Controls.Cast<Control>()
.Where(c => parent.Controls.GetChildIndex(c) > parent.Controls.GetChildIndex(this))
.Where(c => c.Bounds.IntersectsWith(Bounds))
.OrderByDescending(c => parent.Controls.GetChildIndex(c));
}
}
Drag the Spinner control from the Toolbox to your Form
Alternatively, you can add it at runtime with this code
Spinner spinner = new Spinner();
spinner.Location = new Point(50, 50);
Controls.Add(spinner);
Then, you can change its properties from the Designer Properties category Appearance
The main properties you may want to customize are:
NodeCount (default: 8)
NodeRadius (default: 4)
NodeResizeRatio (default: 1.0f)
NodeFillColor (default: Black)
NodeBorderColor (default: White)
NodeBorderSize (default: 2)
SpinnerRadius (default: 100)
BackColor (default: Transparent) (supports alpha transparency)
BackColor = Color.FromArgb(50, Color.Blue.R, Color.Blue.G, Color.Blue.B))

Related

C# winforms - How to combine scrollbar with mouse wheel zooming?

Problem - I'm writing a program that draws graphics, and zooming is one of the features. Currently, a picturebox is placed on a panel, and the picturebox has vertical and horizontal scroll bars on the right and bottom. How to combine scrollbar with mouse wheel zooming? And I'm not sure if I should use paint to draw the graphics or set a bitmap to draw the graphics onto it?
Expected - When the mouse wheel is scrolled, the entire canvas(picturebox) include drawn graphics are scaled according to the current mouse position as the center (the horizontal and vertical scroll bars change according to the zoom center). When the mouse wheel is pressed and moved, the canvas can be dragged freely.
Expected as follows:
The initial code
private List<Point> _points;
private int _pointRadius = 50;
private float _scale = 1f;
private float _offsetX = 0f;
private float _offsetY = 0f;
private void picturebox_MouseDown(object sender, MouseEventArgs e)
{
_points.Add(e.Location);
}
private void picturebox_MouseWheel(object sender, MouseEvnetArgs e)
{
if(e.Delta < 0)
{
_scale += 0.1f;
_offsetX = e.X * (1f - _scale);
_offsetY = e.X * (1f - _scale);
}
else
{
_scale -= 0.1f;
_offsetX = e.X * (1f - _scale);
_offsetY = e.X * (1f - _scale);
}
picturebox.Invalidate();
}
private void picturebox_Paint(object sender, PaintEventArgs e)
{
e.Graphics.TranslateTransform(_offsetX, _offsetY);
e.Graphics.ScaleTransform(_scaleX, _scaleY);
foreach (Point p in _points)
{
e.Graphics.FillEllipse(Brushes.Black, p.X, - _pointRadius, p.Y - _pointRadius, 2 * _pointRadius, 2 * _pointRadius);
}
}
Hope the answer is modified based on the initial code.
Thanks in advance to everyone who helped me.
Would it be easier if I drew the graphics on a Bitmap?
Considering the nature of your task and the already implemented solutions in my ImageViewer I created a solution that draws the result in a Metafile, which is both elegant, consumes minimal memory and allows zooming without quality issues.
Here is the stripped version of my ImageViewer:
public class MetafileViewer : Control
{
private HScrollBar sbHorizontal = new HScrollBar { Visible = false };
private VScrollBar sbVertical = new VScrollBar { Visible = false };
private Metafile? image;
private Size imageSize;
private Rectangle targetRectangle;
private Rectangle clientRectangle;
private float zoom = 1;
private bool sbHorizontalVisible;
private bool sbVerticalVisible;
private int scrollFractionVertical;
public MetafileViewer()
{
Controls.AddRange(new Control[] { sbHorizontal, sbVertical });
sbHorizontal.ValueChanged += ScrollbarValueChanged;
sbVertical.ValueChanged += ScrollbarValueChanged;
}
void ScrollbarValueChanged(object? sender, EventArgs e) => Invalidate();
public Metafile? Image
{
get => image;
set
{
image = value;
imageSize = image?.Size ?? default;
InvalidateLayout();
}
}
public bool TryTranslate(Point mouseCoord, out PointF canvasCoord)
{
canvasCoord = default;
if (!targetRectangle.Contains(mouseCoord))
return false;
canvasCoord = new PointF((mouseCoord.X - targetRectangle.X) / zoom, (mouseCoord.Y - targetRectangle.Y) / zoom);
if (sbHorizontalVisible)
canvasCoord.X += sbHorizontal.Value / zoom;
if (sbVerticalVisible)
canvasCoord.Y += sbVertical.Value / zoom;
return true;
}
private void InvalidateLayout()
{
Invalidate();
if (imageSize.IsEmpty)
{
sbHorizontal.Visible = sbVertical.Visible = sbHorizontalVisible = sbVerticalVisible = false;
targetRectangle = Rectangle.Empty;
return;
}
Size clientSize = ClientSize;
if (clientSize.Width < 1 || clientSize.Height < 1)
{
targetRectangle = Rectangle.Empty;
return;
}
Size scaledSize = imageSize.Scale(zoom);
// scrollbars visibility
sbHorizontalVisible = scaledSize.Width > clientSize.Width
|| scaledSize.Width > clientSize.Width - SystemInformation.VerticalScrollBarWidth && scaledSize.Height > clientSize.Height;
sbVerticalVisible = scaledSize.Height > clientSize.Height
|| scaledSize.Height > clientSize.Height - SystemInformation.HorizontalScrollBarHeight && scaledSize.Width > clientSize.Width;
if (sbHorizontalVisible)
clientSize.Height -= SystemInformation.HorizontalScrollBarHeight;
if (sbVerticalVisible)
clientSize.Width -= SystemInformation.VerticalScrollBarWidth;
if (clientSize.Width < 1 || clientSize.Height < 1)
{
targetRectangle = Rectangle.Empty;
return;
}
Point clientLocation = Point.Empty;
var targetLocation = new Point((clientSize.Width >> 1) - (scaledSize.Width >> 1),
(clientSize.Height >> 1) - (scaledSize.Height >> 1));
// both scrollbars
if (sbHorizontalVisible && sbVerticalVisible)
{
sbHorizontal.Dock = sbVertical.Dock = DockStyle.None;
sbHorizontal.Width = clientSize.Width;
sbHorizontal.Top = clientSize.Height;
sbHorizontal.Left = 0;
sbVertical.Height = clientSize.Height;
sbVertical.Left = clientSize.Width;
}
// horizontal scrollbar
else if (sbHorizontalVisible)
sbHorizontal.Dock = DockStyle.Bottom;
// vertical scrollbar
else if (sbVerticalVisible)
sbVertical.Dock = DockStyle.Right;
// adjust scrollbar values
if (sbHorizontalVisible)
{
sbHorizontal.Minimum = targetLocation.X;
sbHorizontal.Maximum = targetLocation.X + scaledSize.Width;
sbHorizontal.LargeChange = clientSize.Width;
sbHorizontal.SmallChange = 32;
sbHorizontal.Value = Math.Min(sbHorizontal.Value, sbHorizontal.Maximum - sbHorizontal.LargeChange);
}
if (sbVerticalVisible)
{
sbVertical.Minimum = targetLocation.Y;
sbVertical.Maximum = targetLocation.Y + scaledSize.Height;
sbVertical.LargeChange = clientSize.Height;
sbVertical.SmallChange = 32;
sbVertical.Value = Math.Min(sbVertical.Value, sbVertical.Maximum - sbVertical.LargeChange);
}
sbHorizontal.Visible = sbHorizontalVisible;
sbVertical.Visible = sbVerticalVisible;
clientRectangle = new Rectangle(clientLocation, clientSize);
targetRectangle = new Rectangle(targetLocation, scaledSize);
if (sbVerticalVisible)
clientRectangle.X = SystemInformation.VerticalScrollBarWidth;
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
InvalidateLayout();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (image == null || e.ClipRectangle.Width <= 0 || e.ClipRectangle.Height <= 0)
return;
if (targetRectangle.IsEmpty)
InvalidateLayout();
if (targetRectangle.IsEmpty)
return;
Graphics g = e.Graphics;
g.IntersectClip(clientRectangle);
Rectangle dest = targetRectangle;
if (sbHorizontalVisible)
dest.X -= sbHorizontal.Value;
if (sbVerticalVisible)
dest.Y -= sbVertical.Value;
g.DrawImage(image, dest);
g.DrawRectangle(SystemPens.ControlText, Rectangle.Inflate(targetRectangle, 1, 1));
}
protected override void OnMouseWheel(MouseEventArgs e)
{
base.OnMouseWheel(e);
switch (ModifierKeys)
{
// zoom
case Keys.Control:
float delta = (float)e.Delta / SystemInformation.MouseWheelScrollDelta / 5;
if (delta.Equals(0f))
return;
delta += 1;
SetZoom(zoom * delta);
break;
// vertical scroll
case Keys.None:
VerticalScroll(e.Delta);
break;
}
}
private void VerticalScroll(int delta)
{
// When scrolling by mouse, delta is always +-120 so this will be a small change on the scrollbar.
// But we collect the fractional changes caused by the touchpad scrolling so it will not be lost either.
int totalDelta = scrollFractionVertical + delta * sbVertical.SmallChange;
scrollFractionVertical = totalDelta % SystemInformation.MouseWheelScrollDelta;
int newValue = sbVertical.Value - totalDelta / SystemInformation.MouseWheelScrollDelta;
SetValueSafe(sbVertical, newValue);
}
internal static void SetValueSafe(ScrollBar scrollBar, int value)
{
if (value < scrollBar.Minimum)
value = scrollBar.Minimum;
else if (value > scrollBar.Maximum - scrollBar.LargeChange + 1)
value = scrollBar.Maximum - scrollBar.LargeChange + 1;
scrollBar.Value = value;
}
private void SetZoom(float value)
{
const float maxZoom = 10f;
float minZoom = image == null ? 1f : 1f / Math.Min(imageSize.Width, imageSize.Height);
if (value < minZoom)
value = minZoom;
if (value > maxZoom)
value = maxZoom;
if (zoom.Equals(value))
return;
zoom = value;
InvalidateLayout();
}
}
And then the updated version of your initial code (add a new point by right click, zoom by Ctrl + mouse scroll):
public partial class RenderMetafileForm : Form
{
private static Size canvasSize = new Size(300, 200);
private List<PointF> points = new List<PointF>();
private const float pointRadius = 5;
public RenderMetafileForm()
{
InitializeComponent();
metafileViewer.MouseClick += MetafileViewer_MouseClick;
UpdateMetafile();
}
private void MetafileViewer_MouseClick(object? sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right && metafileViewer.TryTranslate(e.Location, out var coord))
{
points.Add(coord);
UpdateMetafile();
}
}
private void UpdateMetafile()
{
Graphics refGraph = Graphics.FromHwnd(IntPtr.Zero);
IntPtr hdc = refGraph.GetHdc();
Metafile result;
try
{
result = new Metafile(hdc, new Rectangle(Point.Empty, canvasSize), MetafileFrameUnit.Pixel, EmfType.EmfOnly, "Canvas");
using (var g = Graphics.FromImage(result))
{
foreach (PointF point in points)
g.FillEllipse(Brushes.Navy, point.X - pointRadius, point.Y - pointRadius, pointRadius * 2, pointRadius * 2);
}
}
finally
{
refGraph.ReleaseHdc(hdc);
refGraph.Dispose();
}
Metafile? previous = metafileViewer.Image;
metafileViewer.Image = result;
previous?.Dispose();
}
}
Result:
⚠️ Note: I did not add panning by keyboard or by grabbing the image but you can extract those from the original ImageViewer. Also, I removed DPI-aware scaling but see the ScaleSize extensions in the linked project.

PictureBox updates only on resize

Trying to display a graph on a Form application.
Created a PictureBox control and initialized this class with its value.
On resize, graph always updates; on mouse scroll, it hardly does.
It's GraphBox , PictureBox control, inside a GraphBoxPanel, Panel control.
This the class:
public struct DLT_measure_item
{
public DateTime ts;
public float value;
public int id;
public int X;
public int Y;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct dlt_ser_meas
{
public byte msg_id; // 'D'
public byte meas_count; // Number of measures
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] port; // Module ID (4b) + Port ID (4b)
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public float[] meas; // measure
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] msg_end;
}
public class manageGraph
{
private PictureBox box;
public bool displayGrid = true;
private int horAxisMin_p = 0;
private int horAxisMax_p = 300;
private float verAxisMin_p = 0;
private float verAxisMax_p = 40;
public int horAxisMin
{
get { return this.horAxisMin_p; }
set
{
if (value < horAxisMax_p)
{
this.horAxisMin_p = value;
reDraw();
}
}
}
public int horAxisMax
{
get { return this.horAxisMax_p; }
set
{
if (value > horAxisMin_p)
{
this.horAxisMax_p = value;
reDraw();
}
}
}
public float verAxisMin
{
get { return this.verAxisMin_p; }
set
{
if (value < verAxisMax_p)
{
this.verAxisMin_p = value;
verPointPerUnit = graphArea.Height / (verAxisMax_p - this.verAxisMin_p);
}
}
}
public float verAxisMax
{
get { return this.verAxisMax_p; }
set
{
if (value > verAxisMin_p)
{
this.verAxisMax_p = value;
verPointPerUnit = graphArea.Height / (this.verAxisMax_p - verAxisMin_p);
}
}
}
Pen axes = new Pen(Color.Black, (float)1.5);
public int horAxisSpacing = 30;
public int verAxisSpacing = 20;
public int horAxis = 20;
public int verAxis = 20;
private float horPointPerUnit = 1;
private float verPointPerUnit = 1;
public int horAxisTickLen = 5;
public int verAxisTickLen = 5;
public bool horAxisShowTime = false;
private Rectangle graphArea = new Rectangle();
public void reDraw()
{
box.Image.Dispose();
Bitmap GraphBlankImage = new Bitmap(box.Width, box.Height);
box.Image = GraphBlankImage;
updatePointPerUnit();
drawGrid();
box.Refresh();
}
public manageGraph(PictureBox targetImageBoxbox)
{
box = targetImageBoxbox;
horAxisMin_p = 0;
horAxisMax_p = 300;
verAxisMin_p = 0F;
verAxisMax_p = 50F;
updatePointPerUnit();
}
private Point measToPoint(DLT_measure_item measure) {
Point coords = new Point();
coords.X = graphArea.Width - (int)(
((DateTime.Now - measure.ts).TotalSeconds + horAxisMin_p) * horPointPerUnit ) ;
coords.Y = graphArea.Height - (int)(
((measure.value - verAxisMin_p) * verPointPerUnit));
return coords;
}
public manageGraph(PictureBox targetImageBoxbox,
int xmin, int xmax, float ymin, float ymax)
{
box = targetImageBoxbox;
horAxisMin_p = xmin;
horAxisMax_p = xmax;
verAxisMin_p = ymin;
verAxisMax_p = ymax;
updatePointPerUnit();
}
private void updateGraphArea()
{
graphArea = new Rectangle(0, 0, box.Width - horAxis, box.Height - verAxis);
}
private void updatePointPerUnit()
{
updateGraphArea();
horPointPerUnit = graphArea.Width / (horAxisMax_p - horAxisMin_p);
verPointPerUnit = graphArea.Height / (verAxisMax_p - verAxisMin_p);
}
public void drawGrid()
{
//updatePointPerUnit();
using (Graphics g = Graphics.FromImage(box.Image))
{
// X axis
g.DrawLine(axes, graphArea.Left, graphArea.Bottom, box.Width, graphArea.Bottom);
// Y axis
g.DrawLine(axes, graphArea.Right + 1, graphArea.Top, graphArea.Right +1, graphArea.Bottom);
using (Font ArialFont = new Font("Arial", 10))
{
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
// Put x labels
for (int i = 1; i <= (graphArea.Width / horPointPerUnit); i = i + (int)(horAxisSpacing / horPointPerUnit ) + 1)
{
g.DrawString((i).ToString(), ArialFont, Brushes.Black, graphArea.Width - ( i * horPointPerUnit) - 5, graphArea.Bottom +5);
g.DrawLine(axes, graphArea.Width - (i * horPointPerUnit), graphArea.Bottom + (horAxisTickLen / 2), graphArea.Width - (i * horPointPerUnit), graphArea.Bottom - (horAxisTickLen / 2));
}
// Put y labels
for (int i = 1; i <= (graphArea.Height / verPointPerUnit); i = i + (int)(verAxisSpacing / verPointPerUnit) +1)
{
g.DrawString((i).ToString(), ArialFont, Brushes.Black, graphArea.Right + 1 , graphArea.Height - (i * verPointPerUnit) - 8);
g.DrawLine(axes, graphArea.Width - (verAxisTickLen / 2), (i * verPointPerUnit), graphArea.Width + (verAxisTickLen / 2), (i * verPointPerUnit));
}
}
/*Put some random data*/
DLT_measure_item testmeas = new DLT_measure_item();
Point testGraphPoint = new Point();
testmeas.ts = DateTime.Now;
testmeas.value = 0;
testGraphPoint = measToPoint(testmeas);
g.FillEllipse(Brushes.Blue, testGraphPoint.X, testGraphPoint.Y, 4, 4);
for (double i = 0; i < 300; i++)
{
double x = i;
double freq = 10;
double y = 30 - (i/10);
testmeas.value = (float)y;
testmeas.ts = DateTime.Now.AddSeconds(-1 * i);
testGraphPoint = measToPoint(testmeas);
g.FillEllipse(Brushes.Red, testGraphPoint.X, testGraphPoint.Y, 2,2);
}
}
}
}
The initialization:
public DLThermalogMainForm()
{
InitializeComponent();
Bitmap GraphBlankImage = new Bitmap(GraphBox.Width, GraphBox.Height);
GraphBox.Image = GraphBlankImage;
myGraph = new manageGraph(GraphBox);
myGraph.drawGrid();
}
Those the handlers:
private void Form1_ResizeEnd(object sender, EventArgs e)
{
myGraph.reDraw();
OutputTextBox.AppendText("Resize." + Environment.NewLine);
}
private void GraphBox_MouseMove(object sender, MouseEventArgs e)
{
//Get the focus to have the wheel working
GraphBoxPanel.Focus();
}
private void GraphBoxPanel_MouseWheel(object sender, MouseEventArgs e)
{
// Change x axis max value to zoom in/out the graph
myGraph.horAxisMax += e.Delta/ 120;
myGraph.reDraw();
}
On resize event, it always redraws; on mouse wheel, it does quickly only with small horAxisMax values (does it makes sense???), but for larger, it takes many seconds to update, or doesn't at all.
Thank you very much
Change reDraw like this:
public void reDraw()
{
box.Image.Dispose();
Bitmap GraphBlankImage = new Bitmap(box.ClientSize.Width, box.ClientSize.Height);
updatePointPerUnit();
drawGrid(GraphBlankImage);
box.Image = GraphBlankImage;
}
and drawGrid like this:
public void drawGrid(Bitmap bmp)
{
//updatePointPerUnit(); //??
using (Graphics g = Graphics.FromImage(bmp))
{
...
...
...
}
}
Now the Bitmap with the grid should immediately show up in the PictureBox.
As mentioned a Graphics object is a tool to change an associated Bitmap. To pick it up the Bitmap should be assigned to the PictureBoxe's Image.
Also note, that unless your PictureBox has no Border, there is a small difference between the outer size (aka Bounds) and the inner size, the ClientRectangle / ClientSize. The Image should have the ClientSize
You may wonder why your original code doesn't work? After all an Image is a reference type, so changing it, as you did should be enough..
But looking deeper into the source code we find the reason:
The PictureBox's Image is a property and in its setter there is a call to InstallNewImage:
public Image Image {
get {
return image;
}
set {
InstallNewImage(value, ImageInstallationType.DirectlySpecified);
}
}
The same call is also in a few other places like Load or in the setter of ImageLocation. But changing the Image behind the scene alone will not force the PictureBox to make that call. A Refresh() should also do it.. And, as you found out, resizing it will also cause the PictureBox to pick up the changed data in the Image Bitmap..
The easiest way to force updating is simply to invalidate the control.
timer = new Timer();
timer.Interval = 200; //refreshes every 200 ms
timer.Tick += (sender,e) => targetImageBoxbox.Invalidate();
timer.Start();

Mandelbrot Conversion [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I'm receiving no errors but when I'm running it I'm also unable to see the Mandelbrot it just displays the grey box, I'm currently stuck at this one point thanks for any help, if you see any other parts of my code which contains grammar or coding errors it would be much appreciated if you told me.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
namespace SE_Fractal_Assignment
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public struct HSBColor
{
float h;
float s;
float b;
int a;
public HSBColor(float h, float s, float b)
{
this.a = 0xff;
this.h = Math.Min(Math.Max(h, 0), 255);
this.s = Math.Min(Math.Max(h, 0), 255);
this.b = Math.Min(Math.Max(h, 0), 255);
}
public float H
{
get { return h; }
}
public float S
{
get { return s; }
}
public float B
{
get { return b; }
}
public int A
{
get { return a; }
}
public Color Color
{
get
{
return FromHSB(this);
}
}
public static Color FromHSB(HSBColor hsbColor)
{
float r = hsbColor.b;
float g = hsbColor.b;
float b = hsbColor.b;
if (hsbColor.s != 0)
{
float max = hsbColor.b;
float dif = hsbColor.b * hsbColor.s / 255f;
float min = hsbColor.b - dif;
float h = hsbColor.h * 360f / 255f;
if (h < 60f)
{
r = max;
g = h * dif / 60f + min;
b = min;
}
else if (h < 120f)
{
r = -(h - 120f) * dif / 60f + min;
g = max;
b = min;
}
else if (h < 180f)
{
r = min;
g = max;
b = -(h - 120f) * dif / 60f + min;
}
else if (h < 240f)
{
r = min;
g = -(h - 240f) * dif / 60f + min;
b = max;
}
else if (h < 300f)
{
r = -(h - 240f) * dif / 60f + min;
g = min;
b = max;
}
else if (h <= 360f)
{
r = max;
g = min;
b = -(h - 360f) * dif / 60f + min;
}
else
{
r = 0;
g = 0;
b = 0;
}
}
return Color.FromArgb
(
hsbColor.a,
(int)Math.Round(Math.Min(Math.Max(r, 0), 255)),
(int)Math.Round(Math.Min(Math.Max(g, 0), 255)),
(int)Math.Round(Math.Min(Math.Max(b, 0), 255))
);
}
}
private const int MAX = 256; // max iterations
private const double SX = -2.025; // start value goal
private const double SY = -1.125; // start value imaginary
private const double EX = 0.6; // end value real
private const double EY = 1.125; // end value imaginary
private static int x1, y1, xs, ys, xe, ye;
private static double xstart, ystart, xende, yende, xzoom, yzoom;
private static bool action, rectangle, finished;
private static float xy;
//private Image picture1;
private System.Drawing.Bitmap bitmap;
private Graphics g1;
private Cursor c1, c2;
private HSBColor HSBcol = new HSBColor();
// private HSB HSBcol = new HSB();
private void Form1_Paint(object sender, PaintEventArgs e)
{
g1 = e.Graphics;
g1.DrawImage(bitmap, 0, 0, x1, y1);
g1.Dispose();
}
private void Form1_Load(object sender, EventArgs e)
{
init();
start();
}
public void init()
{
//HSBcol = new HSB();
finished = false;
c1 = Cursors.WaitCursor;
c2 = Cursors.Cross;
x1 = 640;
y1 = 480;
xy = (float)x1 / (float)y1;
bitmap.SetPixel(x1, y1, Color.Blue);
g1 = Graphics.FromImage(bitmap);
finished = true;
// xy = (float)x1 / (float)y1;
//picture = createImage(x1, y1);
//g1 = picture.getGraphics();
}
public void destroy() // delete all instances
{
if (finished)
{
//removeMouseListener(this);
//removeMouseMotionListener(this);
//bitmap = null;
g1 = null;
c1 = null;
c2 = null;
//System.gc(); // garbage collection
GC.Collect();
}
}
public void start()
{
action = false;
rectangle = false;
initvalues();
xzoom = (xende - xstart) / (double)x1;
yzoom = (yende - ystart) / (double)y1;
mandelbrot();
}
public void stop()
{
}
public void paint(Graphics g)
{
update(g);
}
public void update(Graphics g)
{
/* Pen myPen = new Pen(Color.White);
g.DrawImage(bitmap, 0, 0);
if (rectangle)
{
if (xs < xe)
{
if (ys < ye)
{
g.DrawRectangle(myPen, xs, ys, (xe - xs), (ye - ys));
}
}
else
{
g.DrawRectangle(myPen, xs, ys, (xe - xs), (ye - ys));
}
myPen.Dispose();
}*/
}
private void mandelbrot() // calculate all points
{
int x, y;
float h, b, alt = 0.0f;
action = false;
for (x = 0; x < x1; x += 2)
for (y = 0; y < y1; y++)
{
h = pointcolour(xstart + xzoom * (double)x, ystart + yzoom * (double)y);
// color value
if (h != alt)
{
b = 1.0f - h * h; // brightnes
///djm added
///HSBcol.fromHSB(h,0.8f,b);
///
//convert hsb to rgb then make a Java Color
Color color = HSBColor.FromHSB(new HSBColor(h * 255, 0.8f * 255, b * 255));
///g1.setColor(col);
//djm end
//djm added to convert to RGB from HSB
//g1.setColor(Color.getHSBColor(h, 0.8f, b));
//djm test
// Color col = Color.FromArgb(0, 0, 0, 0);
//red = Color.Red;
// green = Color.Green;
// blue = Color.Blue;
//djm
alt = h;
}
Pen pen = new Pen(Color.Aqua);
g1.DrawLine(pen, x, y, x + 1, y);
}
//showStatus("Mandelbrot-Set ready - please select zoom area with pressed mouse.");
//setCursor(c2);
action = true;
}
private float pointcolour(double xwert, double ywert)
// color value from 0.0 to 1.0 by iterations
{
double r = 0.0, i = 0.0, m = 0.0;
int j = 0;
while ((j < MAX) && (m < 4.0))
{
j++;
m = r * r - i * i;
i = 2.0 * r * i + ywert;
r = m + xwert;
}
return (float)j / (float)MAX;
}
private void initvalues() // reset start values
{
xstart = SX;
ystart = SY;
xende = EX;
yende = EY;
if ((float)((xende - xstart) / (yende - ystart)) != xy)
xstart = xende - (yende - ystart) * (double)xy;
}
private void Form1_paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g1 = g;
action = false;
rectangle = false;
initvalues();
xzoom = (xende - xstart) / (double)x1;
yzoom = (yende - ystart) / (double)y1;
//picture = g.DrawImage;
//g.DrawImage(picture,0,0);
update(g);
mandelbrot();
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (action)
{
xs = e.X;
ys = e.Y;
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
// e.consume();
if (action)
{
xe = e.X;
ye = e.Y;
rectangle = true;
//repaint();
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
rectangle = false;
}
private void Form1_Click(object sender, MouseEventArgs e)
{
}
public String getAppletInfo()
{
return "fractal.class - Mandelbrot Set a Java Applet by Eckhard Roessel 2000-2001";
}
}
}
Honestly, the code is so cluttered and disorganized, it's hard to know all of what might be wrong with it. Sorry to be so blunt.
That said, a couple of obvious problems I see involving your "g1" Graphics instance member.
First, you are using the same field for two purposes: when computing the original image, you expect this to be a Graphics instance you can use to draw into your bitmap. But in the Paint event, you set it to the Graphics instance for the window, into which the painting should be done.
Second, in that Paint event, you dispose the Graphics instance before you return. But the instance you're disposing isn't yours. It belongs to the Forms system, and the only thing you should be doing with it is drawing into it.
There actually appear to be two different Paint event handlers and it's not clear which one you're using. You only dispose the Graphics instance in one of those places, so that may or may not be the real problem.
Personally, I would break the problem down into different elements. For a relative novice, it can be hard enough just to correctly draw a bitmap. It can also be difficult to really grasp how Paint event handling should be done. And of course, there's the Mandelbrot computations themselves. Trying to implement all three things (and more) at the same time can be overwhelming, and will take a lot longer assuming you can figure it out at all.
I would start by writing a simple program that just has a single PictureBox, which when you click a button, your program creates a new Bitmap object, into which you draw something simple (say, a rectangle, circle, or maybe just some text) and then assigns that Bitmap object to the PictureBox.Image property.
Once you have that working, then you can change the drawing part of the code to draw a Mandelbrot image instead.
Finally, once you have that working, then you can work on using the Paint event to draw the bitmap into your window directly instead of using the PictureBox control (the main reason for wanting to do this would presumably be that you eventually want to update the image as it's being drawn...if you only want to show it at the very end, then IMHO the PictureBox is a better approach).

Increase bar chart values with button clicks

I am trying to create a graph that will show the progress of a workout. Every five button clicks a tick should be added to the graph. This is a example of how it should look.
For demonstration purposes I am using a button click, In production the clicks will be every twenty revolutions of a wheel.
private int counter = 0;
private void button1_Click(object sender, EventArgs e)
{
counter++;
// code will go here
}
Thanks in advance
You can use either a Bitmap Buffer or a panel to draw on. Here is a headstart: Just a sample.
reference.
This solution is based on WinForms & Panel_Paint(). You may try to add vertical Progress Label and Chart's Y Axis value labeling.
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1(){
InitializeComponent();
}
private int counter = 0;
private int px = 10;
private int py = 180;
private int total5Clicks = 0;
private void button1_Click(object sender, EventArgs e)
{
counter++;
label1.Text = "Total Clicks = " + counter.ToString();
if (Math.Abs(counter % 5) == 0){
if (Math.Abs(counter / 5) > 0){
total5Clicks = total5Clicks + 1;
PaintOnChartPanel(total5Clicks);}
}
}
private void panel1_Paint(object sender, PaintEventArgs e){
}
private void PaintOnChartPanel(int total5Times)
{
//Add a new Panel Paint EventHandler
panel1.Paint += new PaintEventHandler(panel1_Paint);
using (Graphics g = this.panel1.CreateGraphics())
{
Brush brush = new SolidBrush(Color.Green);
g.FillRectangle(brush, px, py, 20, 20);
Pen pen = new Pen(new SolidBrush(Color.White));
g.DrawRectangle(pen, px, py, 20, 20);
//add each total5Click into chart block
g.DrawString((total5Times).ToString(), new Font("Arial", 7),
new SolidBrush(Color.AntiqueWhite),
px + 1, py+8, StringFormat.GenericDefault);
pen.Dispose();}
if (py > 20){
py = py - 20;}
else{
MessageBox.Show("Reached Top of the Panel");
if (px < 200){
px = px + 20;
py = 180;}
else{
MessageBox.Show("Reached Right of the Panel");
}
}
}
}
}
Output Form:
You can determine if you have a multiple of five with
bool drawTickMark = clicks % 5 == 0;
% is the modulo operator which returns the remainder of an integer division. E.g. 13 % 5 = 3 and 13 / 5 = 2 because 2 * 5 + 3 = 13.
clicks % 5 will be zero for clicks = 0, 5, 10, 15, ...
I'm not much of an ASP.NET guy but here's an algorithm you can use to draw the squares
int perColumn = Height / squareSize;
int totalColumns = (squareCount / perColumn) + 1;
for (int y = 0; y <= totalColumns - 1; y++)
{
int itemCount = squareCount - (y * perColumn);
if (itemCount > perColumn)
itemCount = perColumn;
for (int x = 0; x <= itemCount - 1; x++)
e.Graphics.FillRectangle(RandomBrush, New Rectangle((column * SquareSize) + 3, (i * SquareSize) + 3, SquareSize - 2, SquareSize - 2))
public sealed class ClickGraph : Control
{
private int squareCount = 1;
public int SquareCount
{
get
{
return squareCount;
}
set
{
squareCount = value;
Invalidate();
}
}
private int squareSize = 25;
public int SquareSize
{
get
{
return squareSize;
}
set
{
squareSize = value;
Invalidate();
}
}
public ClickGraph()
{
SetStyle(ControlStyles.ResizeRedraw, true);
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.Clear(BackColor);
int perColumn = Height / squareSize;
int totalColumns = (squareCount / perColumn) + 1;
for (int y = 0; y <= totalColumns - 1; y++)
{
int itemCount = squareCount - (y * perColumn);
if (itemCount > perColumn)
itemCount = perColumn;
for (int x = 0; x <= itemCount - 1; x++)
e.Graphics.FillRectangle(RandomBrush, New Rectangle((column * SquareSize) + 3, (i * SquareSize) + 3, SquareSize - 2, SquareSize - 2))
}
}
}

How to print one large image in many pages?

I want to print one tall (long) image in many pages. So in every page, I take a suitable part from the image and I draw it in the page.
the problem is that I have got the image shrunk (its shape is compressed) in the page,so I added an scale that its value is 1500 .
I think that I can solve the problem if I knew the height of the page (e.Graphics) in pixels.
to convert Inches to Pixel, Do I have to multiply by (e.Graphics.DpiX = 600) or multiply by 96 .
void printdocument_PrintPage(object sender, PrintPageEventArgs e)
{
if (pageImage == null)
return;
e.Graphics.PageUnit = GraphicsUnit.Pixel;
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
float a = (e.MarginBounds.Width / 100) * e.Graphics.DpiX;
float b = ((e.MarginBounds.Height / 100) * e.Graphics.DpiY);
int scale = 1500;
scale = 0; //try to comment this
RectangleF srcRect = new RectangleF(0, startY, pageImage.Width, b - scale);
RectangleF destRect = new RectangleF(0, 0, a, b);
e.Graphics.DrawImage(pageImage, destRect, srcRect, GraphicsUnit.Pixel);
startY = Convert.ToInt32(startY + b - scale);
e.HasMorePages = (startY < pageImage.Height);
}
could you please make it works correctly.
you can download the source code from (here).
thanks in advanced.
I tried to complete your task.
Here you go. Hope it helps.
This method prints the image on several pages (or one if image is small).
private void printImage_Btn_Click(object sender, EventArgs e)
{
list = new List<Image>();
Graphics g = Graphics.FromImage(image_PctrBx.Image);
Brush redBrush = new SolidBrush(Color.Red);
Pen pen = new Pen(redBrush, 3);
decimal xdivider = image_PctrBx.Image.Width / 595m;
int xdiv = Convert.ToInt32(Math.Ceiling(xdivider));
decimal ydivider = image_PctrBx.Image.Height / 841m;
int ydiv = Convert.ToInt32(Math.Ceiling(ydivider));
/*int xdiv = image_PctrBx.Image.Width / 595; //This is the xsize in pt (A4)
int ydiv = image_PctrBx.Image.Height / 841; //This is the ysize in pt (A4)
// # 72 dots-per-inch - taken from Adobe Illustrator
if (xdiv >= 1 && ydiv >= 1)
{*/
for (int i = 0; i < xdiv; i++)
{
for (int y = 0; y < ydiv; y++)
{
Rectangle r;
try
{
r = new Rectangle(i * Convert.ToInt32(image_PctrBx.Image.Width / xdiv),
y * Convert.ToInt32(image_PctrBx.Image.Height / ydiv),
image_PctrBx.Image.Width / xdiv,
image_PctrBx.Image.Height / ydiv);
}
catch (Exception)
{
r = new Rectangle(i * Convert.ToInt32(image_PctrBx.Image.Width / xdiv),
y * Convert.ToInt32(image_PctrBx.Image.Height),
image_PctrBx.Image.Width / xdiv,
image_PctrBx.Image.Height);
}
g.DrawRectangle(pen, r);
list.Add(cropImage(image_PctrBx.Image, r));
}
}
g.Dispose();
image_PctrBx.Invalidate();
image_PctrBx.Image = list[0];
PrintDocument printDocument = new PrintDocument();
printDocument.PrintPage += PrintDocument_PrintPage;
PrintPreviewDialog previewDialog = new PrintPreviewDialog();
previewDialog.Document = printDocument;
pageIndex = 0;
previewDialog.ShowDialog();
// don't forget to detach the event handler when you are done
printDocument.PrintPage -= PrintDocument_PrintPage;
}
This method prints every picture in the List in the needed dimensions (A4 size):
private void PrintDocument_PrintPage(object sender, PrintPageEventArgs e)
{
// Draw the image for the current page index
e.Graphics.DrawImageUnscaled(list[pageIndex],
e.PageBounds.X,
e.PageBounds.Y);
// increment page index
pageIndex++;
// indicate whether there are more pages or not
e.HasMorePages = (pageIndex < list.Count);
}
This method crops the image and returns every part of the image:
private static Image cropImage(Image img, Rectangle cropArea)
{
Bitmap bmpImage = new Bitmap(img);
Bitmap bmpCrop = bmpImage.Clone(cropArea, System.Drawing.Imaging.PixelFormat.DontCare);
return (Image)(bmpCrop);
}
The Image gets loaded from the PictureBox, so make sure the image is loaded. (No exception handling yet).
internal string tempPath { get; set; }
private int pageIndex = 0;
internal List<Image> list { get; set; }
These variables are defined as global variables.
You can download a sample project here:
http://www.abouchleih.de/projects/PrintImage_multiplePages.zip // OLD Version
http://www.abouchleih.de/projects/PrintImage_multiplePages_v2.zip // NEW
I have Created a Class file for multiple page print a single large image.
Cls_PanelPrinting.Print Print =new Cls_PanelPrinting.Print(PnlContent/Image);
You have to Pass the panel or image.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Drawing.Printing;
namespace Cls_PanelPrinting
{
public class Print
{
readonly PrintDocument printdoc1 = new PrintDocument();
readonly PrintPreviewDialog previewdlg = new PrintPreviewDialog();
public int page = 1;
internal string tempPath { get; set; }
private int pageIndex = 0;
internal List<Image> list { get; set; }
private int _Line = 0;
private readonly Panel panel_;
public Print(Panel pnl)
{
panel_ = pnl;
printdoc1.PrintPage += (printdoc1_PrintPage);
PrintDoc();
}
private void printdoc1_PrintPage(object sender, PrintPageEventArgs e)
{
Font myFont = new Font("Cambria", 10, FontStyle.Italic, GraphicsUnit.Point);
float lineHeight = myFont.GetHeight(e.Graphics) + 4;
float yLineTop = 1000;
int x = e.MarginBounds.Left;
int y = 25; //e.MarginBounds.Top;
e.Graphics.DrawImageUnscaled(list[pageIndex],
x,y);
pageIndex++;
e.HasMorePages = (pageIndex < list.Count);
e.Graphics.DrawString("Page No: " + pageIndex, myFont, Brushes.Black,
new PointF(e.MarginBounds.Right, yLineTop));
}
public void PrintDoc()
{
try
{
Panel grd = panel_;
Bitmap bmp = new Bitmap(grd.Width, grd.Height, grd.CreateGraphics());
grd.DrawToBitmap(bmp, new Rectangle(0, 0, grd.Width, grd.Height));
Image objImage = (Image)bmp;
Bitmap objBitmap = new Bitmap(objImage, new Size(700, objImage.Height));
Image PrintImage = (Image)objBitmap;
list = new List<Image>();
Graphics g = Graphics.FromImage(PrintImage);
Brush redBrush = new SolidBrush(Color.Red);
Pen pen = new Pen(redBrush, 3);
decimal xdivider = panel_.Width / 595m;
// int xdiv = Convert.ToInt32(Math.Ceiling(xdivider));
decimal ydivider = panel_.Height / 900m;
int ydiv = Convert.ToInt32(Math.Ceiling(ydivider));
int xdiv = panel_.Width / 595; //This is the xsize in pt (A4)
for (int i = 0; i < xdiv; i++)
{
for (int y = 0; y < ydiv; y++)
{
Rectangle r;
if (panel_.Height > 900)
{
try
{
if (list.Count > 0)
{
r = new Rectangle(0, (900 * list.Count), PrintImage.Width, PrintImage.Height - (900 * list.Count));
}
else
{
r = new Rectangle(0, 0, PrintImage.Width, 900);
}
list.Add(cropImage(PrintImage, r));
}
catch (Exception)
{
list.Add(PrintImage);
}
}
else { list.Add(PrintImage); }
}
}
g.Dispose();
PrintImage = list[0];
PrintDocument printDocument = new PrintDocument();
printDocument.PrintPage += printdoc1_PrintPage;
PrintPreviewDialog previewDialog = new PrintPreviewDialog();
previewDialog.Document = printDocument;
pageIndex = 0;
printDocument.DefaultPageSettings.PrinterSettings.PrintToFile = true;
string path = "d:\\BillIng.xps";
if (File.Exists(path))
File.Delete(path);
printDocument.DefaultPageSettings.PrinterSettings.PrintFileName = "d:\\BillIng.xps";
printDocument.PrintController = new StandardPrintController();
printDocument.Print();
printDocument.PrintPage -= printdoc1_PrintPage;
}
catch { }
}
private static Image cropImage(Image img, Rectangle cropArea)
{
Bitmap bmpImage = new Bitmap(img);
Bitmap bmpCrop = bmpImage.Clone(cropArea, System.Drawing.Imaging.PixelFormat.DontCare);
return (Image)(bmpCrop);
}
}
}

Categories