I tried to set the background color of a data grid view to be "transparent" from properties but it it said "not a valid property".
How can I do it?
I did this solution to a specific problem (when the grid was contained in a form with background image) with simples modifications you can adapt it to create a generic transparent Grid, just ask if the parent have background image, else just use the parent backcolor to paint your grid, and that is all.
You must inherit from DataGridView and override the PaintBackground method like this:
protected override void PaintBackground(Graphics graphics, Rectangle clipBounds, Rectangle gridBounds)
{
base.PaintBackground(graphics, clipBounds, gridBounds);
Rectangle rectSource = new Rectangle(this.Location.X, this.Location.Y, this.Width, this.Height);
Rectangle rectDest = new Rectangle(0, 0, rectSource.Width, rectSource.Height);
Bitmap b = new Bitmap(Parent.ClientRectangle.Width, Parent.ClientRectangle.Height);
Graphics.FromImage(b).DrawImage(this.Parent.BackgroundImage, Parent.ClientRectangle);
graphics.DrawImage(b, rectDest, rectSource, GraphicsUnit.Pixel);
SetCellsTransparent();
}
public void SetCellsTransparent()
{
this.EnableHeadersVisualStyles = false;
this.ColumnHeadersDefaultCellStyle.BackColor = Color.Transparent;
this.RowHeadersDefaultCellStyle.BackColor = Color.Transparent;
foreach (DataGridViewColumn col in this.Columns)
{
col.DefaultCellStyle.BackColor = Color.Transparent;
col.DefaultCellStyle.SelectionBackColor = Color.Transparent;
}
}
i did this with Deumber's solution and it works, but causes some troubles that i avoided by adding small improvements:
A. scrolling the DGV messes up the background. solution: put this somewhere:
public partial class main : Form
{
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000;
return cp;
}
}
}
the background will still scroll, but be corrected immediately after each scroll step. it's noticeable, but was acceptable for me. does anyone know a better solution to support scrolling with this?
B. the designer has troubles using it. solution:
protected override void PaintBackground(Graphics graphics, Rectangle clipBounds, Rectangle gridBounds)
{
base.PaintBackground(graphics, clipBounds, gridBounds);
if (main.ActiveForm != null && this.Parent.BackgroundImage != null)
{
Rectangle rectSource = new Rectangle(this.Location.X, this.Location.Y, this.Width, this.Height);
Rectangle rectDest = new Rectangle(-3, 3, rectSource.Width, rectSource.Height);
Bitmap b = new Bitmap(Parent.ClientRectangle.Width, Parent.ClientRectangle.Height);
Graphics.FromImage(b).DrawImage(this.Parent.BackgroundImage, Parent.ClientRectangle);
graphics.DrawImage(b, rectDest, rectSource, GraphicsUnit.Pixel);
SetCellsTransparent();
}
}
now the designer treats it just like a DGV. it will fail if you ever want to draw the DGV while you have no ActiveForm, but that's not the case usually. it's also possible to just keep the if-line while you might still want to use the designer, and delete it for the release.
Having a transparent color in the DataGridView BackGroundColor property is not possible.
So I decided to sync this property with the parent's BackColor. The good old databinding feature of WinForms is very good at this :
myDataGridView.DataBindings.Add(nameof(DataGrid.BackgroundColor),
this,
nameof(Control.BackColor));
Just after InitializeComponents();
I know this is pretty old, but this works very well.
You need to set all the rows and columns to transparent. Easier way is:
for (int y = 0; y < gridName.Rows[x].Cells.Count; y++)
{
yourGridName.Rows[x].Cells[y].Style.BackColor =
System.Drawing.Color.Transparent;
}
Set datagridview's backcolor same with the form's color. To do this, select datagridview: go to Properties -> RowTemplate -> DefaultCellStyle -> BackColor and choose the color of your form.
Related
I've been asked to carry out some modernisation work on a C# WinForms application, and I'm using VS2019 with C#.Net 4.7.2.
The project owner wants to change the border colour of all the legacy Windows controls that were used originally. The initial idea was - using the MetroIT framework - to keep the original Windows controls in the Form Designer, but override them by defining new classes which extend the MetroIT equivalents but changing the class type of the declarations in the Designer's InitializeComponent() method.
That approach doesn't really work as a "drop-in" replacement because the MetroIT controls tend to subclass Control whereas existing code expects the legacy Windows properties/methods to be available.
Instead, therefore, I went down the route of trying to override the OnPaint method. That worked fine for CheckBox and RadioButton, but I'm now struggling to get it to work for a ListBox. The following is as far as I've got; it certainly isn't correct, as I'll explain, but it feels sort of close ?
public class MyListBox : ListBox
{
public MyListBox()
{
SetStyle(ControlStyles.UserPaint, true);
this.DrawMode = DrawMode.OwnerDrawVariable;
BorderStyle = BorderStyle.None;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Brush textBrush = new SolidBrush(Color.Black);
float y = 1;
foreach (String strItem in Items)
{
e.Graphics.DrawString(strItem, DefaultFont, textBrush, 1, y);
y += DefaultFont.Height;
}
Rectangle borderRectangle = this.ClientRectangle;
ControlPaint.DrawBorder(e.Graphics, borderRectangle, Color.Blue, ButtonBorderStyle.Solid);
}
}
Initially, with no data in the ListBox, the control is drawn correctly.
However, as soon as a scroll bar appears, the base Windows code draws the scroll bar over the top of my border instead of just inside it (whereas an unmodified ListBox re-draws the border around the top, bottom and right-hand side of the scroll bar):
Is there a way to change my code so that the border draws itself around the edges of the scroll bar instead of excluding it ?
The other way in which my code is wrong is that, as soon as I start to scroll, the border painting code is applying itself to some of the ListBox items:
Is there a way I can complete this, or am I wasting my time because the base scroll bar cannot be modified ?
Thanks
EDIT 1:
Further to the suggestions of #GuidoG:
Yes, to be clear, I really only want to change the border to blue. Happy to leave the listbox items as they would ordinarily be painted WITHOUT any border.
So, to achieve that, I have removed your suggested code to do DrawItem, and now I only have the code to paint the border - but clearly I must be doing something wrong, because the listbox has now reverted to looking like the standard one with the black border.
So here's what I have now:
In my Dialog.Designer.cs InitializeComponent():
this.FieldsListBox = new System.Windows.Forms.ListBox();
this.FieldsListBox.FormattingEnabled = true;
this.FieldsListBox.Location = new System.Drawing.Point(7, 98);
this.FieldsListBox.Name = "FieldsListBox";
this.FieldsListBox.Size = new System.Drawing.Size(188, 121);
this.FieldsListBox.Sorted = true;
this.FieldsListBox.TabIndex = 11;
this.FieldsListBox.SelectedIndexChanged += new System.EventHandler(this.FieldsListBox_SelectedIndexChanged);
In my Dialog.cs Form declaration:
public SelectByAttributesDialog()
{
InitializeComponent();
BlueThemAll(this);
}
private void BlueThemAll(Control parent)
{
foreach (Control item in parent.Controls)
{
Blue(item);
if (item.HasChildren)
BlueThemAll(item);
}
}
private void Blue(Control control)
{
Debug.WriteLine("Blue: " + control.Name.ToString());
Rectangle r = new Rectangle(control.Left - 1, control.Top - 1, control.Width + 1, control.Height + 1);
using (Graphics g = control.Parent.CreateGraphics())
{
using (Pen selPen = new Pen(Color.Blue))
{
g.DrawRectangle(selPen, r);
}
}
}
I know that Blue() is being called for all the controls in this dialog, in the sense that the Debug.WriteLine() is outputting every control name, but no borders are being changed to blue (and certainly not the listbox).
I've been away from Windows Form programming for a very long time so I apologise for that, and I'm clearly doing something wrong, but have no idea what.
Solution 1: let the form drawn blue borders around everything:
This means you dont have to subclass any controls, but you put some code on the form that will draw the borders for you around each control.
Here is an example that works for me
// method that draws a blue border around a control
private void Blue(Control control)
{
Rectangle r = new Rectangle(control.Left - 1, control.Top - 1, control.Width + 1, control.Height + 1);
using (Graphics g = control.Parent.CreateGraphics())
{
using (Pen selPen = new Pen(Color.Blue))
{
g.DrawRectangle(selPen, r);
}
}
}
// recursive method that finds all controls and call the method Blue on each found control
private void BlueThemAll(Control parent)
{
foreach (Control item in parent.Controls)
{
Blue(item);
if (item.HasChildren)
BlueThemAll(item);
}
}
where to call this ?
// draw the blue borders when the form is resized
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
panel3.Invalidate(); // on panel3 there is a control that moves position because it is anchored to the bottom
Update();
BlueThemAll(this);
}
// draw the borders when the form is moved, this is needed when the form moved off the screen and back
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
BlueThemAll(this);
}
// draw the borders for the first time
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
BlueThemAll(this);
}
If you also want blue borders around each item of the listbox:
To get some border around each item in your listbox use the DrawItem event in stead of the paint event, maybe this link could help.
I modified the code to get it drawing blue borders
listBox1.DrawMode = DrawMode.OwnerDrawFixed;
listBox1.DrawItem += ListBox1_DrawItem;
private void ListBox1_DrawItem(object sender, DrawItemEventArgs e)
{
try
{
e.DrawBackground();
Brush myBrush = new SolidBrush(e.ForeColor);
Rectangle r = new Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width - 1, e.Bounds.Height);
using (Pen selPen = new Pen(Color.Blue))
{
e.Graphics.DrawRectangle(selPen, r);
}
e.Graphics.DrawString(((ListBox)sender).Items[e.Index].ToString(), e.Font, myBrush, e.Bounds, StringFormat.GenericDefault);
e.DrawFocusRectangle();
}
catch { }
}
and it looks like this
Solution 2: Subclassing the listbox:
If you need this in a subclassed listbox, then you can do this.
However, this will only work if the listbox has at least one pixel space left around it, because to get around the scrollbar you have to draw on the parent of the listbox, not on the listbox itself. The scrollbar will always draw itself over anything you draw there.
public class myListBox : ListBox
{
public myListBox(): base()
{
this.DrawMode = DrawMode.OwnerDrawFixed;
BorderStyle = BorderStyle.None;
this.DrawItem += MyListBox_DrawItem;
}
private void MyListBox_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
Brush myBrush = new SolidBrush(e.ForeColor);
e.Graphics.DrawString(this.Items[e.Index].ToString(), e.Font, myBrush, e.Bounds, StringFormat.GenericDefault);
e.DrawFocusRectangle();
Rectangle r = new Rectangle(this.Left - 1, this.Top - 1, this.Width + 2, this.Height + 2);
using (Graphics g = this.Parent.CreateGraphics())
{
ControlPaint.DrawBorder(g, r, Color.Blue, ButtonBorderStyle.Solid);
}
}
}
and this will look like so
I am trying to implement a "Fillable Form" in which editable text fields appear over top of an image of a pre-preprinted form for a dot matrix printer. (using c# and Windows Forms and targeting .Net 2.0) My first idea was to use the image as the Windows Form background, but it looked horrible when scrolling and also did not scroll properly with the content.
My next attempt was to create a fixed-size window with a panel that overflows the bounds of the window (for scrolling purposes.) I added a PictureBox to the panel, and added my textboxes on top of it. This works fine, except that TextBoxes do not support transparency, so I tried several methods to make the TextBoxes transparent. One approach was to use an odd background color and a transparency key. Another, described in the following links, was to create a derived class that allows transparency:
Transparency for windows forms textbox
TextBox with a Transparent Background
Neither method works, because as I have come to find out, "transparency" in Windows Forms just means that the background of the window is painted onto the control background. Since the PictureBox is positioned between the Window background and the TextBox, it gives the appearance that the TextBox is not transparent, but simply has a background color equal to the background color of the Window. With the transparency key approach, the entire application becomes transparent so that you can see Visual Studio in the background, which is not what I want. So now I am trying to implement a class that derives from TextBox and overrides either OnPaint or OnPaintBackground to paint the appropriate part of the PictureBox image onto the control background to give the illusion of transparency as described in the following link:
How to create a transparent control which works when on top of other controls?
First of all, I can't get it working (I have tried various things, and either get a completely black control, or just a standard label background), and second of all, I get intermittent ArgumentExceptions from the DrawToBitmap method that have the cryptic message "Additional information: targetBounds." Based on the following link from MSDN, I believe that this is because the bitmap is too large - in either event it seems inefficient to capture the whole form image here because I really just want a tiny piece of it.
https://msdn.microsoft.com/en-us/library/system.windows.forms.control.drawtobitmap(v=vs.100).aspx
Here is my latest attempt. Can somebody please help me with the OnPaintBackground implementation or suggest a different approach? Thanks in advance!
public partial class TransparentTextbox : TextBox
{
public TransparentTextbox()
{
InitializeComponent();
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint, true);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
//base.OnPaintBackground(e); // not sure whether I need this
if (Parent != null)
{
foreach (Control c in Parent.Controls)
{
if (c.GetType() == typeof(PictureBox))
{
PictureBox formImg = (PictureBox)c;
Bitmap bitmap = new Bitmap(formImg.Width, formImg.Height);
formImg.DrawToBitmap(bitmap, formImg.Bounds);
e.Graphics.DrawImage(bitmap, -Left, -Top);
break;
}
}
Debug.WriteLine(Name + " didn't find the PictureBox.");
}
}
}
NOTE: This has been tagged as a duplicate, but I referenced the "duplicate question" in my original post, and explained why it was not working. That solution only works if the TextBox sits directly over the Window - if another control (such as my Panel and PictureBox) sit between the window and the TextBox, then .Net draws the Window background onto the TextBox background, effectively making its background look gray, not transparent.
I think I have finally gotten to the bottom of this. I added a Bitmap variable to my class, and when I instantiate the textboxes, I am setting it to contain just the portion of the form image that sits behind the control. Then I overload OnPaintBackground to display the Bitmap, and I overload OnPaint to manually draw the text string. Here is the updated version of my TransparentTextbox class:
public partial class TransparentTextbox : TextBox
{
public Bitmap BgBitmap { get; set; }
public TransparentTextbox()
{
InitializeComponent();
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint, true);
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawString(this.Text, this.Font, Brushes.Black, new PointF(0.0F, 0.0F));
}
protected override void OnPaintBackground(PaintEventArgs e)
{
e.Graphics.DrawImage(BgBitmap, 0, 0);
}
}
... and here is the relevant part of how I instantiate:
Bitmap bgImage = (Bitmap)Bitmap.FromStream(Document.FormImage);
PictureBox pb = new PictureBox();
pb.Image = bgImage;
pb.Size = pb.Image.Size;
pb.Top = 0;
pb.Left = 0;
panel1.Controls.Add(pb);
foreach (FormField field in Document.FormFields)
{
TransparentTextbox tb = new TransparentTextbox();
tb.Width = (int)Math.Ceiling(field.MaxLineWidth * 96.0);
tb.Height = 22;
tb.Font = new Font("Courier", 12);
tb.BorderStyle = BorderStyle.None;
tb.Text = "Super Neat!";
tb.TextChanged += tb_TextChanged;
tb.Left = (int)Math.Ceiling(field.XValue * 96.0);
tb.Top = (int)Math.Ceiling(field.YValue * 96.0);
tb.Visible = true;
Bitmap b = new Bitmap(tb.Width, tb.Height);
using (Graphics g = Graphics.FromImage(b))
{
g.DrawImage(bgImage, new Rectangle(0, 0, b.Width, b.Height), tb.Bounds, GraphicsUnit.Pixel);
tb.BgBitmap = b;
}
panel1.Controls.Add(tb);
}
I still need to work on how the text looks when I highlight it, and other things like that, but I feel like I am on the right track. +1 to Reza Aghaei and Mangist for commenting with other viable solutions!
is there a way to add a drop shadow to controls?
are there any controls out there with this feature?
You have to overwrite the CreateParamsproperty like this:
private const int CS_DROPSHADOW = 0x00020000;
protected override CreateParams CreateParams
{
get
{
// add the drop shadow flag for automatically drawing
// a drop shadow around the form
CreateParams cp = base.CreateParams;
cp.ClassStyle |= CS_DROPSHADOW;
return cp;
}
}
This question has been around for 6 years and needs an answer. I hope that anyone who needs to do this can extrapolate an answer for any control set from my solution. I had a panel and wanted to draw a drop shadow underneath every child control - in this instance one or more panels (but the solution should hold good for other control types with some minor code changes).
As the drop shadow for a control has to be drawn on the surface of that control's container we start by adding a function to the container's Paint() event.
Container.Paint += dropShadow;
dropShadow() looks like this:
private void dropShadow(object sender, PaintEventArgs e)
{
Panel panel = (Panel)sender;
Color[] shadow = new Color[3];
shadow[0] = Color.FromArgb(181, 181, 181);
shadow[1] = Color.FromArgb(195, 195, 195);
shadow[2] = Color.FromArgb(211, 211, 211);
Pen pen = new Pen(shadow[0]);
using (pen)
{
foreach (Panel p in panel.Controls.OfType<Panel>())
{
Point pt = p.Location;
pt.Y += p.Height;
for (var sp = 0; sp < 3; sp++)
{
pen.Color = shadow[sp];
e.Graphics.DrawLine(pen, pt.X, pt.Y, pt.X + p.Width - 1, pt.Y);
pt.Y++;
}
}
}
}
Clearly you can pick a different control type from the container's collection and you can vary the colour and depth of the shadow with some minor tweaks.
The top answer does in fact generate a shadow, but I personally wasn't satisfied with it for a few reasons:
It only works for rectangles (granted, WinForms controls are all rectangles, but we might want to use this in other cases)
More importantly: It's not smooth. It doesn't look as natural as other shadows in other programs look.
Finally, it's slightly annoying to configure.
So, because of all these things, I ended up writing my own for my project and I thought I'd share it here:
public partial class Form1 : Form
{
List<Control> shadowControls = new List<Control>();
Bitmap shadowBmp = null;
public Form1()
{
InitializeComponent();
shadowControls.Add(panel1);
this.Refresh();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
if (shadowBmp == null || shadowBmp.Size != this.Size)
{
shadowBmp?.Dispose();
shadowBmp = new Bitmap(this.Width, this.Height, PixelFormat.Format32bppArgb);
}
foreach (Control control in shadowControls)
{
using (GraphicsPath gp = new GraphicsPath())
{
gp.AddRectangle(new Rectangle(control.Location.X, control.Location.Y, control.Size.Width, control.Size.Height));
DrawShadowSmooth(gp, 100, 60, shadowBmp);
}
e.Graphics.DrawImage(shadowBmp, new Point(0, 0));
}
}
private static void DrawShadowSmooth(GraphicsPath gp, int intensity, int radius, Bitmap dest)
{
using (Graphics g = Graphics.FromImage(dest))
{
g.Clear(Color.Transparent);
g.CompositingMode = CompositingMode.SourceCopy;
double alpha = 0;
double astep = 0;
double astepstep = (double)intensity / radius / (radius / 2D);
for (int thickness = radius; thickness > 0; thickness--)
{
using (Pen p = new Pen(Color.FromArgb((int)alpha, 0, 0, 0), thickness))
{
p.LineJoin = LineJoin.Round;
g.DrawPath(p, gp);
}
alpha += astep;
astep += astepstep;
}
}
}
}
In this implementation, all Controls added to the shadowControls will be painted with a smooth shadow. You should be able to implement this for non-rectangular shapes because the main function to generate the shadows takes a GraphicsPath. Please note that it's important you draw the shadow to another bitmap before drawing it to the form because the main function requires a compositing mode of SourceCopy to work, which means if you don't draw it to another surface first anything behind the shadow will be completely replaced and the transparency aspect is useless. I'm on a roll of answering 10-year-old questions, but hopefully, this helps someone!
There is in WPF if you can stretch to using that instead, I don't believe there is an alternative in Windows Forms due to the limited capabilities of GDI+.
Here's a controversial opinion, you do it without code.
Set your main panel Border Style to Fixed Single.
Create 3 panels below it, each 1 pixel larger in every direction.
Each of the 3 panels is of a lighter shade of gray.
Not perfect but cheap and easy.
panel with pseudo-shadow
I want to make a panel have a thick border. Can I set this somehow?
PS, I am using C#. VS 2008.
Jim,
I've made a user control and given is a ParentControlDesigner. As I indicated in my comment it's not a perfect solution to what you're asking for. But it should be a good starting point. Oh any FYI, I've got it with a customizable border color too. I was inspired by another SO post to pursue this... It was trickier than I expected.
To get things to rearrange correctly when setting the border size a call to PerformLayout is made. The override to DisplayRectangle and the call to SetDisplayRectLocation in OnResize cause the proper repositioning of the child controls. As well the child controls don't have the expected "0,0" when in the upper left most... unless border width is set to 0... And OnPaint provides the custom drawing of the border.
Best of luck to ya! Making custom controls that are parents is tricky, but not impossible.
[Designer(typeof(ParentControlDesigner))]
public partial class CustomPanel : UserControl
{
Color _borderColor = Color.Blue;
int _borderWidth = 5;
public int BorderWidth
{
get { return _borderWidth; }
set { _borderWidth = value;
Invalidate();
PerformLayout();
}
}
public CustomPanel() { InitializeComponent(); }
public override Rectangle DisplayRectangle
{
get
{
return new Rectangle(_borderWidth, _borderWidth, Bounds.Width - _borderWidth * 2, Bounds.Height - _borderWidth * 2);
}
}
public Color BorderColor
{
get { return _borderColor; }
set { _borderColor = value; Invalidate(); }
}
new public BorderStyle BorderStyle
{
get { return _borderWidth == 0 ? BorderStyle.None : BorderStyle.FixedSingle; }
set { }
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaintBackground(e);
if (this.BorderStyle == BorderStyle.FixedSingle)
{
using (Pen p = new Pen(_borderColor, _borderWidth))
{
Rectangle r = ClientRectangle;
// now for the funky stuff...
// to get the rectangle drawn correctly, we actually need to
// adjust the rectangle as .net centers the line, based on width,
// on the provided rectangle.
r.Inflate(-Convert.ToInt32(_borderWidth / 2.0 + .5), -Convert.ToInt32(_borderWidth / 2.0 + .5));
e.Graphics.DrawRectangle(p, r);
}
}
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
SetDisplayRectLocation(_borderWidth, _borderWidth);
}
}
Just implement the panel's Paint event and draw a border. For example:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
namespace WindowsFormsApplication1 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
panel1.Paint += panel1_Paint;
}
VisualStyleRenderer renderer = new VisualStyleRenderer(VisualStyleElement.Button.PushButton.Normal);
private void panel1_Paint(object sender, PaintEventArgs e) {
renderer.DrawEdge(e.Graphics, panel1.ClientRectangle,
Edges.Bottom | Edges.Left | Edges.Right | Edges.Top,
EdgeStyle.Raised, EdgeEffects.Flat);
}
}
}
Play around with the arguments to find something you like. You ought to add code to fallback to ControlPaint.DrawBorder if visual styles aren't enabled. Meh.
If this is just about presentation, put a panel that fills the form with a background color of the border color you want and a Dock style of Fill. Place another panel inside this one with the standard background color and a Dock style of Fill. Play with Padding and Margin of the two panels to get the border size you wish (I forget which param applies correclty to the inner panel and the outer panel). Place your controls on the interior panel. With both panels set to Dock=Fill, form resizing is automatically handled for you. You may need to experiment with some of the controls, but I have done this many times with no issues for both application main windows and popup forms.
This is an old post but I still find it useful. And I just found another way.
ControlPaint.DrawBorder(e.Graphics, control.ClientRectangle,
Color.Black, BORDER_SIZE, ButtonBorderStyle.Inset,
Color.Black, BORDER_SIZE, ButtonBorderStyle.Inset,
Color.Black, BORDER_SIZE, ButtonBorderStyle.Inset,
Color.Black, BORDER_SIZE, ButtonBorderStyle.Inset);
This is kind of rigging it but I've always just used a label for each side border. You'll have to set the autosize property to false and dock one to each side (left, right, top, bottom). Then just set the width/height/background color to do what you want.
You could easily make this a user control and just expose some custom public properties to set the width/height for you and the background color of all the labels to change the color.
I'm trying to create a transparent button in C# (.NET 3.5 SP1) to use in my WinForms application. I've tried everything to get the button to be transparent (it should show the gradient background underneath the button) but it's just not working.
Here is the code I'm using:
public class ImageButton : ButtonBase, IButtonControl
{
public ImageButton()
{
this.SetStyle(
ControlStyles.SupportsTransparentBackColor |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint, true);
this.BackColor = Color.Transparent;
}
protected override void OnPaint(PaintEventArgs pevent)
{
Graphics g = pevent.Graphics;
g.FillRectangle(Brushes.Transparent, this.ClientRectangle);
g.DrawRectangle(Pens.Black, this.ClientRectangle);
}
// rest of class here...
}
The problem is that the button seems to be grabbing random UI memory from somewhere and filling itself with some buffer from Visual Studio's UI (when in design mode). At runtime it's grabbing some zero'd buffer and is completely black.
My ultimate goal is to paint an image on an invisible button instead of the rectangle. The concept should stay the same however. When the user hovers over the button then a button-type shape is drawn.
Any ideas?
EDIT: Thanks everybody, the following worked for me:
public class ImageButton : Control, IButtonControl
{
public ImageButton()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.Opaque, true);
SetStyle(ControlStyles.ResizeRedraw, true);
this.BackColor = Color.Transparent;
}
protected override void OnPaint(PaintEventArgs pevent)
{
Graphics g = pevent.Graphics;
g.DrawRectangle(Pens.Black, this.ClientRectangle);
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
// don't call the base class
//base.OnPaintBackground(pevent);
}
protected override CreateParams CreateParams
{
get
{
const int WS_EX_TRANSPARENT = 0x20;
CreateParams cp = base.CreateParams;
cp.ExStyle |= WS_EX_TRANSPARENT;
return cp;
}
}
// rest of class here...
}
WinForms (and underlying User32) does not support transparency at all. WinForms however can simulate transparency by using control style you provide - SupportsTransparentBackColor, but in this case all that "transparent" control does, it to allow drawing parent its background.
ButtonBase uses some windows styles that prevent working this mechanism. I see two solutions: one is to derive your control from Control (instead of ButtonBase), and second is to use Parent's DrawToBitmap to get background under your button, and then draw this image in OnPaint.
In winforms there are some tricks to allow a control having its background correctly painted when using transparency. You can add this code to the OnPaint or OnPaintBackground to get the controls you have in the background being painted:
if (this.Parent != null)
{
GraphicsContainer cstate = pevent.Graphics.BeginContainer();
pevent.Graphics.TranslateTransform(-this.Left, -this.Top);
Rectangle clip = pevent.ClipRectangle;
clip.Offset(this.Left, this.Top);
PaintEventArgs pe = new PaintEventArgs(pevent.Graphics, clip);
//paint the container's bg
InvokePaintBackground(this.Parent, pe);
//paints the container fg
InvokePaint(this.Parent, pe);
//restores graphics to its original state
pevent.Graphics.EndContainer(cstate);
}
else
base.OnPaintBackground(pevent); // or base.OnPaint(pevent);...
I'm not sure ButtonBase supports transparency... have you checked that out?
I've written a number of transparent controls, but I have always inherited from Control or UserControl.
When you want to block out a control painting it's background - you should override OnPaintBackground instead of OnPaint and not call the base class.
Filling a rectangle with Brushes.Transparent is funny though - you're painting with an invisible color over what's aready there. Or to put it another way: it does nothing!
I know this question is old, but if someone doesn't want to create a control to do this I came up with this code from a different article and changed it an extension method.
public static void ToTransparent(this System.Windows.Forms.Button Button,
System.Drawing.Color TransparentColor)
{
Bitmap bmp = ((Bitmap)Button.Image);
bmp.MakeTransparent(TransparentColor);
int x = (Button.Width - bmp.Width) / 2;
int y = (Button.Height - bmp.Height) / 2;
Graphics gr = Button.CreateGraphics();
gr.DrawImage(bmp, x, y);
}
And the call like:
buttonUpdate.ToTransparent(Color.Magenta);