54.1 How to make my Component add itself to the contained Form's IContainer list?
You do this inorder to ensure that your component gets disposed along with the contained Form (logical parent).
All Form derived classes come with an IContainer field into which many of the .Net components like ImageList and Timer add themselves to. The Form will dispose the contents of this IContainer from within its Dispose.
Scenario 1
In order for your Component to get added to this IContainer list, all you have to do is provide a constructor that takes IContainer as the one and only argument. The design-time will discover this constructor automatically and use it to initialize your component. You should then add yourself to the IContainer in the constructor implementation.
Note that for this to work your Component should not have a custom TypeConverter that can convert your type to an InstanceDescriptor.
Example:
public class MyComponent : Component
{
public MyComponent()
{
}
public MyComponent(IContainer container)
{
container.Add(this);
}
}
Scenario 2
Your components might have more constructors besides the default constructor and you might have a custom TypeConverter that provides an InstanceDescriptor to let your designer use a non-default constructor for initializing your component in code.
In this case, the above approach will not work because you do not have an IContainer-argument only constructor.
You now have to recreate what the design-time did for you. You have to provide a custom IDesignerSerializationProvider to do so. The attached ContainerInsertingSerializationProvider class can be used to get the above effect.
附:IDesignerSerializationProvider
Code
1using System;
2using System.Windows.Forms;
3using System.ComponentModel;
4using System.ComponentModel.Design;
5using System.ComponentModel.Design.Serialization;
6using System.Windows.Forms.Design;
7using System.CodeDom;
8
9namespace Syncfusion.ComponentModel.Design.Serialization
10{
11 // When you use this IDesignerSerializationProvider to serialize your types,
12 // this will insert an IContainer argument to the base class provided Constructor, in code.
13 // Usage Example:
14 // public class YourComponentDesigner : ComponentDesigner
15 // {
16 //
17 // ContainerInsertingSerializationProvider provider;
18 // IDesignerSerializationManager idsm;
19 // public override void Initialize(IComponent component)
20 // {
21 // this.provider = new ContainerInsertingSerializationProvider(typeof(YourComponent), true); // true to do this for derived classes as well.
22 // idsm = (IDesignerSerializationManager)this.GetService(typeof(IDesignerSerializationManager));
23 // if(this.idsm != null)
24 // this.idsm.AddSerializationProvider(this.provider);
25 // }
26 // protected override void Dispose(bool disposing)
27 // {
28 // if(this.idsm != null)
29 // serManager.RemoveSerializationProvider(this.provider);
30 // }
31 //
32 // }
33 public class ContainerInsertingSerializationProvider : IDesignerSerializationProvider
34 {
35 ContainerInsertingCodeDomSerializer serializer;
36 Type sourceType;
37 bool applyOnDerivedClasses = false;
38 public ContainerInsertingSerializationProvider(Type sourceType, bool applyOnDerivedClasses)
39 {
40 this.serializer = new ContainerInsertingCodeDomSerializer();
41 this.sourceType = sourceType;
42 this.applyOnDerivedClasses = applyOnDerivedClasses;
43 }
44
45 public virtual object GetSerializer(IDesignerSerializationManager manager, object currentSerializer,
46 Type objectType, Type serializerType)
47 {
48 if( (objectType != null) && ((objectType == this.sourceType)
49 || (this.applyOnDerivedClasses && (objectType.IsSubclassOf(this.sourceType)))) )
50 return this.serializer;
51
52 return null;
53 }
54
55 public void Dispose()
56 {
57 this.serializer = null;
58 }
59 }
60
61 public class ContainerInsertingCodeDomSerializer : CodeDomSerializer
62 {
63 public ContainerInsertingCodeDomSerializer()
64 {
65 }
66
67 public override object Deserialize(IDesignerSerializationManager manager, object codeObject)
68 {
69 CodeDomSerializer baseClassSerializer =
70 (CodeDomSerializer)manager.GetSerializer(typeof(Control), typeof(CodeDomSerializer));
71
72 return baseClassSerializer.Deserialize(manager, codeObject);
73 }
74
75 public override object Serialize(IDesignerSerializationManager manager, object obj)
76 {
77 CodeDomSerializer baseClassSerializer =
78 (CodeDomSerializer)manager.GetSerializer(typeof(Component), typeof(CodeDomSerializer));
79
80 object serializedInfo = baseClassSerializer.Serialize(manager, obj);
81 if(serializedInfo is CodeStatementCollection)
82 {
83 CodeStatementCollection stmtColl = serializedInfo as CodeStatementCollection;
84 CodeAssignStatement stmt = stmtColl[0] as CodeAssignStatement;
85 if(stmt != null)
86 {
87 CodeObjectCreateExpression createExpr = stmt.Right as CodeObjectCreateExpression;
88 if(createExpr != null)
89 {
90 CodeFieldReferenceExpression containerFieldRef = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "components");
91 createExpr.Parameters.Insert(0, containerFieldRef);
92 this.EnsureContainer(manager);
93 }
94 }
95 }
96 return serializedInfo;
97 }
98 private void EnsureContainer(IDesignerSerializationManager manager)
99 {
100 object rootCodeDomSerializer = manager.Context[Type.GetType("System.ComponentModel.Design.Serialization.RootCodeDomSerializer")];
101 if(rootCodeDomSerializer != null)
102 {
103 PropertyDescriptor propDesc = TypeDescriptor.GetProperties(rootCodeDomSerializer)["ContainerRequired"];
104 propDesc.SetValue(rootCodeDomSerializer, true);
105 }
106 }
107 }
108
109}
54.2 How do I prevent the default values of my Localized properties form being set?
It is normal to have Properties in your Control/Component whose default values are inherited from some other Control/Component.
In such cases you will normally prevent the designer from storing the property's value in code (using either DefaultValue attribute or the ShouldSerializeXXX pattern). However, if that property is Localizable and Localization is turned on, then the property's value will be forced to be stored in the resource. This will break your property-inheritance logic.
For example:
[
Localizable(true)
...
]
public Font MyControlButtonFont
{
get
{
if(this.buttonFont == null)
return this.Font;
else
return this.buttonFont;
}
set
{
this.buttonFont = value;
}
}
private bool ShouldSerializeMyControlButtonFont()
{
if(this.MyControlButtonFont == this.Font)
return false;
else
return true;
}
In the above case the MyControlDefaultFont inherits its value from the Font property, if its value is not set. And you use null to determine whether the value is set or not.
But when Localization is ON, the property gets SET and you lose the inheritance logic.
You can avoid this by specifying an AmbientValue attribute for your property, as follows:
[
Localizable(true),
AmbientValue(null)
...
]
public Font MyControlButtonFont
This will use the AmbientValue as the value to persist when there is default-value in your property. This will prevent your property from getting SET unnecessarily.
54.3 How do I force the changes in base class fields to be serialized via a base class property in the inherited type's designer?
Sometimes you might want to let the designer serializer serialize the changes in base fields via a property rather than the field itself using the AccesssedThroughProperty attribute as follows:
public class MyBaseForm : Form
{
[AccessedThroughProperty("MyList")]
private ArrayList myList;
public ArrayList MyList
{
return this.myList;
}
}
Then when the above form is inherited and items get added to the inherited form's designer, code will be added as follows in the inherited form's InitializeComponent:
private void InitializeComponent()
{
... ... ... ...
this.MyList.Add(aNewItem);
... ... ... ...
}