I have this property:
[DisplayName("Conexión")]
[TypeConverter(typeof(Converters.DevicesTypeConverter))]
[Description("Conexión con el dispositivo a usar.")]
[Required]
public string Conexion { get; set; }
I need to get the type converter instance of it. I tried with :
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(this);
PropertyDescriptor property = properties.Find("Conexion", false);
var converter = TypeDescriptor.GetConverter(property);
And even with:
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(this);
PropertyDescriptor property = properties.Find("Conexion", false);
var converter = TypeDescriptor.GetConverter(property.PropertyType);
With that, I can only get converter of the property type, that is, the converter for string type, not the actual property converter, DevicesTypeConverter.
Any help please?
EDIT:
What I am trying to do is the following. I have 2 properties in the class that I need to set by mean of a property grid.
"Conexion" property is a list of values that depends on other property.
This dependency works well this way:
When the other property changes, and then I expand the "Conexion" property, GetStandardValuesSupported method on "Conexion" is called. Inside that method I use "context.Instance" to call a method on the object instance that retrieves the list this way:
public List<object> GetDevicesList()
{
if (this.Driver == null)
return null;
var devices = this.Driver.GetList();
if (devices == null)
return null;
return devices.Select(l => (object)l.Value).ToList();
}
The returned list is stored in a private variable in the converter, so this perfectly show the list that depends on the other property.
So far so good. The problem occurs when the object already has value in its properties. Since the list of "Conexion" is exclusive, the property value of it, when I assign it to the property grid, appears empty.
This is obvious because the dependent list only get populated when GetStandardValuesSupported is called, and that happens only when I try to edit the property.
Now I need what I asked in the question. I need to call GetStandardValuesSupported explicitly in object constructor in order to force the dependent list to be loaded before the "Conexion" property is assigned. With that, I am sure the property will appear initialized since the list will have its value.
I think your solution should work but the GetConverter returns null. The problem is only reduced to call the GetStandardValuesSupported() of my custom type converter so I could also use Activator.CreateInstance but the problem is that the type of the converter is null and not the type of DevicesTypeConverter.
You have to jump through several layers to get an instance of the TypeConverter specified in the TypeConverterAttribute (LINQPad demo):
//Get the type you are interested in.
var type = typeof(MyClass);
//Get information about the property you are interested in on the type.
var prop = type.GetProperty("Conexion");
//Pull off the TypeConverterAttribute.
var attr = prop.GetCustomAttribute<TypeConverterAttribute>();
//The attribute only stores the name of the TypeConverter as a string.
var converterTypeName = attr.ConverterTypeName;
// Get the actual Type of the TypeConverter from the string.
var converterType = Type.GetType(converterTypeName);
//Create an instance of the TypeConverter.
var converter = (TypeConverter) Activator.CreateInstance(converterType);
You can use your existing method of getting the converter now that you have the type:
var converter = TypeDescriptor.GetConverter(converterType);
You were close. Once you have the PropertyDescriptor for the property you're interested in, use the Converter property to get the Converter.
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(this);
PropertyDescriptor propertyDescriptor = properties.Find("Conexion", false);
propertyDescriptor.Converter // <- This is what you want
Related
I am building a generic usercontrol. which will be a combination of one label and one textbox. This will be a class library project.
Text of the label and textBox will be bounded from outside projects. In the projects where I imported the dll.
I am trying to make the user control fully generic. For say, I will send any object, and a property name of the object to the dll. dll will read the object, get the object properties, get value for the sent parameter and act as another ready controls in win forms.
myControl1.DataBindings.Add("DataSource",object,"PropertyName1");
By just saying that, myController1 will get the value associated with PropertyName1 of the object and bind values with the textbox and label.
You wanna use reflection for this.
This is how you do it using an extension method, but you can easily make it a regular function if that's what you need. I tried to explain what it does with comments.
public static T GetProperty<T>(this object obj, string name)
{
Type type = obj.GetType(); // Get the object's type
PropertyInfo pinfo = type.GetProperty(name); // Get the property information for the specified property name
object ret = pinfo?.GetValue(obj); // If pinfo != null, get the value of the property on obj
return ret == null ? default(T) : (T)ret; // If ret is null, return the default value for T (for example, "" if T is string). If it isn't, return ret casted to T
}
Usage example:
Form1.Text == Form1.GetProperty<string>("Text"); // true
Imagine I have created an object name of oItem. And this has a lot of attributes such as ItemCode, ItemName.....(around 300). And I want to assign new values to some of these attributes which were selected by the user of my application. User will give these attribute names as strings.
Ex: string attribute1 = "ItemCode".
Now what I want to do is assign a value to this attribute like:
oItem.attribute1 = "01234";
Is there a way to do something like this? I know you can convert a c# function call to a string. Therefore I think this should be possible too. Any help would be highly appreciated. Thanks!
UPDATE: This is a part of my SAP add-On. So these attributes are from a database table. The hard part is user can add more columns(user defined fields) to this which increase the number of attributes as well as I only know the original 300 attributes.
If you have 300 properties you should really refactor this class. I think that you can use a Dictionarystring, string> in this case.
Dictionary<string, string> Items = new Dictionary<string, string>
{
{"attribute1", "01234"}, {"attribute2", "56789"}, {"attribute3", "76543"}, // ...
};
You can access the values very efficiently:
string attribute1 = Items["attribute1"];
or add/modify them in a similar way:
Items["attribute4"] = "23456";
To make it easier for you there's a "dynamic" keyword in C#.
A sample from msdn dynamic object article:
// The class derived from DynamicObject.
public class DynamicDictionary : DynamicObject
{
// The inner dictionary.
Dictionary<string, object> dictionary
= new Dictionary<string, object>();
// This property returns the number of elements
// in the inner dictionary.
public int Count
{
get
{
return dictionary.Count;
}
}
// If you try to get a value of a property
// not defined in the class, this method is called.
public override bool TryGetMember(
GetMemberBinder binder, out object result)
{
// Converting the property name to lowercase
// so that property names become case-insensitive.
string name = binder.Name.ToLower();
// If the property name is found in a dictionary,
// set the result parameter to the property value and return true.
// Otherwise, return false.
return dictionary.TryGetValue(name, out result);
}
// If you try to set a value of a property that is
// not defined in the class, this method is called.
public override bool TrySetMember(
SetMemberBinder binder, object value)
{
// Converting the property name to lowercase
// so that property names become case-insensitive.
dictionary[binder.Name.ToLower()] = value;
// You can always add a value to a dictionary,
// so this method always returns true.
return true;
}
}
class Program
{
static void Main(string[] args)
{
// Creating a dynamic dictionary.
dynamic person = new DynamicDictionary();
// Adding new dynamic properties.
// The TrySetMember method is called.
person.FirstName = "Ellen";
person.LastName = "Adams";
// Getting values of the dynamic properties.
// The TryGetMember method is called.
// Note that property names are case-insensitive.
Console.WriteLine(person.firstname + " " + person.lastname);
// Getting the value of the Count property.
// The TryGetMember is not called,
// because the property is defined in the class.
Console.WriteLine(
"Number of dynamic properties:" + person.Count);
// The following statement throws an exception at run time.
// There is no "address" property,
// so the TryGetMember method returns false and this causes a
// RuntimeBinderException.
// Console.WriteLine(person.address);
}
}
// This example has the following output:
// Ellen Adams
// Number of dynamic properties: 2
It is really easy to do this for SAP B1 addon using c# with the provided library functions. Simply include using SAPbobsCOM;
And then you can do it as follows
yourobject.UserFields.Fields.Item("attribute").Value = "value";
I have a problem. I'm trying to add properties to propertyGrid control, with its correct type converter.
I'm using reflection, so actually i know what exactly type of value came only when i'm initializing my propertyGrid.
Something like that:
public SettingsProperty(SettingsFieldValue value, SettingsFieldInfo info)
{
Value = value;
Info = info;
_defaultValue = Info.DefaultValue;
Name = Info.Label;
Description = Info.Description;
PropertyName = Info.FieldName;
TypeName = Info.FieldType.ToString();
CurrentValue = GetValueAsString(Value);
DefaultValue = GetValueAsString(_defaultValue);
}
So. It can be bool, int, double, array, dictionary or complex object with its nested properties.
Is there any way to create converter for that property at runtime?
Like, if came object, it became expandableTypeConverter. If boolean - boolean converter etc.
Thanks for any help or thoughts.
I have a DataGrid that is bound to ObservableCollection.
What I am wondering is: without resorting to looking at an item and retrieving the object type, can I somehow use the actual DataGrid object and the ItemSource to find the type of objects?
So if I have the following:
DataGrid dg = DataGridObject as DataGrid;
Console.WriteLine("binding5=" + dg.ItemsSource.GetType());
output = System.Collections.ObjectModel.ObservableCollection`1[UserManagement.UserViewModel]
Can I extract UserManagement.UserViewModel into an object variable somehow
If I understand you correctly, you want to find out the type of object inside the collection that is set as the DataGrid.ItemsSource property. To do this, you can use some basic reflection. Try this:
var collection = ListBox.ItemsSource;
Type collectionType = collection.GetType();
Type itemType = collectionType.GetGenericArguments().Single();
with assumption that the collection is of type ObservableCollection<>
here you go
Type collectionType = dg.ItemsSource.GetType();
if (collectionType.IsGenericType && collectionType.GetGenericTypeDefinition().IsAssignableFrom(typeof(ObservableCollection<>)))
{
Type objType = collectionType.GenericTypeArguments[0];
}
here we will confirm if the type is a generic type and its generic definition is assignable from ObservableCollection<> then will take the first type argument which will be the type of elements
I've got COM object attached to property grid.
Type typeObj = Type.GetTypeFromProgID(progIdService);
var obj = Activator.CreateInstance(typeObj);
propertyGrid1.SelectedObject = obj;
Now I need some way to translate object fields into my language using some translator. I was trying to use wrapper around object but with COM object I have no PropertyInfo, I have only PropertyDescription so I'm still looking for all the possible variants of doing it.
What you could do is reuse the DynamicTypeDescriptor class described in my answer to this question here on SO: PropertyGrid Browsable not found for entity framework created property, how to find it?
like this:
DynamicTypeDescriptor dtp = new DynamicTypeDescriptor(typeObj);
// get current property definition and remove it
var current = dtp.Properties["ThePropertyToChange"];
dtp.RemoveProperty("ThePropertyToChange");
// add a new one, but change its display name
DynamicTypeDescriptor.DynamicProperty prop = new DynamicTypeDescriptor.DynamicProperty(dtp, current, obj);
prop.SetDisplayName("MyNewPropertyName");
dtp.AddProperty(prop);
propertyGrid1.SelectedObject = dtp.FromComponent(obj);
I think you can use reflection to get the property names, although I haven't tried with COM objects yet. Be sure to include the System.Reflection namespace, then you can use it like that:
var props = myComObject.GetType().GetProperties();
foreach (var prop in props)
{
MessageBox(prop.Name);
}