HotDog's Blog

Hotdog (Robert Verpalen) about C# and vb.net

This blog hosted by:
http://blogs.vbcity.com      
  Home :: Syndication  :: Login

MayJune 2006Jul
SMTWTFS
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

Articles

Archives

Topics

CONTACT

Fun but useful linkies

General

VS 2005

Wolfenstein ET

Thursday, June 01, 2006 #

For the description of most controls we use labels and I for one don't like to add a label manually for each control. Been using a component that paints values, but never got around to put it in a nice coat and with design time support up until now. The design time being the most work (although greatly alleviated by finding out how to use the isComplete parameter of the InstanceDescriptor ;-) ), the component is easy in use and deployment.
Usage: drop it to your form (or container control) and you get a new property on each control (”Label on LabelProviderName”).
You can simply type in the labeltext and a label will be painted on its parent.

To set more details, when the text is set a LabelInfo object will be created automatically. A plus sign appears in front of the property once it has been set, to indicate it can be expanded. Other properties for the label such as the Alignment, Font and Color can then be set.
To make things complete, the LabelProvider component itself also contains a set of those properties. This is the leading set. Unless a property is set specifically for a single label, the settings of the main component will be used for all labels. (The effects can be easily tried out in the designer)


Per request some quick setup instructions:
Usage
Add this code to your project (or a dll containing this code). The component will automatically show up in the visual studio 2005 toolbox. Drag the LabelProvider to your form or control in the form designer.
Now each control you select will have an additional property: ”Label on ...”, where ... is the name of the component. The text you type in in this property, will become the label for that control.

That's it for the normal label usage. For more settings, you can change the settings on the component itself. Or you can change the settings per label: press the [+] in front of the “Label on ...“  property for additional settings

Added per 22-9-6:
ability to show icon (or show only icon, then a tooltip will be shown for the text, thus the labelprovider becomes an infoprovider)
Changable cursor
Click event
Blinking possibility

Small thing added on 19-10-6:
text is drawn as disabled if the attached control is disabled

 

Code CopyHideScrollFull
using System;
using
System.Collections.Generic;
using
System.Text;
using
System.ComponentModel;
using
System.ComponentModel.Design;
using
System.ComponentModel.Design.Serialization;
using
System.Drawing;
using
System.Drawing.Design;
using
System.Windows.Forms;
using
System.Windows.Forms.Design;
using
System.Reflection;

