PropertyGrid FAQ
    Сообщений 55    Оценка 2272 [+4/-0]         Оценить  
Система Orphus

PropertyGrid FAQ

Автор: Алексей Кирюшкин
The RSDN Group

Источник: RSDN Magazine #3-2006
Опубликовано: 06.12.2006
Исправлено: 10.12.2016
Версия текста: 1.6
Как заменить имя переменной в левой колонке “человеческим” именем свойства?
Как отобразить расширенную подсказку по свойству в нижнем окне?
Как сгруппировать свойства по категориям?
Как отобразить свойство, недоступное для редактирования?
Как заменить стандартные True/False в отображении свойств типа bool?
Как заменить стандартное отображение имен членов перечисления?
Как показать свою картинку для каждого значения из перечисления?
Как организовать выбор значения из выпадающего списка, формируемого программно?
Как избавиться от ошибки “Конструктор для типа "System.String" не найден” при редактировании свойств типа StringCollection?
Как реализовать отображение составного свойства?
Как организовать выбор файла с заданным расширением?
Как организовать редактирование свойства в собственной форме?
Как организовать редактирование свойства в выпадающем списке?
Как скрыть пароль при редактировании?
Как управлять видимостью свойства в зависимости от значения другого свойства?
Как избавиться от стандартного “(Collection)” в правой колонке для свойств-коллекций?
Как заменить стандартные подписи (Members, properties) в окне Collection Editor?
Как добавить в Collection Editor окно с расширенной подсказкой по редактируемым свойствам?
Как задать отличный от алфавитного порядок следования свойств внутри категории?
Как запомнить и восстановить положение разделителя колонок в PropertyGrid?
p.s.

Тестовый проект (VS2005)

PropertyGrid – удобный компонент для визуального редактирования свойств объектов. Объект для редактирования задается в дизайнере WinForms, либо непосредственно в коде:

      private PersonData _personData = new PersonData();
propertyGrid1.SelectedObject = _personData;

Хотя многие стандартные типы PropertyGrid редактировать умеет (см. рисунок 1), любое практическое применение требует все же ручной доводки.


Рисунок 1.

В данном FAQ собраны ответы на некоторые вопросы, возникающие при использовании PropertyGrid.

Как заменить имя переменной в левой колонке “человеческим” именем свойства?

Для этого предназначен атрибут DisplayName:

        using System.ComponentModel;
...
      [DisplayName("День рождения")]
      public DateTime Birthday
      {
         get { return _birthday; }
         set { _birthday = value; }
      }


Рисунок 2.

Как отобразить расширенную подсказку по свойству в нижнем окне?

Для этого предназначен атрибут Description:

      [DisplayName("День рождения")]
      [Description("День рождения он день рождения и есть")]
      public DateTime Birthday
      {
         get { return _birthday; }
         set { _birthday = value; }
      }


Рисунок 3.

Как сгруппировать свойства по категориям?

Используйте атрибут Category:

[DisplayName("ФИО")]
[Description("Фамилия Имя Отчество")]
[Category("1. Идентификация")]
publicstring Name
{
  get { return _name; }
  set { _name = value; }
}

/// <summary>/// День рождения/// </summary>
[DisplayName("День рождения")]
[Description("День рождения он день рождения и есть")]
[Category("2. Общие")]
public DateTime Birthday
{
  get { return _birthday; }
  set { _birthday = value; }
}


Рисунок 4.

Как отобразить свойство, недоступное для редактирования?

Можно либо само свойство сделать read-only (оставив только get), либо использовать атрибут ReadOnly:

[DisplayName("ID")]
[Description("Идентификатор")]
[Category("1. Идентификация")]
[ReadOnly(true)]
publicint Id
{
  get { return _id; }
  set { _id = value; }
}


Рисунок 5.

Как заменить стандартные True/False в отображении свойств типа bool?

Используйте атрибут TypeConverter:

