I've got a custom control, "ButtonControl", which contains a property of a custom class "Corners". This class has four properties of the custom class "Corner", which has a value for the angle and the radius of a corner (both type int). Whenever I rebuild the solution, the properties window in the designer shows the message "Object does not match target type." for each "Corner" property of the "Corners" property. When I reopen the solution everything works fine.
I've added the code to github: https://github.com/Willunsnix/ASD-Root
Can somebody please point me in the right direction?
Below the code for Corner.cs
using System;
using System.Collections;
using System.ComponentModel;
using System.Globalization;
namespace ASD.Drawing.Properties
{
[TypeConverter(typeof(CornerConverter))]
public class Corner : IEquatable<Corner>
{
#region Private Members
private const int DEFAULT_ANGLE = 90;
private const int DEFAULT_RADIUS = 0;
private int _angle = DEFAULT_ANGLE;
private int _radius = DEFAULT_RADIUS;
private bool ShouldSerializeAngle()
{
return _angle != DEFAULT_ANGLE;
}
private void ResetAngle()
{
Angle = DEFAULT_ANGLE;
}
private bool ShouldSerializeRadius()
{
return _radius != DEFAULT_RADIUS;
}
private void ResetRadius()
{
Radius = DEFAULT_RADIUS;
}
#endregion Private Members
#region Constructors
public Corner()
{
Angle = DEFAULT_RADIUS;
Radius = 0;
}
public Corner(int angle, int radius)
{
Angle = angle > 0 ? angle : DEFAULT_RADIUS;
Radius = radius > 0 ? radius : 0;
}
#endregion Constructors
#region Properties
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[RefreshProperties(RefreshProperties.Repaint)]
[NotifyParentProperty(true)]
[Description("Angle of the corner.")]
public int Angle
{
get { return _angle; }
set { _angle = value; }
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[RefreshProperties(RefreshProperties.Repaint)]
[NotifyParentProperty(true)]
[Description("Radius of the corner")]
public int Radius
{
get { return _radius; }
set { _radius = value; }
}
#endregion Properties
#region Methods
public override bool Equals(object obj) => this.Equals(obj as Corner);
public bool Equals(Corner p)
{
if (p is null)
{
return false;
}
if (Object.ReferenceEquals(this, p))
{
return true;
}
if (this.GetType() != p.GetType())
{
return false;
}
return (Angle == p.Angle) && (Radius == p.Radius);
}
public override int GetHashCode() => (Angle, Radius).GetHashCode();
public static bool operator ==(Corner lhs, Corner rhs)
{
if (lhs is null)
{
if (rhs is null)
{
return true;
}
return false;
}
return lhs.Equals(rhs);
}
public static bool operator !=(Corner lhs, Corner rhs) => !(lhs == rhs);
public override string ToString()
{
return $"[{Angle.ToString().Trim()}:{Radius.ToString().Trim()}]";
}
#endregion Methods
public partial class CornerConverter : ExpandableObjectConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (ReferenceEquals(sourceType, typeof(string)))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (ReferenceEquals(value.GetType(), typeof(string)))
{
var stringArray = value.ToString().Replace("[", "").Replace("]", "").Split(':');
if (stringArray.Length >= 2)
{
if (int.TryParse(stringArray[0], out int angle) && int.TryParse(stringArray[1], out int radius))
{
return new Corner(angle, radius);
}
}
throw new FormatException();
}
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
return $"[{((Corner)value).Angle}:{((Corner)value).Radius}]";
}
return base.ConvertTo(context, culture, value, destinationType);
}
public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
{
return true;
}
public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
{
return new Corner((int)propertyValues["Angle"], (int)propertyValues["Radius"]);
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
return TypeDescriptor.GetProperties(typeof(Corner));
}
}
}
}
Next the code for Corners.cs:
using System;
using System.Collections;
using System.ComponentModel;
using System.Globalization;
namespace ASD.Drawing.Properties
{
[TypeConverter(typeof(Converter))]
public class Corners
{
#region Private Members
private Corner _topLeft = new Corner();
private Corner _topRight = new Corner();
private Corner _bottomLeft = new Corner();
private Corner _bottomRight = new Corner();
private Corner GetCornerFromString(string cornerString)
{
var cornerArray = cornerString.Trim().Replace("[", "").Replace("]", "").Split(':');
if (cornerArray.Length >= 2)
{
if (int.TryParse(cornerArray[0], out int angle) && int.TryParse(cornerArray[1], out int radius))
{
return new Corner(angle, radius);
}
}
throw new FormatException();
}
private bool ShouldSerializeTopLeft()
{
return _topLeft != new Corner();
}
private void ResetTopLeft()
{
TopLeft = new Corner();
}
private bool ShouldSerializeTopRight()
{
return _topRight != new Corner();
}
private void ResetTopRight()
{
TopRight = new Corner();
}
private bool ShouldSerializeBottomRight()
{
return _bottomRight != new Corner();
}
private void ResetBottomRight()
{
BottomRight = new Corner();
}
private bool ShouldSerializeBottomLeft()
{
return _bottomLeft != new Corner();
}
private void ResetBottomLeft()
{
BottomLeft = new Corner();
}
#endregion Private Members
#region Constructors
public Corners()
{
TopLeft = new Corner();
TopRight = new Corner();
BottomLeft = new Corner();
BottomRight = new Corner();
}
public Corners(Corner topLeft, Corner topRight, Corner bottomRight, Corner bottomLeft)
{
TopLeft = topLeft;
TopRight = topRight;
BottomLeft = bottomRight;
BottomRight = bottomLeft;
}
public Corners(string allCornersText)
{
string[] cornerTextArray = allCornersText.Split(':');
if (cornerTextArray.Length >= 4)
{
Corner[] corners = new Corner[4];
for (int i = 0; i < 4; i++) { corners[i] = GetCornerFromString(cornerTextArray[i]); }
TopLeft = corners[0];
TopRight = corners[1];
BottomLeft = corners[2];
BottomRight = corners[3];
}
else
{
throw new FormatException();
}
}
public Corners(string topLeft, string topRight, string bottomRight, string bottomLeft)
{
TopLeft = GetCornerFromString(topLeft);
TopRight = GetCornerFromString(topRight);
BottomLeft = GetCornerFromString(bottomRight);
BottomRight = GetCornerFromString(bottomLeft);
}
#endregion Constructors
#region Properties
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[RefreshProperties(RefreshProperties.Repaint)]
[NotifyParentProperty(true)]
[Description("Border settings for top left corner")]
public Corner TopLeft
{
get { return _topLeft; }
set { _topLeft = value; }
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[RefreshProperties(RefreshProperties.Repaint)]
[NotifyParentProperty(true)]
[Description("Border settings for top right corner")]
public Corner TopRight
{
get { return _topRight; }
set { _topRight = value; }
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[RefreshProperties(RefreshProperties.Repaint)]
[NotifyParentProperty(true)]
[Description("Border settings for bottom right corner")]
public Corner BottomRight
{
get { return _bottomRight; }
set { _bottomRight = value; }
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[RefreshProperties(RefreshProperties.Repaint)]
[NotifyParentProperty(true)]
[Description("Border settings for bottom left corner")]
public Corner BottomLeft
{
get { return _bottomLeft; }
set { _bottomLeft = value; }
}
#endregion Properties
#region Methods
public void CopyAngles(Corners source)
{
_topLeft.Angle = source.TopLeft.Angle;
_topRight.Angle = source.TopRight.Angle;
_bottomLeft.Angle = source.BottomLeft.Angle;
_bottomRight.Angle = source.BottomRight.Angle;
}
public void CopyRadiuses(Corners source)
{
_topLeft.Radius = source.TopLeft.Radius;
_topRight.Radius = source.TopRight.Radius;
_bottomLeft.Radius = source.BottomLeft.Radius;
_bottomRight.Radius = source.BottomRight.Radius;
}
public void ResetAngles()
{
_topLeft.Angle = 90;
_topRight.Angle = 90;
_bottomLeft.Angle = 90;
_bottomRight.Angle = 90;
}
public void ResetCorners()
{
_topLeft = new Corner();
_topRight = new Corner();
_bottomLeft = new Corner();
_bottomRight = new Corner();
}
public void ResetRadiuses()
{
_topLeft.Radius = 0;
_topRight.Radius = 0;
_bottomLeft.Radius = 0;
_bottomRight.Radius = 0;
}
public bool ValidAngles()
{
return (_topLeft.Angle + _topRight.Angle + _bottomLeft.Angle + _bottomRight.Angle) == 360;
}
public override string ToString()
{
return $"{TopLeft.ToString().Trim()};{TopRight.ToString().Trim()};{BottomLeft.ToString().Trim()};{BottomRight.ToString().Trim()}";
}
#endregion Methods
public partial class Converter : ExpandableObjectConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (ReferenceEquals(sourceType, typeof(string)))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (ReferenceEquals(value.GetType(), typeof(string)))
{
var stringArray = value.ToString().Split(';');
bool isValid = true;
string message = string.Empty;
if (stringArray.Length >= 4)
{
Corner[] corners = new Corner[4];
for (int i = 0; i < 4 && isValid; i++)
{
var cornerString = stringArray[i].Split(':');
if (int.TryParse(cornerString[0], out int angle) && int.TryParse(cornerString[1], out int radius))
{
corners[i] = new Corner(angle, radius);
}
else
{
isValid = false;
message = "Parsing of the corner[" + i.ToString() + "] failed!";
}
}
if (isValid) return new Corners(corners[0], corners[1], corners[2], corners[3]);
}
else message = "Not enough parameters in string value, or wrong separator used in splitting the value!";
throw new FormatException(message);
}
return base.ConvertFrom(context, culture, value);
}
public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
{
return true;
}
public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
{
return base.CreateInstance(context, propertyValues);
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
return TypeDescriptor.GetProperties(typeof(Corners));
}
}
}
}
And finally the code for ButtonControl.cs:
using ASD.Drawing.Enums;
using ASD.Drawing.Properties;
using ASD.Forms.Base;
using ASD.Forms.Interfaces;
using ASD.Global.Helpers;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace ASD.Forms.Controls
{
[ToolboxItem(true)]
public partial class ButtonControl : BaseControl
{
#region Enumerables
public enum ButtonState
{
Normal = 0,
Pressed = 1
}
#endregion Enumerables
#region Private Members
private ButtonState _buttonState = ButtonState.Normal;
private Color _buttonColor = Color.Red;
private string _caption = String.Empty;
private Timer _repeatTimer = null;
private bool _repeatState = false;
private int _startRepeatInterval = 500;
private int _repeatInterval = 100;
private ShapenStyle _buttonStyle = ShapenStyle.Rectangular;
private Corners _corners = new Corners();
private ShapeOrientation _orientation = ShapeOrientation.Horizontal;
private void SetPropertyAttributes()
{
switch (_buttonStyle)
{
case ShapenStyle.Circular:
case ShapenStyle.Elliptical:
PropertyHandler.SetPropertyVisibility(this, "Corners", false);
PropertyHandler.SetPropertyVisibility(this, "Orientation", false);
break;
case ShapenStyle.Parallellogram:
PropertyHandler.SetPropertyVisibility(this, "Corners", true);
PropertyHandler.SetPropertyVisibility(this, "Orientation", true);
PropertyHandler.SetPropertyVisibilityByPath(this, "Corners.TopLeft", false);
break;
case ShapenStyle.Rectangular:
default:
PropertyHandler.SetPropertyVisibility(this, "Corners", true);
PropertyHandler.SetPropertyVisibility(this, "Orientation", false);
PropertyHandler.SetPropertyVisibilityByPath(this, "Corners.TopLeft", true);
break;
}
}
private void SetTheCorners(Corners value)
{
switch (_buttonStyle)
{
case ShapenStyle.Parallellogram:
_corners.CopyRadiuses(value);
if (_corners.TopRight.Angle != value.TopLeft.Angle)
{
_corners.BottomRight.Angle = _corners.TopLeft.Angle;
_corners.TopRight.Angle = 180 - _corners.TopLeft.Angle;
_corners.BottomLeft.Angle = _corners.TopRight.Angle;
Caption = "Par: TopLeft";
}
else if (_corners.TopRight.Angle != value.TopRight.Angle)
{
_corners.TopLeft.Angle = 180 - _corners.TopRight.Angle;
_corners.BottomRight.Angle = _corners.TopLeft.Angle;
_corners.BottomLeft.Angle = _corners.TopRight.Angle;
Caption = "Par: TopRight";
}
else if (_corners.BottomRight.Angle != value.BottomRight.Angle)
{
_corners.TopLeft.Angle = _corners.BottomRight.Angle;
_corners.TopRight.Angle = 180 - _corners.TopLeft.Angle;
_corners.BottomLeft.Angle = _corners.TopRight.Angle;
Caption = "Par: BottomRight";
}
else
{
_corners.TopLeft.Angle = 180 - _corners.BottomLeft.Angle;
_corners.BottomRight.Angle = _corners.TopLeft.Angle;
_corners.TopRight.Angle = _corners.BottomLeft.Angle;
Caption = "Par: BottomLeft";
}
break;
case ShapenStyle.Rectangular:
_corners = value;
_corners.ResetAngles();
Caption = "Rectangle";
break;
default:
_corners = value;
break;
}
}
#endregion Private Members
#region Constructors
public ButtonControl()
{
InitializeComponent();
Size = new Size(50, 50);
SetPropertyAttributes();
_repeatTimer = new Timer
{
Enabled = false,
Interval = _startRepeatInterval
};
_repeatTimer.Tick += OnTimerTick;
}
#endregion Constructors
#region Methods
protected override IRenderer CreateDefaultRenderer()
{
return new Renderers.ButtonRenderer();
}
#endregion Methods
#region Properties
[RefreshProperties(RefreshProperties.All)]
[Category("Appearance")]
[Description("Style of the button")]
public ShapenStyle Style
{
get { return _buttonStyle; }
set
{
_buttonStyle = value;
CalculateDimensions();
SetPropertyAttributes();
}
}
[RefreshProperties(RefreshProperties.All)]
[Browsable(true)]
[Category("Appearance")]
[Description("Corners of the shape. For regular shapes the topleft angle is the key angle from which most calculations take place.")]
public Corners Corners
{
get { return _corners; }
set { SetTheCorners(value); Invalidate(); }
}
[Browsable(false)]
[Category("Appearance")]
[Description("Orientation of the button shape")]
public ShapeOrientation Orientation
{
get { return _orientation; }
set { _orientation = value; Invalidate(); }
}
[Category("Appearance")]
[Description("Color of the body of the button")]
public Color ButtonColor
{
get { return _buttonColor; }
set
{
_buttonColor = value;
Invalidate();
}
}
[Category("Appearance")]
[Description("Caption of the button")]
public string Caption
{
get { return _caption; }
set
{
_caption = value;
Invalidate();
}
}
[Category("Behavior")]
[Description("State of the button")]
public ButtonState State
{
get { return _buttonState; }
set
{
_buttonState = value;
Invalidate();
}
}
[Category("Behavior")]
[Description("Enable/Disable the repetition of the event if the button is pressed")]
public bool RepeatState
{
get { return _repeatState; }
set { _repeatState = value; }
}
[Category("Behavior")]
[Description("Interval to wait in ms for start the repetition")]
public int StartRepeatInterval
{
get { return _startRepeatInterval; }
set { _startRepeatInterval = value; }
}
[Category("Behavior")]
[Description("Interva in ms for the repetition")]
public int RepeatInterval
{
get { return _repeatInterval; }
set { _repeatInterval = value; }
}
[Browsable(false)]
public override Color BackColor { get => base.BackColor; set => base.BackColor = value; }
[Browsable(false)]
public override Image BackgroundImage { get => base.BackgroundImage; set => base.BackgroundImage = value; }
[Browsable(false)]
public override ImageLayout BackgroundImageLayout { get => base.BackgroundImageLayout; set => base.BackgroundImageLayout = value; }
#endregion Properties
#region Events
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
if (Style == ShapenStyle.Circular)
{
Width = Math.Min(Width, Height);
Height = Width;
}
}
private void OnTimerTick(object sender, EventArgs e)
{
_repeatTimer.Enabled = false;
if (_repeatTimer.Interval == _startRepeatInterval)
_repeatTimer.Interval = _repeatInterval;
ButtonEventArgs ev = new ButtonEventArgs
{
State = State
};
OnButtonRepeatState(ev);
_repeatTimer.Enabled = true;
}
private void OnMouseDown(object sender, MouseEventArgs e)
{
State = ButtonState.Pressed;
Invalidate();
ButtonEventArgs ev = new ButtonEventArgs
{
State = State
};
OnButtonChangeState(ev);
if (RepeatState != false)
{
_repeatTimer.Interval = StartRepeatInterval;
_repeatTimer.Enabled = true;
}
}
private void OnMuoseUp(object sender, MouseEventArgs e)
{
State = ButtonState.Normal;
Invalidate();
ButtonEventArgs ev = new ButtonEventArgs
{
State = State
};
OnButtonChangeState(ev);
_repeatTimer.Enabled = false;
}
#endregion Events
#region Delegate Events
public event ButtonChangeState ButtonChangeState;
public event ButtonRepeatState ButtonRepeatState;
protected virtual void OnButtonChangeState(ButtonEventArgs e)
{
ButtonChangeState?.Invoke(this, e);
}
protected virtual void OnButtonRepeatState(ButtonEventArgs e)
{
ButtonRepeatState?.Invoke(this, e);
}
#endregion Delegate Events
}
#region Delegates
public delegate void ButtonChangeState(object sender, ButtonEventArgs e);
public delegate void ButtonRepeatState(object sender, ButtonEventArgs e);
#endregion Delegates
public class ButtonEventArgs : EventArgs
{
public ButtonEventArgs()
{
}
public ButtonControl.ButtonState State { get; set; }
}
}
What I have tried:
I've searched the internet and found some references i.e. suggestions to serialization. I've tried these suggestions, but nothing seems to work.