namespace
Subro.Controls
{
[ProvideProperty("Label", typeof(Control))]
[ToolboxBitmap(typeof(Label))]
[DefaultEvent("LabelClicked")]
public
class LabelProvider : Component, IExtenderProvider
{
#region Constructors
public
LabelProvider()
: this(null)
{
}
public
LabelProvider(IContainer container)
{
if (container != null)
container.Add(this);
defsettings.SettingChanged += new EventHandler(defsettings_SettingChanged);
}
#endregion
#region
IExtenderProvider Members
public
bool CanExtend(object extendee)
{
return extendee is Control;
}

int IndexOf(Control c)
{
for (int i = 0; i < list.Count; i++)
{
if (list[i].Control == c) return i;
}
return
-1;
}
[DefaultValue(null)]
public
LabelInfo GetLabel(Control c)
{
int i = IndexOf(c);
if
(i == -1)
{
return null;
}
return
list[i].LabelInfo;
}
public void SetLabel(Control c, LabelInfo value)
{
int i = IndexOf(c);
if
(value == null)
{
if (i == -1) return;
list[i].Dispose();
list.RemoveAt(i);
}
else
if (i == -1)
{
list.Add(new ControlLabel(this, c, value));
}
else

{
list[i].LabelInfo = value;
}
}
public class LabelClickedEventArgs : EventArgs
{
public readonly Control Control;
public
readonly LabelInfo LabelInfo;
public
LabelClickedEventArgs(Control c, LabelInfo inf)
{
this.Control = c;
this
.LabelInfo = inf;
}
}
public event EventHandler<LabelClickedEventArgs> LabelClicked;
void OnLabelClicked(ControlLabel l)
{
if (LabelClicked != null)
LabelClicked(this, new LabelClickedEventArgs(l.Control, l.LabelInfo));
}

class ControlLabel : IDisposable
{
public readonly Control Control;

LabelProvider Owner;
public
ControlLabel(LabelProvider Owner, Control c, LabelInfo li)
{
if (c == null) throw new ArgumentNullException();
this
.Control = c;
this
.LabelInfo = li;
this
.Owner = Owner;
li.DefaultSettings = Owner.defsettings;
ResetParent();
Control.ParentChanged += ehParentChanged;
Control.LocationChanged += ehLocationChanged;
Control.EnabledChanged += new EventHandler(Control_EnabledChanged);
}

#region enter/leave
bool focused, cursorchanged;
public
bool Focused
{
get { return focused; }
}
Control ParentControl
{
get { return Parent.Parent; }
}
Cursor
beforeEnter;
public
void CheckEnterLeave(Point p)
{
CheckEnterLeave(bounds.Contains(p));
}
void
CheckEnterLeave(bool hasfocus)
{
if (hasfocus != focused)
{
if (hasfocus)
{
if (inf.Cursor != null && inf.Cursor != ParentControl.Cursor)
{
beforeEnter = ParentControl.Cursor;
ParentControl.Cursor = inf.Cursor;
cursorchanged = true;
}
if
(!inf.ShowText)
Parent.ShowTooltip(this, new Point((int)bounds.Right,(int)bounds.Top));
}
else

{
if (cursorchanged)
{
ParentControl.Cursor = beforeEnter;
cursorchanged = false;
}
Parent.RemoveTooltip(this);
}
focused = hasfocus;
}
}
public void Click()
{
Owner.OnLabelClicked(this);
}
#endregion
#region
label
LabelInfo
inf;
public
LabelInfo LabelInfo
{
get
{
return inf;
}
set

{
RemoveInfoEvents();
inf = value;
if
(inf != null)
inf.SettingChanged += ehSettingChanged;
Invalidate(true);
}
}
void
LabelInfo_SettingChanged(object sender, EventArgs e)
{
Invalidate(true);
}
void
Control_EnabledChanged(object sender, EventArgs e)
{
Invalidate(false);
}
EventHandler
ehSettingChanged
{
get
{
return new EventHandler(LabelInfo_SettingChanged);
}
}
void
RemoveInfoEvents()
{
if (inf != null)
{
inf.SettingChanged -= ehSettingChanged;
}
}
RectangleF bounds;
void
Invalidate(bool ClearBounds)
{
if (ClearBounds)
{
if (!bounds.IsEmpty) bounds = new RectangleF();
StopBlink();
}
if
(Parent != null)
Parent.Invalidate(this);
}
public bool BoundsSet
{
get { return !bounds.IsEmpty; }
}
public RectangleF Bounds
{
get { return bounds; }
}
#endregion

#region label bounds
public
bool ContainsLabel
{
get
{
return inf != null &&
(!string.IsNullOrEmpty(inf.Text) || inf.ShowIcon);
}
}
public
void Paint(Graphics g)
{
if (!ContainsLabel) return;
if
(bounds.IsEmpty) InitBounds(g);
if
(blinker != null && blinker.ShouldHide) return;
inf.Paint(g, bounds,this.Control.Enabled);
}
void
InitBounds(Graphics g)
{
bounds = inf.GetBounds(g, Control.Bounds);
InitBlink(inf.Blink);
}
private void InitBlink(LabelInfo.BlinkInfo blinkInfo)
{
if (blinkInfo == null || blinkInfo.Style == BlinkStyle.None || blinkInfo.Rate == 0) return;
blinker = new Blinker(blinkInfo, this);
}
void StopBlink()
{
if (blinker != null)
{
blinker.Stop();
blinker = null;
}
}
Blinker
blinker;
class
Blinker
{
bool hide;
LabelInfo
.BlinkInfo inf;
ControlLabel Owner;
DateTime
start = DateTime.Now;
bool
hasend;
DateTime
end;
public
Blinker(LabelInfo.BlinkInfo inf, ControlLabel Owner)
{
this.inf = inf;
this
.Owner = Owner;
hasend = inf.Style == BlinkStyle.LimitedTime && inf.Duration > 0;
if
(hasend) end = start.AddMilliseconds(inf.Duration);
SetTimer();
}
System.Threading.Timer timer;
public bool ShouldHide
{
get { return hide; }
}
void Invalidate()
{
if (Owner.Control.InvokeRequired)
Owner.Control.Invoke(new System.Threading.ThreadStart(Invalidate));
else
Owner.Invalidate(false);
}
void
SwitchHide(object state)
{
if (hasend && DateTime.Now >= end)
Stop();
else if (!stopped)
{
hide = !hide;
Invalidate();
}
}
void SetTimer()
{
timer =
new System.Threading.Timer(
  new System.Threading.TimerCallback(SwitchHide),
null, inf.Rate, inf.Rate);
}

bool stopped;
public
void Stop()
{
stopped = true;
if
(hide)
{
hide = false;
Invalidate();
}
//timer.Dispose();

//timer = null;
}
}
#endregion
#region
location changed

void Control_LocationChanged(object sender, EventArgs e)
{
Invalidate(true);
}
EventHandler
ehLocationChanged
{
get { return new EventHandler(Control_LocationChanged); }
}
#endregion
#region
Parent
ParentReference
Parent;
void
RemoveFromParent()
{
if (Parent != null)
{
Parent.Remove(this);
}
}
void
ResetParent()
{
RemoveFromParent();
Parent = Owner.GetParentReference(this);
}
void
Control_ParentChanged(object sender, EventArgs e)
{
ResetParent();
}
EventHandler
ehParentChanged
{
get
{
return new EventHandler(Control_ParentChanged);
}
}
#endregion
#region
IDisposable Members
bool isdisposed;
public
bool IsDisposed { get { return isdisposed; } }
public void Dispose()
{
Control.ParentChanged -= ehParentChanged;
Control.LocationChanged -= ehLocationChanged;
Control.EnabledChanged -= new EventHandler(Control_EnabledChanged);
RemoveInfoEvents();
RemoveFromParent();
CheckEnterLeave(false);
isdisposed = true;
}
#endregion
}
List
<ParentReference> parents = new List<ParentReference>();
ParentReference
GetParentReference(ControlLabel cl)
{
Control Parent = cl.Control.Parent;
if
(Parent == null) return null;
ParentReference
pr = null;
for
(int i = 0; i < parents.Count; i++)
{
if (parents[i].Parent == Parent)
{
pr = parents[i];
break
;
}
}
if
(pr == null)
{
pr = new ParentReference(Parent, this);
parents.Add(pr);
}
pr.Add(cl);
return
pr;
}
class
ParentReference
{
public Control Parent;
LabelProvider
Owner;
public
ParentReference(Control Parent, LabelProvider Owner)
{
this.Parent = Parent;
this
.Parent.Paint += ehPaint;
this
.Owner = Owner;
Parent.MouseMove += new MouseEventHandler(Parent_MouseMove);
Parent.MouseLeave += new EventHandler(Parent_MouseLeave);
Parent.Click += new EventHandler(Parent_Click);
}
#region focus
void
Parent_MouseMove(object sender, MouseEventArgs e)
{
CheckBounds();
}
void
Parent_MouseLeave(object sender, EventArgs e)
{
CheckBounds();
}
void
CheckBounds()
{
Point p = Parent.PointToClient(Control.MousePosition);
foreach
(ControlLabel c in children)
{
c.CheckEnterLeave(p);
}
}
void
Parent_Click(object sender, EventArgs e)
{
foreach (ControlLabel c in GetFocusedControls())
{
c.Click();
}
}
IEnumerable<ControlLabel> GetFocusedControls()
{
for (int i = 0; i < children.Count; i++)
{
ControlLabel c = children[i];
if
(!c.IsDisposed && c.Focused) yield return c;
if
(c.IsDisposed) i--;
}
}
#endregion
#region Paint
PaintEventHandler
ehPaint
{
get
{
return new PaintEventHandler(Parent_Paint);
}
}
void
Parent_Paint(object sender, PaintEventArgs e)
{
foreach (ControlLabel cl in children)
{
cl.Paint(e.Graphics);
}
}
public
void Invalidate(ControlLabel cl)
{
if (cl.BoundsSet)
Parent.Invalidate(new Region(cl.Bounds));
else
Invalidate();
}
///
<summary>
///
Invalidates all current labels
///

public
void Invalidate()
{
Region r = new Region();
foreach
(ControlLabel cl in children)
{
if (cl.BoundsSet)
{
r.Union(cl.Bounds);
}
else

{
Parent.Invalidate();
return
;
}
}
Parent.Invalidate(r);
}
#endregion
#region Children
List<ControlLabel> children = new List<ControlLabel>();
public
void Add(ControlLabel cl)
{
children.Add(cl);
Invalidate(cl);
}
public
void Remove(ControlLabel cl)
{
children.Remove(cl);
Invalidate(cl);
if
(children.Count == 0)
{
Dispose();
}
}
#endregion
#region tooltip
ToolTip
tt;
public void ShowTooltip(ControlLabel c, Point p)
{
if (tt == null)
{
tt = new ToolTip();
tt.ShowAlways = true;
}
tt.Show(c.LabelInfo.Text, Parent, p.X, p.Y, 5000);
//tt.SetToolTip(c.Control, c.LabelInfo.Text);
}
public
void RemoveTooltip(ControlLabel c)
{
if (tt == null) return;
tt.Hide(Parent);
//tt.SetToolTip(c.Control, null);
}
#endregion
public
void Dispose()
{
Parent.Paint -= ehPaint;
Parent.MouseMove -= new MouseEventHandler(Parent_MouseMove);
Parent.MouseLeave -= new EventHandler(Parent_MouseLeave);
Parent.Click -= new EventHandler(Parent_Click);
Owner.parents.Remove(this);
if
(tt != null)
{
tt.Dispose();
tt = null;
}
}
}


List<ControlLabel> list = new List<ControlLabel>();


#endregion
#region
Default settings
private
LabelInfo defsettings = new LabelInfo(null);
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public
LabelInfo DefaultSettings
{
get { return defsettings; }
}
void
defsettings_SettingChanged(object sender, EventArgs e)
{
foreach (ParentReference p in parents)
{
p.Invalidate();
}
}
public
override ISite Site
{
get
{
return base.Site;
}
set

{
defsettings.DesignTime = value != null && value.DesignMode;
base
.Site = value;
}
}
#endregion
#region
Designer
public
class LabelLayoutEditor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.DropDown;
}
IWindowsFormsEditorService
iw;
public
override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
iw = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
LayoutPicker
f = new LayoutPicker();
if
(value != null)
f.Result = (LabelLayout)value;
if (context.Instance is LabelInfo)
{
LabelInfo inf = context.Instance as LabelInfo;
if
(inf.DefaultSettings != null && inf.DefaultSettings.Layout != LabelLayout.NotSet)
{
f.RevertValue = inf.DefaultSettings.Layout;
}
}
f.TablePanel.LayoutChosen += new EventHandler<LayoutPicker.LayoutPickerTable.LayoutChosenEventArgs>(TablePanel_LayoutChosen);
iw.DropDownControl(f);
return f.Result;
}
void
TablePanel_LayoutChosen(object sender, LayoutPicker.LayoutPickerTable.LayoutChosenEventArgs e)
{
iw.CloseDropDown();
}
}
#endregion
}
[TypeConverter(typeof(LabelInfo.LabelInfoConverter))]
public
class LabelInfo
{
#region Constructor
public
LabelInfo(string Text)
{
this.Text = Text;
Init();
}
#endregion
#region
setings
enum
LabelInfoSettings
{
Font, Padding, Layout, ForeColor, BackColor, Icon, ShowIcon, ShowText, Cursor, BlinkStyle, BlinkRate, BlinkDuration
}
object
[] settings;
void
Init()
{
Array arr = Enum.GetValues(typeof(LabelInfoSettings));
settings = new object[arr.Length];
}
bool designtime;
internal
bool DesignTime
{
get
{
if (owner != null) return owner.DesignTime;
return
designtime;
}
set

{
if (designtime == value) return;
designtime = value;
if
(designtime)
{
if (blink == null)
blink = new BlinkInfo(this);
}
}
}
object
GetSetting(LabelInfoSettings setting)
{
int i = (int)setting;
if
(settings[i] == null)
{
if (owner != null)
return owner.GetSetting(setting);
return GetDefaultSetting(setting);
}
return
settings[i];
}

T GetSetting<T>(LabelInfoSettings setting)
{
object o = GetSetting(setting);
if
(IsNullValue(setting, o))
return GetDefaultSetting<T>(setting);
return (T)o;
}
object
GetDefaultSetting(LabelInfoSettings setting)
{
switch (setting)
{
case LabelInfoSettings.Font:
return DefaultFont;
case LabelInfoSettings.Padding:
return 5;
case LabelInfoSettings.Layout:
return LabelLayout.Default;
case LabelInfoSettings.ForeColor:
return Color.Black;
case LabelInfoSettings.BackColor:
return Color.Empty;
case LabelInfoSettings.Icon:
return DefaultIcon;
case LabelInfoSettings.ShowIcon:
return false;
case LabelInfoSettings.ShowText:
return true;
case LabelInfoSettings.BlinkDuration:
return 5000;
case LabelInfoSettings.BlinkRate:
return 500;
case LabelInfoSettings.BlinkStyle:
return BlinkStyle.None;
}
return
null;
}
T GetDefaultSetting<T>(LabelInfoSettings setting)
{
return (T)GetDefaultSetting(setting);
}
bool
IsNullValue(LabelInfoSettings setting, object value)
{
if (value == null) return true;
if
(value is ValueType)
{
if (setting == LabelInfoSettings.Layout)
return (LabelLayout)value == LabelLayout.NotSet;
if (value is Color)
return ((Color)value).IsEmpty;
if (value is bool)
return false;
return (int)Convert.ChangeType(value, typeof(int)) == 0;
}
return
false;
}
bool
HasSetting(LabelInfoSettings setting)
{
return settings[(int)setting] != null;
}
void
SetSetting(LabelInfoSettings setting, object value)
{
object cur = GetSetting(setting);
if
(IsNullValue(setting, value))
{
if (cur == null) return;
value = null;
}
else
if (value.Equals(cur))
return;
settings[(int)setting] = value;
OnSettingChanged();
}
void
ResetSetting(LabelInfoSettings setting)
{
settings[(int)setting] = owner == null ? GetDefaultSetting(setting) : null;
}
bool
ShouldSerializeSetting(LabelInfoSettings setting)
{
object o = settings[(int)setting];
return
!IsNullValue(setting, o);
}

public event EventHandler SettingChanged;
void
OnSettingChanged()
{
if (SettingChanged != null)
SettingChanged(this, EventArgs.Empty);
}
#endregion
#region Text
private
string text;
[NotifyParentProperty(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public
string Text
{
get { return text; }
set

{
if (value != null && value.Trim().Length == 0)
text = null;
else
text = value;
if (TextChanged != null)
TextChanged(this, EventArgs.Empty);
OnSettingChanged();
}
}
public
override string ToString()
{
return text;
}
public
event EventHandler TextChanged;
#endregion
#region
Layout
[AmbientValue(LabelLayout.NotSet)]
public
LabelLayout Layout
{
get
{
return GetSetting<LabelLayout>(LabelInfoSettings.Layout);
}
set

{
SetSetting(LabelInfoSettings.Layout, value);
}
}
bool ShouldSerializeLayout()
{
return ShouldSerializeSetting(LabelInfoSettings.Layout);
}

#endregion
#region
Color
///
<summary>
///
The font color. NB: setting this value will overwrite the <see cref="ForeBrush"/> setting
///

[AmbientValue(typeof(Color), "Empty")]
public
Color Color
{
get
{
return GetSetting<Color>(LabelInfoSettings.ForeColor);
}
set

{
SetSetting(LabelInfoSettings.ForeColor, value);
brush = null;
}
}
bool
ShouldSerializeColor()
{
return ShouldSerializeSetting(LabelInfoSettings.ForeColor);
}
void
ResetColor()
{
ResetSetting(LabelInfoSettings.ForeColor);
}

[AmbientValue(typeof(Color), "Empty")]
public
Color BackColor
{
get
{
return GetSetting<Color>(LabelInfoSettings.BackColor);
}
set

{
SetSetting(LabelInfoSettings.BackColor, value);
backbrush = null;
}
}
bool
ShouldSerializeBackColor()
{
return ShouldSerializeSetting(LabelInfoSettings.BackColor);
}
void
ResetBackColor()
{
ResetSetting(LabelInfoSettings.BackColor);
}
#endregion
#region
Brush

private Brush brush, backbrush;
///
<summary>
///
In designtime, a solidbrush can be set through a color picker, but in runtime, the more advanced
///
brushes can be set as well
///

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[AmbientValue(null)]
public
Brush ForeBrush
{
get
{
if (brush == null)
{
if (owner == null)
brush = new SolidBrush(Color);
else
return owner.ForeBrush;
}
return
brush;
}
set

{
brush = value;
OnSettingChanged();
}
}
Brush
GetForeBrush()
{
if (brush != null)
return brush;
if (HasSetting(LabelInfoSettings.ForeColor))
return new SolidBrush(Color);
return ForeBrush;
}
///
<summary>
///
In designtime, a solidbrush can be set through a color picker, but in runtime, the more advanced
///
brushes can be set as well
///

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[AmbientValue(null)]
public
Brush BackBrush
{
get
{
if (backbrush == null && owner != null)
return owner.BackBrush;
return backbrush;
}
set

{
backbrush = value;
OnSettingChanged();
}
}
Brush
GetBackBrush()
{
if (backbrush != null)
return backbrush;
if (HasSetting(LabelInfoSettings.BackColor))
return new SolidBrush(BackColor);
if (owner != null)
return owner.GetBackBrush();
return BackBrush;
}
#endregion
#region
Bounds

static bool HasLayout(LabelLayout layoutFlags, LabelLayout CheckBit)
{
return (layoutFlags & CheckBit) == CheckBit;
}
const
int IconPadding = 0;
public
void Paint(Graphics g, RectangleF rect,bool Enabled)
{
Brush b = GetBackBrush();
if
(b != null)
g.FillRectangle(b, rect);
if (ShowIcon)
{
Point p = new Point((int)rect.X, (int)(rect.Y + (rect.Height - Icon.Height) * .5));
if
(Enabled)
g.DrawIcon(Icon, p.X, p.Y);
else
ControlPaint.DrawImageDisabled(g, Icon.ToBitmap(), p.X, p.Y, Color.Empty);

rect.Offset(Icon.Width + IconPadding, 0);
}
if
(ShowText)
{
if (Enabled)
g.DrawString(text, Font, GetForeBrush(), rect);
else
ControlPaint.DrawStringDisabled(g, text, Font, Color.LightGray, rect, null);
}
}
/// <summary>
///
Calculates where the label should be situated on its parent
///

///
<param name="g">
///
<returns>
public
RectangleF GetBounds(Graphics g, Rectangle RelateTo)
{
SizeF
sizeI = ShowIcon ? Icon.Size : new SizeF(),
size = ShowText ? g.MeasureString(text, Font) : new SizeF();
if (!sizeI.IsEmpty)
{
if (size.IsEmpty)
size = sizeI;
else
{
size.Width += IconPadding + sizeI.Width;
if
(sizeI.Height > size.Height) size.Height = sizeI.Height;
}
}
return GetBounds(size, RelateTo, Padding, Layout);
}
public
static RectangleF GetBounds(SizeF size, Rectangle RelateTo, int padding, LabelLayout layout)
{
PointF pf = RelateTo.Location;
int

h = RelateTo.Height,
w = RelateTo.Width;
if
(HasLayout(layout, LabelLayout.Left))
pf.X -= padding + size.Width;
else if (HasLayout(layout, LabelLayout.Right))
pf.X += padding + w;
else if (HasLayout(layout, LabelLayout.Center))
pf.X += (w - size.Width) / 2;
else if (HasLayout(layout, LabelLayout.RightFromCenter))
pf.X += w - size.Width;
if (HasLayout(layout, LabelLayout.Top))
pf.Y -= padding + size.Height;
else if (HasLayout(layout, LabelLayout.Bottom))
pf.Y += h + padding;
else if (HasLayout(layout, LabelLayout.Middle))
pf.Y += (h - size.Height) / 2;
else if (HasLayout(layout, LabelLayout.UnderMiddle))
pf.Y += h - size.Height;

return new RectangleF(pf, size);
}
public
static void PaintSymbol(Graphics g, Rectangle bounds, LabelLayout Layout)
{
float perc = .50f;
RectangleF
rect = bounds;
rect.Width *= perc;
rect.Height *= perc;
rect.X = (bounds.Width - rect.Width) / 2;
rect.Y = (bounds.Height - rect.Height) / 2;
int
PenWidth = 2;
Rectangle
r = Rectangle.Round(rect);
g.DrawRectangle(new Pen(Color.Navy, PenWidth), r);
SizeF
size = rect.Size;
perc = .3f;
size.Width *= perc;
size.Height *= perc;
rect = LabelInfo.GetBounds(size, r, PenWidth + 1, Layout);
r = Rectangle.Round(rect);
g.DrawRectangle(new Pen(Color.YellowGreen, PenWidth), r);
}
#endregion
#region
defaults
private
LabelInfo owner;
///
<summary>
///
For design time support, the owner is set to determine the defaults
///

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public
LabelInfo DefaultSettings
{
get { return owner; }
set
{ owner = value; }
}
#endregion
#region
Font
static
Font DefaultFont = new Font("Times New Roman", 10);
[AmbientValue(null)]
public
Font Font
{
get
{
return GetSetting<Font>(LabelInfoSettings.Font);
}
set

{
SetSetting(LabelInfoSettings.Font, value);
}
}
bool
ShouldSerializeFont()
{
return ShouldSerializeSetting(LabelInfoSettings.Font);
}
void
ResetFont()
{
ResetSetting(LabelInfoSettings.Font);
}
#endregion
#region
Cursor
[AmbientValue(null)]
public
Cursor Cursor
{
get
{
return GetSetting<Cursor>(LabelInfoSettings.Cursor);
}
set

{
SetSetting(LabelInfoSettings.Cursor, value);
}
}
bool
ShouldSerializeCursor()
{
return ShouldSerializeSetting(LabelInfoSettings.Cursor);
}
void
ResetCursor()
{
ResetSetting(LabelInfoSettings.Cursor);
}
#endregion
#region
Padding
///
<summary>
///
The amount of space that should exist between the end of the text and the control
///

[AmbientValue(-1)]
public
int Padding
{
get
{
return GetSetting<int>(LabelInfoSettings.Padding);
}
set

{
SetSetting(LabelInfoSettings.Padding, value);
}
}
bool
ShouldSerializePadding()
{
return ShouldSerializeSetting(LabelInfoSettings.Padding);
}
void
ResetPadding()
{
ResetSetting(LabelInfoSettings.Padding);
}
#endregion
#region
Icon
[System.Runtime.InteropServices.DllImport("shell32.dll")]
extern
static IntPtr ExtractIcon(int hInst, string lpszExeFileName, int nIconIndex);
static
Icon GetIcon(int index)
{
//IntPtr handle = ExtractIcon(0, "user32.dll", index);
IntPtr
handle = ExtractIcon(0, "shell32.dll", index);
Icon
ic = Icon.FromHandle(handle);
Bitmap
bmp = new Bitmap(16, 16);
Graphics
.FromImage(bmp).DrawIcon(ic, new Rectangle(0, 0, bmp.Width, bmp.Height));
return
Icon.FromHandle(bmp.GetHicon());
}
static
Icon DefaultIcon = GetIcon(73);

[AmbientValue(null)]
public
Icon Icon
{
get
{
return GetSetting<Icon>(LabelInfoSettings.Icon);
}
set

{
SetSetting(LabelInfoSettings.Icon, value);
}
}
bool
ShouldSerializeIcon()
{
return ShouldSerializeSetting(LabelInfoSettings.Icon);
}
void
ResetIcon()
{
ResetSetting(LabelInfoSettings.Icon);
}
#endregion
#region
ShowIcon
[AmbientValue(false)]
public
bool ShowIcon
{
get
{
return GetSetting<bool>(LabelInfoSettings.ShowIcon);
}
set

{
SetSetting(LabelInfoSettings.ShowIcon, value);
}
}
bool
ShouldSerializeShowIcon()
{
return ShouldSerializeSetting(LabelInfoSettings.ShowIcon);
}
void
ResetShowIcon()
{
ResetSetting(LabelInfoSettings.ShowIcon);
}
#endregion
#region
ShowText
[AmbientValue(true)]
public
bool ShowText
{
get
{
return GetSetting<bool>(LabelInfoSettings.ShowText);
}
set

{
SetSetting(LabelInfoSettings.ShowText, value);
}
}
bool
ShouldSerializeShowText()
{
return ShouldSerializeSetting(LabelInfoSettings.ShowText);
}
void
ResetShowText()
{
ResetSetting(LabelInfoSettings.ShowText);
}
#endregion
#region
Blink
BlinkInfo blink;
[AmbientValue(null)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public
BlinkInfo Blink
{
get
{
if (blink == null)
{
blink = new BlinkInfo(this);
}
return
blink;
}
}
bool
ShouldSerializeBlink()
{
return
ShouldSerializeSetting(LabelInfoSettings.BlinkStyle)
|| ShouldSerializeSetting(LabelInfoSettings.BlinkRate)
|| ShouldSerializeSetting(LabelInfoSettings.BlinkDuration);
}
[AmbientValue(null)]
[TypeConverter(typeof(ExpandableObjectConverter))]
public
class BlinkInfo
{
public readonly LabelInfo Owner;
public
BlinkInfo(LabelInfo Owner)
{
this.Owner = Owner;
}
#region Style
[AmbientValue(true)]
public
BlinkStyle Style
{
get
{
return Owner.GetSetting<BlinkStyle>(LabelInfoSettings.BlinkStyle);
}
set

{
Owner.SetSetting(LabelInfoSettings.BlinkStyle, value);
}
}
bool
ShouldSerializeStyle()
{
return Owner.ShouldSerializeSetting(LabelInfoSettings.BlinkStyle);
}
void
ResetStyle()
{
Owner.ResetSetting(LabelInfoSettings.BlinkStyle);
}
#endregion
#region
Rate
[AmbientValue(0)]
public
int Rate
{
get
{
return Owner.GetSetting<int>(LabelInfoSettings.BlinkRate);
}
set

{
Owner.SetSetting(LabelInfoSettings.BlinkRate, value);
}
}
bool
ShouldSerializeRate()
{
return Owner.ShouldSerializeSetting(LabelInfoSettings.BlinkRate);
}
void
ResetRate()
{
Owner.ResetSetting(LabelInfoSettings.BlinkRate);
}
#endregion
#region
Duration
[AmbientValue(0)]
public
int Duration
{
get
{
return Owner.GetSetting<int>(LabelInfoSettings.BlinkDuration);
}
set

{
Owner.SetSetting(LabelInfoSettings.BlinkDuration, value);
}
}
bool
ShouldSerializeDuration()
{
return Owner.ShouldSerializeSetting(LabelInfoSettings.BlinkDuration);
}
void
ResetDuration()
{
Owner.ResetSetting(LabelInfoSettings.BlinkDuration);
}
#endregion
public override string ToString()
{
if (Style == BlinkStyle.None) return "None";
string
res = "Blink every " + Rate + " milliseconds";
if
(Style == BlinkStyle.LimitedTime) res += " for a timespan of " + Duration + " milliseconds";
return
res;
}
}
#endregion
#region
Type Converter
public
class DefaultInstanceConverter<T> : ExpandableObjectConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return base.CanConvertTo(context, destinationType)
|| destinationType == typeof(InstanceDescriptor);
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (value is T && destinationType == typeof(InstanceDescriptor))
{
return GetInstanceDescriptor((T)value);
}
return
base.ConvertTo(context, culture, value, destinationType);
}
protected virtual InstanceDescriptor GetInstanceDescriptor(T obj)
{
ConstructorInfo ci = obj.GetType().GetConstructor(Type.EmptyTypes);
if
(ci == null)
throw new Exception(obj.GetType().FullName + " does not contain parameterless constructors");
return new InstanceDescriptor(ci, null, false);
}
}
public class LabelInfoConverter : DefaultInstanceConverter<LabelInfo>
{
protected override InstanceDescriptor GetInstanceDescriptor(LabelInfo li)
{
if (string.IsNullOrEmpty(li.text)) return null;
bool
isComplete = true;
foreach
(LabelInfoSettings s in Enum.GetValues(typeof(LabelInfoSettings)))
{
if (li.ShouldSerializeSetting(s))
{
isComplete = false;
break
;
}
}
return
new InstanceDescriptor(
typeof(LabelInfo).GetConstructor(new Type[] { typeof(string) }),
new object[] { li.text }, isComplete);
}

public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return base.CanConvertFrom(context, sourceType)
|| sourceType == typeof(string);
}
public
override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
Control c = context.Instance as Control;
if
(c != null && value is string)
{
string txt = value as string;
LabelInfo
li = context.PropertyDescriptor.GetValue(c) as LabelInfo;
if
(li == null)
{
li = new LabelInfo(txt);
context.PropertyDescriptor.SetValue(c, li);
}
else
li.Text = txt;
return li;
}
return
base.ConvertFrom(context, culture, value);
}
public
override bool IsValid(ITypeDescriptorContext context, object value)
{
LabelInfo inf = value as LabelInfo;
if
(inf == null) return false;
return
!string.IsNullOrEmpty(inf.text);
}
}
#endregion
}