[DisplayName("Наличие страховки")]
[Description("Наличие страховки")]
[Category("3. Дополнительно")]
[TypeConverter(typeof(BooleanTypeConverter))]
publicbool Insurance
{
  get { return _insurance; }
  set { _insurance = value; }
}

Реализация BooleanTypeConverter проста:

        class
        BooleanTypeConverter : BooleanConverter
{
  publicoverrideobject ConvertTo(ITypeDescriptorContext context, 
    CultureInfo culture,
    object value, 
    Type destType)
  {
    return (bool)value ? 
      "Есть" : "Нет";
  }

  publicoverrideobject ConvertFrom(ITypeDescriptorContext context, 
    CultureInfo culture,
    object value)
  {
    return (string)value == "Есть";
  }
} 


Рисунок 6.

Как заменить стандартное отображение имен членов перечисления?

Необходимо задать атрибут Description с нужным именем для каждого члена перечисления:

        enum SEX
{
  [Description("Муж.")]
  Man,
  [Description("Жен.")]
  Woman,
  [Description("Неизв.")]
  Unknown
}

Реализовать EnumTypeConverter, осуществляющий преобразование к строке с учетом атрибута Description:

EnumTypeConverter

и задать атрибут TypeConverter для отображаемого свойства:

[DisplayName("Пол")]
[Description("Пол")]
[Category("2. Общие")]
[TypeConverter(typeof(EnumTypeConverter))]
public SEX Sex
{
  get {return _sex;}
  set {_sex = value;}
}


Рисунок 7.

Как показать свою картинку для каждого значения из перечисления?

Необходимо реализвать своего наследника от UITypeEditor с кодом отрисовки (в данном случае изображения хранятся в ресурсах с именами, соответствующими именам членов перечисления):

        /// <summary>
        /// Добавляет картинки, соответствующие каждому члену перечисления
        /// </summary>
        public
        class
        SexEditor : UITypeEditor
{
  publicoverridebool GetPaintValueSupported(ITypeDescriptorContext context)
  {
    returntrue;
  }

  publicoverridevoid PaintValue(PaintValueEventArgs e)
  {
    // картинки хранятся в ресурсах с именами, соответствующими// именам каждого члена перечисления SEX
string resourcename = ((SEX)e.Value).ToString();

    // достаем картинку из ресурсов
    Bitmap sexImage =
      (Bitmap)Resources.ResourceManager.GetObject(resourcename);
    Rectangle destRect = e.Bounds;
    sexImage.MakeTransparent();

    // и отрисовываем
    e.Graphics.DrawImage(sexImage, destRect);
  }
} 

и привязать его с помощью атрибута Editor к редактируемому свойству:

[DisplayName("Пол")]
[Description("Пол")]
[Category("2. Общие")]
[TypeConverter(typeof(EnumTypeConverter))]
[Editor(typeof(SexEditor), typeof(UITypeEditor))]
public SEX Sex
{
  get { return _sex; }
  set { _sex = value; }
}


Рисунок 8.

Как организовать выбор значения из выпадающего списка, формируемого программно?

Необходимо реализовать TypeConverter, предоставляющий список, из которого можно будет делать выбор:

        /// <summary>
        /// TypeConverter для списка должностей
        /// </summary>
        class
        PostTypeConverter : StringConverter
{
  /// <summary>/// Будем предоставлять выбор из списка/// </summary>publicoverridebool GetStandardValuesSupported(
    ITypeDescriptorContext context)
  {
    returntrue;
  }

  /// <summary>/// ... и только из списка/// </summary>publicoverridebool GetStandardValuesExclusive(
    ITypeDescriptorContext context)
  {
    // false - можно вводить вручную// true - только выбор из спискаreturntrue;
  }

  /// <summary>/// А вот и список/// </summary>publicoverride StandardValuesCollection GetStandardValues(
    ITypeDescriptorContext context)
  {
    // возвращаем список строк из настроек программы// (базы данных, интернет и т.д.)returnnew StandardValuesCollection(Settings.Default.PostList);
  }
}

