Attribute Editor
Typeconverter
In the first part of this seriesArticleI have discussed the relationship between control development and propertygrid. I wonder if you have a comprehensive understanding of propertygrid and whether you have made a project, pull propertygrid in and drum?
"New stove"
Now let's think about the question: what is the result of propertygrid's failure to split properties and events into two tabs for display?
It's too messy.
If the controls you designed have many attributes and some of them are highly correlated or operate on one aspect, we can classify them and put them together. How can we do this?
You can specify the following attributes for this control class:
[Propertytab (typeof (yourpropertytab), propertytabscope. Component)]
Public class yourcontrolclass
{
}
The previous parameter specifies the class for processing propertytab. The next parameter specifies when the component is dedicated to the current component and the document is dedicated to the current file, global can only be explicitly removed from parent parts, but static cannot.
Internal class yourpropertytab: propertytab
{
Internal yourcontroltype target;
Public override string tabname
{
Get
{
Return "the name of the tab ";
}
}
Public override Bitmap bitmap
{
Get
{
Return new Bitmap (base. bitmap, new size (16, 16); // This Is A. BMP file with the same name as yourpropertytab.
}
}
Public override bool canextend (Object O)
{
Return o is yourcontroltype; // when to use this tab
}
Public override propertydescriptorcollection getproperties (object component, attribute [] attrs ){
Return getproperties (null, component, attrs );
}
/** // Main logic. Here we define how to implement tab display.
Public override propertydescriptorcollection getproperties (itypedescriptorcontext context, object component, attribute [] attrs)
{
Yourcontroltype UC = component as yourcontroltype;
If (UC = NULL)
{
// The following Code When the implementation is not yourcontroltype, the logic of its own type is used.
Typeconverter Tc = typedescriptor. getconverter (component );
If (TC! = NULL)
{
Return TC. getproperties (context, component, attrs );
}
Else
{
Return typedescriptor. getproperties (component, attrs );
}
}
Target = UC;
Arraylist proplist = new arraylist ();
// .. Create an attribute list
Proplist. Add (New yourpropertydescriptor (this );
Propertydescriptor [] props = (propertydescriptor []) proplist. toarray (typeof (propertydescriptor ));
Return new propertydescriptorcollection (props );
}
// We also need to create a custom propertydescriptor for the getproperties method.
Private class yourpropertydescriptor: propertydescriptor
{
Yourpropertytab owner;
Public numpointspropertydescriptor (yourpropertytab owner): // pay attention to the parameters here.
Base ("propertyname", new attribute [] {categoryattribute. data, refreshpropertiesattribute. all}) // The second parameter specifies whether the entire attribute page is refreshed, all-refreshed, default-no, in conjunction with // other attributes when the attribute is changed, repaint-Repaint Properties window
{
This. Owner = owner;
}
Public override type propertytype // attribute type
{
Get
{
Return typeof (INT );
}
}
What type is the property associated object?
Public override type componenttype
{
Get
{
Return typeof (yourcontroltype );
}
}
Public override bool isreadonly {get {return false ;}}
Public override object getvalue (Object O) // related to the attributes of the associated object
{
Return (yourcontroltype) O). proterty_1;
}
Public override void setvalue (Object o, object Value) // related to the attributes of the associated object
{
Yourcontroltype UC = O as yourcontroltype;
UC. property_1 = (INT) value;
}
Public override void resetvalue (Object O) {}// Wang wenshengyi
Public override bool canresetvalue (Object O) // Wang wenshengyi
{
Return false;
}
/** // Does this property maid in code generation?
Public override bool shouldserializevalue (Object O)
{
Return false;
}
}
}
Type Converter
In the previous article, we have already touched on typeconverter. Now we can take a full look at this interpreter:
1. typeconverter is derived from the system. componentmodel. typeconverter class.
2. Use [typeconverter (typeof (yourtypeconverter)] to bind to the attribute.
3. typeconverter must be used during the design and runtime, because there is a conversion between a specific type-string in both stages.
4. Function 1: convert the property to a string and display it in the property browser. Convert the value of the property browser to the type required by the property.
5. Function 2: provides a expand/collapse UI for sub-attributes. For example:
To implement this function, you must first associate the attribute with the converter class system. componentmodel. expandableobjectconverter through typeconverterattribute or inherit from the converter class.
Public class yourconverter: expandableobjectconverter {
Do something, example: override coversomething Function
}
Public class yourcontrol
{
[Typeconverter (typeof (yourconverter)]
Public yourpropertyclass expandableproperty
{
.
}
}
6. Provide a drop-down list of design period values for attributes, as described in my previous article.
7. The following converters are available in system. Web. UI. webcontrols namespace & system. componentmodel namespace:
Unitconverter, booleanconverter, charconverter, enumconverter, collectionconverter, arrayconverter, basenumberconverter,
Byteconverter, referenceconverter, cultureinfoconverter, datetimeconverter, decimalconverter, doubleconverter, expandable
Objectconverter, guidconverter, int16 (32/64) converter, converter, singleconverter, stringconverter, timespanconverter, typelistconverter, uint16 (32/64) converter, objectconverter, propertyconverter, converter, datafieldconverter, upload
Sourceconverter, fontnamesconverter, fontunitconverter, targetconverter, validatedcontrolconverter, cursorconverter ...... (I will not list winform)
Next, we will focus on how to convert different types and strings.
The following instance must achieve this purpose.
Public class yourtypeconverter: typeconverter {// type converter must be directly or indirectly inherited from the typeconverter class,
// The above example inherits from expandableobjectconverter and also indirectly inherits the typeconverter class
Public override bool canconvertfrom (itypedescriptorcontext context, type sourcetype) {// can the source type be converted to the converter association type?
If (sourcetype = typeof (string )){
Return true;
}
Return base. canconvertfrom (context, sourcetype); // convert string to true. Then, simply call the method of the base class.
}
Public override bool canconvertion (itypedescriptorcontext context, type destinationtype) {// can the string be converted to the target class?
If (destinationtype = typeof (string) |
(Destinationtype = typeof (instancedescriptor) {// system. componentmodel. Design. serialization. instancedescriptor
// Provide the information required to create an instance of the object. Here, an instance of the converter association type is returned.
Return true;
}
Return base. canconvertize (context, destinationtype); // only the converto target type can be set to true (string is implemented later). If the string type can be converted to the target type, // It will also be true, because here we can convert it to string, and then call the String Conversion.
}
Public override object convertfrom (itypedescriptorcontext context, cultureinfo culture, object Value) {// convert from one type to another
If (value = NULL ){
Return new yourtype (); // null value, implemented by default
}
If (value is string) {// if it is string, customize the conversion Logic
String S = (string) value;
If (S. Length = 0 ){
Return new yourtype ();
}
// The following example converts "20, 30" to new yourtype (20, 30), which is separated by two steps and int
String [] parts = S. Split (culture. textinfo. listseparator [0]);
If (parts. length! = 2 ){
Throw new argumentexception ("invalid yourtype", "value ");
}
Typeconverter intconverter = typedescriptor. getconverter (typeof (int32 ));
Return new yourtype (INT) intconverter. convertfromstring (context, culture, parts [0]),
(INT) intconverter. convertfromstring (context, culture, parts [1]);
}
Return base. convertfrom (context, culture, value); // call the method of the base class.
}
Public override object convertion (itypedescriptorcontext context, cultureinfo culture, object value, type destinationtype) {// convert our type to another type
If (value! = NULL) {// first confirm that the source type is not the type to be converted.
If (! (Value is yourtype )){
Throw new argumentexception ("invalid yourtype", "value ");
}
}
If (destinationtype = typeof (string) {// convert the logic to string
If (value = NULL ){
Return string. empty;
}
Yourtype = (yourtype) value;
// In the following example, the two int attributes of yourtype are converted into a string of "attribute value 1, attribute value 2.
Typeconverter intconverter = typedescriptor. getconverter (typeof (int32 ));
Return string. Join (culture. textinfo. listseparator,
New String [] {
Intconverter. converttostring (context, culture, yourtype. value_1 ),
Intconverter. converttostring (context, culture, yourtype. value_2)
});
}
Else if (destinationtype = typeof (instancedescriptor )){
// The following example shows the processing logic if the source type itself is the type,
// Here the methods under system. Reflection namespace are used to call yourtype constructors and constructors with two parameters respectively.
If (value = NULL ){
Return NULL;
}
Memberinfo MI = NULL; // obtain its constructor through access to yourtype metadata
Object [] ARGs = NULL;
Yourtype = (yourtype) value;
If (yourtype. isempty ){
Mi = typeof (yourtype). getconstructor (New Type [0]);
}
Else {
Type inttype = typeof (INT );
Mi = typeof (yourtype). getconstructor (New Type [] {inttype, inttype });
ARGs = new object [] {yourtype. value_1, yourtype. value_2 };
}
If (Mi! = NULL ){
Return new instancedescriptor (MI, argS); // create an instance based on the selected constructor and Parameters
}
Else {
Return NULL;
}
}
Return base. convertize (context, culture, value, destinationtype); // call the method of the base class.
}
}
As you can see in the code above, we always call the base class method in the override method. This saves Implementation of logic such as the return when the conversion fails, and avoids inexplicable errors.
Now, let's look back at the figure above. We use a converter inherited from expandableobjectconverter, and we can see that we can assign values to the sub-attributes of the attribute separately, you can also assign a value to the attribute. As shown in, the string value ", 50, 50" is the value converted by typeconverter, typeconverter will also convert it to yourtype. property_1 = 100, youtype. property2 = 50 ...... the actual instance type and the value assigned to it