C#: Overriding OnPaint on ProgressBar not working? - c#

Was thinking it should be pretty easy to create a ProgressBar that drew some text upon itself. However, I am not quite sure what is happening here...
I added the following two overrides:
protected override void OnPaintBackground(PaintEventArgs pevent)
{
base.OnPaintBackground(pevent);
var flags = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.WordEllipsis;
TextRenderer.DrawText(pevent.Graphics, "Hello", Font, Bounds, Color.Black, flags);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var flags = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.WordEllipsis;
TextRenderer.DrawText(e.Graphics, "Hello", Font, Bounds, Color.Black, flags);
}
However, I get no text, and the methods doesn't even seem to be called. What is going on here?
Update: Thanks to the two answers so far, I have gotten it to actually call the OnPaint by using SetStyle(ControlStyles.UserPaint, true), and I have gotten it to draw the text in the right place by sending in new Rectangle(0, 0, Width, Height) instead of Bounds.
I do get text now, but the ProgressBar is gone... and the point was kind of to have the text on top of the ProgressBar. Any idea how I can solve this?

You could override WndProc and catch the WmPaint message.
The example below paints the Text property of the progressbar in its center.
public class StatusProgressBar : ProgressBar
{
const int WmPaint = 15;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case WmPaint:
using (var graphics = Graphics.FromHwnd(Handle))
{
var textSize = graphics.MeasureString(Text, Font);
using(var textBrush = new SolidBrush(ForeColor))
graphics.DrawString(Text, Font, textBrush, (Width / 2) - (textSize.Width / 2), (Height / 2) - (textSize.Height / 2));
}
break;
}
}
}

I needed to do this myself and I thought that I would post a simplified example of my solution since I could not find any examples. It is actually pretty simple if you use the ProgressBarRenderer class:
class MyProgressBar : ProgressBar
{
public MyProgressBar()
{
this.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
}
protected override void OnPaint(PaintEventArgs e)
{
Rectangle rect = this.ClientRectangle;
Graphics g = e.Graphics;
ProgressBarRenderer.DrawHorizontalBar( g, rect );
rect.Inflate(-3, -3);
if ( this.Value > 0 )
{
Rectangle clip = new Rectangle( rect.X, rect.Y, ( int )Math.Round( ( ( float )this.Value / this.Maximum ) * rect.Width ), rect.Height );
ProgressBarRenderer.DrawHorizontalChunks(g, clip);
}
// assumes this.Maximum == 100
string text = this.Value.ToString( ) + '%';
using ( Font f = new Font( FontFamily.GenericMonospace, 10 ) )
{
SizeF strLen = g.MeasureString( text, f );
Point location = new Point( ( int )( ( rect.Width / 2 ) - ( strLen.Width / 2 ) ), ( int )( ( rect.Height / 2 ) - ( strLen.Height / 2 ) ) );
g.DrawString( text, f, Brushes.Black, location );
}
}
}

Your problem is that you're passing in Bounds as your Rectangle parameter. Bounds contains the Height and Width of your control, which is what you want, but it also contains the Top and Left properties of your control, relative to the parent form, so your "Hello" is being offset on the control by however much your control is offset on its parent form.
Replace Bounds with new Rectangle(0, 0, this.Width, this.Height) and you should see your "Hello".

It seems that if you call 'SetStyle(ControlStyles.UserPaint, true)' the standard OnPaint method implemented for ProgressBar could not be invoked (using base.OnPaint(e) does not work at all). The strangest thing is that even if you actually create a UserControl, and try to draw draw some text upon the progress bar... it doesn't seem to work too... Of course you may place a Label on top of it... but I suppose it is not actually what you wanted to achieve.
Ok, it seems that I have managed to solve this problem. It is although a little complicated. First you need to create a transparent Label control. Code below:
public class TransparentLabel : System.Windows.Forms.Label
{
public TransparentLabel()
{
this.SetStyle(ControlStyles.Opaque, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
}
Second thing is to create UserControl, place a ProgressBar on it (Dock=Fill) - this will be the control that we will use instead of standard ProgressBar. Code:
public partial class UserControl2 : UserControl
{
public UserControl2()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
this.progressBar1.SendToBack();
this.transparentLabel1.BringToFront();
this.transparentLabel1.Text = this.progressBar1.Value.ToString();
this.transparentLabel1.Invalidate();
}
public int Value
{
get { return this.progressBar1.Value; }
set
{
this.progressBar1.Value = value;
}
}
}
The strange thing with ProgressBar is that it 'overdraws' the controls that are being placed upon it, so it is needed to send progressbar to back, and bring the label control to front. I haven't found more elegant solution at the moment.
This works, the label is being displayed on the progressbar, the background of the label control is transparent, so I think it looks like you wanted it to look :)
I may share my sample code if you wish...
Oh, btw. this strange behaviour of ProgressBar control that I have mentioned, is responsible for that it is not possible to use Graphics object to draw anything on a control that derives from ProgressBar. The text (or whatever you draw using Graphics object) is actually being drawn but... behind the ProgressBar control (if you take a closer look, you may see this user drawn things flickering when the Value of the ProgressBar changes and it need to repaint itself).

