How to prevent graphical stutter when scrolling a large zoomed out picture - c#

I have a large TIFF picture (5.9 MB, 13k X 16k resolution) that the user loads into a scrollable panel that he then can zoom in/out of, scroll and mark points, regions etc. on.
For the scrollable double-buffered panel I am using a modification of Bob Powell's awesome ZoomPicBox
The panel displays only the part of the picture currently in view.
The stutter occurs when scrolling the image when zoomed out (even if the interpolationMode is set to low).
Is there anything that can be done about it (preferably without hardware acceleration)?
The paint event of the panel:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
if (_image == null)
{
base.OnPaintBackground(e);
return;
}
//scale
System.Drawing.Drawing2D.Matrix ScaleMat = new System.Drawing.Drawing2D.Matrix(_zoom, 0, 0, _zoom, 0, 0);
//move to position of scrollbas
ScaleMat.Translate(this.AutoScrollPosition.X / (_zoom), this.AutoScrollPosition.Y / (_zoom));
e.Graphics.Transform = ScaleMat;
e.Graphics.InterpolationMode = _interpolationMode;
e.Graphics.DrawImage(_image, new Rectangle(0, 0, _image.Width, _image.Height), 0, 0, _image.Width, _image.Height, GraphicsUnit.Pixel);
base.OnPaint(e);
}
* _zoom ,_image and _interpolationMode are private fields of the control.
The constructor:
public PicBoxPlus()
{
MouseMove += PicBoxPlus_MouseMove;
KeyDown += PicBoxPlus_KeyDown;
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer, true);
this.AutoScroll = true;
}
Then I tried implementing Sinatr's code but something is wrong, because all I get is a black image (of the right size). Anyone has an idea what could be wrong?
The new paint event:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
if (mCachedImage == null)
{
base.OnPaintBackground(e);
return;
}
//scale
System.Drawing.Drawing2D.Matrix ScaleMat = new System.Drawing.Drawing2D.Matrix(mZoom, 0, 0, mZoom, 0, 0);
//move to position of scrollbas
ScaleMat.Translate(this.AutoScrollPosition.X / (mZoom), this.AutoScrollPosition.Y / (mZoom));
try
{
if (mCachedImage == null)
{
mCachedImage = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
using (var cacheGraphics = Graphics.FromImage(mCachedImage))
{
cacheGraphics.Transform = ScaleMat;
cacheGraphics.InterpolationMode = _interpolationMode;
cacheGraphics.DrawImage(mCachedImage, new Rectangle(0, 0, mCachedImage.Width, mCachedImage.Height), 0, 0, mCachedImage.Width, mCachedImage.Height, GraphicsUnit.Pixel);
}
e.Graphics.DrawImage(mCachedImage, Point.Empty);
}
}
catch (Exception ex)
{
throw ex;
}
base.OnPaint(e);
}
The Image and zoom properties:
public Bitmap Image
{
get { return mCachedImage; }
set
{
mCachedImage = value;
UpdateScaleFactor();
this.Invalidate();
}
}
public Single Zoom
{
get { return mZoom; }
set
{
if (value <= 0||value < 0.001)
{
value = 0.001f;
}
mZoom = value;
UpdateScaleFactor();
ResetCache(); // Sinatr's function
this.Invalidate();
}
}
Loading the image from the main form:
panelMap.Image = (Bitmap)Image.FromFile("pic.tiff");