public enum BlinkStyle
{
None,
Permanent,
LimitedTime
}
[Flags]
[Editor(typeof(LabelProvider.LabelLayoutEditor), typeof(UITypeEditor))]
public
enum LabelLayout
{
NotSet = 0,
Top = 1,
AboveMiddle = 2,
Middle = 4,
UnderMiddle = 8,
Bottom = 16,
Left = 32,
LeftFromCenter = 64,
Center = 128,
RightFromCenter = 256,
Right = 512,
Default = LabelLayout.Left | LabelLayout.AboveMiddle
}
#region
LayoutPicker
///
<summary>
///
Control to select a <see cref="LabelLayout"/>. Used in design time for LabelLayout properties
///

[ToolboxItem(false)]
public
class LayoutPicker : UserControl
{
public LayoutPicker()
{
table.Dock = DockStyle.Fill;
Controls.Add(table);
}
LayoutPickerTable
table = new LayoutPickerTable();
[Browsable(false)]
public
LayoutPickerTable TablePanel
{
get
{
return table;
}
}
[DefaultValue(LabelLayout.Default)]
public
LabelLayout Result
{
get { return table.Result; }
set
{ table.Result = value; }
}
LabelLayout
revertvalue;
[DefaultValue(LabelLayout.NotSet)]
public
LabelLayout RevertValue
{
get { return revertvalue; }
set

{
revertvalue = value;
if
((revertvalue == LabelLayout.NotSet) == (lblRevert == null))
return;
if (lblRevert == null)
{
lblRevert = new Label();
lblRevert.ForeColor = Color.Blue;
lblRevert.Dock = DockStyle.Bottom;
lblRevert.Font = new Font(lblRevert.Font, FontStyle.Underline);
lblRevert.Click += new EventHandler(lblRevert_Click);
lblRevert.Text = "Set to parent";
lblRevert.TextAlign = ContentAlignment.MiddleCenter;
lblRevert.Cursor = Cursors.Hand;
Controls.Add(lblRevert);
}
else

{
lblRevert.Dispose();
lblRevert = null;
}
}
}
void
lblRevert_Click(object sender, EventArgs e)
{
Result = revertvalue;
table.InvokeLayoutChosen(false);
}
Label
lblRevert;
public
class LayoutPickerTable : TableLayoutPanel
{
Label MiddleControl = new Label();
int
cnt = 5;
public
LayoutPickerTable()
: this(LabelLayout.Default)
{
}
public
LayoutPickerTable(LabelLayout Current)
{
res = startvalue = Current;
RowCount = ColumnCount = cnt;
float
perc = 100 / cnt;
for
(int i = 0; i < cnt; i++)
{
ColumnStyles.Add(new ColumnStyle(SizeType.Percent, perc));
RowStyles.Add(new RowStyle(SizeType.Percent, perc));
}

for (int i = 0; i < cnt; i++)
{
AddButton(i, 0);
AddButton(i, cnt - 1);
if
(i > 0 && i < cnt - 1)
{
AddButton(0, i);
AddButton(cnt - 1, i);
}
}

MiddleControl.Dock = DockStyle.Fill;
MiddleControl.BackColor = Color.Gray;
MiddleControl.TextAlign = ContentAlignment.BottomCenter;
Controls.Add(MiddleControl, 1, 1);
SetRowSpan(MiddleControl, 3);
SetColumnSpan(MiddleControl, 3);
MiddleControl.Paint += new PaintEventHandler(MiddleControl_Paint);
}
void
Navigate(int Col, int Row, LayoutButton b)
{
TableLayoutPanelCellPosition cell = GetCellPosition(b);
Navigate(Col, Row, cell.Column, cell.Row);
}
void
Navigate(int Col, int Row, int curcol, int currow)
{
currow = Math.Max(0, Math.Min(cnt - 1, Row + currow));
curcol = Math.Max(0, Math.Min(cnt - 1, Col + curcol));
LayoutButton
b = GetControlFromPosition(curcol, currow) as LayoutButton;
if
(b == null)
{
Navigate(Col, Row, curcol, currow);
}
else
b.Focus();
}
protected
override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Cancel)
{
Cancel();
}
}
void
Cancel()
{
Result = startvalue;
OnLayoutChosen(true);
}

