Drawing a line in Winforms - c#

I am having trouble drawing a line within a group box in a simple windows form.
here is my code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
DrawLShapeLine(groupBox1.CreateGraphics(), 10, 10, 20, 40);
}
public void DrawLShapeLine(System.Drawing.Graphics g, int intMarginLeft, int intMarginTop, int intWidth, int intHeight)
{
Pen myPen = new Pen(Color.Black);
myPen.Width = 2;
// Create array of points that define lines to draw.
int marginleft = intMarginLeft;
int marginTop = intMarginTop;
int width = intWidth;
int height = intHeight;
int arrowSize = 3;
Point[] points =
{
new Point(marginleft, marginTop),
new Point(marginleft, height + marginTop),
new Point(marginleft + width, marginTop + height),
// Arrow
new Point(marginleft + width - arrowSize, marginTop + height - arrowSize),
new Point(marginleft + width - arrowSize, marginTop + height + arrowSize),
new Point(marginleft + width, marginTop + height)
};
g.DrawLines(myPen, points);
}
}
If I attach the DrawLShapeLine method to a button click event, it draws fine, but it does not draw on load of the form.
Please advice.

Quick & dirty:
How about creating a panel with the width of 1 pixel and give it a backgroundcolor?

Hook up an event handler for the Paint event of the GroupBox and call DrawLShapeLine from within that event handler instead. You should then use the Graphics object supplied by in event arguments:
private void groupBox1_Paint(object sender, PaintEventArgs e)
{
DrawLShapeLine(e.Graphics, 10, 10, 20, 40);
}
As your code looks now it will attempt to paint in the GroupBox when the form requires painting. The group box may be painted at any other occasion, which will the line you paint disappear.

Another option would be to use the line control that is available in Visual Basic Power Packs.
http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/d9e082c8-5386-4481-a744-1c9029805696/
If you have Visual Studio 2008 SP1, or Visual Studio 2010, you won't need to download anything.
If you do not see the Visual Basic PowerPacks control in the Toolbox, right click in the Toolbox and select Show All in the context menu.

Add a label with no text, a 3D border and a height of 2 (you have to set the height in the properties page, not with the GUI)!

I'm not sure if something else is going on, but you should draw the line on the GroupBox's Paint event, not the Form's.

The System.Drawing.Pen can be used to draw line in Windows Form.
Graphics surface = CreateGraphics();
Pen pen1 = new Pen(Color.Black, 2);
surface.DrawLine(pen1, this.Width / 2, 0, this.Width / 2, this.Height);

Related

How can I resize the width and height of a UserControl according to a child Label?

I'm coding a clone messaging app. I have a user control called "Bubble" that I use as a message bubble. And its user control layout is like this:
It contains only lblMessage, lblTime and pictureBox. lblMessage's AutoSize property off and Anchor is top-left-right. My goal is to size or wrap this according to the content. I am able to resize vertically using this code.
void SetHeight()
{
//SizeF maxSize = new Size(500, int.MaxValue);
Graphics g = CreateGraphics();
SizeF size = g.MeasureString(lblMessage.Text, lblMessage.Font, lblMessage.Width);
lblMessage.Height = int.Parse(Math.Round(size.Height + 2, 0).ToString());
lblTime.Top= lblMessage.Bottom +10;
this.Height = lblTime.Bottom + 10;
}
My result is like this:
I can resize vertically, but not horizontally at the same time. The way I resize it vertically is to use width property of lblMessage as the limit size. I want to resize user control according to size of short text. I thought about creating a maxSize for MeasureString but I can't get it to work. I'm trying to find a function that continues from a bottom line when it reaches a certain width value. I look forward to your help.
I searched all stackowerflow questions in depth but none of them worked in my case.
**Edit after Jimi comments
I tried to apply his solution to my project. I don't think I did everything right, but with a little bit of comment, something came up that worked for me at least.
private void Bubble_Paint(object sender, PaintEventArgs e)
{
float minimumWidth = lblTime.Width + pictureBox1.Width + 35;
float maximumWidth = 500;
string measureString = lblMessage.Text;
Font stringFont = new Font("Helvetica", 12.0F);
CharacterRange[] characterRanges = { new CharacterRange(0, measureString.Length) };
float layautWidth = maximumWidth - minimumWidth;
RectangleF layoutRect = new RectangleF(10, 6, layautWidth, this.Height);
StringFormat stringFormat = new StringFormat();
stringFormat.SetMeasurableCharacterRanges(characterRanges);
Color myColor = Color.FromArgb(211, 212, 212);
SolidBrush myBrush = new SolidBrush(myColor);
Region[] stringRegions = e.Graphics.MeasureCharacterRanges(measureString, stringFont, layoutRect, stringFormat);
RectangleF measureRect1 = stringRegions[0].GetBounds(e.Graphics);
//When I gave Rectangle to the DrawString, some last letter was truncated, so I used plain text printing until I got to the bottom line.
if (measureRect1.Height < 30)
e.Graphics.DrawString(measureString, stringFont, myBrush, 10, 6, stringFormat);
else e.Graphics.DrawString(measureString, stringFont, myBrush, measureRect1, stringFormat);
e.Graphics.DrawRectangle(new Pen(Color.Transparent, 0), Rectangle.Round(measureRect1));
this.Width = Convert.ToInt32(measureRect1.Width + minimumWidth);
lblTime.Top = Convert.ToInt32(measureRect1.Height) + 10;
this.Height = lblTime.Bottom + 10;
}
Result in container preview:
user control that goes down one line from the specified limit
and for one line
dynamically result inside the application