Is not tested, but should give an idea.
Bitmap _cached = null;
override void OnPaint(PaintEventArgs e)
{
if(_cached == null)
{
_cached = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
using(var graphics = Graphics.FromImage(_cached)
{
// draw into this graphics once -> it will be cached in _cached bitmap
}
}
e.Graphics.DrawImage(_cached, Point.Empty);
}
// call this if _zoom or ClientSize is changed
void ResetCache()
{
_cache = null;
this.Invalidate(); // mandatory for _zoom change
}
Also, I don't know how to do you present your zoomed-in picture, but usually there is an offset so that you can move (pan) image.

Related

Rendering the Bitmap in PictureBox control is very slow

I've created a graph(Diagram) on PictureBox control. This control has the ability to zoom/pen. When I want to zoom, it slows down.
Thanks for helping someone
public void Render(bool resizeToo = false)
{
if (resizeToo)
{
plt.settings.Resize(pb.Width, pb.Height);
if (plt2 != null)
{
plt2.settings.Resize(plt.settings.width, plt.settings.height);
}
}
if (plt2 == null)
{
pb.Image = plt.figure.GetBitmap();
}
else
{
// if plt2 contains a GraphControl, match its X axis to the user control then overlay it
plt2.settings.axisX.Set(plt.settings.axisX.x1, plt.settings.axisX.x2);
Bitmap bmp1 = plt.figure.GetBitmap();
Bitmap bmp2 = plt2.figure.GetBitmap();
Bitmap bmpMerged = new Bitmap(bmp1);
using (Graphics gfx = Graphics.FromImage(bmpMerged))
gfx.DrawImage(bmp2, new Rectangle(0, 0, bmp2.Width, bmp2.Height));
pb.Image = bmpMerged;
}
Application.DoEvents();
}

Windows Form TextBox to show images

I currently have a Windows Form project where I have created a simple chat application. Currently the chat is output to a multi line text box, however I want to now enhance it a little and add some styling. In time I wish to have some image, format it nicely and perhaps some HTML (although this isnt vital) in the future. I am just wondering what I should use to achieve this. I did think of updating a HTML page and then reload it with each new message, but this wouldn't give a very good user experience. I have also looked at the richtextbox class but this seems to be a little limited for what I am after. I am hoping some one can point me in the right direction as to what to use.
I am trying to achieve something similar as what I have highlighted in red:
While some of the other comments indicate that WPF is well suited to this, in the real world, it is not always possible or desirable to switch.
A regular owner-drawn list box is well suited to this purpose.
To create one, simply set the DrawMode on the list box to OwnerDrawVariable, e.g.
list.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
Then you just need to provide two event handlers, the first to measure the item (tell the list box how tall the item will be), and another to actually render it. e.g.
this.list.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.list_DrawItem);
this.list.MeasureItem += new System.Windows.Forms.MeasureItemEventHandler(this.list_MeasureItem);
Rendering an image into the list is fairly simple with GDI+ DrawImage (where g is your graphics context_:
Bitmap bmp = Bitmap.FromFile("test.jpg");
Rectangle source = new Rectangle(0, 0, bmp.Width, bmp.Height);
Rectangle dest = source;
g.DrawImage(bmp, dest, source, GraphicsUnit.Pixel);
This is a sample Windows Form which has an owner-drawn list box of all fonts on the system, producing variable-height owner-drawn list items:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Font_Display
{
public class Test : System.Windows.Forms.Form
{
private Font head;
private System.Windows.Forms.ListBox list;
private System.ComponentModel.Container components = null;
public Test()
{
InitializeComponent();
head = new Font("Arial", 10, GraphicsUnit.Pixel);
}
protected override void Dispose(bool disposing)
{
if (disposing) {
if (components != null) {
components.Dispose();
}
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.list = new System.Windows.Forms.ListBox();
this.SuspendLayout();
//
// list
//
this.list.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
this.list.IntegralHeight = false;
this.list.Location = new System.Drawing.Point(12, 12);
this.list.Name = "list";
this.list.Size = new System.Drawing.Size(604, 323);
this.list.TabIndex = 0;
this.list.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.list_DrawItem);
this.list.MeasureItem += new System.Windows.Forms.MeasureItemEventHandler(this.list_MeasureItem);
//
// Test
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 15);
this.ClientSize = new System.Drawing.Size(520, 358);
this.Controls.Add(this.list);
this.Name = "Test";
this.Text = "Display";
this.Load += new System.EventHandler(this.Test_Load);
this.Resize += new System.EventHandler(this.Display_Resize);
this.ResumeLayout(false);
}
#endregion
[STAThread]
static void Main()
{
Application.Run(new Test());
}
private void Test_Load(object sender, EventArgs e)
{
try {
// Loop all font families
FontFamily[] families = FontFamily.Families;
foreach (FontFamily family in families) {
try { list.Items.Add(new Font(family, 20, FontStyle.Regular, GraphicsUnit.Pixel)); continue; }
catch { }
}
Display_Resize(this, EventArgs.Empty);
}
catch {
}
}
private void Display_Resize(object sender, System.EventArgs e)
{
Rectangle r = this.ClientRectangle;
list.SetBounds(list.Left,
list.Top,
r.Width - (list.Left * 2),
r.Height - (list.Top + list.Left));
}
public string TextValue = "Example String";
public StringFormat Format
{
get
{
StringFormat format = StringFormat.GenericTypographic;
format.FormatFlags |= StringFormatFlags.NoWrap;
return format;
}
}
private void list_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
{
Brush back = null;
Brush fore = null;
Brush htext = null;
Rectangle r;
try {
Font font = (Font)list.Items[e.Index];
// Loop
if ((e.State & DrawItemState.Selected) != 0) {
back = new SolidBrush(Color.DarkBlue);
fore = new SolidBrush(Color.White);
htext = new SolidBrush(Color.Orange);
}
else {
back = new SolidBrush(Color.White);
fore = new SolidBrush(Color.Black);
htext = new SolidBrush(Color.DarkRed);
}
// Fill the rect
e.Graphics.FillRectangle(back, e.Bounds);
// Get the size of the header
SizeF szHeader = e.Graphics.MeasureString(font.Name, head, int.MaxValue, Format);
SizeF szText = e.Graphics.MeasureString(TextValue, font, int.MaxValue, Format);
// Draw the string
r = e.Bounds;
r.Height = (int)szHeader.Height;
e.Graphics.DrawString(font.Name, head, htext, r, Format);
// Draw the string
r = e.Bounds;
r.Y = (int)(e.Bounds.Y + szHeader.Height);
r.Height = (int)szText.Height;
e.Graphics.DrawString(TextValue, font, fore, r, Format);
}
catch {
}
finally {
if (fore != null) fore.Dispose();
if (back != null) back.Dispose();
if (htext != null) htext.Dispose();
}
}
private void list_MeasureItem(object sender, System.Windows.Forms.MeasureItemEventArgs e)
{
try {
Font font = (Font)list.Items[e.Index];
SizeF szHeader = e.Graphics.MeasureString(font.Name, head, int.MaxValue, Format);
SizeF szText = e.Graphics.MeasureString(TextValue, font, int.MaxValue, Format);
// Return it
e.ItemHeight = (int)(szText.Height + szHeader.Height);
e.ItemWidth = (int)Math.Max(szText.Width, szHeader.Width);
}
catch {
}
}
}
}

