PropertyGridInternals.MergePropertyDescriptor returns null value

G

Guest

I've specialised a System.ComponentModel.TypeConverter class to handle a
bespoke object which I need to display on a PropertyGrid. I override the
GetStandardValues fnt and retrieve the currently displayed object using --
myObj = context.PropertyDescriptor.GetValue(context.Instance) as myObjType;

This works great if only one item is added to the propertyGrid eg.
myPropertyGrid.SelectedObject(myPropsToDisplay);
But, if I add multiple objects to the propertyGrid
eg. myPropertyGrid.SelectedObjects(myPropsToDisplayArray);
the
context.PropertyDescriptor.GetValue(context.Instance);
returns null. Is this a bug or am I missing something?

Thanks for any help.
 
L

Linda Liu [MSFT]

Hi Jimmer,

I performed a test based on your sample code and did see the problem.

I derive a new class named MyConverter from TypeConverter. The following is
the code in MyConverter class.

using System.ComponentModel;
public class MyConverter : TypeConverter
{
public override bool
GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
public override TypeConverter.StandardValuesCollection
GetStandardValues(ITypeDescriptorContext context)
{
object myobj =
context.PropertyDescriptor.GetValue(context.Instance); //
statement3: get the current displayed object
return new StandardValuesCollection(new String[] {
"string1", "string2", "string3" });
}
}

Then I apply MyConverter to MyClass like below.
public class MyClass
{
private string name;
private int grade;

[TypeConverter(typeof(MyConverter))]
public string Name
{
get { return name;}
set { name = value;}
}

public int Grade
{
get { return grade;}
set { grade = value;}
}
}

I drag&drop a PropertyGrid onto the form and add the following code in the
form's Load event handler.
private void Form1_Load(object sender, EventArgs e)
{
MyClass ins1 = new MyClass();
ins1.Name = "this is my name";

MainClass ins2 = new MainClass();
object [] objs = {ins1,ins2};

this.propertyGrid1.SelectedObjects = objs; // statement1: add
several objects to the propertyGrid1
this.propertyGrid1.SelectedObject = ins1; // statement2:
add one object to the propertyGrid1
}

If I comment out the statement1, the statement3 in the overridden
GetStandardValues method in the MyConverter class returns the string
variable correctly when the program is running. However, if I comment out
the statement2, the statement3 returns null. I set a break point on the
statement3 and I see the type of the statement "context.Instance" is
"object[ ]" when I add several objects to the PropertyGrid. This is the
reason why the statement3 returns null in this case. We should enumerate
the elements in the array and get the current displayed objects one by one.

The following is a sample for this.

public override TypeConverter.StandardValuesCollection
GetStandardValues(ITypeDescriptorContext context)
{
if (context.Instance is object)
{
object myobj =
context.PropertyDescriptor.GetValue(context.Instance);
}
else if (context.Instance is object[])
{
for (int i = 0; i < ((object[])context.Instance).Length;
i++)
{
object myobj = ((object[])context.Instance);

}
}
return new StandardValuesCollection(new String[] { "string1",
"string2", "string3" });
}

Hope this helps.

If you have anything unclear or concerns, please feel free to let me know.

Sincerely,
Linda Liu
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
G

Guest

Linda,
Thanks for the reply. Problem is that when multiple objects are added to a
property grid I need to get the value of each object per property row, or in
other words, every time GetStandardValues is called i need the value of
everything in the context.instance array. So what I need is something like
(modified from your code) ---

if (context.Instance is Array)
{
for (int i = 0; i < ((object[])context.Instance).Length; i++)
{
object myobj =
context.PropertyDescriptor.GetValue(((object[])context.Instance));
//Statement A
}
}

but statement A won't work as the propertyDescriptor is a
mergepropertydescriptor type and the GetValue ftn only takes an object array.
Therefore I would have expected ...

object[] myobj = context.PropertyDesciptor.GetValue(context.Instance);

to work fine, but it doesn't as GetValue returns an object not object[].
(and returns null if assigned to an object type)

I hope this clarifies the problem.
Again, thanks in advance for your help.
Jimmer



Linda Liu said:
Hi Jimmer,

I performed a test based on your sample code and did see the problem.

I derive a new class named MyConverter from TypeConverter. The following is
the code in MyConverter class.

using System.ComponentModel;
public class MyConverter : TypeConverter
{
public override bool
GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
public override TypeConverter.StandardValuesCollection
GetStandardValues(ITypeDescriptorContext context)
{
object myobj =
context.PropertyDescriptor.GetValue(context.Instance); //
statement3: get the current displayed object
return new StandardValuesCollection(new String[] {
"string1", "string2", "string3" });
}
}