Always visible drawing on form

I want to draw always visible cross over picturebox on winform. I know ho to use Graphics and it's method DrawLine. But problem is that, painted cross is hidden behind the picturebox. this textbox is continually refreshed and this cross should be always visible.
Do you have some solution for this?
Here is code:
Point picBoxLocation = pictureBox.Location;
Size picBoxSize = pictureBox.Size;
Pen myPen = new Pen(System.Drawing.Color.Red, 5);
Point left = new Point(picBoxLocation.X, picBoxSize.Height/2);
Point right = new Point(picBoxLocation.X+picBoxSize.Width, picBoxSize.Height / 2);
Point up = new Point((picBoxLocation.X + picBoxSize.Width) / 2, picBoxLocation.Y);
Point bottom = new Point((picBoxLocation.X + picBoxSize.Width) / 2, (picBoxLocation.Y+picBoxSize.Height)/2);
Graphics graphics = this.CreateGraphics();
graphics.DrawLine(myPen, left, right);
graphics.DrawLine(myPen, up, bottom);
Try to get Graphics object in OnPaint callback:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawLine(...);
}

Change the BackColor of the ToolStripSeparator control

Is it possible to change the BackColor of ToolStripSeparator control?
There is a BackColor property in the designer, but it doesn't appear to be used - the color is always white.
I just pointed my separators' Paint event to this custom proc:
private void mnuToolStripSeparator_Custom_Paint (Object sender, PaintEventArgs e)
{
ToolStripSeparator sep = (ToolStripSeparator)sender;
e.Graphics.FillRectangle(new SolidBrush(CUSTOM_COLOR_BACKGROUND), 0, 0, sep.Width, sep.Height);
e.Graphics.DrawLine(new Pen(CUSTOM_COLOR_FOREGROUND), 30, sep.Height / 2, sep.Width - 4, sep.Height / 2);
}
Where the CUSTOM_COLOR_FOREGROUND is a solid/named Color, such as Color.White for example.
I see the question was asked 2 years ago, but I still can't find a simple and clear solution for this on the web. So...
I've just faced the problem today and found that it's pretty simple to solve it.
Having the same situation:
Solution:
Create a class which inherits the ToolStripSeparator class and add a method to the Paint EventHandler to draw the separator:
public class ExtendedToolStripSeparator : ToolStripSeparator
{
public ExtendedToolStripSeparator()
{
this.Paint += ExtendedToolStripSeparator_Paint;
}
private void ExtendedToolStripSeparator_Paint(object sender, PaintEventArgs e)
{
// Get the separator's width and height.
ToolStripSeparator toolStripSeparator = (ToolStripSeparator)sender;
int width = toolStripSeparator.Width;
int height = toolStripSeparator.Height;
// Choose the colors for drawing.
// I've used Color.White as the foreColor.
Color foreColor = Color.FromName(Utilities.Constants.ControlsRelatedConstants.standardForeColorName);
// Color.Teal as the backColor.
Color backColor = Color.FromName(Utilities.Constants.ControlsRelatedConstants.standardBackColorName);
// Fill the background.
e.Graphics.FillRectangle(new SolidBrush(backColor), 0, 0, width, height);
// Draw the line.
e.Graphics.DrawLine(new Pen(foreColor), 4, height / 2, width - 4, height / 2);
}
}
Then add the separator:
ToolStripSeparator toolStripSeparator = new ExtendedToolStripSeparator();
this.DropDownItems.Add(newGameToolStripMenuItem);
this.DropDownItems.Add(addPlayerToolStripMenuItem);
this.DropDownItems.Add(viewResultsToolStripMenuItem);
// Add the separator here.
this.DropDownItems.Add(toolStripSeparator);
this.DropDownItems.Add(exitToolStripMenuItem);
Result:
The default toolstrip renderer ignores the BackColor property and uses hard-coded colors.
You can refer following link to use your own renderer to paint the separators the way you want them.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
toolStrip1.Renderer = new MyRenderer();
}
private class MyRenderer : ToolStripProfessionalRenderer
{
protected override void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e)
{
if ((e.Item as ToolStripSeparator) == null)
{
base.OnRenderSeparator(e);
return;
}
Rectangle bounds = new Rectangle(Point.Empty, e.Item.Size);
bounds.Y += 3;
bounds.Height = Math.Max(0, bounds.Height - 6);
if (bounds.Height >= 4)
bounds.Inflate(0, -2);
int x = bounds.Width / 2;
using(Pen pen = new Pen(Color.DarkBlue))
e.Graphics.DrawLine(pen, x, bounds.Top, x, bounds.Bottom - 1);
using (Pen pen = new Pen(Color.Blue))
e.Graphics.DrawLine(pen, x + 1, bounds.Top + 1, x + 1, bounds.Bottom);
}
}
}
Source: http://social.msdn.microsoft.com/forums/en-US/winforms/thread/6cceab5b-7e06-40cf-82da-56cdcc57eb5d

