0

我在属性网格中显示一个包含几个成员(每个成员都是自己的类)的类。我有以下情况(简化,这不是实际的设计和/或类):

public class DataType1
{
    public int Value1 { get; }
    public int Value2 { get; }
}

public class DataType2
{
    public int ValueA { get; }
    public int ValueB { get; }
}

public class DisplayedData
{
    [TypeConverter(typeof(ExpandableObjectConverter))]
    [ReadOnly(true)]
    public DataType1 Data1 { get; }

    [TypeConverter(typeof(ExpandableObjectConverter))]
    [ReadOnly(true)]
    public DataType2 Data2 { get; }
}

如您所见,每个成员(Data1、Data2)都显示为可展开的对象,因此我可以看到它的所有成员。

现在,问题是这样的:这些数据类中的每一个都是从远程源单独读取的,并且每次读取都可能失败(带有特定错误)。

我希望能够DisplayedData在属性网格中显示组合对象 ( ),如果读取成功,每个成员都可以展开,否则显示错误代码(只是一个字符串)。

有任何想法吗?

4

2 回答 2

2

正如 Arif 建议的那样,我最终基于 ExpandableObjectConverter 创建了自己的 TypeConverter。我的解决方案有点简单(我认为)。

我更改DataType1DataType2继承自同一个基类(我们称之为它BaseDataType),它包含一个错误代码(以及是否有错误)。

然后在我的类型转换器中,我做了类似的事情(再次简化):

public class MyDynamicTypeConverter : ExpandableObjectConverter
{
    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType.Equals(typeof(string)))
        {
            BaseDataType baseDisplay = GetBaseDisplay(context);
            if (baseDisplay.ReadFailed)
            {
                // Display the error message
                return baseDisplay.ErrorMessageReadFailed;
            }
        }
        return base.ConvertTo(context, culture, value, destinationType);
    }

    public override bool GetPropertiesSupported(ITypeDescriptorContext context)
    {
        BaseDataType baseDisplay = GetBaseDisplay(context);
        if (baseDisplay.ReadFailed)
        {
            // If read failed, do not expand the display for this object
            return false;
        }
        return base.GetPropertiesSupported(context);
    }

    private BaseDataType GetBaseDisplay(ITypeDescriptorContext context)
    {
        // Extract base data type using reflections
        object obj = context.Instance.GetType().GetProperty(context.PropertyDescriptor.Name).GetValue(context.Instance, null);
        return (BaseDataType)obj;
    }
}

这样,转换器本身会查询读取的数据对象,并根据是否存在错误代码来决定如何显示它。

当然涉及的代码更多(例如在需要时设置相关的错误代码等),但这是主要思想。

于 2012-07-26T15:07:20.467 回答
2

我认为如果您基于 ExpandableObjectConverter 实现自己的 TypeConverter,您可能可以处理这种情况。例如。假设您无法获取 Data2 的数据,所以我假设 Data2 将为 Null。您可以使用自己的转换器在网格中显示属性,但我想知道如何在其前面显示“错误”文本。基本上,对于 Expandable 对象,该类的 ToString() 用于在组中显示文本。

我有一个你可以使用的技巧,可能不是最优雅的解决方案,但我希望它会起作用......你可以做的是在你的类 DisplayedData 添加另一个属性,这样你的类就变成这样了......

public class DisplayedData
{
    [TypeConverter(typeof(ExpandableObjectConverter))]
    [ReadOnly(true)]
    public DataType1 Data1 { get;  }

    [TypeConverter(typeof(ExpandableObjectConverter))]
    [ReadOnly(true)]
    [DisplayName("Data2")]
    [Browsable(true)]
    public DataType2 Data2 { get;  } //We have Browsable set to true for this

    [DisplayName("Data2")]
    [Browsable(false)]
    public string Data2Error { get;  } //An additional property with Browsable set to false
}

(上面的类没有设置器,我想知道您将如何填充它们,但这可能是部分代码)

现在,当您在此类的对象中填充数据时,您会看到您无法读取 Data2 的值,因此 Data2 为 Null。那你写这个...

 if (data.Data2 ==  null)//data is an object of DisplayedData class that we are showing in PropertyGrid
        {
            PropertyDescriptor descriptor = TypeDescriptor.GetProperties(data.GetType())["Data2"];
            BrowsableAttribute attribute = (BrowsableAttribute)descriptor.Attributes[typeof(BrowsableAttribute)];
            FieldInfo fieldToChange = attribute.GetType().GetField("Browsable",
                                  BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.Public |
                                  BindingFlags.Instance);
            fieldToChange.SetValue(attribute, false);

            data.Data2Error = "Error";
            descriptor = TypeDescriptor.GetProperties(data.GetType())["Data2Error"];
            attribute = (BrowsableAttribute)descriptor.Attributes[typeof(BrowsableAttribute)];
            fieldToChange = attribute.GetType().GetField("Browsable",
                                  BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.Public |
                                  BindingFlags.Instance);
            fieldToChange.SetValue(attribute, true);
        }

        propertyGrid1.SelectedObject = data; //Reassign object to PropertyGrid

基本上在运行时我们试图隐藏属性 Data2 并显示属性 Data2Error,这两个属性具有相同的显示名称,因此在属性网格中显示相同的名称。为此,我们使用反射并获取属性“Browsable”并设置其属性。

于 2012-07-26T08:15:25.203 回答