В данном случае возвращается список строк из настроек программы. Затем нужно задать этот класс в качестве параметра атрибута TypeConverter для редактируемого свойства:

        /// <summary>
        /// Должность
        /// </summary>
[DisplayName("Должность")]
[Description("Занимаемая должность согласно штатного расписания")]
[Category("3. Дополнительно")]
[TypeConverter(typeof(PostTypeConverter))]
publicstring Post
{
  get { return _post; }
  set { _post = value; }
}


Рисунок 9.

Аналогично реализуется и выбор из списка фиксированных значений например double – см. класс PossibleValuesTypeConverter в тестовом проекте.

Как избавиться от ошибки “Конструктор для типа "System.String" не найден” при редактировании свойств типа StringCollection?

Свойства типа StringCollection

      [DisplayName("Дети")]
      [Description("Дети")]
      [Category("2. Общие")]
      [PropertyOrder(30)]
      publicStringCollection Children
      {
         get { return _children; }
         set { _children = value; }
      }

открываются для редактирования в PropertyGrid, но при попытке добавить строку к списку выдается сообщение об ошибке “Конструктор для типа "System.String" не найден” (Constructor on type 'System.String' not found):


Рисунок 10.

Исправить положение можно добавив атрибут Editor со следующими параметрами:

      [DisplayName("Дети")]
      [Description("Дети")]
      [Category("2. Общие")]
      [PropertyOrder(30)]
      [Editor(
        "System.Windows.Forms.Design.StringCollectionEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
        "System.Drawing.Design.UITypeEditor,System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
      )]
      public StringCollection Children
      {
         get { return _children; }
         set { _children = value; }
      }

Окно редактирования StringCollection примет при этом следующий вид:


Рисунок 11.

Как реализовать отображение составного свойства?

Бывает так, что тип свойства является сложным объектом, также имеющим свойства. Хотелось бы иметь возможность редактировать свойства этого объекта в раскрывающемся списке. К классу, используемому в качестве типа составного свойства, необходимо применить атрибут TypeConverter с ExpandableObjectConverter в качестве параметра:

        /// <summary>
        /// Данные, входящие в адрес
        /// </summary>
[TypeConverter(typeof(ExpandableObjectConverter))]
class AddressData
{
  /// <summary>/// Конструктор/// </summary>public AddressData(string town, string street, uint house) 
  {
    _town = town;
    _street = street;
    _house = house;
  }

  privatestring _town;

  /// <summary>/// Город/// </summary>
  [DisplayName("Город")]
  [Description("Наименование населенного пункта")]
  publicstring Town
  {
    get { return _town; }
    set { _town = value; }
  }

  privatestring _street;

  /// <summary>/// Улица/// </summary>
  [DisplayName("Улица")]
  [Description("Название улицы")]
  publicstring Street
  {
    get { return _street; }
    set { _street = value; }
  }

  privateuint _house;

  /// <summary>/// Номер дома/// </summary>
  [DisplayName("Дом")]
  [Description("Номер дома")]
  publicuint House
  {
    get { return _house; }
    set { _house = value; }
  }

  /// <summary>/// Представление в виде строки/// </summary>publicoverridestring ToString()
  {
    return _town + ", " + _street + " - " + _house;
  }

} 

Задавать дополнительные атрибуты для редактируемого свойства не нужно:

[DisplayName("Место жительства")]
[Description("Адрес")]
[Category("3. Дополнительно")]
publicAddressData Address
{
   get { return _address; }
   set { _address = value; }
}


Рисунок 12.

Как организовать выбор файла с заданным расширением?

Задайте атрибут Editor:

[DisplayName("Личное дело")]
[Description("Имя файла личного дела")]
[Category("3. Дополнительно")]
[Editor(typeof(DocFileEditor), typeof(UITypeEditor))]
publicstring PersonalFileName
{
  get { return _personalfilename; }
  set { _personalfilename = value; }
}

Собственно фильтр расширений задается в DocFileEditor:

        class
        DocFileEditor : FileNameEditor
{
  /// <summary>/// Настройка фильтра расширений /// </summary>protectedoverridevoid InitializeDialog(OpenFileDialog ofd)
  {
    ofd.CheckFileExists = false;
    ofd.Filter = "Doc files (*.doc)|*.doc|All files (*.*)|*.*";
  }
}


Рисунок 13.


Рисунок 14.

Как организовать редактирование свойства в собственной форме?

Необходимо реализовать наследника UITypeEditor, обеспечивающего вызов нужной формы (в данном случае IPAddressEditorForm), передачу ей редактируемого значения и получение результата:

        public
        class
        IPAddressEditor : UITypeEditor 
{

  /// <summary>/// Реализация метода редактирования/// </summary>publicoverride Object EditValue(
    ITypeDescriptorContext context, 
    IServiceProvider provider, 
    Object value)
  {
    if((context != null) && (provider != null)) 
    {
      IWindowsFormsEditorService svc =
        (IWindowsFormsEditorService)
        provider.GetService(typeof(IWindowsFormsEditorService));

      if(svc!= null) 
      {
        using (IPAddressEditorForm ipfrm = 
          new IPAddressEditorForm((IPAddress)value))
        {
          if (svc.ShowDialog(ipfrm) == DialogResult.OK)
          {
            value = ipfrm.IP;
          }
        }
      }
    }

    returnbase.EditValue(context, provider, value);
  }

  /// <summary>/// Возвращаем стиль редактора - модальное окно/// </summary>publicoverride UITypeEditorEditStyle GetEditStyle(
ITypeDescriptorContext context)
  {
    if (context != null)
      returnUITypeEditorEditStyle.Modal;
    elsereturnbase.GetEditStyle(context);
  }

}

а затем привязать его к редактируемому свойству при помощи атрибута Editor:

[DisplayName("IP адрес")]
[Description("IP адрес компьютера рабочего места")]
[Category("3. Дополнительно")]
[Editor(typeof(IPAddressEditor), typeof(UITypeEditor))]
public IPAddress IPaddress
{
  get { return _ipAddress; }
  set { _ipAddress = value; }
}


Рисунок 15.

Как организовать редактирование свойства в выпадающем списке?

Опять же, реализовать своего наследника UITypeEditor, отображающего выпадающий список с нужным control-ом, передать ему исходное значение редактируемого свойства и принять результат по окончании редактирования (ForeignLangsControl – составной UserControl c элементами, необходимыми для редактирования свойства):

        public
        class
        ForeignLangsDropDownEditor : UITypeEditor 
{

  /// <summary>/// Реализация метода редактирования/// </summary>publicoverride Object EditValue(
    ITypeDescriptorContext context, 
    IServiceProvider provider, 
    Object value)
  {
    if((context != null) && (provider != null)) 
    {
      IWindowsFormsEditorService svc =
        (IWindowsFormsEditorService)
        provider.GetService(typeof(IWindowsFormsEditorService));

      if(svc!= null) 
      {
        ForeignLangsControl flctrl = 
new ForeignLangsControl((ForeignLangs)value);
        flctrl.Tag = svc;

        svc.DropDownControl(flctrl);

        value = flctrl.Foreignlangs;
      }
    }

    returnbase.EditValue(context, provider, value);
  }

  /// <summary>/// Возвращаем стиль редактора - выпадающее окно/// </summary>publicoverride UITypeEditorEditStyle GetEditStyle(
ITypeDescriptorContext context)
  {
    if (context != null)
      returnUITypeEditorEditStyle.DropDown;
    elsereturnbase.GetEditStyle(context);
  }

}