c#.Net Progress Bar Issue

I am reading data from a file and would like to attach a progress bar to this operation. I found the following code on stackoverflow - this code is due to William Daniel, Sept 20, stackoverflow post titled, " How to Change Color of Progress Bar in C#.Net 3.5"
class CustomProgressBar : ProgressBar
{
public CustomProgressBar()
{
this.SetStyle(ControlStyles.UserPaint, true);
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
// None... Helps control the flicker.
}
protected override void OnPaint(PaintEventArgs e)
{
const int inset = 2;
using (Image offscreenImage = new Bitmap(this.Width, this.Height))
{
using (Graphics offscreen = Graphics.FromImage(offscreenImage))
{
Rectangle rect = new Rectangle(0, 0, this.Width, this.Height);
if (ProgressBarRenderer.IsSupported)
ProgressBarRenderer.DrawHorizontalBar(offscreen, rect);
rect.Inflate(new Size(-inset, -inset)); // Deflate inner rectangle
rect.Width = (int)(rect.Width * ((double)this.Value / this.Maximum));
if (rect.Width == 0) rect.Width = 1;
LinearGradientBrush brush = new LinearGradientBrush(rect,
this.BackColor, this.ForeColor, LinearGradientMode.Vertical);
offscreen.FillRectangle(brush, inset, inset, rect.Width, rect.Height);
e.Graphics.DrawImage(offscreenImage, 0, 0);
offscreenImage.Dispose();
}
}
}
}
The code works fine except the following:
The gradient does not seem to extend the entire width of the bar. It is there but is much more heavily concentrated near the bottom of the bar and thins out very quickly as we get to the top of the bar. Any suggestions as to how I can fix this?
If I place the progress bar on a form and open an Internet Explorer window over the portion of the form with the progress bar on it, some of the text from the internet window bleeds onto the progress bar in the form. I have no idea why, and how to fix this.
As an aside, how does one estimate the length of an operation that you wish to show via a progress bar?
Any help would be greatly appreciated. Thank you.
As for first part of your question - try using following constructor for your brush:
new LinearGradientBrush(new Point(0, 0), new Point(0, Height - inset * 2), BackColor, ForeColor);
Your current brush has top control point at Y=inset - that's why area from Y=0 to Y=inset is all painted in solid color.
You are not drawing at all in the non-filled portion of your progressbar. Try calling FillRectangle for area from rect.Right to this.Width with background color. It should prevent bleeding from overlapping windows.
try this one, you don't need on each paint to create an image...
class CustomProgressBar : ProgressBar
{
public CustomProgressBar()
{
this.SetStyle(ControlStyles.UserPaint|ControlStyles.OptimizedDoubleBuffer|ControlStyles.DoubleBuffer, true);
this.UpdateStyles();
}
protected override void OnPaint(PaintEventArgs e)
{
const int inset = 2;
Rectangle rect = this.ClientRectangle;
var offscreen = e.Graphics;
if (ProgressBarRenderer.IsSupported){
ProgressBarRenderer.DrawHorizontalBar(offscreen, rect);
}
rect.Inflate(new Size(-inset, -inset)); // Deflate inner rectangle
rect.Width = Convert.ToInt32(Math.Round((rect.Width * ((double)this.Value / this.Maximum))));
if (rect.Width == 0) rect.Width = 1;
LinearGradientBrush brush = new LinearGradientBrush(rect,this.BackColor, this.ForeColor, LinearGradientMode.Vertical);
offscreen.FillRectangle(brush, inset, inset, rect.Width, rect.Height);
offscreen.DrawString(Value.ToString(), this.Font,Brushes.Black,rect);
}
}
use
const int inset = 1;
for your other question

