Displaying and editing Child Object properties in a Property Grid

If you have a complex member property (a class that isn’t a .NET system type) and you want to display and edit through PropertyGrid, follow these steps:

  1. Derive from base class System.ComponentModel.ExpandableObjectConverter
  2. Add TypeConverter(typeof(yourClass)) attribute on this class
  3. Optionally Add Description(“your description”) attribute as well.  Common descriptions include “Expand to see options” as your property value will not be editable without expanding drop down
  4. Override ToString for better display of your object ‘state’ in the property grid
  5. IMPORTANT: provide a default constructor – otherwise it excepts quietly and you are not sure why

Optionally…Override  the 4  base class methods for conversion / serialization to string

public override object ConvertTo(ITypeDescriptorContext context,
CultureInfo culture,
object value,
System.Type destinationType)
{
if (destinationType == typeof(System.String) && value is StatusCommand)
{

//Convert / Serialize your object to string (usually in a parsable property bag)

}
return base.ConvertTo(context, culture, value, destinationType);
}


public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{

StatusCommand statusCommand = new StatusCommand();

if (value is string)
{
try
{

//Parse the string
string s = (string)value;
string[] elements = s.Split(';');

foreach (var elem in elements)
{
string[] fields = elem.Split('=');
switch (fields[0])
{
case "Version":
break;
break;
case "FirmwareVersionMajor":
statusCommand.FirmwareVersionMajor = Convert.ToByte(fields[1]);
break;
case "FirmwareVersionMinor":
statusCommand.FirmwareVersionMinor = Convert.ToByte(fields[1]);
break;


}
}

return statusCommand;
}
catch (Exception ex)
{

Trace.TraceError(ex.ToString());
throw new ArgumentException(
"Can not convert '" + (string)value +
"' to type SpellingOptions");
}
}
return base.ConvertFrom(context, culture, value);
}

public override bool CanConvertFrom(ITypeDescriptorContext context,
System.Type sourceType)
{
if (sourceType == typeof(string))
return true;

return base.CanConvertFrom(context, sourceType);
}

public override bool CanConvertTo(ITypeDescriptorContext context,
System.Type destinationType)
{
if (destinationType == typeof(StatusCommand))
return true;

return base.CanConvertTo(context, destinationType);
}

D3 Sliders – SVG

SVG Slider Libraries that are close to desired behavior

Web Page

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Title</title>
 <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
 <script src="simpleD3Slider.js"></script>
</head>

<body>
<div id="vis"></div>
</body>

<script>

 var svg = d3.select("#vis").append("svg").attr("width", 1000).attr("height", 700),
 slider1 = new simpleSlider(),
 slider2 = new simpleSlider(),
 circle = svg.append("circle").attr("cx", 50).attr("cy", 100).attr("r", 30);


 slider1.width(200).x(30).y(200).value(1.0).event(function(){
 circle.attr("r", 30 * slider1.value());
 });

 slider2.width(200).x(30).y(230).value(0.5).event(function(){
 circle.attr("cx", 50 + (170 * slider2.value()));
 });

 svg.call(slider1);
 svg.call(slider2);

</script>
</html>
/* Simple, reusable slider in pure d3 */

function simpleSlider () {

 var width = 100,
 value = 0.5, /* Domain assumes to be [0 - 1] */
 event,
 x = 0,
 y = 0;

 function slider (selection) {

 //Line to represent the current value
 var valueLine = selection.append("line")
 .attr("x1", x)
 .attr("x2", x + (width * value))
 .attr("y1", y)
 .attr("y2", y)
 .style({stroke: "#51CB3F",
 "stroke-linecap": "round",
 "stroke-width": 6 });

 //Line to show the remaining value
 var emptyLine = selection.append("line")
 .attr("x1", x + (width * value))
 .attr("x2", x + width)
 .attr("y1", y)
 .attr("y2", y)
 .style({
 "stroke": "#ECECEC",
 "stroke-linecap": "round",
 "stroke-width": 6
 });

 var drag = d3.behavior.drag().on("drag", function() {
 var newX = d3.mouse(this)[0];

 if (newX < x)
 newX = x;
 else if (newX > x + width)
 newX = x + width;

 value = (newX - x) / width;
 valueCircle.attr("cx", newX);
 valueLine.attr("x2", x + (width * value));
 emptyLine.attr("x1", x + (width * value));

 if (event)
 event();

 d3.event.sourceEvent.stopPropagation();
 })

 //Draggable circle to represent the current value
 var valueCircle = selection.append("circle")
 .attr("cx", x + (width * value))
 .attr("cy", y)
 .attr("r", 8)
 .style({
 "stroke": "black",
 "stroke-width": 1.0,
 "fill": "white"
 })
 .call(drag);
 }


 slider.x = function (val) {
 x = val;
 return slider;
 }

 slider.y = function (val) {
 y = val;
 return slider;
 }

 slider.value = function (val) {
 if (val) {
 value = val;
 return slider;
 } else {
 return value;
 }
 }

 slider.width = function (val) {
 width = val;
 return slider;
 }

 slider.event = function (val) {
 event = val;
 return slider;
 }

 return slider;
}