Implementing a data converter component
From OPC Labs Knowledge Base
using System;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Runtime.Serialization;
using System.Security;
using OpcLabs.BaseLib.Data;
using OpcLabs.BaseLib.Extensions;
using OpcLabs.BaseLib.Runtime.Serialization.Extensions;
using OpcLabs.BaseLib.Utilities;
using OpcLabs.BaseLib.Widgets;
namespace OpcLabs.BaseLib.Components
{
/// <summary>
/// Assigns different colors based on status information.
/// </summary>
[Category("Data Converters")]
[Description("Assigns different colors based on status information.")] // used e.g. in Toolbox
[Serializable]
[ToolboxBitmap(typeof(StatusToColorConverter))]
public sealed class StatusToColorConverter
: Widget
, ICloneable
, IDataConverter
{
#region Public Constructors
/// <overloads>
/// <summary>
/// Initializes an instance of the <see cref="StatusToColorConverter"/> class.
/// </summary>
/// </overloads>
public StatusToColorConverter()
{
Construct();
}
/// <param name="unknownColor">The color returned for Unknown status.</param>
/// <param name="normalColor">The color returned for Normal status.</param>
/// <param name="warningColor">The color returned for Warning status.</param>
/// <param name="errorColor">The color returned for Error status.</param>
public StatusToColorConverter(Color unknownColor, Color normalColor, Color warningColor, Color errorColor)
: this()
{
UnknownColor = unknownColor;
NormalColor = normalColor;
WarningColor = warningColor;
ErrorColor = errorColor;
}
/// <summary>
/// Initializes an instance of the <see cref="StatusToColorConverter"/> class with Designer support.
/// </summary>
/// <param name="container">The component container to which the new instance will be added.</param>
public StatusToColorConverter(IContainer container)
{
Construct();
container.Add(this); // place it *after* the Construct, as the VS designer can call us from here
}
/// <summary>Initializes a new instance of the class, copying values from a given object.</summary>
/// <param name="statusToColorConverter">The object to be copied from.</param>
public StatusToColorConverter(StatusToColorConverter statusToColorConverter)
: base(statusToColorConverter)
{
UnknownColor = statusToColorConverter.UnknownColor;
NormalColor = statusToColorConverter.NormalColor;
WarningColor = statusToColorConverter.WarningColor;
ErrorColor = statusToColorConverter.ErrorColor;
}
#endregion
#region Private Constructors
/// <summary>Initializes a new instance of the class with serialized data.</summary>
/// <param name="info">The SerializationInfo that holds the serialized object data.</param>
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.
/// </param>
private StatusToColorConverter(SerializationInfo info, StreamingContext context)
: base(info, context)
{
UnknownColor = info.GetObject<Color>("UnknownColor");
NormalColor = info.GetObject<Color>("NormalColor");
WarningColor = info.GetObject<Color>("WarningColor");
ErrorColor = info.GetObject<Color>("ErrorColor");
}
#endregion
#region Static Public Properties
/// <summary>
/// The default Error color.
/// </summary>
static public Color DefaultErrorColor => Color.Red;
/// <summary>
/// The default Normal color.
/// </summary>
static public Color DefaultNormalColor => Color.White;
/// <summary>
/// The default Unknown color.
/// </summary>
static public Color DefaultUnknownColor => Color.Gray;
/// <summary>
/// The default Warning color.
/// </summary>
static public Color DefaultWarningColor => Color.Yellow;
#endregion
#region Static Public Methods
/// <summary>Determines whether the two objects are equal.</summary>
/// <param name="left">First object to be compared.</param>
/// <param name="right">Second object to be compared.</param>
/// <returns><c>True</c> if the objects are equal; <c>false</c> otherwise.</returns>
static public bool operator ==(StatusToColorConverter left, StatusToColorConverter right)
{
return Equals(left, right);
}
/// <summary>Determines whether the two objects are not equal.</summary>
/// <param name="left">First object to be compared.</param>
/// <param name="right">Second object to be compared.</param>
/// <returns><c>True</c> if the objects are not equal; <c>false</c> if they are equal.</returns>
static public bool operator !=(StatusToColorConverter left, StatusToColorConverter right)
{
return !Equals(left, right);
}
#endregion
#region Public Properties
/// <summary>
/// The color returned for Error status.
/// </summary>
[Description("The color returned for Error status.")]
public Color ErrorColor { get; set; }
/// <summary>
/// The color returned for Normal status.
/// </summary>
[Description("The color returned for Normal status.")]
public Color NormalColor { get; set; }
/// <summary>
/// The color returned for Unknown status.
/// </summary>
[Description("The color returned for Unknown status.")]
public Color UnknownColor { get; set; }
/// <summary>
/// The color returned for Warning status.
/// </summary>
[Description("The color returned for Warning status.")]
public Color WarningColor { get; set; }
#endregion
#region Public Methods
/// <summary>
/// Determines whether the specified <see cref="StatusToColorConverter"/> is equal to the current object.
/// </summary>
/// <param name="other">The <see cref="StatusToColorConverter"/> to compare with the current object.</param>
/// <returns><c>true</c> if the specified object is equal to the current object; otherwise, <c>false</c>.</returns>
public bool Equals(StatusToColorConverter other)
{
if (other == null)
return false;
return
base.Equals(other) &&
ErrorColor.Equals(other.ErrorColor) &&
NormalColor.Equals(other.NormalColor) &&
UnknownColor.Equals(other.UnknownColor) &&
WarningColor.Equals(other.WarningColor);
}
#endregion
#region ICloneable Members
object ICloneable.Clone()
{
return Clone();
}
#endregion
#region IDataConverter Members
/// <inheritdoc cref="IDataConverter.Convert"/>
/// <exception cref="ArgumentException">When the value is not of any of the supported types.</exception>
/// <remarks>
/// Values are converted to colors according to following rules:
/// <ul>
/// <li><c>null</c> converts to <see cref="NormalColor"/>.</li>
/// <li>A <see cref="StatusInfo.Normal"/> converts to <see cref="NormalColor"/>,
/// a <see cref="StatusInfo.Warning"/> converts to <see cref="WarningColor"/>,
/// a <see cref="StatusInfo.Error"/> converts to <see cref="ErrorColor"/>, and
/// a <see cref="StatusInfo.Unknown"/> converts to <see cref="UnknownColor"/>.</li>
/// <li>An <see cref="Exception"/>-typed value converts to <see cref="WarningColor"/>.</li>
/// <li>Empty strings convert to <see cref="NormalColor"/>,
/// non-empty strings convert to <see cref="ErrorColor"/>.</li>
/// <li>Zero integers convert to <see cref="NormalColor"/>,
/// positive integers convert to <see cref="WarningColor"/>,
/// negative integers convert to <see cref="ErrorColor"/>.</li>
/// <li>A <c>true</c> Boolean converts to <see cref="NormalColor"/>,
/// a <c>false</c> Boolean converts to <see cref="ErrorColor"/>.</li>
/// </ul>
/// </remarks>
public object Convert(
object value, object parameter, CultureInfo culture)
{
Color color = ConvertToColor(value);
return color;
}
/// <inheritdoc cref="IDataConverter.ConvertBack"/>
/// <exception cref="NotSupportedException">Thrown.</exception>
/// <remarks>Conversion in this direction is not supported.</remarks>
public object ConvertBack(object value, object parameter, CultureInfo culture)
{
throw new NotSupportedException("Inverse conversion is not supported by StatusToColorConverter.");
}
#endregion
#region ISerializable Members
/// <inheritdoc cref="ISerializable.GetObjectData"/>
[SecurityCritical]
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("UnknownColor", UnknownColor);
info.AddValue("NormalColor", NormalColor);
info.AddValue("WarningColor", WarningColor);
info.AddValue("ErrorColor", ErrorColor);
}
#endregion
#region Protected Methods
/// <inheritdoc cref="Widget.Equals2"/>
protected override bool Equals2(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != /*this.*/GetType()) return false;
return Equals((StatusToColorConverter)obj);
}
/// <inheritdoc cref="Widget.GetHashCode2"/>
protected override int GetHashCode2()
{
unchecked
{
var hashCode = base.GetHashCode2();
hashCode = (hashCode * 397) ^ ErrorColor.GetHashCode();
hashCode = (hashCode * 397) ^ NormalColor.GetHashCode();
hashCode = (hashCode * 397) ^ UnknownColor.GetHashCode();
hashCode = (hashCode * 397) ^ WarningColor.GetHashCode();
return hashCode;
}
}
#endregion
#region Private Methods
/// <summary>
/// Creates a new object that is a copy of the current instance.
/// </summary>
/// <returns>A new object that is a copy of this instance.</returns>
private StatusToColorConverter Clone()
{
return new StatusToColorConverter(this);
}
private void Construct()
{
ResetErrorColor();
ResetNormalColor();
ResetUnknownColor();
ResetWarningColor();
}
private Color ConvertToColor(object value)
{
if (value == null)
return NormalColor; // this is actually required so that we can test against Exception properties
{
if (value is StatusInfo)
return DetermineColor((StatusInfo)value);
}
{
var exception = value as Exception;
if (exception != null)
return DetermineColor(exception);
}
{
var s = value as string;
if (s != null)
return DetermineColor(s);
}
{
if (value is Byte)
return DetermineColor(Math.Sign((Byte) value));
if (value is SByte)
return DetermineColor(Math.Sign((SByte)value));
if (value is UInt16)
return DetermineColor(Math.Sign((UInt16)value));
if (value is Int16)
return DetermineColor(Math.Sign((Int16)value));
if (value is UInt32)
return DetermineColor(Math.Sign((UInt32)value));
if (value is Int32)
return DetermineColor(Math.Sign((Int32)value));
if (value is UInt64)
return DetermineColor(MathUtilities.Sign((UInt64)value));
if (value is Int64)
return DetermineColor(Math.Sign((Int64)value));
if (value is UIntPtr)
return DetermineColor(MathUtilities.Sign((UIntPtr)value));
if (value is IntPtr)
return DetermineColor(MathUtilities.Sign((IntPtr)value));
}
{
if (value is bool)
return DetermineColor((bool) value);
}
throw new ArgumentException(
String.Format(CultureInfo.CurrentCulture,
"We do not know how to convert a status of type '{0}' to a color.",
value.GetType()));
}
private Color DetermineColor(bool b)
{
return b ? NormalColor : ErrorColor;
}
private Color DetermineColor(
Exception exception)
{
return ErrorColor;
}
private Color DetermineColor(int sign)
{
switch (sign)
{
case -1:
return ErrorColor;
case 0:
return NormalColor;
case +1:
return WarningColor;
}
return UnknownColor;
}
private Color DetermineColor(StatusInfo statusInfo)
{
switch (statusInfo)
{
case StatusInfo.Normal:
return NormalColor;
case StatusInfo.Warning:
return WarningColor;
case StatusInfo.Error:
return ErrorColor;
}
return UnknownColor;
}
private Color DetermineColor(string s)
{
return DetermineColor(s.IsEmpty());
}
/// <summary>
/// Reset the <see cref="ErrorColor"/> property to its default value.
/// </summary>
// The method name is of the essence - for the designer, it must match the name of the property
private void ResetErrorColor()
{
ErrorColor = DefaultErrorColor;
}
/// <summary>
/// Reset the <see cref="NormalColor"/> property to its default value.
/// </summary>
// The method name is of the essence - for the designer, it must match the name of the property
private void ResetNormalColor()
{
NormalColor = DefaultNormalColor;
}
/// <summary>
/// Reset the <see cref="UnknownColor"/> property to its default value.
/// </summary>
// The method name is of the essence - for the designer, it must match the name of the property
private void ResetUnknownColor()
{
UnknownColor = DefaultUnknownColor;
}
/// <summary>
/// Reset the <see cref="WarningColor"/> property to its default value.
/// </summary>
// The method name is of the essence - for the designer, it must match the name of the property
private void ResetWarningColor()
{
WarningColor = DefaultWarningColor;
}
/// <summary>
/// Determines whether the <see cref="ErrorColor"/> property is equal to its default value.
/// </summary>
/// <returns><c>true</c> if the property has changed from its default value; <c>false</c> otherwise.</returns>
// The method name is of the essence - for the designer, it must match the name of the property
private bool ShouldSerializeErrorColor()
{
return ErrorColor != DefaultErrorColor;
}
/// <summary>
/// Determines whether the <see cref="NormalColor"/> property is equal to its default value.
/// </summary>
/// <returns><c>true</c> if the property has changed from its default value; <c>false</c> otherwise.</returns>
// The method name is of the essence - for the designer, it must match the name of the property
private bool ShouldSerializeNormalColor()
{
return NormalColor != DefaultNormalColor;
}
/// <summary>
/// Determines whether the <see cref="UnknownColor"/> property is equal to its default value.
/// </summary>
/// <returns><c>true</c> if the property has changed from its default value; <c>false</c> otherwise.</returns>
// The method name is of the essence - for the designer, it must match the name of the property
private bool ShouldSerializeUnknownColor()
{
return UnknownColor != DefaultUnknownColor;
}
/// <summary>
/// Determines whether the <see cref="WarningColor"/> property is equal to its default value.
/// </summary>
/// <returns><c>true</c> if the property has changed from its default value; <c>false</c> otherwise.</returns>
// The method name is of the essence - for the designer, it must match the name of the property
private bool ShouldSerializeWarningColor()
{
return WarningColor != DefaultWarningColor;
}
#endregion
}
}