Is it possible to rotate a button control in WinForms?

Is it possible to rotate a button or any control at a particular angle in WinForms? If so, how?
If you really want to (I have no idea why one would..*) you could try to use a Button subclass, maybe like that:
public partial class TurnButton : Button
{
public TurnButton()
{
InitializeComponent();
}
int angle = 0; // current rotation
Point oMid; // original center
protected override void OnLayout(LayoutEventArgs levent)
{
base.OnLayout(levent);
if (oMid == Point.Empty) oMid = new Point(Left + Width / 2, Top + Height / 2);
}
protected override void OnPaint(PaintEventArgs pe)
{
int mx = this.Size.Width / 2;
int my = this.Size.Height / 2;
SizeF size = pe.Graphics.MeasureString(Text, Font);
string t_ = Text;
Text = "";
base.OnPaint(pe);
if (!this.DesignMode)
{
Text = t_; pe.Graphics.TranslateTransform(mx, my);
pe.Graphics.RotateTransform(angle);
pe.Graphics.TranslateTransform(-mx, -my);
pe.Graphics.DrawString(Text, Font, SystemBrushes.ControlText,
mx - (int)size.Width / 2, my - (int)size.Height / 2);
}
}
protected override void OnClick(EventArgs e)
{
this.Size = new Size(Height, Width);
this.Location = new Point(oMid.X - Width / 2, oMid.Y - Height / 2);
angle = (angle + 90) % 360;
Text = angle + "°";
base.OnClick(e);
}
}
(* I have no idea why I wrote that, either ;-)
You can't rotate controls. That's simply not supported by the native API controls that WinForms uses.
And one might wonder why it even should be supported. What could you possibly be trying to do that you'd need to rotate a button control? It would be much easier to draw it in a different place with a different shape in the first place, rather than trying to rotate an existing control. (Do note that you can also resize and reposition a control at run-time, if that would fit your needs. Investigate the Size and Location properties.)
The only workaround is to draw the control's image to a bitmap, hide the control, and draw the bitmap onto the form in the location you want it to appear. Of course, that won't result in a control that the user can interact with. They won't be able to click an image of a button, because it's not a real button. If that's acceptable to you, you should probably be using an image in the first place, rather than a button.
This is similar to the question asked here:
Rotating a .NET panel in Windows Forms
The quick summary of answers from that question is that while it may be possible to do it, it would be very, very complicated.
A possible workaround in some cases would be this:
Use a tabControl , and resize it so you only have the button left. Set the allignment to left/right, and you have your button rotated 90/270 degrees.
public class VerticalButton : Button
{
public string VirticalText { get; set; }
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
StringFormat stringFormat = new StringFormat();
stringFormat.FormatFlags = StringFormatFlags.DirectionVertical;
SolidBrush solidBrush = new SolidBrush(this.ForeColor);
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
pe.Graphics.DrawString(VirticalText, this.Font, solidBrush,
new Rectangle(0, 0, Width, Height), stringFormat);
}
}

Categories