Fading control in C#

I'm tring to build a Control derived class which supports an Opcacity property.
This control could host both text and image and will beable to fade them out and in.
Here is my code:
internal class FadeControl : Control
{
private int opacity = 100;
public FadeControl()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
}
public int Opacity
{
get
{
return opacity;
}
set
{
if (value > 100) opacity = 100;
else if (value < 1) opacity = 1;
else opacity = value;
if (Parent != null)
Parent.Invalidate(Bounds, true);
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle = cp.ExStyle | 0x20;
return cp;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
//do nothing
}
protected override void OnMove(EventArgs e)
{
RecreateHandle();
}
protected override void OnPaint(PaintEventArgs e)
{
using (Graphics g = e.Graphics)
{
Rectangle bounds = new Rectangle(0, 0, Width - 1, Height - 1);
int alpha = (opacity * 255) / 100;
using (Brush bckColor = new SolidBrush(Color.FromArgb(alpha, BackColor)))
{
if (BackColor != Color.Transparent)
g.FillRectangle(bckColor, bounds);
}
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.Matrix33 = (float)alpha / 255;
ImageAttributes imageAttr = new ImageAttributes();
imageAttr.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
if (BackgroundImage != null)
g.DrawImage(BackgroundImage, bounds, 0, 0, Width, Height, GraphicsUnit.Pixel, imageAttr);
if (Text != string.Empty)
{
using (Brush txtBrush = new SolidBrush(Color.FromArgb(alpha, ForeColor)))
{
g.DrawString(Text, Font, txtBrush, 5, 5);
}
}
}
}
protected override void OnBackColorChanged(EventArgs e)
{
if (Parent != null)
Parent.Invalidate(Bounds, true);
base.OnBackColorChanged(e);
}
protected override void OnParentBackColorChanged(EventArgs e)
{
Invalidate();
base.OnParentBackColorChanged(e);
}
}
I've putted the control on a form which has a timer on it.
The timer set the control's opacity from 0 to 100 and back and its working well.
The problem I'm trying to solved is that the control flickers while changing its opacity.
Setting the control toControlStyles.DoubleBuffer will make the control invisible on the form.
Any advice will be welcome.
I was unable to use both a double buffer and WS_EX_TRANSPARENT (0x20) for the transparent background. So I decided to implement the transparent background by copying the content of the parent control and use double buffer to prevent flicker.
The following is the final source code, tested and working:
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
internal class FadeControl : Control
{
private int opacity = 100;
private Bitmap backgroundBuffer;
private bool skipPaint;
public FadeControl()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.DoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint, true);
}
public int Opacity
{
get
{
return opacity;
}
set
{
if (value > 100) opacity = 100;
else if (value < 1) opacity = 1;
else opacity = value;
if (Parent != null)
Parent.Invalidate(Bounds, true);
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
//do nothig
}
protected override void OnMove(EventArgs e)
{
RecreateHandle();
}
private void CreateBackgroundBuffer(Control parent)
{
int offsetX;
int offsetY;
GetOffsets(out offsetX, out offsetY, parent);
backgroundBuffer = new Bitmap(Width + offsetX, Height + offsetY);
}
protected override void OnResize(EventArgs e)
{
var parent = Parent;
if (parent != null)
{
CreateBackgroundBuffer(parent);
}
base.OnResize(e);
}
private void GetOffsets(out int offsetX, out int offsetY, Control parent)
{
var parentPosition = parent.PointToScreen(Point.Empty);
offsetY = Top + parentPosition.Y - parent.Top;
offsetX = Left + parentPosition.X - parent.Left;
}
private void UpdateBackgroundBuffer(int offsetX, int offsetY, Control parent)
{
if (backgroundBuffer == null)
{
CreateBackgroundBuffer(parent);
}
Rectangle parentBounds = new Rectangle(0, 0, Width + offsetX, Height + offsetY);
skipPaint = true;
parent.DrawToBitmap(backgroundBuffer, parentBounds);
skipPaint = false;
}
private void DrawBackground(Graphics graphics, Rectangle bounds)
{
int offsetX;
int offsetY;
var parent = Parent;
GetOffsets(out offsetX, out offsetY, parent);
UpdateBackgroundBuffer(offsetX, offsetY, parent);
graphics.DrawImage(backgroundBuffer, bounds, offsetX, offsetY, Width, Height, GraphicsUnit.Pixel);
}
private void Draw(Graphics graphics)
{
Rectangle bounds = new Rectangle(0, 0, Width, Height);
DrawBackground(graphics, bounds);
int alpha = (opacity * 255) / 100;
using (Brush bckColor = new SolidBrush(Color.FromArgb(alpha, BackColor)))
{
if (BackColor != Color.Transparent)
{
graphics.FillRectangle(bckColor, bounds);
}
}
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.Matrix33 = (float)alpha / 255;
ImageAttributes imageAttr = new ImageAttributes();
imageAttr.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
if (BackgroundImage != null)
{
graphics.DrawImage(BackgroundImage, bounds, 0, 0, Width, Height, GraphicsUnit.Pixel, imageAttr);
}
if (Text != string.Empty)
{
using (Brush txtBrush = new SolidBrush(Color.FromArgb(alpha, ForeColor)))
{
graphics.DrawString(Text, Font, txtBrush, 5, 5);
}
}
}
protected override void OnPaint(PaintEventArgs e)
{
if (!skipPaint)
{
Graphics graphics = e.Graphics;
Draw(graphics);
}
}
protected override void OnBackColorChanged(EventArgs e)
{
if (Parent != null)
{
Parent.Invalidate(Bounds, true);
}
base.OnBackColorChanged(e);
}
protected override void OnParentBackColorChanged(EventArgs e)
{
Invalidate();
base.OnParentBackColorChanged(e);
}
}
Note that the method CreateParams is no longer present, also I've changed the contructor.
The field skipPaint is to know when not to paint in order to be able to able to tell the parent to draw itself to a bitmap during OnPaint without having infinite recursion.
backgroundBuffer is not to implement double buffering, but to keep a copy of the contents of the parent without the control rendered. It is updated each paint, I know there are more efficient solutions...* yet this approach keeps it simple and shouldn't be a bottleneck unless you have too many of these controls on the same container.
*: A better solution would be to update it each time the parent invalidates. Futhermore shared it among all the FadeControls in the same parent.

