How to show Drop down control in Property Grid? - winforms

I am adding the Property grid control in my project.
I have to show the Drop down box in one field of Property Grid.
Is there any solution to apply this.

You have to declare a type editor for the property in your PropertyGrid and then add to the list of choices. This example creates a Type Converter and then overrides the GetStandardValues() method to provide choices to the drop-down:
private String _formatString = null;
[Category("Display")]
[DisplayName("Format String")]
[Description("Format string governing display of data values.")]
[DefaultValue("")]
[TypeConverter(typeof(FormatStringConverter))]
public String FormatString { get { return _formatString; } set { _formatString = value; } }
public class FormatStringConverter : StringConverter
{
public override Boolean GetStandardValuesSupported(ITypeDescriptorContext context) { return true; }
public override Boolean GetStandardValuesExclusive(ITypeDescriptorContext context) { return true; }
public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
List<String> list = new List<String>();
list.Add("");
list.Add("Currency");
list.Add("Scientific Notation");
list.Add("General Number");
list.Add("Number");
list.Add("Percent");
list.Add("Time");
list.Add("Date");
return new StandardValuesCollection(list);
}
}
The key is the property being assigned a Type Converter in the line:
[TypeConverter(typeof(FormatStringConverter))]
That provides you with the opportunity to introduce your own behavior via overrides.
Here's a simpler example, which allows the Enum type of a Property to automatically provide its values to the PropertyGrid drop-down:
public enum SummaryOptions
{
Sum = 1,
Avg,
Max,
Min,
Count,
Formula,
GMean,
StdDev
}
private SummaryOptions _sumType = SummaryOptions.Sum;
[Category("Summary Values Type")]
[DisplayName("Summary Type")]
[Description("The summary option to be used in calculating each value.")]
[DefaultValue(SummaryOptions.Sum)]
public SummaryOptions SumType { get { return _sumType; } set { _sumType = value; } }
By virtue of the fact that the property is an Enum type, those enum values pass through to become the drop-down options automatically.

Related

How to use custom object's list with typeconverter in winform?

