Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[44.200.196.38] |
|
Сообщ.
#1
,
|
|
|
По заявкам посетителей - описание работы с 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 который будет управлять процессом редактирования и обязательно запомнить его во внешней переменной - авось пригодится: 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 с кучей элементов управления на нём. Не забывайте задавать событие по которому будет происходить закрытие вашео редактора: .... в методе EditValue ListBox lb=new ListBox(); //То что будет в выпадающем окне lb.SelectedValueChanged+=new EventHandler(lb_SelectedValueChanged); //Событие для закрытия окна lb.BorderStyle=BorderStyle.None; //Для красоты .... //Здесь заполняем ListBox edSvc.DropDownControl(lb);//Выпадаем окно //Этот метод блокирует поток пока не закрыто окно .... return myNewValue;//Возвращаем новое значение свойства Кроме того нам надо закрывать выпадающее окно при выборе элемента в списке: 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. К примеру вот так: 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 - проблем меньше будет). Делается это так (в примере набор свойтсв формируется из БД): 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 и многое другое |