C# vertical label in a Windows Forms

Is it possible to display a label vertically in a Windows Forms?
Labels are easy, all you have to do is override the Paint event and draw the text vertically. Do note that GDI is optimised for Drawing text horizontally. If you rotate text (even if you rotate through multiples of 90 degrees) it will looks notably worse.
Perhaps the best thing to do is draw your text (or get a label to draw itself) onto a bitmap, then display the bitmap rotated.
Some C# code for drawing a Custom Control with vertical text. Note that ClearType text NEVER works if the text is not horizontal:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
public partial class VerticalLabel : UserControl
{
public VerticalLabel()
{
InitializeComponent();
}
private void VerticalLabel_SizeChanged(object sender, EventArgs e)
{
GenerateTexture();
}
private void GenerateTexture()
{
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
format.Trimming = StringTrimming.EllipsisCharacter;
Bitmap img = new Bitmap(this.Height, this.Width);
Graphics G = Graphics.FromImage(img);
G.Clear(this.BackColor);
SolidBrush brush_text = new SolidBrush(this.ForeColor);
G.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
G.DrawString(this.Name, this.Font, brush_text, new Rectangle(0, 0, img.Width, img.Height), format);
brush_text.Dispose();
img.RotateFlip(RotateFlipType.Rotate270FlipNone);
this.BackgroundImage = img;
}
}
Create a class myLabel which can rotate it's Text on any angle specified by you.
You can use it by code or simply dragging from ToolBox
using System.Drawing;
class myLabel:System.Windows.Forms.Label
{
public int RotateAngle { get; set; } // to rotate your text
public string NewText { get; set; } // to draw text
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
Brush b =new SolidBrush(this.ForeColor);
e.Graphics.TranslateTransform(this.Width / 2, this.Height / 2);
e.Graphics.RotateTransform(this.RotateAngle);
e.Graphics.DrawString(this.NewText, this.Font,b , 0f, 0f);
base.OnPaint(e);
}
}
Now this custom control is used into your form.
You have to set below properties
1. mylbl.Text = ""; //which can be changed by NewText property
2. mylbl.AutoSize = false; // adjust according to your text
3. mylbl.NewText = "Hello"; // whatever you want to display
4. mylbl.ForeColor = Color.Red; // color to display
5. mylbl.RotateAngle = -90; //angle to rotate
I expanded on Javed Akram's answer to resize the widget automatically (I needed this feature). It works for both positive and negative angles, the way that Javed states:
1. mylbl.Text = ""; // which can be changed by NewText property
2. mylbl.AutoSize = false; // adjust according to your text
3. mylbl.NewText = "Hello"; // whatever you want to display
4. mylbl.ForeColor = Color.Red; // color to display
5. mylbl.RotateAngle = -90; // angle to rotate
Here is the code:
public class RotatingLabel : System.Windows.Forms.Label
{
private int m_RotateAngle = 0;
private string m_NewText = string.Empty;
public int RotateAngle { get { return m_RotateAngle; } set { m_RotateAngle = value; Invalidate(); } }
public string NewText { get { return m_NewText; } set { m_NewText = value; Invalidate(); } }
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
Func<double, double> DegToRad = (angle) => Math.PI * angle / 180.0;
Brush b = new SolidBrush(this.ForeColor);
SizeF size = e.Graphics.MeasureString(this.NewText, this.Font, this.Parent.Width);
int normalAngle = ((RotateAngle % 360) + 360) % 360;
double normaleRads = DegToRad(normalAngle);
int hSinTheta = (int)Math.Ceiling((size.Height * Math.Sin(normaleRads)));
int wCosTheta = (int)Math.Ceiling((size.Width * Math.Cos(normaleRads)));
int wSinTheta = (int)Math.Ceiling((size.Width * Math.Sin(normaleRads)));
int hCosTheta = (int)Math.Ceiling((size.Height * Math.Cos(normaleRads)));
int rotatedWidth = Math.Abs(hSinTheta) + Math.Abs(wCosTheta);
int rotatedHeight = Math.Abs(wSinTheta) + Math.Abs(hCosTheta);
this.Width = rotatedWidth;
this.Height = rotatedHeight;
int numQuadrants =
(normalAngle >= 0 && normalAngle < 90) ? 1 :
(normalAngle >= 90 && normalAngle < 180) ? 2 :
(normalAngle >= 180 && normalAngle < 270) ? 3 :
(normalAngle >= 270 && normalAngle < 360) ? 4 :
0;
int horizShift = 0;
int vertShift = 0;
if (numQuadrants == 1)
{
horizShift = Math.Abs(hSinTheta);
}
else if (numQuadrants == 2)
{
horizShift = rotatedWidth;
vertShift = Math.Abs(hCosTheta);
}
else if (numQuadrants == 3)
{
horizShift = Math.Abs(wCosTheta);
vertShift = rotatedHeight;
}
else if (numQuadrants == 4)
{
vertShift = Math.Abs(wSinTheta);
}
e.Graphics.TranslateTransform(horizShift, vertShift);
e.Graphics.RotateTransform(this.RotateAngle);
e.Graphics.DrawString(this.NewText, this.Font, b, 0f, 0f);
base.OnPaint(e);
}
}
I found a way to simply do it without adding code or classes to your project!
When you create your label, simply add:
this.label1.text = "V\nE\nR\nT\nI\nC\nA\nL\n";
This worked for me!
You can rotate text instead of the label control in the OnPaint event or Paint method:
private void uc1_Paint(object sender, PaintEventArgs e)
{
string Name;
var g = e.Graphics;
g.DrawString(Name, new Font("Tahoma", 8), Brushes.Black, 0, 0,
new StringFormat(StringFormatFlags.DirectionVertical));
}
2015 update on an old post. Since most of the other answers seem to heavily affect VS2013's designer in terms of usability, I'd suggest this solution:
http://www.codeproject.com/Articles/19774/Extended-Vertical-Label-Control-in-C-NET
It absolutely works. I found it on net and little changed
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.ComponentModel;
public class VerticalLabel : System.Windows.Forms.Label
{
private bool bFlip = true;
public VerticalLabel()
{
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
StringFormat stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Center;
stringFormat.Trimming = StringTrimming.None;
stringFormat.FormatFlags = StringFormatFlags.DirectionVertical;
Brush textBrush = new SolidBrush(this.ForeColor);
Matrix storedState = g.Transform;
if (bFlip)
{
g.RotateTransform(180f);
g.TranslateTransform(-ClientRectangle.Width,-ClientRectangle.Height);
}
g.DrawString(
this.Text,
this.Font,
textBrush,
ClientRectangle,
stringFormat);
g.Transform = storedState;
}
[Description("When this parameter is true the VLabel flips at 180 degrees."),Category("Appearance")]
public bool Flip180
{
get
{
return bFlip;
}
set
{
bFlip = value;
this.Invalidate();
}
}
}
Used pieces from others
Jeremy
public partial class VerticalLabel_UserControl : UserControl
{
private IComponentChangeService _changeService;
private string strPropertyText = "Vertical Text";
public VerticalLabel_UserControl()
{
InitializeComponent();
}
[EditorBrowsable(EditorBrowsableState.Always)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[Bindable(true)]
public override string Text { get { return base.Text; } set { base.Text = value; this.Invalidate(); } }
private void VerticalLabel_UserControl_SizeChanged(object sender, EventArgs e)
{
GenerateTexture();
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
}
private void GenerateTexture()
{
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
// format.Trimming = StringTrimming.EllipsisCharacter;
Bitmap img = new Bitmap(this.Height, this.Width);
Graphics G = Graphics.FromImage(img);
G.Clear(this.BackColor);
SolidBrush brush_text = new SolidBrush(this.ForeColor);
G.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
G.DrawString(this.strPropertyText, this.Font, brush_text, new Rectangle(0, 0, img.Width, img.Height), format);
img.RotateFlip(RotateFlipType.Rotate270FlipNone);
this.BackgroundImage = img;
brush_text.Dispose();
}
public override System.ComponentModel.ISite Site
{
get
{
return base.Site;
}
set
{
_changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService));
if (_changeService != null)
_changeService.ComponentChanged -= new ComponentChangedEventHandler(OnComponentChanged);
base.Site = value;
if (!DesignMode)
return;
_changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService));
if (_changeService != null)
_changeService.ComponentChanged += new ComponentChangedEventHandler(OnComponentChanged);
}
}
private void OnComponentChanged(object sender, ComponentChangedEventArgs ce)
{
VerticalLabel_UserControl label = ce.Component as VerticalLabel_UserControl;
if (label == null || !label.DesignMode)
return;
if (((IComponent)ce.Component).Site == null || ce.Member == null || ce.Member.Name != "Text")
return;
//Causes the default text to be updated
string strName = this.Name.ToLower();
string strText = this.Text.ToLower();
if (strText.Contains(strName))
{
this.Text = "Vertical Text";
}
else
{
strPropertyText = this.Text;
}
//Prints the text vertically
GenerateTexture();
}
}
I just turned off the AutoSize property and resized the label vertically. I made the label wide enough for only one character. Then I changed TextAlign to center to make the alignment look better. This worked great for me.