void MiddleControl_Paint(object sender, PaintEventArgs e)
{
LabelInfo.PaintSymbol(e.Graphics, MiddleControl.Bounds, res);
}
LabelLayout
startvalue;
class
LayoutButton : Button
{
public new readonly LabelLayout Layout;
public
LayoutButton(LabelLayout Layout)
{
this.Layout = Layout;
}
public
override Color BackColor
{
get
{
if (Selected)
return Color.Gray;
return Control.DefaultBackColor;
}
set

{
//base.BackColor = value;
}
}
public
bool Selected
{
get
{
if (Parent == null) return false;
return
Owner.res == Layout;
}
}
LayoutPickerTable
Owner
{
get { return Parent as LayoutPickerTable; }
}
protected
override bool IsInputKey(Keys keyData)
{
switch (keyData)
{
case Keys.Down:
Owner.Navigate(0, 1, this);
break
;
case Keys.Up:
Owner.Navigate(0, -1, this);
break
;
case Keys.Left:
Owner.Navigate(-1, 0, this);
break
;
case Keys.Right:
Owner.Navigate(1, 0, this);
break
;
default:
return base.IsInputKey(keyData);
}
return
true;
}
}


void AddButton(int Column, int Row)
{
LabelLayout lay = GetLayout(Column, Row);
LayoutButton
b = new LayoutButton(lay);
b.Dock = DockStyle.Fill;
b.TabStop = false;
b.Click += new EventHandler(b_Click);
Controls.Add(b, Column, Row);
buttons.Add(b);
}

