public static Boolean TextBoxValidation(TextBox txt, String AdditionalMsg)
{
if (txt.Text.Trim() == "")
{
MessageBox.Show("Please Enter " + AdditionalMsg);
return false;
}
return true;
}
This is my code; when the user does not fill some entry then a message is shown. I want something more creative: when the user does not fill some entry into the textbox, a red border blinks around my textbox and a message is shown to user just like a tooltip.
Refer to the picture I have uploaded:
I kind of wanted something like that for a while and this is what I come up with:
Since you can't set border color for TextBox, I made a UserControl with textbox inside:
public partial class UCTextBoxCustomcs : UserControl
{
private ToolTip _errorToolTip;
// keep original background color so you can change it when txet value is OK
private Color _orgBgColor;
public new Color BackColor
{
get { return _orgBgColor; }
set
{
base.BackColor = value;
_orgBgColor = value;
}
}
public new string Text
{
get { return this.txbContent.Text; }
set { this.txbContent.Text = value; }
}
public Color InvalidBgColor { get; set; }
private bool _IsValid;
public bool IsValid
{
get { return _IsValid; }
set
{
_IsValid = value;
if (value)
{
base.BackColor = _orgBgColor;
_errorToolTip.SetToolTip(this.txbContent, "");
_errorToolTip.ShowAlways = false;
_errorToolTip.Hide(this.txbContent);
}
else
{
base.BackColor = InvalidBgColor;
_errorToolTip.ShowAlways = true;
this._errorToolTip.BackColor = InvalidBgColor;
_errorToolTip.Show(this.ErrorText, this.txbContent,this.txbContent.Width +3 ,0);
}
}
}
private string _ErrorText;
public string ErrorText
{
get
{
return _ErrorText;
}
set
{
_ErrorText = value;
if (value == null || value.Length == 0) IsValid = true;
else IsValid = false;
}
}
public UCTextBoxCustomcs()
{
this._errorToolTip = new ToolTip();
// BackColor in ToolTip is ignored, so if you want to change it,
// you have to draw it yourself
this._errorToolTip.OwnerDraw = true;
_errorToolTip.Draw += new DrawToolTipEventHandler(_errorToolTip_Draw);
_errorToolTip.Popup += new PopupEventHandler(_errorToolTip_Popup);
// white background so it looks like TextBox
this.BackColor = Color.White;
InitializeComponent();
this.txbContent.BorderStyle = BorderStyle.None;
// Intelisense tells you this property isn't there, but it is
// you have to set it to false so TextBox height can be changed
// when MultiLine is set to false
this.txbContent.AutoSize = false;
this.txbContent.Multiline = false;
// Leave 1 pixel around TextBox for pseudo-border
this.Padding = new Padding(1);
this.txbContent.Dock = DockStyle.Fill;
this.InvalidBgColor = Color.Red;
this.IsValid = true;
}
void _errorToolTip_Popup(object sender, PopupEventArgs e)
{
using (Font f = new Font("Calibri", 9))
{
Size ttSize = TextRenderer.MeasureText(
_errorToolTip.GetToolTip(e.AssociatedControl), f);
e.ToolTipSize = new Size(ttSize.Width + 6, ttSize.Height + 6);
}
}
void _errorToolTip_Draw(object sender, DrawToolTipEventArgs e)
{
// In this case a simple rectangle is drawn, but you can draw whatever you want
// Draw the custom background.
e.Graphics.FillRectangle(new SolidBrush(this.InvalidBgColor), e.Bounds);
// Draw the standard border.
e.DrawBorder();
// Draw the custom text.
// The using block will dispose the StringFormat automatically.
using (StringFormat sf = new StringFormat())
{
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
sf.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.None;
sf.FormatFlags = StringFormatFlags.NoWrap;
using (Font f = new Font("Calibri", 9))
{
Rectangle textBounds = new Rectangle(
e.Bounds.Left+3,
e.Bounds.Top+3,
e.Bounds.Width-6,
e.Bounds.Height-6);
e.Graphics.DrawString(e.ToolTipText, f,
SystemBrushes.ActiveCaptionText, e.Bounds, sf);
}
}
}
protected override void OnValidating(CancelEventArgs e)
{
this.ValidateChildren();
base.OnValidating(e);
}
}
How to use:
private void ucTextBoxCustomcs1_Validating(object sender, CancelEventArgs e)
{
if (ucTextBoxCustomcs1.Text.Length == 0)
{
ucTextBoxCustomcs1.ErrorText = "Cant be empty";
}
else ucTextBoxCustomcs1.ErrorText = null;
}
It looks like this:
Related
Video link: https://drive.google.com/open?id=1pErIL2TcE_wMNYaH0FlWxuijqSwbkYY3
There is something that I cannot translate the problem/bug as I don't know how to address this kind of problem. When the font family selection change, all the label inside the groupbox will be changed too. The code for it as per below:-
private void ScreenPage_FontFamilyCombobox_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
if (ScreenPage_FontFamilyCombobox.Text != "")
{
ScreenPage_FontFamilyNotInstalled.Visible = false;
UpdateScreenSample();
}
}
catch (Exception ex)
{
UpdateEvents("Exception ScreenPage_FontFamilyCombobox_SelectedIndexChanged. " + ex.Message);
}
}
private void UpdateScreenSample()
{
try
{
//foreach (var TransLabels in ScreenPage_SampleGroupbox.Controls.OfType<GroupBox>().SelectMany(groupBox => groupBox.Controls.OfType<CodeMess.TransparentLabel>()))
//{
// float fntSize = TransLabels.Font.Size;
// FontStyle fntStyle = TransLabels.Font.Style;
// TransLabels.Font = new Font(ScreenPage_FontFamilyCombobox.Text, fntSize, fntStyle);
//}
var TransLabels = ScreenPage_SampleGroupbox.Controls.OfType<CodeMess.TransparentLabel>();
foreach (CodeMess.TransparentLabel tL in TransLabels)
{
float fntSize = tL.Font.Size;
FontStyle fntStyle = tL.Font.Style;
tL.Font = new Font(ScreenPage_FontFamilyCombobox.Text, fntSize, fntStyle);
}
//foreach (Control control in ScreenPage_SampleGroupbox.Controls)
//{
// if (control is ApplizoneConfiguration.CodeMess.TransparentLabel transLabel)
// {
// float fntSize = control.Font.Size;
// control.Font = new Font(ScreenPage_FontFamilyCombobox.Text, fntSize, FontStyle.Regular);
// }
//}
}
catch (Exception ex)
{
UpdateEvents("Exception UpdateScreenSample. " + ex.Message);
}
}
There is 3 code that I try to see if there any change but seems not. The font shadow trail will be disappear if I change to another tab and go back to the ScreenPage tab again.
The code for Transparent Label class is below which I use to transparent the background as it has dock fill image inside the groupbox on the bottom of all label:-
using System;
using System.Windows.Forms;
namespace ApplizoneConfiguration.CodeMess
{
public class TransparentLabel : Label
{
public TransparentLabel()
{
this.SetStyle(ControlStyles.Opaque, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
}
protected override CreateParams CreateParams
{
get
{
CreateParams parms = base.CreateParams;
parms.ExStyle |= 0x20; // Turn on WS_EX_TRANSPARENT
return parms;
}
}
}
}
There is a way to overcome this?
I have go through code by #Jimi answer linked from comment. And modified the code combined with solution from here and it a bit OK. Here is the full code with commenting
Sample Screen2Gif from solutions by Reza Aghaei here result with delay effect:-
Combine with solutions from Jimi + Reza result with these:-
Code:-
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Globalization;
using System.ComponentModel;
using System.Windows.Forms;
using System.Linq;
namespace Configuration.CodeMess
{
public class TransparentLabel : Label, INotifyPropertyChanged
{
internal int WS_EX_TRANSPARENT = 0x00000020;
//internal Font m_CustomFont = new Font("Segoe UI", 9, FontStyle.Regular, GraphicsUnit.Pixel);
//internal Color m_BackGroundColor;
//internal int m_InnerPadding = 20;
//internal int m_FontPadding = 5;
//internal int m_Opacity = 50;
public event PropertyChangedEventHandler PropertyChanged;
public TransparentLabel() => InitializeComponent();
public void InitializeComponent()
{
this.SetStyle(ControlStyles.Opaque |
ControlStyles.SupportsTransparentBackColor |
ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
this.Refresh();
}
//protected override void OnPaint(PaintEventArgs e)
//{
// if (Parent != null)
// {
// using (var bmp = new Bitmap(Parent.Width, Parent.Height))
// {
// Parent.Controls.Cast<Control>()
// .Where(c => Parent.Controls.GetChildIndex(c) > Parent.Controls.GetChildIndex(this))
// .Where(c => c.Bounds.IntersectsWith(this.Bounds))
// .OrderByDescending(c => Parent.Controls.GetChildIndex(c))
// .ToList()
// .ForEach(c => c.DrawToBitmap(bmp, c.Bounds));
// e.Graphics.DrawImage(bmp, -Left, -Top);
// using (var b = new SolidBrush(Color.FromArgb(this.Opacity, this.TransparentBackColor)))
// {
// e.Graphics.FillRectangle(b, this.ClientRectangle);
// }
// e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
// TextRenderer.DrawText(e.Graphics, this.Text, this.Font, this.ClientRectangle, this.ForeColor, Color.Transparent);
// }
// }
//}
private int opacity;
public int Opacity
{
get { return opacity; }
set
{
if (value >= 0 && value <= 255)
opacity = value;
this.Invalidate();
}
}
public Color transparentBackColor;
public Color TransparentBackColor
{
get { return transparentBackColor; }
set
{
transparentBackColor = value;
this.Invalidate();
}
}
[Browsable(false)]
public override Color BackColor
{
get
{
return Color.Transparent;
}
set
{
base.BackColor = Color.Transparent;
}
}
//public new Font Font
//{
// get => this.m_CustomFont;
// set
// {
// this.FontAdapter(value);
// NotifyPropertyChanged(nameof(this.Font));
// }
//}
//public int InnerPadding
//{
// get => this.m_InnerPadding;
// set
// {
// this.m_InnerPadding = CheckValue(value, 0, this.ClientRectangle.Height - 10);
// NotifyPropertyChanged(nameof(this.InnerPadding));
// }
//}
//public int FontPadding
//{
// get => this.m_FontPadding;
// set
// {
// this.m_FontPadding = CheckValue(value, 0, this.ClientRectangle.Height - 10);
// NotifyPropertyChanged(nameof(this.FontPadding));
// }
//}
//public int Opacity
//{
// get => this.m_Opacity;
// set
// {
// this.m_Opacity = CheckValue(value, 0, 255);
// this.BackColor = Color.FromArgb(this.m_Opacity, this.BackColor);
// NotifyPropertyChanged(nameof(this.Opacity));
// }
//}
//public override Color BackColor
//{
// get => this.m_BackGroundColor;
// set
// {
// this.m_BackGroundColor = Color.FromArgb(this.m_Opacity, value);
// base.BackColor = this.m_BackGroundColor;
// NotifyPropertyChanged(nameof(this.BackColor));
// }
//}
//private void NotifyPropertyChanged(string PropertyName)
//{
// this.Refresh();
// PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
//}
//protected override void OnLayout(LayoutEventArgs evt)
//{
// base.OnLayout(evt);
// base.AutoSize = false;
// this.Opacity = this.m_Opacity;
//}
protected override void OnPaint(PaintEventArgs e)
{
StringFormat format = new StringFormat(StringFormatFlags.LineLimit, CultureInfo.CurrentUICulture.LCID)
{
LineAlignment = StringAlignment.Center,
Alignment = StringAlignment.Center
};
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
TextRenderer.DrawText(e.Graphics, this.Text, this.Font, this.ClientRectangle, this.ForeColor, Color.Transparent);
//using (SolidBrush CircleBrush = new SolidBrush(this.BackColor))
//using (SolidBrush ForeBrush = new SolidBrush(this.ForeColor))
//{
// this.FontAdapter(this.m_CustomFont);
// RectangleF rect = InnerRectangle();
// e.Graphics.FillEllipse(CircleBrush, rect);
// e.Graphics.DrawString(this.Text, this.m_CustomFont, format);
//}
}
//private RectangleF InnerRectangle()
//{
// Tuple<decimal, decimal> refSize = GetMinMax(this.ClientRectangle.Height, this.ClientRectangle.Width);
// SizeF size = new SizeF((float)refSize.Item1 - (this.m_InnerPadding / 2),
// (float)refSize.Item1 - (this.m_InnerPadding / 2));
// PointF position = new PointF((this.ClientRectangle.Width - size.Width) / 2,
// (this.ClientRectangle.Height - size.Height) / 2);
// return new RectangleF(position, size);
//}
//private void FontAdapter(Font font)
//{
// RectangleF rect = InnerRectangle();
// float FontSize = (CheckValue((int)(rect.Height - this.m_FontPadding), 6,
// (int)(rect.Height - this.m_FontPadding)) / 1.4F);
// using (Font customfont = new Font(font.FontFamily, FontSize, font.Style, GraphicsUnit.Pixel))
// this.m_CustomFont = (Font)customfont.Clone();
//}
//private int CheckValue(int Value, int Min, int Max)
//{
// return (Value < Min) ? Min : ((Value > Max) ? Max : Value);
//}
//private Tuple<decimal, decimal> GetMinMax(ValueType Value1, ValueType Value2)
//{
// if ((Value1 is Enum) || (Value1.GetType().IsNested)) return null;
// if ((Value2 is Enum) || (Value2.GetType().IsNested)) return null;
// return new Tuple<decimal, decimal>(Math.Min(Convert.ToDecimal(Value1), Convert.ToDecimal(Value2)),
// Math.Max(Convert.ToDecimal(Value1), Convert.ToDecimal(Value2)));
//}
protected override CreateParams CreateParams
{
get
{
CreateParams parms = base.CreateParams;
parms.ExStyle |= 0x20; // Turn on WS_EX_TRANSPARENT
return parms;
}
}
}
}
Open to any amendment for improvement.
I implemented a dynamic RichTextBox (it can increase its width based on the content). But when i previously entered some text and double-click it to change the string in in the text turns transparent.
From the moment I start adding text, the characters become visible again.
On double-click, I just do this:
myAnnotationTextBox.Show();
myAnnotationTextBox.Focus();
The code of my custom RichTextBox:
public class AlphaBlendTextBox : RichTextBox
{
private const int _initialWidth = 100;
public AlphaBlendTextBox(Font textBoxFont, Color theCurrentForeColor)
{
this.Font = textBoxFont;
this.ForeColor = theCurrentForeColor;
this.BackColor = Color.White;
this.AutoSize = false;
this.WordWrap = false;
this.Multiline = true;
this.Height = textBoxFont.Height;
this.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.None;
this.BorderStyle = BorderStyle.None;
this.HideSelection = false;
}
protected override void OnContentsResized(ContentsResizedEventArgs e)
{
base.OnContentsResized(e);
if (String.IsNullOrEmpty(Text))
{
Width = _initialWidth;
}
else
{
Width = e.NewRectangle.Width;
Height = e.NewRectangle.Height;
}
}
}
I want to color all "Unselectable" Text from combo box. How can i do this? I tried it but i am unable to do this.
My Code is Given Below:
private class ComboBoxItem
{
public int Value { get; set; }
public string Text { get; set; }
public bool Selectable { get; set; }
}
private void Form1_Load(object sender, EventArgs e)
{
this.comboBox1.ValueMember = "Value";
this.comboBox1.DisplayMember = "Text";
this.comboBox1.Items.AddRange(new[] {
new ComboBoxItem() { Selectable = true, Text="Selectable0", Value=0, },
new ComboBoxItem() { Selectable = true, Text="Selectable1", Value=1},
new ComboBoxItem() { Selectable = true, Text="Selectable2", Value=2},
new ComboBoxItem() { Selectable = false, Text="Unselectable", Value=3},
new ComboBoxItem() { Selectable = true, Text="Selectable3", Value=4},
new ComboBoxItem() { Selectable = false, Text="Unselectable", Value=5},
});
this.comboBox1.SelectedIndexChanged += (cbSender, cbe) =>
{
var cb = cbSender as ComboBox;
if (cb.SelectedItem != null && cb.SelectedItem is ComboBoxItem && ((ComboBoxItem)cb.SelectedItem).Selectable == false)
{
// deselect item
cb.SelectedIndex = -1;
}
};
}
I am working in C#.NET.
You need to set the foreground property on the ComboBoxItem to the colour you require.
new ComboBoxItem() { Selectable = false, Text="Unselectable", Value=3, Foreground = Brushes.Red},
MSDN page
You need to set the ComboBox.DrawMode to OwnerDrawxxx and script the DrawItem event e.g. like this:
private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
// skip without valid index
if (e.Index >= 0)
{
ComboBoxItem cbi = (ComboBoxItem)comboBox1.Items[e.Index];
Graphics g = e.Graphics;
Brush brush = new SolidBrush(e.BackColor);
Brush tBrush = new SolidBrush(cbi.Text == "Unselectable" ? Color.Red : e.ForeColor);
g.FillRectangle(brush, e.Bounds);
e.Graphics.DrawString(comboBox1.Items[e.Index].ToString(), e.Font,
tBrush, e.Bounds, StringFormat.GenericDefault);
brush.Dispose();
tBrush.Dispose();
}
e.DrawFocusRectangle();
}
This part cbi.Text == "Unselectable"is not good, obviously. Since you already have a Property Selectable it should really read !cbi.Selectable". Off course you must make sure that the property is in synch with the text.
I am creating dynamic text fields and storing Position and font etc details in an Arraylist. So for example if I click 3 times on Form I am generating 3 random numbers and showing it on clicked position on form. I have one selector button when it is clicked then adding more text functionality is disabled.
Now after when selector button is clicked and if I click ( MouseDown event) on any text then it should move along mouse until MouseUp event is not fired and place on new drop position.
After hours of struggling and searching I found This Solution, So I saved the position and checked with IsPointOnLine method but still its not draggable.
Thank you for any help.
Update: managed to get foreach on place but its still not dragging the selected element.
Form1.Class
public partial class Form1 : Form
{
private Point mouseDownPosition = new Point(0, 0);
private Point mouseMovePosition = new Point(0, 0);
private int mousePressdDown;
IList drawnItemsList = new List<DrawingData>();
private bool dragging;
private bool isSelectorClicked; // if selector button is clicked
DrawingData draggingText;
Random rnd;
int clicked = 0;
public Form1()
{
InitializeComponent();
this.rnd = new Random();
this.isSelectorClicked = false;
dragging = false;
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
mouseMovePosition = e.Location;
if (e.Button == MouseButtons.Left)
mousePressdDown = 1;
if (isSelectorClicked)
{
foreach (DrawingData a in drawnItemsList)
{
if (a.IsPointOnLine(e.Location, 5))
{
draggingText = a;
dragging = true;
}
}
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
mouseDownPosition = e.Location;
if (dragging)
{
draggingText.cur = mouseDownPosition;
this.Invalidate();
}
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
if (mousePressdDown == 1 && isSelectorClicked == false)
{
label1.Text = "X: " + mouseMovePosition.X.ToString();
label2.Text = "Y: " + mouseMovePosition.Y.ToString();
this.Invalidate();
DrawingData a = new DrawingData(mouseMovePosition, mouseDownPosition, rnd.Next().ToString(), FontStyle.Bold, 30, "Candara", Brushes.Blue);
drawnItemsList.Add(a);
this.clicked++;
}
mousePressdDown = 0;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
if(drawnItemsList.Count != 0)
{
foreach (DrawingData a in drawnItemsList)
{
draw(e.Graphics, a);
}
if (mousePressdDown != 0)
{
draw(e.Graphics, mouseDownPosition, mouseMovePosition, FontStyle.Bold, 30, "Candara", Brushes.Blue);
}
}
}
private void draw(Graphics e, Point mold, Point mcur, FontStyle fontWeight, int fontSize, string fontName, Brush fontColor)
{
Pen p = new Pen(Color.Black, 2);
using (Font useFont = new Font(fontName, fontSize, fontWeight))
{
string header2 = rnd.Next().ToString();
RectangleF header2Rect = new RectangleF();
int moldX = mold.X - 5;
int moldY = mold.Y;
header2Rect.Location = new Point(moldX, moldY);
header2Rect.Size = new Size(600, ((int)e.MeasureString(header2, useFont, 600, StringFormat.GenericTypographic).Height));
e.DrawString(header2, useFont, fontColor, header2Rect);
}
}
private void draw(Graphics e, DrawingData a)
{
Pen p = new Pen(Color.Black, 2);
using (Font useFont = new Font(a.FontName, a.FontSize, a.FontWeight))
{
string header2 = rnd.Next().ToString();
RectangleF header2Rect = new RectangleF();
int moldX = a.old.X - 5;
int moldY = a.old.Y;
header2Rect.Location = new Point(moldX, moldY);
header2Rect.Size = new Size(600, ((int)e.MeasureString(header2, useFont, 600, StringFormat.GenericTypographic).Height));
e.DrawString(a.Rand, useFont, a.FontColor, header2Rect);
}
}
private void Select_button_Click(object sender, EventArgs e)
{
this.isSelectorClicked = true;
}
private void WriteNewText_button_Click(object sender, EventArgs e)
{
this.isSelectorClicked = false;
}
}
DrawingData.Class
[Serializable]
public class DrawingData
{
private Point Start; // mouseDown position
private Point End; // mouseUp poslition
private string randValue; // random data value
private FontStyle fontWeight;
private int fontSize;
private string fontName;
private Brush fontColor;
public DrawingData()
{
Start = new Point(0, 0);
End = new Point(0, 0);
randValue = String.Empty;
fontWeight = FontStyle.Bold;
}
public DrawingData(Point old, Point cur, string rand, FontStyle fs, int fSize, string fName, Brush fColor)
{
Start = old;
End = cur;
randValue = rand;
fontWeight = fs;
fontSize = fSize;
fontName = fName;
fontColor = fColor;
}
public float slope
{
get
{
return (((float)End.Y - (float)Start.Y) / ((float)End.X - (float)Start.X));
}
}
public float YIntercept
{
get
{
return Start.Y - slope * Start.X;
}
}
public bool IsPointOnLine(Point p, int cushion)
{
float temp = (slope * p.X + YIntercept);
if (temp >= (p.Y - cushion) && temp <= (p.Y + cushion))
{
return true;
}
else
{
return false;
}
}
public FontStyle FontWeight
{
get
{
return fontWeight;
}
set
{
fontWeight = value;
}
}
public int FontSize
{
get
{
return fontSize;
}
set
{
fontSize = value;
}
}
public string FontName
{
get
{
return fontName;
}
set
{
fontName = value;
}
}
public Brush FontColor
{
get
{
return fontColor;
}
set
{
fontColor = value;
}
}
public Point old
{
get
{
return Start;
}
set
{
Start = value;
}
}
public Point cur
{
get
{
return End;
}
set
{
End = value;
}
}
public string Rand
{
get
{
return randValue;
}
set
{
randValue = value;
}
}
}
SOLVED: I was missing these lines in my mouse move method:
m.Start = new Point(deltaStart.X + e.Location.X, deltaStart.Y + e.Location.Y);
m.End = new Point(deltaEnd.X + e.Location.X, deltaEnd.Y + e.Location.Y);
You should move stuff in Form1_MouseMove instead of Form1_MouseUp.
Find the element you want to move in the Arraylist and assign it new position.
Redraw the element.
Also you should use IList<DrawingData> instead of ArrayList
I have a class that extends TabControl, basically to have one with the tabs down the left hand side instead of along the top. To do I have set it to be custom drawn.
The problem is when this is put onto a form via the designer, it easily makes the designer loose track of itself and just give up and display nothing. To fix it I have to close the designer and re-open it and then everything is fine until I hit debug, or I go to a code window for a while and come back, where it gives up again.
Is there anything I can do to help visual studio a bit? As while its not throwing errors, it is getting a little tedious now. I'm using visual studio 2008.
Here is the code that extends the tab control, if anyone sees any issues that could be causing this it'd be very appreciated.
public class VerticalTabControl : TabControl
{
private Color tabColour1 = Color.AliceBlue;
public Color TabColour1
{
get { return tabColour1; }
set { tabColour1 = value; this.Refresh(); }
}
private Color tabColour2 = Color.White;
public Color TabColour2
{
get { return tabColour2; }
set { tabColour2 = value; this.Refresh(); }
}
private Color selectedTabColor1 = Color.AliceBlue;
public Color SelectedTabColor1
{
get { return selectedTabColor1; }
set { selectedTabColor1 = value; this.Refresh(); }
}
private Color selectedTabColor2 = Color.White;
public Color SelectedTabColor2
{
get { return selectedTabColor2; }
set { selectedTabColor2 = value; this.Refresh(); }
}
private Color backgroundColour = Color.White;
public Color BackgroundColour
{
get { return backgroundColour; }
set { backgroundColour = value; this.Refresh(); }
}
private Color tabTextColour = Color.Black;
public Color TabTextColour
{
get { return tabTextColour; }
set { tabTextColour = value; this.Refresh(); }
}
protected override void OnParentChanged(EventArgs e)
{
base.OnParentChanged(e);
this.Parent.Resize += new EventHandler(Parent_Resize);
}
void Parent_Resize(object sender, EventArgs e)
{
this.Refresh();
}
public VerticalTabControl()
: base()
{
this.Alignment = TabAlignment.Left;
this.SizeMode = TabSizeMode.Fixed;
this.ItemSize = new Size(50, 120);
this.DrawMode = TabDrawMode.OwnerDrawFixed;
this.DrawItem += new DrawItemEventHandler(VerticalTabControl_DrawItem);
}
void VerticalTabControl_DrawItem(object sender, DrawItemEventArgs e)
{
Graphics g = e.Graphics;
TabControl ctrl = sender as TabControl;
String sText = ctrl.TabPages[e.Index].Text;
Rectangle r = new Rectangle(e.Bounds.Left, e.Bounds.Top, e.Bounds.Width, e.Bounds.Height);
if (e.Index == ctrl.SelectedIndex)
{
using (LinearGradientBrush gb = new LinearGradientBrush(r, this.selectedTabColor1, this.selectedTabColor2, LinearGradientMode.Horizontal))
{
e.Graphics.FillRectangle(gb, r);
}
}
else
{
using (LinearGradientBrush gb = new LinearGradientBrush(r, this.tabColour1, this.tabColour2, LinearGradientMode.Horizontal))
{
e.Graphics.FillRectangle(gb, r);
}
}
// Set up the page and the various pieces.
TabPage page = ctrl.TabPages[e.Index];
// Set up the offset for an icon, the bounding rectangle and image size and then fill the background.
int iconOffset = 0;
Rectangle tabBackgroundRect = e.Bounds;
// If we have images, process them.
if (this.ImageList != null)
{
// Get sice and image.
Size size = this.ImageList.ImageSize;
Image icon = null;
if (page.ImageIndex > -1)
icon = this.ImageList.Images[page.ImageIndex];
else if (page.ImageKey != "")
icon = this.ImageList.Images[page.ImageKey];
// If there is an image, use it.
if (icon != null)
{
Point startPoint = new Point(tabBackgroundRect.X + 6,
tabBackgroundRect.Y + 2 + ((tabBackgroundRect.Height - size.Height) / 2));
e.Graphics.DrawImage(icon, new Rectangle(startPoint, size));
iconOffset = size.Width + 4;
}
}
// Draw out the label.
SizeF sizeText = g.MeasureString(sText, ctrl.Font);
int iX = e.Bounds.Left + 6 + iconOffset;
int iY = e.Bounds.Top + (e.Bounds.Height / 2) - (int)(sizeText.Height / 2);
using (Brush ForeBrush = new SolidBrush(tabTextColour))
{
g.DrawString(sText, ctrl.Font, ForeBrush, iX, iY);
}
Rectangle rec = ctrl.GetTabRect(ctrl.TabPages.Count - 1);
Rectangle recF = new Rectangle(0, rec.Bottom, this.ItemSize.Height, ctrl.Height - rec.Bottom);
using (SolidBrush bb = new SolidBrush(backgroundColour))
{
g.FillRectangle(bb, recF);
}
}
}
turns out this was the offending bit of code
protected override void OnParentChanged(EventArgs e)
{
base.OnParentChanged(e);
this.Parent.Resize += new EventHandler(Parent_Resize);
}
'this.Parent' was sometimes null and so an exception would have been thrown which would have caused the designer to fail.
Have now fixed this and also tidied up the event hooking so I didn't leave hooks everywhere. Seems to work fine now. Thanks for your help whoever answered this and deleted their answer for some reason.