Here's another solution along with other people's suggestions. I subclassed the progressbar control to make this work. I mixed and matched codes from various places for this. The paint event could be cleaner, but that's for you to do ;)
public class LabeledProgressBar: ProgressBar
{
private string labelText;
public string LabelText
{
get { return labelText; }
set { labelText = value; }
}
public LabeledProgressBar() : base()
{
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.Paint += OnLabelPaint;
}
public void OnLabelPaint(object sender, PaintEventArgs e)
{
using(Graphics gr = this.CreateGraphics())
{
string str = LabelText + string.Format(": {0}%", this.Value);
LinearGradientBrush brBG = new LinearGradientBrush(e.ClipRectangle,
Color.GreenYellow, Color.Green, LinearGradientMode.Horizontal);
e.Graphics.FillRectangle(brBG, e.ClipRectangle.X, e.ClipRectangle.Y,
e.ClipRectangle.Width * this.Value / this.Maximum, e.ClipRectangle.Height);
e.Graphics.DrawString(str, SystemFonts.DefaultFont,Brushes.Black,
new PointF(this.Width / 2 - (gr.MeasureString(str, SystemFonts.DefaultFont).Width / 2.0F),
this.Height / 2 - (gr.MeasureString(str, SystemFonts.DefaultFont).Height / 2.0F)));
}
}
}

Related

OnPaint Paint Issue

I have created a custom checkBox to increase the height of the rectangle of the checkbox. I have override the OnPaint method as given below. When I make the control transparent, background becomes white, instead of transparent. What could be the issue?
protected override void OnPaint(PaintEventArgs pevent)
{
base.OnPaint(pevent);
int h = this.Height;
if (BackColor == Color.Transparent)
{
pevent.Graphics.Clear(this.Parent.BackColor);
}
else
{
pevent.Graphics.Clear(BackColor);
}
Rectangle rc = new Rectangle(new Point(0, 0), new Size(h, h));
ControlPaint.DrawCheckBox(pevent.Graphics, rc,
this.Checked ? ButtonState.Checked : ButtonState.Normal);
SizeF stringMeasure = pevent.Graphics.MeasureString(Text, Font);
int topOffset = (int)(ClientRectangle.Height - stringMeasure.Height) / 2;
pevent.Graphics.DrawString(Text, Font, new SolidBrush(ForeColor), this.Height, topOffset);
}
I am not sure if you derived your checkbox from the given class CheckBox or you derived it from Control. If you derived from control, you should add in the constructor the following line:
public CustomChecBox()
: base()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
}
Note, that there is also a function called OnPaintBackground(...) which you can override, but you should not need to to this.
In your OnPaint()-function, you should replace
if (BackColor == Color.Transparent)
{
pevent.Graphics.Clear(this.Parent.BackColor);
}
else
{
pevent.Graphics.Clear(BackColor);
}
with
pevent.Graphics.Clear(BackColor);
or
pevent.Graphics.FillRectangle(new SolidBrush(this.BackColor), this.ClientRectangle);
But this is, what happens in OnPaintBackground(...). If you set the BackColor to transparent and you added the SetStyle-Methid in the contructor, then your control should be really transparent. I hope this helps.

How to disable a User Control to draw it's background (or Region)