LabelLayout GetLayout(int col, int row)
{
res = 0;
if
(col == 0)
res |= LabelLayout.Left;
else if (col == cnt - 1)
res |= LabelLayout.Right;
else
res |= (LabelLayout)((int)LabelLayout.LeftFromCenter << col - 1);
if (row == 0)
res |= LabelLayout.Top;
else if (row == cnt - 1)
res |= LabelLayout.Bottom;
else
res |= (LabelLayout)((int)LabelLayout.AboveMiddle << row - 1);
return res;
}
void
b_Click(object sender, EventArgs e)
{
LayoutButton b = sender as LayoutButton;
Result = b.Layout;
OnLayoutChosen(false);
}
void
OnLayoutChosen(bool Cancelled)
{
if (LayoutChosen != null)
LayoutChosen(this, new LayoutChosenEventArgs(Result, Cancelled));
}
public
void InvokeLayoutChosen(bool Cancelled)
{
OnLayoutChosen(Cancelled);
}
public
class LayoutChosenEventArgs : EventArgs
{
public readonly bool Cancelled;
public
readonly LabelLayout Result;
public
LayoutChosenEventArgs(LabelLayout Result, bool Cancelled)
{
this.Result = Result;
this
.Cancelled = Cancelled;
}
}
public
event EventHandler<LayoutChosenEventArgs> LayoutChosen;
LabelLayout
res;
List
<LayoutButton> buttons = new List<LayoutButton>();
public
LabelLayout Result
{
get { return res; }
set

{
LayoutButton b = GetSelectedButton();
res = value;
MiddleControl.Text = res.ToString();
if
(b != null)
b.Invalidate();
if ((b = GetSelectedButton()) != null)
b.Invalidate();
}
}