Crop a picture using a rectangle

I want to crop an image in c#. As in most photo editing software I want to use the rectangle box which can be resized and repositioned via a mouse. In addition, I would like to know how to highlight the cropped area, as show in this photo.
Your image link is no longer available.
So assuming that in a panel you have your picturebox with the image to crop.
First you need to create event handlers for mouse actions to be able to draw a rectangular region which you wish to crop :
private void picBox_MouseDown(object sender, MouseEventArgs e)
{
Cursor = Cursors.Default;
if (Makeselection)
{
try
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
Cursor = Cursors.Cross;
cropX = e.X;
cropY = e.Y;
cropPen = new Pen(Color.Crimson, 1);
cropPen.DashStyle = DashStyle.Solid;
}
picBox.Refresh();
}
catch (Exception ex)
{
}
}
}
private void picBox_MouseUp(object sender, MouseEventArgs e)
{
if (Makeselection)
{
Cursor = Cursors.Default;
}
}
private void picBox_MouseMove(object sender, MouseEventArgs e)
{
Cursor = Cursors.Default;
if (Makeselection)
{
picBox.Cursor = Cursors.Cross;
try
{
if (picBox.Image == null)
return;
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
picBox.Refresh();
cropWidth = e.X - cropX;
cropHeight = e.Y - cropY;
picBox.CreateGraphics().DrawRectangle(cropPen, cropX, cropY, cropWidth, cropHeight);
}
}
catch (Exception ex)
{
}
}
}
private void picBox_MouseLeave(object sender, EventArgs e)
{
tabControl.Focus();
}
private void picBox_MouseEnter(object sender, EventArgs e)
{
picBox.Focus();
}
Now, comes the button click function for cropping the image :
private void btnCrop_Click_1(object sender, EventArgs e)
{
Cursor = Cursors.Default;
try
{
if (cropWidth < 1)
{
return;
}
Rectangle rect = new Rectangle(cropX, cropY, cropWidth, cropHeight);
//First we define a rectangle with the help of already calculated points
Bitmap OriginalImage = new Bitmap(picBoxScreenshot.Image, picBoxScreenshot.Width, picBoxScreenshot.Height);
//Original image
Bitmap _img = new Bitmap(cropWidth, cropHeight);
// for cropinfo image
Graphics g = Graphics.FromImage(_img);
// create graphics
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
//set image attributes
g.DrawImage(OriginalImage, 0, 0, rect, GraphicsUnit.Pixel);
picBox.Image = _img;
picBox.Width = _img.Width;
picBox.Height = _img.Height;
PictureBoxLocation();
cropWidth = 0;
}
catch (Exception ex){}
}
private void PictureBoxLocation()
{
int _x = 0;
int _y = 0;
if (panel1.Width > picBox.Width)
{
_x = (panel1.Width - picBox.Width) / 2;
}
if (panel1.Height > picBox.Height)
{
_y = (panel1.Height - picBox.Height) / 2;
}
picBox.Location = new Point(_x, _y);
picBox.Refresh();
}
In order to draw a picture lighter or darker (or alter the colors in any way) you use a ColorMatrix, like this.
The outside of the selection box seems to have a black image laid over it with a alpha of about 30%. To do this you would just take each pixel outside of the content area and draw a black pixel with a 30% alpha on top of it. This would give the desired dimmed out effect.
As for how you can get a rectangle to be dynamically seizable in C#.

Categories

Resources