My question : How to disable a User Control to draw it's background (or Region)
Note : I already tried to override and empty OnPaintBackground or set background color to transparent.
I'm trying to bypass winform paint for my custom user controls in a custom container.
For that I thought to give a try to this : Beginners-Starting-a-2D-Game-with-GDIplus
My setup is :
A Form containing:
A User control (DrawingBoard)
A Container with elements I can drag and drop to this DrawingBoard (it's a listbox).
My render loop is inside the DrawingBoard with all elements specified in the previous link.
public DrawingBoard()
{
InitializeComponent();
//Resize event are ignored
SetStyle(ControlStyles.FixedHeight, true);
SetStyle(ControlStyles.FixedWidth, true);
SetStyle(System.Windows.Forms.ControlStyles.AllPaintingInWmPaint, true);// True is better
SetStyle(System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer, true); // True is better
// Disable the on built PAINT event. We dont need it with a renderloop.
// The form will no longer refresh itself
// we will raise the paint event ourselves from our renderloop.
SetStyle(System.Windows.Forms.ControlStyles.UserPaint, false); // False is better
}
#region GDI+ RENDERING
public Timer t = new Timer();
//This is your BackBuffer, a Bitmap:
Bitmap B_BUFFER = null;
//This is the surface that allows you to draw on your backbuffer bitmap.
Graphics G_BUFFER = null;
//This is the surface you will use to draw your backbuffer to your display.
Graphics G_TARGET = null;
Size DisplaySize = new Size(1120, 630);
bool Antialiasing = false;
const int MS_REDRAW = 32;
public void GDIInit()
{
B_BUFFER = new Bitmap(DisplaySize.Width, DisplaySize.Height);
G_BUFFER = Graphics.FromImage(B_BUFFER); //drawing surface
G_TARGET = CreateGraphics();
// Configure the display (target) graphics for the fastest rendering.
G_TARGET.CompositingMode = CompositingMode.SourceCopy;
G_TARGET.CompositingQuality = CompositingQuality.AssumeLinear;
G_TARGET.SmoothingMode = SmoothingMode.None;
G_TARGET.InterpolationMode = InterpolationMode.NearestNeighbor;
G_TARGET.TextRenderingHint = TextRenderingHint.SystemDefault;
G_TARGET.PixelOffsetMode = PixelOffsetMode.HighSpeed;
// Configure the backbuffer's drawing surface for optimal rendering with optional
// antialiasing for Text and Polygon Shapes
//Antialiasing is a boolean that tells us weather to enable antialiasing.
//It is declared somewhere else
if (Antialiasing)
{
G_BUFFER.SmoothingMode = SmoothingMode.AntiAlias;
G_BUFFER.TextRenderingHint = TextRenderingHint.AntiAlias;
}
else
{
// No Text or Polygon smoothing is applied by default
G_BUFFER.CompositingMode = CompositingMode.SourceOver;
G_BUFFER.CompositingQuality = CompositingQuality.HighSpeed;
G_BUFFER.InterpolationMode = InterpolationMode.Low;
G_BUFFER.PixelOffsetMode = PixelOffsetMode.Half;
}
t.Tick += RenderingLoop;
t.Interval = MS_REDRAW;
t.Start();
}
void RenderingLoop(object sender, EventArgs e)
{
try
{
G_BUFFER.Clear(Color.DarkSlateGray);
UIPaint(G_BUFFER);
G_TARGET.DrawImageUnscaled(B_BUFFER, 0, 0);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
#endregion
Then my elements get the event fired and try to draw what I would like:
public override void UIPaint(Graphics g)
{
Pen p = new Pen(Color.Blue,4);
p.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset;
g.DrawLines(p, new Point[] { new Point(Location.X, Location.Y), new Point(Location.X + Width, Location.Y), new Point(Location.X + Width, Location.Y + Height), new Point(Location.X, Location.Y + Height), new Point(Location.X, Location.Y - 2) });
g.DrawImageUnscaled(GDATA.GetWindowImage(), Location);
}
here is what happening on my DrawingBoard :
As I can't post image ... here is the link : http://s8.postimage.org/iqpxtaoht/Winform.jpg
The background is DarkSlateGray because my G_BUFFER state to clear each tick with that, Ok
The blue rectangle is what I paint, but it get cropped, KO
The Texture is cropped, KO
The region that crop my drawing is the control size.
So from there I've tried everything I could to disable WinForm to make some magic drawing in background. Tried to override and empty everything that got paint/update/refresh/invalidate/validate on Form/DrawingBoard/Elements but nothing allowed me to get my texture or drawing to not get cropped by the control background : (
I also tried to set the background of the Element as transparent and also to set Form.TransparencyKey = blabla with each element BackColor = blabla. But failed each time.
I'm certainly missing something : / But I don't know what.
Why don't you want to draw the background? To avoid problems with flickering in my custom control (derived from class Control), here's what I did:
(1) override OnPaintBackground:
protected override void OnPaintBackground(PaintEventArgs pevent)
{
}
(2) Draw in an offscreen Bitmap and then transfer it to the screen:
Bitmap _completeFrame;
protected override void OnPaint(PaintEventArgs pe)
{
DrawFrame(); // draws onto _completeFrame, calling new Bitmap()
// when _completeFrame is null or its Size is wrong
var g = pe.Graphics;
g.DrawImage(_completeFrame, new Point());
}

Change the borderColor of the TextBox

How can I change the BorderColor of the Textbox when a user Clicks on it or focuses on it?
You can handle WM_NCPAINT message of TextBox and draw a border on the non-client area of control if the control has focus. You can use any color to draw border:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class ExTextBox : TextBox
{
[DllImport("user32")]
private static extern IntPtr GetWindowDC(IntPtr hwnd);
private const int WM_NCPAINT = 0x85;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WM_NCPAINT && this.Focused)
{
var dc = GetWindowDC(Handle);
using (Graphics g = Graphics.FromHdc(dc))
{
g.DrawRectangle(Pens.Red, 0, 0, Width - 1, Height - 1);
}
}
}
}
Result
The painting of borders while the control is focused is completely flicker-free:
BorderColor property for TextBox
In the current post I just change the border color on focus. You can also add a BorderColor property to the control. Then you can change border-color based on your requirement at design-time or run-time. I've posted a more completed version of TextBox which has BorderColor property:
in the following post:
BorderColor property for TextBox
try this
bool focus = false;
private void Form1_Paint(object sender, PaintEventArgs e)
{
if (focus)
{
textBox1.BorderStyle = BorderStyle.None;
Pen p = new Pen(Color.Red);
Graphics g = e.Graphics;
int variance = 3;
g.DrawRectangle(p, new Rectangle(textBox1.Location.X - variance, textBox1.Location.Y - variance, textBox1.Width + variance, textBox1.Height +variance ));
}
else
{
textBox1.BorderStyle = BorderStyle.FixedSingle;
}
}
private void textBox1_Enter(object sender, EventArgs e)
{
focus = true;
this.Refresh();
}
private void textBox1_Leave(object sender, EventArgs e)
{
focus = false;
this.Refresh();
}
This is an ultimate solution to set the border color of a TextBox:
public class BorderedTextBox : UserControl
{
TextBox textBox;
public BorderedTextBox()
{
textBox = new TextBox()
{
BorderStyle = BorderStyle.FixedSingle,
Location = new Point(-1, -1),
Anchor = AnchorStyles.Top | AnchorStyles.Bottom |
AnchorStyles.Left | AnchorStyles.Right
};
Control container = new ContainerControl()
{
Dock = DockStyle.Fill,
Padding = new Padding(-1)
};
container.Controls.Add(textBox);
this.Controls.Add(container);
DefaultBorderColor = SystemColors.ControlDark;
FocusedBorderColor = Color.Red;
BackColor = DefaultBorderColor;
Padding = new Padding(1);
Size = textBox.Size;
}
public Color DefaultBorderColor { get; set; }
public Color FocusedBorderColor { get; set; }
public override string Text
{
get { return textBox.Text; }
set { textBox.Text = value; }
}
protected override void OnEnter(EventArgs e)
{
BackColor = FocusedBorderColor;
base.OnEnter(e);
}
protected override void OnLeave(EventArgs e)
{
BackColor = DefaultBorderColor;
base.OnLeave(e);
}
protected override void SetBoundsCore(int x, int y,
int width, int height, BoundsSpecified specified)
{
base.SetBoundsCore(x, y, width, textBox.PreferredHeight, specified);
}
}
WinForms was never good at this and it's a bit of a pain.
One way you can try is by embedding a TextBox in a Panel and then manage the drawing based on focus from there:
public class BorderTextBox : Panel {
private Color _NormalBorderColor = Color.Gray;
private Color _FocusBorderColor = Color.Blue;
public TextBox EditBox;
public BorderTextBox() {
this.DoubleBuffered = true;
this.Padding = new Padding(2);
EditBox = new TextBox();
EditBox.AutoSize = false;
EditBox.BorderStyle = BorderStyle.None;
EditBox.Dock = DockStyle.Fill;
EditBox.Enter += new EventHandler(EditBox_Refresh);
EditBox.Leave += new EventHandler(EditBox_Refresh);
EditBox.Resize += new EventHandler(EditBox_Refresh);
this.Controls.Add(EditBox);
}
private void EditBox_Refresh(object sender, EventArgs e) {
this.Invalidate();
}
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.Clear(SystemColors.Window);
using (Pen borderPen = new Pen(this.EditBox.Focused ? _FocusBorderColor : _NormalBorderColor)) {
e.Graphics.DrawRectangle(borderPen, new Rectangle(0, 0, this.ClientSize.Width - 1, this.ClientSize.Height - 1));
}
base.OnPaint(e);
}
}
Using OnPaint to draw a custom border on your controls is fine. But know how to use OnPaint to keep efficiency up, and render time to a minimum. Read this if you are experiencing a laggy GUI while using custom paint routines: What is the right way to use OnPaint in .Net applications?
Because the accepted answer of PraVn may seem simple, but is actually inefficient. Using a custom control, like the ones posted in the answers above is way better.
Maybe the performance is not an issue in your application, because it is small, but for larger applications with a lot of custom OnPaint routines it is a wrong approach to use the way PraVn showed.
set Text box Border style to None
then write this code to container form "paint" event
private void Form1_Paint(object sender, PaintEventArgs e)
{
System.Drawing.Rectangle rect = new Rectangle(TextBox1.Location.X,
TextBox1.Location.Y, TextBox1.ClientSize.Width, TextBox1.ClientSize.Height);
rect.Inflate(1, 1); // border thickness
System.Windows.Forms.ControlPaint.DrawBorder(e.Graphics, rect,
Color.DeepSkyBlue, ButtonBorderStyle.Solid);
}
With PictureBox1
.Visible = False
.Width = TextBox1.Width + 4
.Height = TextBox1.Height + 4
.Left = TextBox1.Left - 2
.Top = TextBox1.Top - 2
.SendToBack()
.Visible = True
End With
Here is my complete Flat TextBox control that supports themes including custom border colors in normal and focused states.
The control uses the same concept mentioned by Reza Aghaei https://stackoverflow.com/a/38405319/5514131 ,however the FlatTextBox control is more customizable and flicker-free.
The control handles the WM_NCPAINT window message in a better way to help eliminate flicker.
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = WindowMessage.WM_NCPAINT AndAlso _drawBorder AndAlso Not DesignMode Then 'Draw the control border
Dim w As Integer
Dim h As Integer
Dim clip As Rectangle
Dim hdc As IntPtr
Dim clientRect As RECT = Nothing
GetClientRect(Handle, clientRect)
Dim windowRect As RECT = Nothing
GetWindowRect(Handle, windowRect)
w = windowRect.Right - windowRect.Left
h = windowRect.Bottom - windowRect.Top
clip = New Rectangle(CInt((w - clientRect.Right) / 2), CInt((h - clientRect.Bottom) / 2), clientRect.Right, clientRect.Bottom)
hdc = GetWindowDC(Handle)
Using g As Graphics = Graphics.FromHdc(hdc)
g.SetClip(clip, CombineMode.Exclude)
Using sb = New SolidBrush(BackColor)
g.FillRectangle(sb, 0, 0, w, h)
End Using
Using p = New Pen(If(Focused, _borderActiveColor, _borderNormalColor), BORDER_WIDTH)
g.DrawRectangle(p, 0, 0, w - 1, h - 1)
End Using
End Using
ReleaseDC(Handle, hdc)
Return
End If
MyBase.WndProc(m)
End Sub
I have removed the default BorderStyle property and replaced it with a simple boolean DrawBorder property that controls whether to draw a border around the control or not.
Use the BorderNormalColor property to specify the border color when the TextBox has no focus, and the BorderActiveColor property to specify the border color when the control receives focus.
The FlatTextBox comes with two themes VS2019 Dark and VS2019 Light, use the Theme property to switch between them.
Complete FlatTextBox control code written in VB.NET
https://gist.github.com/ahmedosama007/37fe2004183a51a4ea0b4a6dcb554176