и, соответственно, привязать полученный редактор к редактируемому свойству:

[DisplayName("Иностанные языки")]
[Description("Какими иностранными языками владеет")]
[Category("3. Дополнительно")]
[Editor(typeof(ForeignLangsDropDownEditor), typeof(UITypeEditor))]
public ForeignLangs Foreignlangs
{
  get { return _fl; }
  set { _fl = value; }
}


Рисунок 16.

Как скрыть пароль при редактировании?

Используйте атрибут PasswordPropertyText:

      [DisplayName("Пароль")]
      [Description("Пароль для доступа на сервер компании")]
      [Category("3. Дополнительно")]
      [PropertyOrder(80)]
      [PasswordPropertyText(true)]
      publicstring Password
      {
         get { return _password; }
         set { _password = value; }
      }


Рисунок 17.

Как управлять видимостью свойства в зависимости от значения другого свойства?

Стандартный атрибут Browsable позволяет задавать видимость свойства в PropertyGrid только на этапе написания кода. Чтобы управлять видимостью свойства в зависимости от значения другого свойства настраиваемого объекта, понадобятся новый атрибут – DynamicPropertyFilter и базовый класс – FilterablePropertyBase:

DynamicPropertyFilter

Указываем базовый класс для класса настраиваемого объекта:

        /// <summary>
        /// Данные для редактирования в PropertyGrid
        /// </summary>
        class PersonData : FilterablePropertyBase
{
  public PersonData()
...
ПРИМЕЧАНИЕ

Решение не очень красивое, так как делает данные зависимыми от средств отображения и редактирования. – прим. ред.

А для свойства, видимость которого зависит от другого свойства – атрибут DynamicPropertyFilter:

        /// <summary>
        /// Должность
        /// </summary>
[DisplayName("Должность")]
[Description("Занимаемая должность согласно штатному расписанию")]
[Category("3. Дополнительно")]
[TypeConverter(typeof(PostTypeConverter))]
publicstring Post
{
  get { return _post; }
  set { _post = value; }
}

/// <summary>/// Имя файла личного дела/// </summary>
[DisplayName("Личное дело")]
[Description("Имя файла личного дела")]
[Category("3. Дополнительно")]
[Editor(typeof(DocFileEditor), typeof(UITypeEditor))]
[DynamicPropertyFilter("Post", "Уборщик, Инженер, Начальник отдела, Начальник сектора, Секретарь")]
publicstring PersonalFileName
{
  get { return _personalfilename; }
  set { _personalfilename = value; }
}

Также нужно добавить обработчик события PropertyGrid – PropertyValueChanged:

        private
        void propertyGrid1_PropertyValueChanged(
  object s, PropertyValueChangedEventArgs e)
{
  propertyGrid1.Refresh();
}

В данном случае свойство PersonalFileName (Имя файла личного дела) будет показано в PropertyGrid только тогда, когда свойство Post (Должность) будет иметь любое из указанных в атрибуте значений:


Рисунок 18.

Если переключить свойство Должность в значение, отсутствующее в параметре атрибута DynamicPropertyFilter, свойство Личное дело исчезнет из списка отображаемых свойств:


Рисунок 19.

Аналогично, вторым параметром атрибута DynamicPropertyFilter можно передавать значения параметров других типов, например, перечисления:

[DynamicPropertyFilter("Sex", "Woman,Unknown")]

или bool:

[DynamicPropertyFilter("Insurance", "True")]

Как избавиться от стандартного “(Collection)” в правой колонке для свойств-коллекций?

Используйте атрибут TypeConverter:

[DisplayName("Телефоны")]
[Description("Список номеров телефонов")]
[Category("3. Дополнительно")]
[TypeConverter(typeof(CollectionTypeConverter))]
public List<PhoneNumber> Phones
{
  get { return _phones; }
  set { _phones = value; }
}

Реализация CollectionTypeConverter проста:

        class
        CollectionTypeConverter : TypeConverter
{
  /// <summary>/// Только в строку/// </summary>publicoverridebool CanConvertTo(
ITypeDescriptorContext context, Type destType)
  {
    return destType == typeof (string);
  }

  /// <summary>/// И только так/// </summary>publicoverrideobject ConvertTo(
ITypeDescriptorContext context, CultureInfo culture,
    object value, Type destType)
  {
    return"< Список... >";
  }
}


Рисунок 20.

При переходе к редактированию коллекции отобразится стандартное окно Collection Editor с данными редактируемой коллекции:


Рисунок 21.

Как заменить стандартные подписи (Members, properties) в окне Collection Editor?

“Members”, “properties”, “Add” и “Remove” можно заменить русскими аналогами (при наличии установленного .NET Framework 2.0 Russian Language Pack) переключением CurrentUICulture,как это сделано в методе Main() тестовой программы:

Thread.CurrentThread.CurrentUICulture = new CultureInfo("ru-RU", false);

Однако, если надпись “Члены” над списком телефонов вас тоже не устраивает, можно применить и более радикальный способ. Заодно решим и следующую проблему.

Как добавить в Collection Editor окно с расширенной подсказкой по редактируемым свойствам?

Добавим к свойству-коллекции атрибут Editor со специализированной версией CollectionEditor:

[DisplayName("Телефоны")]
[Description("Список номеров телефонов")]
[Category("3. Дополнительно")]
[TypeConverter(typeof(CollectionTypeConverter))]
[Editor(typeof(PhoneNumbersCollectionEditor), typeof(UITypeEditor))]
public List<PhoneNumber> Phones
{
  get { return _phones; }
  set { _phones = value; }
}

Новая реализация CollectionEditor, PhoneNumbersCollectionEditor, умеет запоминать положение и размеры своего окна, меняет стандартные подписи на соответствующие редактируемым данным и добавляет окно с расширенной подсказкой по свойствам:

PhoneNumbersCollectionEditor

Результат что называется, налицо:


Рисунок 22.

Как задать отличный от алфавитного порядок следования свойств внутри категории?

Нужно реализовать новый атрибут для задания порядка сортировки – PropertyOrderAttribute, и наследника ExpandableObjectConverter (PropertySorter), возвращающего список свойств, упорядоченный согласно значениям, заданным для них в атрибуте PropertyOrder:

PropertySorter

Задаем атрибут TypeConverter с параметром PropertySorter для всего класса с настраиваемыми свойствами:

        /// <summary>
        /// Данные для редактирования в PropertyGrid
        /// </summary>
[TypeConverter(typeof(PropertySorter))]
class PersonData : FilterablePropertyBase
{

и указываем атрибут PropertyOrder для упорядочиваемых свойств:

[DisplayName("Место жительства")]
[Description("Адрес")]
[PropertyOrder(30)]
[Category("3. Дополнительно")]
public AddressData Address
...

[DisplayName("Наличие страховки")]
[Description("Наличие страховки")]
[PropertyOrder(40)]
[Category("3. Дополнительно")]
[TypeConverter(typeof(BooleanTypeConverter))]
publicbool Insurance
...

[DisplayName("Телефоны")]
[Description("Список номеров телефонов")]
[PropertyOrder(50)]
[Category("3. Дополнительно")]
[TypeConverter(typeof(CollectionTypeConverter))]
[Editor(typeof(PhoneNumbersCollectionEditor), typeof(UITypeEditor))]
public List<PhoneNumber> Phones
...

[DisplayName("Личное дело")]
[Description("Имя файла личного дела")]
[Category("3. Дополнительно")]
[PropertyOrder(20)]
[Editor(typeof(DocFileEditor), typeof(UITypeEditor))]
[DynamicPropertyFilter("Post", 
  "Уборщик, Инженер, Начальник отдела, Начальник сектора, Секретарь")]
publicstring PersonalFileName
...

[DisplayName("IP-адрес")]
[Description("IP-адрес компьютера рабочего места")]
[PropertyOrder(70)]
[Category("3. Дополнительно")]
[Editor(typeof(IPAddressEditor), typeof(UITypeEditor))]
public IPAddress IPaddress
...

[DisplayName("Иностранные языки")]
[Description("Какими иностранными языками владеет")]
[PropertyOrder(60)]
[Category("3. Дополнительно")]
[Editor(typeof(ForeignLangsDropDownEditor), typeof(UITypeEditor))]
public ForeignLangs Foreignlangs
...

[DisplayName("Должность")]
[Description("Занимаемая должность согласно штатного расписания")]
[Category("3. Дополнительно")]
[PropertyOrder(10)]
[TypeConverter(typeof(PostTypeConverter))]
publicstring Post
...

результат – свойства в заданном порядке:


Рисунок 23.

Как запомнить и восстановить положение разделителя колонок в PropertyGrid?

Это можно сделать при помощи следующих функций:

        /// <summary>
        /// Сохранение положения разделителя в гриде
        /// </summary>
        private
        void
        SaveGridSplitterPos()
{
  Type type = propertyGrid1.GetType();
  FieldInfo field = type.GetField("gridView", 
    BindingFlags.NonPublic | BindingFlags.Instance);

  object valGrid = field.GetValue(propertyGrid1);
  Type gridType = valGrid.GetType();
  Settings.Default.GridSplitterPos = (int)gridType.InvokeMember(
"GetLabelWidth",
    BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance,
    null,
    valGrid, newobject[] { });

  Trace.WriteLine("SaveGridSplitterPos(): " 
    + Settings.Default.GridSplitterPos);
}

/// <summary>/// Восстановление положения разделителя в гриде/// </summary>privatevoidRestoreGridSplitterPos()
{
  try
  {
    Type type = propertyGrid1.GetType();
    FieldInfo field = type.GetField("gridView", 
      BindingFlags.NonPublic | BindingFlags.Instance);

    object valGrid = field.GetValue(propertyGrid1);
    Type gridType = valGrid.GetType();
    gridType.InvokeMember("MoveSplitterTo",
      BindingFlags.NonPublic | BindingFlags.InvokeMethod 
        | BindingFlags.Instance,
      null,
      valGrid, newobject[] { Settings.Default.GridSplitterPos });

    Trace.WriteLine("RestoreGridSplitterPos(): " 
      + Settings.Default.GridSplitterPos);
  }
  catch
  {
    Trace.WriteLine("MainForm::RestoreGridSplitterPos() exception");
  }
}

Вызываются они соответственно перед закрытием и перед загрузкой окна формы, содержащей PropertyGrid:

        private
        void
        MainForm_Load(object sender, EventArgs e)
{
  Trace.WriteLine("MainForm_Load()");

  //устанавливаем редактируемый объект
  propertyGrid1.SelectedObject = _personData;

  // восстанавливаем положение окна
  RestorePos();

  // и разделителя колонок в гридеRestoreGridSplitterPos();
}

privatevoidMainForm_FormClosing(object sender, FormClosingEventArgs e)
{
  Trace.WriteLine("MainForm_FormClosing()");

  // запоминаем положение окна
  SavePos();

  // и разделителя в гридеSaveGridSplitterPos();

  // сохраним возможно измененные значения параметров
  Settings.Default.Save();
}

p.s.

ПРЕДУПРЕЖДЕНИЕ

Все имена, фамилии и ip-адреса вымышлены. Любые совпадения случайны. В ходе экспериментов ни один Иван Иванович из г.Бобруйска не пострадал.


Эта статья опубликована в журнале RSDN Magazine #3-2006. Информацию о журнале можно найти здесь
    Сообщений 55    Оценка 2272 [+4/-0]         Оценить