Изменение атрибутов описания и категории PropertyGrid в runtiome



Я работаю над бизнес-приложением, которое использует PropertyGrid. Мой руководитель проекта хочет, чтобы я локализовал тексты в PropertyGrid во время выполнения. Ура!!! ирония



Я пробовал много дней, чтобы локализовать сетки. Но у меня есть проблемы с изменением атрибутов описание и Категория во время выполнения. Изменение DisplayName работает нормально.



Я привел простой пример, чтобы воспроизвести проблему: создайте форму Windows приложение и из ToolBox Добавьте PropertyGrid и кнопку с настройками по умолчанию.



Вот класс, который я хотел бы отобразить в PropertyGrid:



class Person
{
int age;

public Person()
{
age = 10;
}

[Description("Person's age"), DisplayName("Age"), Category("Fact")]
public int Age
{
get { return age; }
}
}


В конструкторе формы; я создаю объект Person и отображаю его в PropertyGrid.



    public Form1()
{
InitializeComponent();
propertyGrid1.SelectedObject = new Person();
}


Кнопка используется для изменения атрибутов DisplayName, Description и Category во время выполнения.



    private void button1_Click(object sender, EventArgs e)
{
SetDisplayName();
SetDescription();
SetCategory();

propertyGrid1.SelectedObject = propertyGrid1.SelectedObject; // Reset the PropertyGrid
}


МетодSetDisplayName () работает нормально и фактически изменяет DisplayName свойства во время выполнения!



    private void SetDisplayName()
{
Person person = propertyGrid1.SelectedObject as Person;
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(person)["Age"];
DisplayNameAttribute attribute = descriptor.Attributes[typeof(DisplayNameAttribute)] as DisplayNameAttribute;
FieldInfo field = attribute.GetType().GetField("_displayName", BindingFlags.NonPublic | BindingFlags.Instance);
field.SetValue(attribute, "The age");
}


Методы SetDescription () иSetCategory () почти идентичны методуSetDisplayName (), за исключением некоторых изменений типов и строк для доступа к закрытому члену каждого атрибута.



    private void SetDescription()
{
Person person = propertyGrid1.SelectedObject as Person;
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(person)["Age"];
DescriptionAttribute attribute = descriptor.Attributes[typeof(DescriptionAttribute)] as DescriptionAttribute;
FieldInfo field = attribute.GetType().GetField("description", BindingFlags.NonPublic |BindingFlags.Instance);
field.SetValue(attribute, "Age of the person");
}

private void SetCategory()
{
Person person = propertyGrid1.SelectedObject as Person;
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(person)["Age"];
CategoryAttribute attribute = descriptor.Attributes[typeof(CategoryAttribute)] as CategoryAttribute;
FieldInfo[] fields = attribute.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
FieldInfo field = attribute.GetType().GetField("categoryValue", BindingFlags.NonPublic | BindingFlags.Instance);
field.SetValue(attribute, "Info");
}


Оба метода SetDescription () и SetCategory () компилируют и запускают, но не выводят текст ProperytGrid. После последней строки каждого метода вы можете использовать IntelliSense , чтобы увидеть, что объект атрибута ( DescriptionAttribute и CategoryAttribute) имеет измененный член.



После запуска этих трех методов и сброса PropertyGrid (см. Метод button1 click); PropertyGrid изменил только атрибут DisplayName. Атрибутыописание иКатегория остаются неизменными.



Мне бы очень хотелось, чтобы вы помогли решить эту проблему. Пожалуйста любой предложение или решение?



Примечание 1:
Я не хочу, чтобы какие-либо ответы говорили, что это невозможно, и атрибуты могут быть установлены только во время разработки. Это неправда! Эта статья из CodeProject.com покажите пример локализации PropertyGrid и изменения атрибутов в среде выполнения. К сожалению, у меня есть проблема с областью действия примера для тех частей, которые мне нужны для решения этой проблемы.



Примечание 2:
Я хотел бы избежать использования файлов resouce. Это связано с тем, что чтобы локализация располагалась в разных языковых файлах. Каждый файл содержит набор индексов, каждый из которых имеет строковое значение. Все индексы и строковые значения загружаются в объект справочника. Для доступа к строке используется индекс. Я больше всего, к сожалению, пользуюсь этим решением.



С наилучшими пожеланиями,
/Mc_Topaz

568   4  

4 ответов:

Вот хорошая статья для глобализованная-свойство-сетка

Вы можете дать много файлов ресурсов для Person, чем будет локализована соответствующая сетка.

