I have a form . I set the backcolor and transparency key of form to limeGreen to make form transparent.
this.TransparencyKey = System.Drawing.Color.LimeGreen;
this.BackColor = System.Drawing.Color.LimeGreen;
This works fine on window 7 theme but when i changes my theme from windows 7 to windows 7 basic it shows the background of the form ? is there any way i can solve this issue ?
Windows 7 Theme
Windows 7 Basic Theme
Haven't tested on Windows 7 without Aero enabled, but this will work 100% on Windows XP:
public class XForm : Form
{
#region Default value overrides
[DefaultValue(FormStartPosition.Manual)]
public new FormStartPosition StartPosition
{
get { return base.StartPosition; }
set { base.StartPosition = value; }
}
[DefaultValue(FormBorderStyle.None)]
public new FormBorderStyle FormBorderStyle
{
get { return base.FormBorderStyle; }
set { base.FormBorderStyle = value; }
}
[DefaultValue(false)]
public new bool ShowInTaskbar
{
get { return base.ShowInTaskbar; }
set { base.ShowInTaskbar = value; }
}
[DefaultValue(typeof(Color), "LavenderBlush")]
public new Color TransparencyKey
{
get { return base.TransparencyKey; }
set { base.TransparencyKey = value; }
}
[DefaultValue(typeof(Color), "LavenderBlush")]
public new Color BackColor
{
get { return base.BackColor; }
set { base.BackColor = value; }
}
#endregion
public XForm()
: base()
{
// set user paint style
SetStyle(ControlStyles.UserPaint | ControlStyles.ResizeRedraw | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
// override
FormBorderStyle = FormBorderStyle.None;
StartPosition = FormStartPosition.Manual;
ShowInTaskbar = false;
TransparencyKey = BackColor = Color.LavenderBlush;
}
protected override void OnPaint(PaintEventArgs e)
{
// do nothing
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// half opaque background
using (HatchBrush brush = new HatchBrush(HatchStyle.Percent50, this.TransparencyKey))
e.Graphics.FillRectangle(brush, ClientRectangle);
}
}
It doesn't makes it completely transparent, but hatched, yet it should be easy to figure out of how to fix it =P
P.S.: a tip, don't use smoothing (by using different colors) or you will have that ugly border around your image (unless it is intended).
Related
Basically I'm creating my own form
public class CryForm : System.Windows.Forms.Form
for several reasons, one of which is a very specific style.
Therefore I want the Form.BackColor property to be 'locked' to Black, so that it cannot be changed from 'outside'
CryForm1.BackColor = Color.whatevercolorulike
should not be possible anymore.
Is there any way to achieve this or should I come up with a completely different solution?
This should work, although you won't get a compile time error when trying to set the property.
public override Color BackColor
{
get { return Color.Black; }
set { }
}
You can make it explicit that changing the BackColor is not supported. It will result in a runtime exception if anything is trying to change it:
public override Color BackColor
{
get { return Color.Black; }
set { throw new NotSupportedException("CryForm doesn't support changing the BackColor"); }
}
If your need to is to 'lock' the background color of the form at design-time, probably the most efficient and least error-prone solution would be to override the BackColor property and mark with an attribute, then inherit from the form in the designer.
You could declare:
public class FixedBackgroundForm : Form
{
protected new static readonly Color DefaultBackColor = Color.Black;
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override Color BackColor
{
get { return base.BackColor; }
set { base.BackColor = value; }
}
public FixedBackgroundForm()
{
this.BackColor = DefaultBackColor
}
}
Which would both set your form background color to Black automatically, and prevent changing of the background color from within the designer.
When you add new forms to your project, inherit from FixedBackgroundForm:
public partial class Form1 : FixedBackgroundForm
{
...
}
If you needed to "fix" the background color to black no matter what, simply use this line for the BackColor setter:
set { base.BackColor = DefaultBackColor; }
Another option is to add this to the form load event:
this.BackColorChanged += (s, e2) =>
{
if (this.BackColor != Color.Black)
this.BackColor = Color.Black;
};
Just add
public new Color BackColor
{
get { return Color.Black; }
}
to your code!
I currently have a custom Render set up for a MenuStrip on a C# windows forms application:
private class HeaderMenuRender : ProfessionalColorTable
{
public override Color MenuItemSelectedGradientBegin
{
get
{
return Color.Gray;
}
}
public override Color MenuItemSelectedGradientEnd
{
get
{
return Color.Gray;
}
}
public override Color MenuItemPressedGradientBegin
{
get
{
return Color.Gray;
}
}
public override Color MenuItemPressedGradientEnd
{
get
{
return Color.Gray;
}
}
public override Color MenuItemBorder
{
get
{
return Color.Gray;
}
}
public override Color MenuBorder
{
get
{
return Color.Gray;
}
}
}
This is then applied to an existing MenuStrip control on a form to create a Custom render.
However when running the application and making a selection from the menu which is displayed, despite the Menu options being the correct colour, there is a small 'White' (arguably could be 'Control' colour) bar which spans the length of the selected MenuStrip option (indicated in the blue box below):
Is there a particular property of the custom Renderer that I am not including or something I am missing which is setting this specific part of the MenuStrip selected item? The examples I have seen elsewhere do not seem to have this issue.
In the above code, it seems you made items Gray using BackColor property.
You can use override ToolStripDropDownBackground to return Color.Gray, this removes that while line.
Also you can overriding ImageMarginGradientBegin, ImageMarginGradientMiddle and ImageMarginGradientEnd to make also image/checkbox area Gray.
Here is What I added to your codes to get the desired value:
public override Color ImageMarginGradientBegin
{
get { return Color.Gray; }
}
public override Color ImageMarginGradientMiddle
{
get { return Color.Gray; }
}
public override Color ImageMarginGradientEnd
{
get { return Color.Gray; }
}
public override Color ToolStripDropDownBackground
{
get { return Color.Gray; }
}
I have used ProgressBar Control in my c# desktop application.I have used it in a thread other then the thread in which control has been declared.Its working Fine.
Now I am wondering how i can show some text inside progress bar control like "Initiating Registration" etc.Also I want to use it as Marquee progress bar.Please help me.
You will have to override the OnPaint method, call the base implementation and the paint your own text.
You will need to create your own CustomProgressBar and then override OnPaint to draw what ever text you want.
Custom Progress Bar Class
namespace ProgressBarSample
{
public enum ProgressBarDisplayText
{
Percentage,
CustomText
}
class CustomProgressBar: ProgressBar
{
//Property to set to decide whether to print a % or Text
public ProgressBarDisplayText DisplayStyle { get; set; }
//Property to hold the custom text
public String CustomText { get; set; }
public CustomProgressBar()
{
// Modify the ControlStyles flags
//http://msdn.microsoft.com/en-us/library/system.windows.forms.controlstyles.aspx
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
}
protected override void OnPaint(PaintEventArgs e)
{
Rectangle rect = ClientRectangle;
Graphics g = e.Graphics;
ProgressBarRenderer.DrawHorizontalBar(g, rect);
rect.Inflate(-3, -3);
if (Value > 0)
{
// As we doing this ourselves we need to draw the chunks on the progress bar
Rectangle clip = new Rectangle(rect.X, rect.Y, (int)Math.Round(((float)Value / Maximum) * rect.Width), rect.Height);
ProgressBarRenderer.DrawHorizontalChunks(g, clip);
}
// Set the Display text (Either a % amount or our custom text
int percent = (int)(((double)this.Value / (double)this.Maximum) * 100);
string text = DisplayStyle == ProgressBarDisplayText.Percentage ? percent.ToString() + '%' : CustomText;
using (Font f = new Font(FontFamily.GenericSerif, 10))
{
SizeF len = g.MeasureString(text, f);
// Calculate the location of the text (the middle of progress bar)
// Point location = new Point(Convert.ToInt32((rect.Width / 2) - (len.Width / 2)), Convert.ToInt32((rect.Height / 2) - (len.Height / 2)));
Point location = new Point(Convert.ToInt32((Width / 2) - len.Width / 2), Convert.ToInt32((Height / 2) - len.Height / 2));
// The commented-out code will centre the text into the highlighted area only. This will centre the text regardless of the highlighted area.
// Draw the custom text
g.DrawString(text, f, Brushes.Red, location);
}
}
}
}
Sample WinForms Application
using System;
using System.Linq;
using System.Windows.Forms;
using System.Collections.Generic;
namespace ProgressBarSample
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Set our custom Style (% or text)
customProgressBar1.DisplayStyle = ProgressBarDisplayText.CustomText;
customProgressBar1.CustomText = "Initialising";
}
private void btnReset_Click(object sender, EventArgs e)
{
customProgressBar1.Value = 0;
btnStart.Enabled = true;
}
private void btnStart_Click(object sender, EventArgs e)
{
btnReset.Enabled = false;
btnStart.Enabled = false;
for (int i = 0; i < 101; i++)
{
customProgressBar1.Value = i;
// Demo purposes only
System.Threading.Thread.Sleep(100);
// Set the custom text at different intervals for demo purposes
if (i > 30 && i < 50)
{
customProgressBar1.CustomText = "Registering Account";
}
if (i > 80)
{
customProgressBar1.CustomText = "Processing almost complete!";
}
if (i >= 99)
{
customProgressBar1.CustomText = "Complete";
}
}
btnReset.Enabled = true;
}
}
}
I have written a no blinking/flickering TextProgressBar
You can find the source code here: https://github.com/ukushu/TextProgressBar
WARNING: It's a little bit buggy! But still, I think it's better than another answers here. As I have no time for fixes, if you will do sth with them, please send me update by some way:) Thanks.
Samples:
AVOID FLICKERING TEXT
The solution provided by Barry above is excellent, but there's is the "flicker-problem".
As soon as the Value is above zero the OnPaint will be envoked repeatedly and the text will flicker.
There is a solution to this. We do not need VisualStyles for the object since we will be drawing it with our own code.
Add the following code to the custom object Barry wrote and you will avoid the flicker:
[DllImportAttribute("uxtheme.dll")]
private static extern int SetWindowTheme(IntPtr hWnd, string appname, string idlist);
protected override void OnHandleCreated(EventArgs e)
{
SetWindowTheme(this.Handle, "", "");
base.OnHandleCreated(e);
}
I did not write this myself. It found it here: https://stackoverflow.com/a/299983/1163954
I've testet it and it works.
I wold create a control named for example InfoProgresBar, that provide this functionality with a label or two (Main Job, Current Job) and ProgressBar and use it instead of that ProgressBar.
I have used this simple code, and it works!
for (int i = 0; i < N * N; i++)
{
Thread.Sleep(50);
progressBar1.BeginInvoke(new Action(() => progressBar1.Value = i));
progressBar1.CreateGraphics().DrawString(i.ToString() + "%", new Font("Arial",
(float)10.25, FontStyle.Bold),
Brushes.Red, new PointF(progressBar1.Width / 2 - 10, progressBar1.Height / 2 - 7));
}
It just has one simple problem and this is it: when progress bar start to rising, percentage some times hide, and then appear again.
I did't write it myself.I found it here:
text on progressbar in c#
I used this code, and it does work.
I tried placing a label with transparent background over a progress bar but never got it to work properly. So I found Barry's solution here very useful, although I missed the beautiful Vista style progress bar. So I merged Barry's solution with http://www.dreamincode.net/forums/topic/243621-percent-into-progress-bar/ and managed to keep the native progress bar, while displaying text percentage or custom text over it. I don't see any flickering in this solution either. Sorry to dig up and old thread but I needed this today and so others may need it too.
public enum ProgressBarDisplayText
{
Percentage,
CustomText
}
class ProgressBarWithCaption : ProgressBar
{
//Property to set to decide whether to print a % or Text
private ProgressBarDisplayText m_DisplayStyle;
public ProgressBarDisplayText DisplayStyle {
get { return m_DisplayStyle; }
set { m_DisplayStyle = value; }
}
//Property to hold the custom text
private string m_CustomText;
public string CustomText {
get { return m_CustomText; }
set {
m_CustomText = value;
this.Invalidate();
}
}
private const int WM_PAINT = 0x000F;
protected override void WndProc(ref Message m)
{
base.WndProc(m);
switch (m.Msg) {
case WM_PAINT:
int m_Percent = Convert.ToInt32((Convert.ToDouble(Value) / Convert.ToDouble(Maximum)) * 100);
dynamic flags = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.WordEllipsis;
using (Graphics g = Graphics.FromHwnd(Handle)) {
using (Brush textBrush = new SolidBrush(ForeColor)) {
switch (DisplayStyle) {
case ProgressBarDisplayText.CustomText:
TextRenderer.DrawText(g, CustomText, new Font("Arial", Convert.ToSingle(8.25), FontStyle.Regular), new Rectangle(0, 0, this.Width, this.Height), Color.Black, flags);
break;
case ProgressBarDisplayText.Percentage:
TextRenderer.DrawText(g, string.Format("{0}%", m_Percent), new Font("Arial", Convert.ToSingle(8.25), FontStyle.Regular), new Rectangle(0, 0, this.Width, this.Height), Color.Black, flags);
break;
}
}
}
break;
}
}
}
Just want to point out something on #codingbadger answer. When using "ProgressBarRenderer" you should always check for "ProgressBarRenderer.IsSupported" before using the class. For me, this has been a nightmare with Visual Styles errors in Win7 that I couldn't fix. So, a better approach and workaround for the solution would be:
Rectangle clip = new Rectangle(rect.X, rect.Y, (int)Math.Round(((float)Value / Maximum) * rect.Width), rect.Height);
if (ProgressBarRenderer.IsSupported)
ProgressBarRenderer.DrawHorizontalChunks(g, clip);
else
g.FillRectangle(new SolidBrush(this.ForeColor), clip);
Notice that the fill will be a simple rectangle and not chunks. Chunks will be used only if ProgressBarRenderer is supported
I have created a InfoProgressBar control which uses a TransparentLabel control. Testing on a form with a Timer, I get some slight glitches displaying the text every 30-40 value changes if using a timer interval of less than 250 milliseconds (probably because of the time required to update the screen is greater than the timer interval).
It would be possible to modify UpdateText method to insert all the calculated values into CustomText but it isn't something that I have needed yet. This would remove the need for the DisplayType property and enumerate.
The TransparentLabel class was created by adding a new UserControl and changing it to inheriting from Label with the following implementation:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace Utils.GUI
{
public partial class TransparentLabel : Label
{
// hide the BackColor attribute as much as possible.
// setting the base value has no effect as drawing the
// background is disabled
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override Color BackColor
{
get
{
return Color.Transparent;
}
set
{
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20; // WS_EX_TRANSPARENT
return cp;
}
}
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
if(Parent != null) Parent.Invalidate(Bounds, false);
}
}
public override ContentAlignment TextAlign
{
get
{
return base.TextAlign;
}
set
{
base.TextAlign = value;
if(Parent != null) Parent.Invalidate(Bounds, false);
}
}
public TransparentLabel()
{
InitializeComponent();
SetStyle(ControlStyles.Opaque, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
base.BackColor = Color.Transparent;
}
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
RecreateHandle();
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
// do nothing
}
}
}
I did not make any changes to the related designer code but here it is for completeness.
namespace Utils.GUI
{
partial class TransparentLabel
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component 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()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}
I then created another new UserControl and changed it to derive from ProgressBar with the following implementation:
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Windows.Forms.Design.Behavior;
namespace Utils.GUI
{
[Designer(typeof(InfoProgressBarDesigner))]
public partial class InfoProgressBar : ProgressBar
{
// designer class to add font baseline snapline by copying it from the label
private class InfoProgressBarDesigner : ControlDesigner
{
public override IList SnapLines
{
get
{
IList snapLines = base.SnapLines;
InfoProgressBar control = Control as InfoProgressBar;
if(control != null)
{
using(IDesigner designer = TypeDescriptor.CreateDesigner(control.lblText, typeof(IDesigner)))
{
if(designer != null)
{
designer.Initialize(control.lblText);
ControlDesigner boxDesigner = designer as ControlDesigner;
if(boxDesigner != null)
{
foreach(SnapLine line in boxDesigner.SnapLines)
{
if(line.SnapLineType == SnapLineType.Baseline)
{
snapLines.Add(new SnapLine(SnapLineType.Baseline, line.Offset, line.Filter, line.Priority));
break;
}
}
}
}
}
}
return snapLines;
}
}
}
// enum to select the type of displayed value
public enum ProgressBarDisplayType
{
Custom = 0,
Percent = 1,
Progress = 2,
Remain = 3,
Value = 4,
}
private string _customText;
private ProgressBarDisplayType _displayType;
private int _range;
[Bindable(false)]
[Browsable(true)]
[DefaultValue("{0}")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
// {0} is replaced with the result of the selected calculation
public string CustomText
{
get
{
return _customText;
}
set
{
_customText = value;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(ProgressBarDisplayType.Percent)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public ProgressBarDisplayType DisplayType
{
get
{
return _displayType;
}
set
{
_displayType = value;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
// don't use the lblText font as if it is null, it checks the parent font (i.e. this property) and gives an infinite loop
public override Font Font
{
get
{
return base.Font;
}
set
{
base.Font = value;
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(100)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new int Maximum
{
get
{
return base.Maximum;
}
set
{
base.Maximum = value;
_range = base.Maximum - base.Minimum;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new int Minimum
{
get
{
return base.Minimum;
}
set
{
base.Minimum = value;
_range = base.Maximum - base.Minimum;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(ContentAlignment.MiddleLeft)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public ContentAlignment TextAlign
{
get
{
return lblText.TextAlign;
}
set
{
lblText.TextAlign = value;
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(typeof(Color), "0x000000")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public Color TextColor
{
get
{
return lblText.ForeColor;
}
set
{
lblText.ForeColor = value;
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new int Value
{
get
{
return base.Value;
}
set
{
base.Value = value;
UpdateText();
}
}
public InfoProgressBar()
{
InitializeComponent();
CustomText = "{0}";
DisplayType = ProgressBarDisplayType.Percent;
Maximum = 100;
Minimum = 0;
TextAlign = ContentAlignment.MiddleLeft;
TextColor = Color.Black;
Value = 0;
// means the label gets drawn in front of the progress bar
lblText.Parent = this;
_range = base.Maximum - base.Minimum;
}
protected void UpdateText()
{
switch(DisplayType)
{
case ProgressBarDisplayType.Custom:
{
lblText.Text = _customText;
break;
}
case ProgressBarDisplayType.Percent:
{
if(_range > 0)
{
lblText.Text = string.Format(_customText, string.Format("{0}%", (int)((Value * 100) / _range)));
}
else
{
lblText.Text = "100%";
}
break;
}
case ProgressBarDisplayType.Progress:
{
lblText.Text = string.Format(_customText, (Value - Minimum));
break;
}
case ProgressBarDisplayType.Remain:
{
lblText.Text = string.Format(_customText, (Maximum - Value));
break;
}
case ProgressBarDisplayType.Value:
{
lblText.Text = string.Format(_customText, Value);
break;
}
}
}
public new void Increment(int value)
{
base.Increment(value);
UpdateText();
}
public new void PerformStep()
{
base.PerformStep();
UpdateText();
}
}
}
And the designer code:
namespace Utils.GUI
{
partial class InfoProgressBar
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component 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.lblText = new Utils.GUI.TransparentLabel();
this.SuspendLayout();
//
// lblText
//
this.lblText.BackColor = System.Drawing.Color.Transparent;
this.lblText.Dock = System.Windows.Forms.DockStyle.Fill;
this.lblText.Location = new System.Drawing.Point(0, 0);
this.lblText.Name = "lblText";
this.lblText.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.lblText.Size = new System.Drawing.Size(100, 23);
this.lblText.TabIndex = 0;
this.lblText.Text = "transparentLabel1";
this.ResumeLayout(false);
}
#endregion
private TransparentLabel lblText;
}
}
Alliteratively you can try placing a Label control and placing it on top of the progress bar control. Then you can set whatever the text you want to the label. I haven't done this my self. If it works it should be a simpler solution than overriding onpaint.
I need to have 2 groups of controls on the screen: inputs and outputs (so they have 2 states: On or Off). Thus CheckBox seems to be a good choice. Checking any output will set it.
However, when displaying inputs there will be no user interaction with it. User is only allowed to see its value, not to change it.
Question: how to make checkbos visually appears as read-only ?
Could think about possible solutions:
Make CheckBox disabled. Bad: there will be no tooltip (possible to solve it? by fake panel on top?) and visually disabled CheckBox is not nice (and I don't want to make user think it is disabled).
Use different control. Which one? Label doesn't have nice placeholder for the On/Off value. RadioButton look differently, but they usually means there is a single choice out of many, while values of inputs are independent.
Making own component. Drawing the whole CheckBox is a bit overkill (and honestly, I don't know how to do it to have Win7 appearance). Would it be possible to add only ReadOnly appearance to the box part easily?
What do you guys think?
There is a solution that is combination of the existing answers.
checkBox.ForeColor = Color.Gray; // Read-only appearance
checkBox.AutoCheck = false; // Read-only behavior
// Tooltip is possible because the checkbox is Enabled
var toolTip = new ToolTip();
toolTip.SetToolTip(checkBox, "This checkbox is read-only.");
The result is a CheckBox that
appears disabled with gray text
prevents the Checked value from changing when clicked
supports a Tooltip
You have to draw everything yourself. I think you should use some controls with correct layout to mimic it. Here is the demo code for you, note that it does not support AutoSize correctly. Because the drawn stuff is always wider than the default stuff (which the AutoSize works with), implementing the AutoSize is not easy, If you don't care too much about AutoSize, this would be the great control for you:
public class XCheckBox : CheckBox
{
public XCheckBox()
{
SetStyle(ControlStyles.Opaque, false);
ReadOnlyCheckedColor = Color.Green;
ReadOnlyUncheckedColor = Color.Gray;
}
public bool ReadOnly { get; set; }
public bool AlwaysShowCheck { get; set; }
public Color ReadOnlyCheckedColor { get; set; }
public Color ReadOnlyUncheckedColor { get; set; }
protected override void OnPaint(PaintEventArgs pevent)
{
if (ReadOnly)
{
pevent.Graphics.SmoothingMode = SmoothingMode.HighQuality;
pevent.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
if (AlwaysShowCheck || Checked)
{
RenderCheck(pevent.Graphics);
}
RenderText(pevent.Graphics);
}
else base.OnPaint(pevent);
}
private void RenderCheck(Graphics g)
{
float fontScale = Font.Size / 8.25f;
Size glyphSize = CheckBoxRenderer.GetGlyphSize(g, System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal);
glyphSize.Width = (int) (glyphSize.Width * fontScale);
glyphSize.Height = (int)(glyphSize.Height * fontScale);
string checkAlign = CheckAlign.ToString();
using (GraphicsPath gp = new GraphicsPath())
using (Pen pen = new Pen(Checked ? ReadOnlyCheckedColor : ReadOnlyUncheckedColor, 1.5f)
{
LineJoin = LineJoin.Round,
EndCap = LineCap.Round,
StartCap = LineCap.Round
})
{
gp.AddLine(new Point(3, 7), new Point(5, 10));
gp.AddLine(new Point(5, 10), new Point(8, 3));
float dx = checkAlign.EndsWith("Right") ? Math.Max(-4*fontScale, ClientSize.Width - glyphSize.Width - 4 * fontScale) :
checkAlign.EndsWith("Center") ? Math.Max(-4*fontScale, (ClientSize.Width - glyphSize.Width) / 2 - 4 * fontScale) : -4;
float dy = checkAlign.StartsWith("Bottom") ? Math.Max(-4*fontScale, ClientSize.Height - glyphSize.Height - 4*fontScale) :
checkAlign.StartsWith("Middle") ? Math.Max(-4*fontScale, (ClientSize.Height - glyphSize.Height) / 2 - 4*fontScale) : 0;
g.TranslateTransform(dx, dy);
g.ScaleTransform(1.5f*fontScale, 1.5f*fontScale);
g.DrawPath(pen, gp);
g.ResetTransform();
}
}
private void RenderText(Graphics g)
{
Size glyphSize = CheckBoxRenderer.GetGlyphSize(g, System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal);
float fontScale = Font.Size / 8.25f;
glyphSize.Width = (int)(glyphSize.Width * fontScale);
glyphSize.Height = (int)(glyphSize.Height * fontScale);
string checkAlign = CheckAlign.ToString();
using (StringFormat sf = new StringFormat())
{
string alignment = TextAlign.ToString();
sf.LineAlignment = alignment.StartsWith("Top") ? StringAlignment.Near :
alignment.StartsWith("Middle") ? StringAlignment.Center : StringAlignment.Far;
sf.Alignment = alignment.EndsWith("Left") ? StringAlignment.Near :
alignment.EndsWith("Center") ? StringAlignment.Center : StringAlignment.Far;
sf.FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.NoClip;
Rectangle textRectangle = ClientRectangle;
if (checkAlign.EndsWith("Left"))
{
textRectangle.Width -= glyphSize.Width;
textRectangle.Offset(glyphSize.Width, 0);
}
else if (checkAlign.EndsWith("Right"))
{
textRectangle.Width -= glyphSize.Width;
textRectangle.X = 0;
}
g.DrawString(Text, Font, new SolidBrush(ForeColor), textRectangle, sf);
}
}
bool suppressCheckedChanged;
protected override void OnClick(EventArgs e)
{
if (ReadOnly) {
suppressCheckedChanged = true;
Checked = !Checked;
suppressCheckedChanged = false;
}
base.OnClick(e);
}
protected override void OnCheckedChanged(EventArgs e)
{
if (suppressCheckedChanged) return;
base.OnCheckedChanged(e);
}
}
NOTE: The code is not fully implemented, everything is kept as simple as possible. You can change the AlwaysShowCheck property to choose the ReadOnly unchecked state, it can be a gray tick mark or nothing. You can set the ReadOnly to true to make it Read-only visual.
AlwaysShowCheck is set to true (the ReadOnly unchecked state is indicated by a gray tick mark)
AlwaysShowCheck is set to false (the ReadOnly unchecked state is indicated by nothing)
This is an old post but still can be usefull so here is my solution.
In order to make a read-only behavior:
Disable highlight when the cursor is over the CheckBox
Disable reacting (logically or visibly) to a mouse click
Have tooltips enabled
We can inherit the CheckBox class and disable mouse and keyboard interaction:
public class ReadOnlyCheckBox : CheckBox
{
[System.ComponentModel.Category("Behavior")]
[System.ComponentModel.DefaultValue(false)]
public bool ReadOnly { get; set; } = false;
protected override void OnMouseEnter(EventArgs e)
{
// Disable highlight when the cursor is over the CheckBox
if (!ReadOnly) base.OnMouseEnter(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
// Disable reacting (logically or visibly) to a mouse click
if (!ReadOnly) base.OnMouseDown(e);
}
protected override void OnKeyDown(KeyEventArgs e)
{
// Suppress space key to disable checking/unchecking
if (!ReadOnly || e.KeyData != Keys.Space) base.OnKeyDown(e);
}
}
In order to make it visually apparent that the CheckBox is read-only, we can change the ForColor according to the ReadOnly property.
Note: changing the ForColor only changes the text color, the colors of the checkmark can only be changed by overriding the OnPaint method and repainting the CheckBox (as far as I know).
Here is an extended version of the previous code that changes the ForColor according to the ReadOnly property:
public class ReadOnlyCheckBox : CheckBox
{
private bool _readOnly = false;
private Color _readOnlyForeColor = Color.Gray;
private Color _normalForeColor = Color.Black;
[System.ComponentModel.Category("Behavior")]
[System.ComponentModel.DefaultValue(false)]
public bool ReadOnly
{
get => _readOnly;
set
{
if (_readOnly != value)
{
_readOnly = value;
UpdateForColor();
}
}
}
[System.ComponentModel.Category("Appearance")]
[System.ComponentModel.DefaultValue(typeof(Color), "Black")]
public Color NormalForeColor
{
get => _normalForeColor;
set
{
if (_normalForeColor != value)
{
_normalForeColor = value;
UpdateForColor();
}
}
}
[System.ComponentModel.Category("Appearance")]
[System.ComponentModel.DefaultValue(typeof(Color), "Gray")]
public Color ReadOnlyForeColor
{
get => _readOnlyForeColor;
set
{
if (_readOnlyForeColor != value)
{
_readOnlyForeColor = value;
UpdateForColor();
}
}
}
// Hide ForeColor from the editor
[System.ComponentModel.Browsable(false)]
[System.ComponentModel.EditorBrowsable(
System.ComponentModel.EditorBrowsableState.Never)]
public override Color ForeColor
{
get => base.ForeColor;
set => base.ForeColor = value;
}
public ReadOnlyCheckBox()
{
UpdateForColor();
}
private void UpdateForColor()
{
ForeColor = ReadOnly ? ReadOnlyForeColor : NormalForeColor;
}
protected override void OnMouseEnter(EventArgs e)
{
// Disable highlight when the cursor is over the CheckBox
if (!ReadOnly) base.OnMouseEnter(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
// Disable reacting (logically or visibly) to a mouse click
if (!ReadOnly) base.OnMouseDown(e);
}
protected override void OnKeyDown(KeyEventArgs e)
{
// Suppress space key to disable checking/unchecking
if (!ReadOnly || e.KeyData != Keys.Space) base.OnKeyDown(e);
}
}
So far the easiest solution (credits go to ShadowWizard) is to set ForeColor = Color.Gray, this makes user think, what CheckBox is disabled.
Compared to Enabled = false, pluses are:
ToolTip is working;
box part looks pretty (it react on mouse hovering and is very clearly seen whenever is checked or unchecked).
No minuses.
It is not necessary to write the entire control, just write a derivative of Checkbox.
A ReadOnly property is added to the control, this causes the control to handle when it can change its value.
public class CheckBoxReadOnly : CheckBox
{
private bool _readOnly = false;
[DefaultValue(false)]
public bool ReadOnly
{
get { return _readOnly; }
set
{
if (_readOnly != value)
{
_readOnly = value;
OnReadOnlyChanged(new EventArgs());
}
}
}
int _flag = 0;
public event EventHandler ReadOnlyChanged;
protected void OnReadOnlyChanged(EventArgs e)
{
ReadOnlyChanged?.Invoke(this, e);
}
protected override void OnCheckedChanged(EventArgs e)
{
if (ReadOnly)
{
_flag++;
if (_flag == 1)
Checked = !Checked;
}
else
{
base.OnCheckedChanged(e);
}
_flag = 0;
}
}
You may provide a listener for an event of clicking on CheckBox, as there's ability to cancel its usual flow at runtime.
In the Property table just make selection mode to none.
Visual studio has now it available under:
Properties -> Properties -> ReadOnly :)
I've created a transparent label control and the transparency works great. However, I find when I update the control's Text field, the original Text doesn't clear before it paints the new text. So if I change the control's Text field several times, it soon becomes unreadable.
Any clues? Thanks!
public partial class TransLabel : Label
{
public TransLabel()
{
InitializeComponent();
this.SetStyle(ControlStyles.Opaque, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
this.Font = new Font("Franklin Gothic Book", 12f, FontStyle.Regular);
this.ForeColor = Color.White;
this.BackColor = Color.Transparent;
}
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
this.Invalidate(); // seems to have no effect
this.Refresh(); // seems to have no effect
}
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
//do nothing
}
protected override void OnMove(EventArgs e)
{
RecreateHandle();
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle = 0x00000020; //WS_EX_TRANSPARENT
return cp;
}
}
}
Try changing your Text property to this:
public override string Text {
get {
return base.Text;
}
set {
base.Text = value;
if (this.Parent != null)
this.Parent.Invalidate(this.Bounds, false);
}
}
Since WinForms doesn't have true support for transparency, I think you have to invalidate the parent container.
Also, when inheriting a control, you usually don't have an InitializeComponent() method.