Then I apply MyConverter to MyClass like below.
public class MyClass
{
private string name;
private int grade;

[TypeConverter(typeof(MyConverter))]
public string Name
{
get { return name;}
set { name = value;}
}

public int Grade
{
get { return grade;}
set { grade = value;}
}
}

I drag&drop a PropertyGrid onto the form and add the following code in the
form's Load event handler.
private void Form1_Load(object sender, EventArgs e)
{
MyClass ins1 = new MyClass();
ins1.Name = "this is my name";

MainClass ins2 = new MainClass();
object [] objs = {ins1,ins2};

this.propertyGrid1.SelectedObjects = objs; // statement1: add
several objects to the propertyGrid1
this.propertyGrid1.SelectedObject = ins1; // statement2:
add one object to the propertyGrid1
}

If I comment out the statement1, the statement3 in the overridden
GetStandardValues method in the MyConverter class returns the string
variable correctly when the program is running. However, if I comment out
the statement2, the statement3 returns null. I set a break point on the
statement3 and I see the type of the statement "context.Instance" is
"object[ ]" when I add several objects to the PropertyGrid. This is the
reason why the statement3 returns null in this case. We should enumerate
the elements in the array and get the current displayed objects one by one.

The following is a sample for this.

public override TypeConverter.StandardValuesCollection
GetStandardValues(ITypeDescriptorContext context)
{
if (context.Instance is object)
{
object myobj =
context.PropertyDescriptor.GetValue(context.Instance);
}
else if (context.Instance is object[])
{
for (int i = 0; i < ((object[])context.Instance).Length;
i++)
{
object myobj = ((object[])context.Instance);

}
}
return new StandardValuesCollection(new String[] { "string1",
"string2", "string3" });
}

Hope this helps.

If you have anything unclear or concerns, please feel free to let me know.

Sincerely,
Linda Liu
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
L

Linda Liu [MSFT]

Hi Jimmer,

Thank your for your reply. I am terribly sorry that I made a mistake in my
previous message.

When the PropertyGrid displays multiple objects, the
context.PropertyDescriptor is of MergePropertyDescriptor type and its
GetValue method requires a parameter of Array type. However, in this case,
this statement always returns null.

I think a workaround is to make use of the TypeDescriptor.GetProperties
method to get the PropertyDescriptor assicated with the property and then
access the value of the property for each object displayed.

The following is a sample.

public override TypeConverter.StandardValuesCollection
GetStandardValues(ITypeDescriptorContext context)
{
if (context.Instance is Array)
{
PropertyDescriptorCollection pds;
PropertyDescriptor pd ;
object[] instances = context.Instance as object[];
object[] myobjs = new object[instances.Length];

for (int i = 0; i < instances.Length; i++)
{
pds = TypeDescriptor.GetProperties(instances);
pd = pds.Find(context.PropertyDescriptor.Name, false);
myobjs = pd.GetValue(instances);
}

}
else
{
object myobj =
context.PropertyDescriptor.GetValue(context.Instance);
}
.....

}

I have looked this issue up in our inner database, but unfortunately I
didn't found any record about this issue. We will scale this issue and
report it to our product team if necessary. And I will get it back to you
in time. Thank you for your understanding!

If you have any concerns, please feel free to let me know.


Sincerely,
Linda Liu
Microsoft Online Community Support
 
L

Linda Liu [MSFT]

Hi Jimmer,

I have reported this issue to our product team. If there's any progress, I
will get it back to you in time.

By the way, what do you think of my workaround? If you have anything
unclear, please feel free to let me know.

Thank you for reporting this issue to us and your support for Microsoft!


Sincerely,
Linda Liu
Microsoft Online Community Support
 
L

Linda Liu [MSFT]

Hi Jimmer,

There's a response from our product team.

When multiple objects are displayed in the PropertyGrid and the property's
values of these objects are different, the statement
"context.PropertyDescriptor.GetValue(context.Instance)" returns null. This
behavior is by design.

Say you have a string ("Light") that can be "Dawn", "DayLight", "Dusk",
"Darkness". You have a derived TypeConverter that returns all 4 in
GetStandardValues.
Now -- you select two objects that have different current values for Light.
The *correct* behavior is to show a blank value and ALL FOUR values in the
droplist. So, if the user selects "Dawn" when two objects are selected,
then Dawn will be set on the properties for those two objects.

If the two objects have the same current value for Light, this value will
be shown in the PropertyGrid and the statement
"context.PropertyDescriptor.GetValue(context.Instance)" returns this value.

My workaround is actually the correct way to handle this question.

Hope this helps.

If you have any other questions in the future, please don't hesitate to
contact us. It's always our pleasure to be of assistance.

Have a nice day!


Sincerely,
Linda Liu
Microsoft Online Community Support
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top