Вот три шага:

  1. наследовать от GlobalizedObject
  2. дайте файл ресурсов для человека с таким же именем (например.Человек.ж-КН.resx)
  3. измените срез нити, который вы хотите отобразить.

Вы можете попробовать, удачи!

Что вы могли бы сделать, это повторно использовать класс DynamicTypeDescriptor, описанный в моем ответе на этот вопрос здесь на SO: PropertyGrid Browsable not found for entity framework created property, как его найти?

Вот так:

  public Form1()
  {
      InitializeComponent();

      Person p = new Person();

      DynamicTypeDescriptor dt = new DynamicTypeDescriptor(typeof(Person));
      propertyGrid1.SelectedObject = dt.FromComponent(p);
  }

  private void button1_Click(object sender, EventArgs e)
  {
      DynamicTypeDescriptor dt = (DynamicTypeDescriptor)propertyGrid1.SelectedObject;
      DynamicTypeDescriptor.DynamicProperty dtp = (DynamicTypeDescriptor.DynamicProperty)dt.Properties["Age"];

      dtp.SetDisplayName("The age");
      dtp.SetDescription("Age of the person");
      dtp.SetCategory("Info");

      propertyGrid1.Refresh();
  }

Ответ Xudong125 решает проблему! Мне удалось обойти решение resource files, используя вместо этого статический источник. Это очень сложно объяснить...

Но создание классов, реализующих ICustomTypeDescriptor и PropertyDescriptor, - это правильный путь.

Ключ переопределить отображаемое имя, описание и методы категории в суб-класса дескриптор свойства. В этих переопределенных методах я указал на общедоступный статический источник и сумел получить строки I разыскиваемый.

/ Mc_Topaz

У меня есть другая причина изменить описание свойства и я нашел довольно грубое, но гораздо более простое решение для того, чтобы просто исправить описание, которое показано в сетке. Преимуществом для меня было то, что класс объекта, показанного в сетке свойств, требовал гораздо меньше изменений.

Моя ситуация была следующей: у меня есть два булевых свойства A и B, где B может использоваться только в том случае, если a задано. Если A ложно, я хочу сделать B доступным только для чтения и установить его описание в нечто вроде "это свойство может быть использовано только в том случае, если вы установите 'A' в True". В объектном коде я установил атрибут описания B для этого сообщения, аналогично тому, как это делает Mc_Topaz.

Чтобы только установить описание, показанное для выбранного свойства, в его правильное текущее значение, я использую следующий обработчик событий SelectedGridItemChanged для моей PropertyGrid с именем pgConfig:

    private void pgConfig_SelectedGridItemChanged(object sender, SelectedGridItemChangedEventArgs e)
    {
      GridItem giSelected = e.NewSelection;
      if ((giSelected != null) && (giSelected.PropertyDescriptor != null))
      {
        string sDescription = GetCurrentPropertyDescription(giSelected.PropertyDescriptor.Name);
        if ((sDescription != null) && (sDescription != giSelected.PropertyDescriptor.Description))
        {
          MethodInfo miSetStatusBox = pgConfig.GetType().GetMethod("SetStatusBox", BindingFlags.NonPublic | BindingFlags.Instance);
          if (miSetStatusBox != null)
            miSetStatusBox.Invoke(pgConfig, new object[] { giSelected.PropertyDescriptor.DisplayName, sDescription });
        }
      }
    }

В примере кода GetCurrentPropertyDescription является частной функцией, которая извлекает текущее свойство описание объекта, отображаемого в сетке свойств (m_da.Config в моем случае):

    private string GetCurrentPropertyDescription(string sPropertyName)
    {
      PropertyDescriptor oPropDescriptor = TypeDescriptor.GetProperties(m_da.Config.GetType())[sPropertyName];
      if (oPropDescriptor != null)
      {
        DescriptionAttribute oDescriptionAttr = (DescriptionAttribute)oPropDescriptor.Attributes[typeof(DescriptionAttribute)];
        if (oDescriptionAttr != null)
          return oDescriptionAttr.Description;
      }
      return null;
    }
Мое решение менее подходит, чем huoxudong125, если вы хотите полной глобализации, но если вы просто хотите динамические описания для некоторых ваших свойств без изменения наследования отображаемого объекта, это вариант. Недостатком моего метода является то, что базовые кэшированные объекты PropertyDescriptor сетки никогда не обновляются, поэтому SetStatusBox всегда будет вызываться дважды, если a выбирается свойство с измененным описанием, что неэффективно.

Comments

    Ничего не найдено.