LayoutButton GetSelectedButton()
{
foreach (LayoutButton btn in buttons)
{
if (btn.Selected)
return btn;
}
return
null;
}
}
}
#endregion

}
. . .
posted @ 4:33 AM

This is just a summary of how I overlooked this silly little thing that could have made my designer workings so much easier in the past: how to use the InstanceDescriptor (returned by a TypeConverter) to create a variable inside InitializeComponent and set the properties of that variable, instead of having to set everything inside the constructor. All that was needed, is to set that last parameter (isComplete) to false.

It's so incredible simple and effective, I still can't believe how I could have missed it all that time. Perhaps some of you out there have always been using it and wonder the same thing, but then again perhaps there are others who missed this in the same way and can be spared the same search and use of unneeded CodeDom solutions.


For a long time now, I've been wanting properties to be able to persist in the designer, where the property in this case is an instance. The easy way is to set the designerserializationvisibility to Content, but then you would always need an instance and that doesn't work when using properties with the IExtenderProvider.
In comes the InstanceDescriptor. Creating one is fairly straight forward: by assigning a custom typeconverter to the type used by the property and enabling that typeconverter to return an InstanceDescriptor all properties using that type can be used (how the property is created in the designer is another thing. For example you can have the typeconverter create one automatically when text is typed by implement a convertFrom(string) )
However, I often ended up creating a bunch of constructors to suit the different scenarios without having to include all parameters all the time.
What I really wanted, was the designer to create a variable inside InitializeComponent, and use the default serialization on the properties of that variable. But I never could find any way besides using custom serialization with the CodeDomSerializer.
Then, one day, while trying to getting an InstanceDescriptor to work with a more dynamic construction by using a parameter array of settings in the constructor of my class, I tried setting the isComplete parameter to false. That didn't work for that, (I had to use an array of setting instances as parameter instead of loose setting instances), suddenly a variable appeared inside the InitializeComponent. Not only that, the properties that had to be saved, were indeed present.
Problem was, at that time I changed around several things and feared it was a fluke inside the IDE (yes, yes, me of little faith. I am indeed sorry), until I reviewed the changes and realized the power of that one parameter. By setting that one parameter to false, my class had become so much easier to maintain and could be an effective solution to so much more other past work arounds, that I just had to write this down here. A long story for such a thing and not likely to be read by much people, but at least it will be a reminder to self to keep re-exploring after the re-exploring of the exploring.

posted @ 1:29 AM