На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: maxim84_
  
> Вся правда о PropertyGrid , Почти вся :)
    По заявкам посетителей - описание работы с PropertyGrid в примерах

    Примечание: все описываемые классы лежат в нэймспейсах System.Windows.Forms, System.ComponentModel и System.Drawing.Design

    Что такое PropertyGrid и что с ним можно сделать
    PropertyGrid это одн из стандартных компонентов wondows forms который есть как в первой, так и во второй версии .NET Framework. Этот компонент позволяет показывать и редактировать свойства практически любых объектов, не требуя написания дополнительного кода.
    В простейшем случае вы пишете свои классы с использовнием свойств (properties), помещаете на форму PropertyGrid и присваеваете его свойству SelectedObject ссылку на обект, после чего с помощью PropertyGrid можно увидеть и изменить любые свойтсва объекта.
    Свойства объекта, если они имеют простой тип (такой как int, string, enum, bool, Size, Color и некоторых других) редактируются средствами PropertyGrid. Если же необходимо редактировать более сложные свойства, к примеру массивы или воженные объекты существует два метода - применение TypeConverter и использование UITypeEditor. Кстати оба эти метода можно сочетать.
    Применение TypeConverter позволяет реализовать преобразование типа данных в string и обратно (как это например реализовано для редактирования System.Drawing.Size), а так же управлять набором совйств объекта (это очень мощное по своей функциональности средство).
    Применение UITypeEditor позволяет добавить редактор к свойтсву, либо в виде выпадающего окошка (обычно на котором отображается список вариантов), либо в виде кнопки с многоточием, нажатие на которую обрабатывается любым способом.

    Атрибуты управляющие PropertyGrid
    Существует несколько атрибутов, управляющих видом свойств в PropertyGrid:
    [Browsable(bool)] - показывать свойтсво или нет
    [ReadOnly(bool)] - возможность редактирования свойства
    [Category(string)] - группа свойства
    [Description(string)] - описание свойства

    использование UITypeEditor
    Для того чтобы свойство вашего типа можно бало легко редактировать необходимо написть свой класс редактора, унаследованный от UITypeEditor, в котором реализовать методы:
    UITypeEditorStyle GetEditStyle(ITypeDescriptorContext)
    object EditValue(ITypeDescriptorContext,IServiceProvider,object)
    Первый метод задаёт тип редактора - DropDown или Modal и в реализации абсолютно тривиален, второй обеспечивает собственно редактирование. При этом есть несколько ньюансов.
    Прежде всего при редактировании значения следует получить IWindowsFormsEditorService который будет управлять процессом редактирования и обязательно запомнить его во внешней переменной - авось пригодится:
    ExpandedWrap disabled
      private IWindowsFormsEditorService edSvc=null;
      public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) {
          edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
          if(edSvc==null)return value;
      .....

    В случае если применяется DorpDown то после этого применяя метод DropDownControl мы показываем выпадающее окошко с элементом управления на нём. Это может быть как ListBox, тогда наш редактор будет напоминать ComboBox, либо что-то другое, к примеру Panel с кучей элементов управления на нём.
    Не забывайте задавать событие по которому будет происходить закрытие вашео редактора:
    ExpandedWrap disabled
      .... в методе EditValue
          ListBox lb=new ListBox(); //То что будет в выпадающем окне
          lb.SelectedValueChanged+=new EventHandler(lb_SelectedValueChanged); //Событие для закрытия окна
          lb.BorderStyle=BorderStyle.None; //Для красоты
      .... //Здесь заполняем ListBox
          edSvc.DropDownControl(lb);//Выпадаем окно
          //Этот метод блокирует поток пока не закрыто окно
      ....
          return myNewValue;//Возвращаем новое значение свойства

    Кроме того нам надо закрывать выпадающее окно при выборе элемента в списке:
    ExpandedWrap disabled
      private void lb_SelectedValueChanged(object sender, EventArgs e) {
          if(edSvc!=null) //Вот и пригодился edSvc
              edSvc.CloseDropDown();
      }

    Если же используется стиль Modal то в методе EditValue можно делать всё что угодно - создавать окна, и т.п., необхоимо лишь вернуть новое значение свойства

    После того как UITypeEditor написан, необходимо назначить его свойству. Это можно сделать с помощью атрибута Editor применённого к классу свойтсва (тогда все свойства данного типа будут редактироваться им), или тем же атрибутом на конкретном свойтсве (редактор будет только для него). Также можно это сделать реализовав в TypeConverter метод GetEditor.


    Использование TypeConverter
    TypeConverter, наряду с его обычным использованием для преобразования типов данных может применяться совместно с PropertyGrid для задания вида свойств в редакторе.
    Для возможности редактирования сложных свойств (таких как, к примеру, Color) в виде строки необхожимо реализовать в классе унаследованом от TypeConverter методы:
    bool CanConvertFrom(ITypeDescriptorContext,Type)
    object ConvertFrom(ITypeDescriptorContext,System.Globalization.CultureInfo,object)
    bool CanConvertTo(ITypeDescriptorContext,Type)
    object ConvertTo(ITypeDescriptorContext,System.Globalization.CultureInfo,object,Type)

    Реализовать эти методы надо для Type=string. К примеру вот так:
    ExpandedWrap disabled
      public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
          if(sourceType==typeof(string))return true;
          return base.CanConvertFrom (context, sourceType);
      }
      public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) {
          if(value is string){
              string s=value as String;
              int ld=s.LastIndexOfAny("0123456789-Ee.,".ToCharArray());
              if(ld==-1){
                  return double.Parse(s.Trim(),System.Globalization.NumberStyles.Any);
              }
              string mul=s.Substring(ld+1);
              s=s.Substring(0,ld+1);
              double v=double.Parse(s.Trim(),System.Globalization.NumberStyles.Any);
              switch(mul.ToLower().Trim()){
                  case "кгц":case "к":case "k":case "khz":
                      v*=1000;
                      break;
                  case "мгц":case "м":case "m":case "mhz":
                      v*=1000000;
                      break;
                  case "ггц":case "г":case "g":case "ghz":
                      v*=1000000000;
                      break;
              }
              return new Frequency(v);
          }
          return base.ConvertFrom (context, culture, value);
      }
      public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
          if(destinationType==typeof(string))return true;
          return base.CanConvertTo (context, destinationType);
      }
      public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) {
          if(destinationType==typeof(string)&&value!=null){
              return value.ToString();
          }
          return base.ConvertTo (context, culture, value, destinationType);
      }


    Также с помощью TypeConverter можно реализовавыть собственный набор свойисв для любых классов. Делается это с помощью перегрузки метода GetProperties и метода GetPropertiesSupported. Однако здесь всплывает неслабый подводный камень. Возвращать из этого надо коллекцию PropertyDescriptor, а все стандартные его наследники НЕ облада.т открытыми конструкторами, так что придётся писать свой.
    Кроме того собственный PropertyDescriptor позволяет получить полный контроль над PropertyGrid - динамичесикй список свойств, изменение типа свойств - всё это возможно. В PropertyDescriptor как минимум необходимо реализовать GetValue и SetValue (кстати лучше наследоваться от SimplePropertyDescriptor - проблем меньше будет). Делается это так (в примере набор свойтсв формируется из БД):
    ExpandedWrap disabled
      internal class VCol{
          internal BasicInsuranceCompanyImplementation ic;
          internal XtensionInterface.DSAutoIns.InsuranceSessionRow profile;
          public VCol(BasicInsuranceCompanyImplementation ic,XtensionInterface.DSAutoIns.InsuranceSessionRow profile){
              this.ic=ic;
              this.profile=profile;
          }
      }
      private class VColTC:TypeConverter{
          protected class MyPD:SimplePropertyDescriptor{
              private DSAutoIns.ParamsRow pr;
              public MyPD(DSAutoIns.ParamsRow pr,Type componentType,Type propertyType):base(componentType,pr.IsStockParamNull()?pr.Name:pr.StockParamsRow.Name,propertyType){
                  this.pr=pr;
              }
              public override object GetValue(object o){
      //Не сильно инетересно - чтение значения из БД
      //-------------8<------------
                  VCol vc=o as VCol;
                  VValue rv=new VValue();
                  rv.SetEmpty();
                  if(vc==null)return rv;
                  if(pr.IsStockParamNull()){
                      DSAutoIns.ValuesDataTable dt=(DSAutoIns.ValuesDataTable)SBAS.XtensionInterface.GlobalDS.Instance.Tables["Values"];
                      DSAutoIns.ValuesRow r;
                      if((r=dt.FindBySessionCompanyParam(vc.profile.id,vc.ic.id,pr.id))!=null){
                          rv.dname=(string)r.ParamValuesRow.Name;
                          rv.v=(int)r["Value"];
                      }
                  }else{
                      DSAutoIns.StockValuesDataTable dt=(DSAutoIns.StockValuesDataTable)SBAS.XtensionInterface.GlobalDS.Instance.Tables["StockValues"];
                      DSAutoIns.StockValuesRow r=dt.FindBySessionParam(vc.profile.id,pr.StockParamsRow.id);
                      if(r!=null){
                          rv.dname=(string)r.StockParamValuesRow.Name;
                          rv.v=(int)r["Value"];
                      }
                  }
                  return rv;
      //-------------8<------------
              }
              public override void SetValue(object o,object vx){
      //Не сильно инетересно - запись значения в БД
      //-------------8<------------
                  VCol vc=o as VCol;
                  VValue vv=(VValue)vx;
                  int v=vv.v;
                  if(vc==null)return;
                  if(pr.IsStockParamNull()){
                      DSAutoIns.ValuesDataTable dt=(DSAutoIns.ValuesDataTable)SBAS.XtensionInterface.GlobalDS.Instance.Tables["Values"];
                      DataRow r=dt.FindBySessionCompanyParam(vc.profile.id,vc.ic.id,pr.id);
                      if(r==null){
                          DSAutoIns.ParamValuesRow vr=((DSAutoIns.ParamValuesDataTable)SBAS.XtensionInterface.GlobalDS.Instance.Tables["ParamValues"]).FindByid((int)v);
                          dt.AddValuesRow(vc.profile,vc.ic.row,pr,vr);
                      }else{
                          r["Value"]=v;
                      }
                  }else{
                      DSAutoIns.StockValuesDataTable dt=(DSAutoIns.StockValuesDataTable)SBAS.XtensionInterface.GlobalDS.Instance.Tables["StockValues"];
                      DataRow r=dt.FindBySessionParam(vc.profile.id,pr.StockParamsRow.id);
                      if(r==null){
                          DSAutoIns.StockParamValuesRow vr=((DSAutoIns.StockParamValuesDataTable)SBAS.XtensionInterface.GlobalDS.Instance.Tables["StockParamValues"]).FindByid((int)v);
                          dt.AddStockValuesRow(vc.profile,pr.StockParamsRow,vr);
                      }else{
                          r["Value"]=v;
                      }
                  }
      //-------------8<------------
              }
          }
      //Гвоздь программы - формирование списка свойств
          public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) {
              PropertyDescriptorCollection pdc=new PropertyDescriptorCollection(null);
              Type t=this.GetType();
              VCol vc=(VCol)value;
              DSAutoIns.CompaniesRow cr=((DSAutoIns.CompaniesDataTable)SBAS.XtensionInterface.GlobalDS.Instance.Tables["Companies"]).FindByid(vc.ic.id);
              MyPD sc;
              DSAutoIns.ParamsRow[] Params=cr.GetParamsRows();
      //Для каждой записи из БД (каждого свойства) создаётся PropertyDescriptor
              foreach(DSAutoIns.ParamsRow pr in Params){
                  sc=new MyPD(pr,t,typeof(Object));
                  pdc.Add(sc);
              }
              return pdc;
          }
      //Сообщаем что будем использовать свойства
          public override bool GetPropertiesSupported(ITypeDescriptorContext context) {
              return true;
          }
      }

    Для расширения функциональности можно реализовать следующие свойтсва PropertyDescriptor:
    Attributes - здесь можно управлять ReadOnly свойства
    Category
    Description
    DisplayName

    И методы:
    CanResetValue
    ResetValue
    GetEditor

    Что не описано в данном FAQ
    IExtendedProperties
    и многое другое :)
    0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
    0 пользователей:


    Рейтинг@Mail.ru
    [ Script execution time: 0,0695 ]   [ 15 queries used ]   [ Generated: 29.03.24, 14:56 GMT ]