I made custom type which can use in my custom winform control.
I want to display this custom type property in to default winform property window or my custom smart grid.
So i made type converter for my custom type.
[Serializable]
public class TestObj
{
private int a;
private int b;
public int A { get { return a; } set { a = value; } }
public int B { get { return b; } set { b = value; } }
public TestObj()
{
}
public TestObj(int a, int b)
{
this.a = a;
this.b = b;
}
}
// TestObj Converter
public class TestObjConverter : TypeConverter
{
//can convert string -> testobj?
public override bool CanConvertFrom(ITypeDescriptorContext context, >
Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
// string -> TestObj
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (value is string)
{
string[] v = ((string)value).Split(new char[] { ' ' });
return new TestObj(Int32.Parse(v[0]), Int32.Parse(v[1]));
}
return base.ConvertFrom(context, culture, value);
}
// TestObj -> string
public override object ConvertTo(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture, object value, Type
destinationType)
{
if (destinationType == typeof(string))
{
return ((TestObj)value).A + " " + ((TestObj)value).B;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
(If i did not made this typeconverter then resx error will occurred.)
Now i can use TestObj type for my custom control's property.
But i want to use list collection property for this type in my custom control class.
private List<TestObj> testData = new List<TestObj>();
[Category("CustomControl"), Description("Property")]
[TypeConverter(typeof(TestObjConverter))]
public List<TestObj> TestData
{
get { return testData; } set { testData = value; }
}
But i can not use this type's list for property.
If i use this, the collection editor was opened and i can add TestObj element too. But visual studio invoke error [invalid resx file, Culture=newtral, PublicKeyTokrn=null ] when i compile this.
How to use my custom type list in winform property?
But
This may be a little late but someone else may run across this. Your TestObj class that is being used for the property must be declared with the type converter and not the actual property you are exposing. the property is typed with the class as any property should.
[TypeConverter(typeof(TestObjConverter))]
public class TestObj
{
...
}
then in your main control class you just expose the property like normal without the type converter declaration like you have done
[Category("CustomControl"), Description("Property")]
public List<TestObj> TestData { get; set; }
I have several custom controls for winforms if you want a working example of this. Just ask me and i will send one to you

WPF Propertygrid with custom sorting

I'm looking for a PropertyGrid for my WPF project that allows me to customize the ordering the properties / categories are listed. Right now I'm using Extended WPF Toolkits (Community Edition) PropertyGrid with CustomPropertyDescriptors. My researches showed, that it's not possible to have custom sorting with that PropertyGrid.
Is there a (preferably free) solution?
Ordering of properties in the Extended WPF Toolkit can be achieved by decorating the property with the PropertyOrderAttribute attribute.
If you don't want to pollute POCO's by decorating them with attributes at design time, or the order is dynamic in some way, then it's possible to add the attribute at run time by creating a type converter and overriding the GetProperties method. For example, if you wish to maintain the index order of a generic IList type:
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using System.ComponentModel;
public class MyExpandableIListConverter<T> : ExpandableObjectConverter
{
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
if (value is IList<T>)
{
IList<T> list = value as IList<T>;
PropertyDescriptorCollection propDescriptions = new PropertyDescriptorCollection(null);
IEnumerator enumerator = list.GetEnumerator();
int counter = -1;
while (enumerator.MoveNext())
{
counter++;
propDescriptions.Add(new ListItemPropertyDescriptor<T>(list, counter));
}
return propDescriptions;
}
else
{
return base.GetProperties(context, value, attributes);
}
}
}
With the ListItemPropertyDescriptor being defined as follows:
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using System.ComponentModel;
public class ListItemPropertyDescriptor<T> : PropertyDescriptor
{
private readonly IList<T> owner;
private readonly int index;
public ListItemPropertyDescriptor(IList<T> owner, int index) : base("["+ index+"]", null)
{
this.owner = owner;
this.index = index;
}
public override AttributeCollection Attributes
{
get
{
var attributes = TypeDescriptor.GetAttributes(GetValue(null), false);
//If the Xceed expandable object attribute is not applied then apply it
if (!attributes.OfType<ExpandableObjectAttribute>().Any())
{
attributes = AddAttribute(new ExpandableObjectAttribute(), attributes);
}
//set the xceed order attribute
attributes = AddAttribute(new PropertyOrderAttribute(index), attributes);
return attributes;
}
}
private AttributeCollection AddAttribute(Attribute newAttribute, AttributeCollection oldAttributes)
{
Attribute[] newAttributes = new Attribute[oldAttributes.Count + 1];
oldAttributes.CopyTo(newAttributes, 1);
newAttributes[0] = newAttribute;
return new AttributeCollection(newAttributes);
}
public override bool CanResetValue(object component)
{
return false;
}
public override object GetValue(object component)
{
return Value;
}
private T Value
=> owner[index];
public override void ResetValue(object component)
{
throw new NotImplementedException();
}
public override void SetValue(object component, object value)
{
owner[index] = (T)value;
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
public override Type ComponentType
=> owner.GetType();
public override bool IsReadOnly
=> false;
public override Type PropertyType
=> Value?.GetType();
}
Portions of this code were adapted from the following SO answer

PropertyGrid control + array of custom objects

I have a PropertyGrid control to which I bind a container with an array of complex objects:
// Collection
public class ParametersCollection
{
private ParameterObject [] _parameters = null;
[Category("SubReportParams")]
public ParameterObject [] Parameters
{
get { return _parameters; }
set { _parameters = value; }
}
public ParametersCollection()
{
// _parameters initialization here...
}
}
// Complex object
public class ParameterObject
{
private string _name = "";
private string _value = "";
[Category("Information"), DisplayName("Name")]
public string Name
{
get { return _name; }
set { _name = value; }
}
[Category("Information"), DisplayName("Value")]
public string Value
{
get { return _value; }
set { _value = value; }
}
}
Everything works fine, except two cases:
In example, if array _parameters has only 2 items, default array size is 4 and items with indexes 2 and 3 are nulls. PropertyGrid displays these items as empty fields. How to force PropertyGrid to ignore these fields and simply not displaying it?
_parameters variable is an array type, so _parameters items are displayed with their indexes from 0 to n. Is there a posibillity to display them with their names from property ParamObject.Name instead of their indexes from an array?
For the fist question, the easiest way is to add a "fake" property computed from the "real" property. It's not perfect, but you can use various attribute to help:
DisplayName to give the fake property the name of the real property
Browsable(false) instructs the property grid to skip the real property
EditorBrowsable(never) instructs Visual Studio's intellisense to not show this property in external code.
[Browsable(false)]
public ParameterObject[] Parameters
{
get { return _parameters; }
set { _parameters = value; }
}
[Category("SubReportParams"), DisplayName("Parameters")]
[EditorBrowsable(EditorBrowsableState.Never)]
public ParameterObject[] NonEmptyParameters
{
get
{
return _parameters.Where(p => p != null).ToArray();
}
}
For the second question, one simple way is to add a ToString() implementation like this:
public class ParameterObject
{
public override string ToString()
{
return Name;
}
}
Otherwise, you can to add a custom TypeConverter to the class.

Inherited Control Visible/Enabled Property Value Always True: PropertyGrid

I have created a custom WinForms hosting environment. Which has a toolbox and a PropertyGrid.
The controls displayed in the Toolbox are inherited from existing WinForm controls.
DropDownList Source:
public interface IPropertyFilter : ICustomTypeDescriptor
{
PropertyDescriptorCollection FilterProperties(PropertyDescriptorCollection pdc);
List<string> GetPropertiesToShow();
}
[Serializable]
public class DropDownList : System.Windows.Forms.ComboBox, IPropertyFilter
{
public DropDownList()
{
}
#region IPropertyFilter Members
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(this, attributes, true);
return FilterProperties(pdc);
}
PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties()
{
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(this, true);
return FilterProperties(pdc);
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
public PropertyDescriptor GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(this, true);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
public PropertyDescriptorCollection FilterProperties(PropertyDescriptorCollection pdc)
{
// Filter out properties that we do not want to display in PropertyGrid
return ControlDesignerHelper.GetBrowsableProperties(pdc, GetPropertiesToShow());
}
// Determines what properties of this control has to be shown in PropertyGrid
public List<string> GetPropertiesToShow()
{
// get a list of common properties that we want to show for all controls
List<string> browsableProps = ControlDesignerHelper.GetBasePropertiesToShow();
// add properties that are specific to this controls
browsableProps.Add("Items");
browsableProps.Add("AutoPostBack");
browsableProps.Add("AppendDataBoundItems");
browsableProps.Add("DataTextField");
browsableProps.Add("DataValueField");
return browsableProps;
}
#endregion
}
I have implemented ICustomTypeDescriptor to filter out properties that I do not want to show in the PropertyGrid.
Problem:
I am facing problem while serializing values of Enabled & Visible properties that are inherited from System.Windows.Forms.Control class.
WriteProperties Method (BasicDesignerLoader):
private void WriteProperties(XmlDocument document, PropertyDescriptorCollection properties, object value, XmlNode parent, string elementName)
{
foreach (PropertyDescriptor prop in properties)
{
System.Diagnostics.Debug.WriteLine(prop.Name);
if (prop.ShouldSerializeValue(value))
{
string compName = parent.Name;
XmlNode node = document.CreateElement(elementName);
XmlAttribute attr = document.CreateAttribute("name");
attr.Value = prop.Name;
node.Attributes.Append(attr);
DesignerSerializationVisibilityAttribute visibility = (DesignerSerializationVisibilityAttribute)prop.Attributes[typeof(DesignerSerializationVisibilityAttribute)];
switch (visibility.Visibility)
{
case DesignerSerializationVisibility.Visible:
if (!prop.IsReadOnly && WriteValue(document, prop.GetValue(value), node))
{
parent.AppendChild(node);
}
break;
case DesignerSerializationVisibility.Content:
object propValue = prop.GetValue(value);
if (typeof(IList).IsAssignableFrom(prop.PropertyType))
{
WriteCollection(document, (IList)propValue, node);
}
else
{
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(propValue, propertyAttributes);
WriteProperties(document, props, propValue, node, elementName);
}
if (node.ChildNodes.Count > 0)
{
parent.AppendChild(node);
}
break;
default:
break;
}
}
}
}
Problem # 1: The ShouldSerializeValue method for the Enabled & Visible property always returns false.
Problem # 2: Even if I skip the ShouldSerializeValue method check the GetValue method of the PropertyDescriptor always returns True.
Current Workaround:
As a workaround I have currently made the Enabled & Visible properties hidden using the BrowsableAttribute, and created two other boolean properties and used the DisplayNameAttribute to change their display name to be Enable & Visible.
But for this workaround I have to write these snippets in every control.
Am I missing something or doing anything wrong? Why are the Enabled & Visible property do not change?
You will find a long discussion about this issue here. (dead link, can't find a new one)
This MSDN page aldo makes this remark:
The InheritedPropertyDescriptor class
modifies the default value of a
property, so that the default value is
the current value at object
instantiation. This is because the
property is inherited from another
instance. The designer defines
resetting the property value as
setting it to the value that was set
by the inherited class. This value may
differ from the default value stored
in metadata.
ShouldSerializeValue's return value is based on the difference between the current value and the default value so I think this is directly related to your problem.
I hope this will help you figure out what happens in your own context.

Binding Custom Object Datagrid Silverlight 4

I create an object Custom as you can see below
public class GridViewModel
{
private List _listRowCust = new List();
public List ListRowCust
{
get { return _listRowCust; }
set { _listRowCust = value; }
}
}
public class RowCustom
{
private List<CellCustom> _listCellCustom = new List<CellCustom>();
public List<CellCustom> ListCellCustom
{
get { return _listCellCustom; }
set { _listCellCustom = value; }
}
public RowCustom() { }
}
I try to bind the custom object on the datagrid object available in silverlight4.
I wish to bind any cell on the datagrid. One line should identify by a row object and each cell by a cellCustom.
I use this code
textColumn = new DataGridTextColumn();
textColumn.Header = "RemainingWork";
textColumn.Binding = new Binding("Cell[0]"); //it's a supposed syntax possibility in fact I have 3 rows with 10 cells
GridElements.Columns.Add(textColumn);
GridElements.ItemsSource = e.GridViewModel.ListRowCust;
I don't find any explanation on How to custom the binding.
Do you have any idea?
Thank you
best regards,
Alexandre
I think you can only bind to public properties.
So CellCustom must have a public property to bind to, if that's the object used for the itemsSource, or data context.

Resources