Making a control transparent

I am currently developing a simple image editing tool using Winforms and .NET 3.5 (work environment).
I have a requirement that when the user clicks a select tool button, a square (rectangle in C#) will appear that they can scale between 100x100 and 400x400. I have this bit fixed - the issue comes with making the background of the rectangle transparent.
I'm a little unclear on if transparency is supported in .NET 3.5, I've tried the following:
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
pnlSelectArea.BackColor = Color.Transparent;
pnlSelectArea.ForeColor = Color.Transparent;
selectArea1.BackColor = Color.Transparent;
selectArea1.ForeColor = Color.Transparent;
But this has no effect - any advice would be appreciated.
This is my special Control which contains an opacity property, it 100% works:
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;
public class TranspCtrl : Control
{
public bool drag = false;
public bool enab = false;
private int m_opacity = 100;
private int alpha;
public TranspCtrl()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.Opaque, true);
this.BackColor = Color.Transparent;
}
public int Opacity
{
get
{
if (m_opacity > 100)
{
m_opacity = 100;
}
else if (m_opacity < 1)
{
m_opacity = 1;
}
return this.m_opacity;
}
set
{
this.m_opacity = value;
if (this.Parent != null)
{
Parent.Invalidate(this.Bounds, true);
}
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle = cp.ExStyle | 0x20;
return cp;
}
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
Rectangle bounds = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
Color frmColor = this.Parent.BackColor;
Brush bckColor = default(Brush);
alpha = (m_opacity * 255) / 100;
if (drag)
{
Color dragBckColor = default(Color);
if (BackColor != Color.Transparent)
{
int Rb = BackColor.R * alpha / 255 + frmColor.R * (255 - alpha) / 255;
int Gb = BackColor.G * alpha / 255 + frmColor.G * (255 - alpha) / 255;
int Bb = BackColor.B * alpha / 255 + frmColor.B * (255 - alpha) / 255;
dragBckColor = Color.FromArgb(Rb, Gb, Bb);
}
else
{
dragBckColor = frmColor;
}
alpha = 255;
bckColor = new SolidBrush(Color.FromArgb(alpha, dragBckColor));
}
else
{
bckColor = new SolidBrush(Color.FromArgb(alpha, this.BackColor));
}
if (this.BackColor != Color.Transparent | drag)
{
g.FillRectangle(bckColor, bounds);
}
bckColor.Dispose();
g.Dispose();
base.OnPaint(e);
}
protected override void OnBackColorChanged(EventArgs e)
{
if (this.Parent != null)
{
Parent.Invalidate(this.Bounds, true);
}
base.OnBackColorChanged(e);
}
protected override void OnParentBackColorChanged(EventArgs e)
{
this.Invalidate();
base.OnParentBackColorChanged(e);
}
}
You will need to use Opacity property and set it to zero to make form invisible.
If you want to make a control Transparent, as you have tried in your example, See this article
How to: Give Your Control a Transparent Background
It say the code you have written, must be in constructor of the control. Hence, I guess, you will need to create a custom control derived from your pnlSelectArea 's type most probaably a button. In in that custom control's constructor you can write code to set its style and color.
Here is what worked for me with because the other solutions did not work.
This is with transparent UserControl added to ListView/TreeView Control Collection
I know it says ButtonRenderer but it should work for any controls.
In the UserControl:
protected override void OnPaint(PaintEventArgs e)
{
ButtonRenderer.DrawParentBackground(e.Graphics, this.ClientRectangle, this);
}
in the Parent control:
protected override void WndProc(ref Message m)
{
if(m.Msg == 0xF)
foreach(Control c in this.Controls) { c.Invalidate(); c.Update(); }
base.WndProc(ref m);
}
great!!
I finally managed to draw transparent shapes.
I've added a virtual method
Draw(g);
right before
bckColor.Dispose();
g.Dispose();
base.OnPaint(e);
and at the end the declaration of the virtual method
protected virtual void Draw(Graphics g){ }
Now I can continue creating my own Transparent shapes, graphics etc ...
There is one simple workaround for this. You can create an image with a transparent background (PNG) and add it for the Image property of the icon. This works fine as information does not have much flexibility in styling. Sometime this might not be suitable for everyone. Remember this is only a workaround.
PS:
Add where ever the text on the image and keep blank for the text property.

Transparent controls on painted form

Using C# WinForms I've got a form that I'm painting a gradient background on it from say light blue to dark blue. I've got some buttons and a couple of labels on it which I've already tried setting the backcolor to transparent as I want the background to show through - especially for the labels, but that doesn't seem to work.
But I've noticed that the painting is also paining the labels, even though the code is only on the form_paint event.
How can I make the labels not contain the same grade shading, but to be transparent? e.g. the labels at the top of the form will be light blue, but the ones at the bottom will be dark blue.
private void frmOptions_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
Rectangle rectangle = e.ClipRectangle;
using (Brush aBrush = new LinearGradientBrush(rectangle, Color.LightBlue, Color.DarkBlue, LinearGradientMode.Vertical))
{
e.Graphics.FillRectangle(aBrush, rectangle);
}
}
Many thanks for any help on this.
EDIT:
I've just been stepping through the lines of code to debug it and noticed that the paint event is being called for EACH object on the form - the labels & buttons, the rectangle on line 2 above has the size of each time. So I'm now really confused why it's getting called for each object on the form.
EDIT 2:
Just been stepping through the code of my quick test project (see comment below) and also noticed that the paint event is beening called for each object. the differenece is that the test project doesn't have the line "Rectangle rectangle = e.ClipRectangle;" - it basically uses the width & height of the form. where my code above sets rectangle to ClipRectangle - which is the size of each label & button... Is this Normal behaviour ? If so, then it looks like I need to folow the answer below by Reniuz.
EDIT 3
I've change the cliprectangle line to "Rectangle rectangle = new Rectangle(0, 0, this.Width, this.Height);" This seems to make the transparent look how it should, but the paint event is still being called for every object on the form - good job there is only 7 of the, so it's drawing 8 times :( Think I'll still look at the "TransparentLabel" code to test that.
Take a look at this article.
Edit:
Also you can use this code:
public class TransparentLabel : Control
{
public TransparentLabel(Label label)
{
//setting default properties
this.Text = label.Text;
this.Font = label.Font;
this.Location = label.Location;
this.Size = label.Size;
this.Parent = label.Parent;
this.BringToFront();
label.Dispose();
TabStop = false;
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// do nothing
}
protected override void OnPaint(PaintEventArgs e)
{
DrawText();
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if(m.Msg == 0x000F)
{
DrawText();
}
}
private void DrawText()
{
using(Graphics graphics = CreateGraphics())
using(SolidBrush brush = new SolidBrush(ForeColor))
{
SizeF size = graphics.MeasureString(Text, Font);
// first figure out the top
float top = 0;
switch(textAlign)
{
case ContentAlignment.MiddleLeft:
case ContentAlignment.MiddleCenter:
case ContentAlignment.MiddleRight:
top = (Height - size.Height) / 2;
break;
case ContentAlignment.BottomLeft:
case ContentAlignment.BottomCenter:
case ContentAlignment.BottomRight:
top = Height - size.Height;
break;
}
float left = -1;
switch(textAlign)
{
case ContentAlignment.TopLeft:
case ContentAlignment.MiddleLeft:
case ContentAlignment.BottomLeft:
if(RightToLeft == RightToLeft.Yes)
left = Width - size.Width;
else
left = -1;
break;
case ContentAlignment.TopCenter:
case ContentAlignment.MiddleCenter:
case ContentAlignment.BottomCenter:
left = (Width - size.Width) / 2;
break;
case ContentAlignment.TopRight:
case ContentAlignment.MiddleRight:
case ContentAlignment.BottomRight:
if(RightToLeft == RightToLeft.Yes)
left = -1;
else
left = Width - size.Width;
break;
}
graphics.DrawString(Text, Font, brush, left, top);
}
}
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
RecreateHandle();
}
}
public override RightToLeft RightToLeft
{
get
{
return base.RightToLeft;
}
set
{
base.RightToLeft = value;
RecreateHandle();
}
}
public override Font Font
{
get
{
return base.Font;
}
set
{
base.Font = value;
RecreateHandle();
}
}
private ContentAlignment textAlign = ContentAlignment.TopLeft;
public ContentAlignment TextAlign
{
get { return textAlign; }
set
{
textAlign = value;
RecreateHandle();
}
}
}
Transparent label will replace your existing label so you can use it like this:
TransparentLabel transparentLabel1 = new TransparentLabel(label1);
TransparentLabel transparentLabel2 = new TransparentLabel(label2);
and then you should see the result(design time on left, run time on right):

Categories