I wrote my own UITypeEditor and I reached my goal with help of @Sefe and with THIS link.
My basic setup:
In this setup, BaseForm
extends System.Windows.Forms.Form
. Here i'm putting my property (List<Control> ActionButtons
) that have a custom UITypeEditor like a modal style.
Resumed workflow (All here are in Design Time):
1 - Open MyForm.
2 - Click at ActionButtons(inherited by BaseForm) ellipsis [...] on MyForm properties panel.
3 - A custom form is opened with inflated objects that I want pick. (this form is called by my CustomUITypeEditor)
4 - Objects that I want are picked, and I close the form. So now, the data are ok in MyForm
and serialized into Designer.cs file. I can reopen that EditorUI clicking again in ellipsis and see objects that I picked before.
5 - Now when I close MyForm and reopen it, all data are lost!!! But the data still serialized into Designer.cs.
6 - If I do all this steps with a string
instead List<Control>
, all are Ok.
Bellow my code:
public class CollectionTypeEditor : UITypeEditor {
private IWindowsFormsEditorService _editorService = null;
private ICollection<Control> mControls = null;
private List<Control> mPickedControls = null;
// Editor like Modal style
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) {
return UITypeEditorEditStyle.Modal;
}
// Opens modal and get returned data
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) {
if (provider == null)
return value;
_editorService = (IWindowsFormsEditorService) provider
.GetService(typeof(IWindowsFormsEditorService));
if (_editorService == null)
return value;
mControls = new List<Control>();
// retrieve old data
mPickedControls = value as List<Control>;
if (mPickedControls == null)
mPickedControls = new List<Control>();
// getting existent controls that will be inflated in modal
Control mContext = (Control) context.Instance;
GetControls(mContext);
// open form and get response
CollectionDesign<Control> frmCollections = new CollectionDesign<Control>(mControls, ref mPickedControls);
var response = _editorService.ShowDialog(frmCollections);
// returning data from editor
return response == DialogResult.OK ? mPickedControls : value;
}
Everything works well here. Code for my variable in BaseForm
. Ps.: this variable will be showed on MyForm
where I make click at ellipsis [...]
[Editor(typeof(CollectionTypeEditor), typeof(UITypeEditor))]
[TypeConverter(typeof(ActionButtonConverter))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<Control> ActionButtons { get; set; }
The serialization attribute was added because the file couldn't be saved. When form is closed and reopened, all data are lost.
The stranger thing is that I wrote other UITypeEditor like the same way, just changing type of data to string
and I can close or reopen my form and all works fine, the data are saved.
I already added a TypeConverter but I think that isn't case here. what is wrong with my code?
I'm getting this error on reopen form:
Severity Code Description Project File Line Suppression State Message Method 'System.CodeDom.CodePropertyReferenceExpression.Add' not found.
UPDATE
Now my List of controls are stored in myForm.designer file when it's closed or reopened, but the controls don't are attached on property grid. i.e.: If I click on ellipsis to add a Button 'addbt' when close and reopen myForm the code is auto generated.
Code auto generated bellow on myForm.designer:
//
// addbt
//
this.addbt.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.addbt.Cursor = System.Windows.Forms.Cursors.Hand;
this.addbt.Font = new System.Drawing.Font("Tahoma", 9F);
this.addbt.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(222)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
this.addbt.Location = new System.Drawing.Point(13, 6);
this.addbt.Name = "addbt";
this.addbt.Size = new System.Drawing.Size(103, 33);
this.addbt.TabIndex = 0;
this.addbt.Text = "Incluir";
this.addbt.UseVisualStyleBackColor = true;
//
// myForm
//
this.ActionButtons.Add(this.addbt);
If I make a click on ellipsis again, the data are attached to property on myForm's PropertyGrid. But when I reopen myForm, this values stored before aren't passed to property on myForm's PropertyGrid (data still stored in auto generated code designer). So when a click on the ellipsis [...] the value
from EditValue
method not comes with data stored. I'm feeling that it's closer :)
Maybe something is wrong with my TypeConverter below:
public class ActionButtonConverter : TypeConverter {
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, CultureInfo culture, object value) {
if(value.GetType() == typeof(string)) {
List<Control> ctrs = value as List<Control>;
if (ctrs != null)
return ctrs;
}
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, CultureInfo culture, object value, Type destinationType) {
if (value == null)
return "null";
if (destinationType == typeof(string))
return "(custom collection)";
return base.ConvertTo(context, culture, value, destinationType);
}
I think that I have anything error in deserialization. When myForm is reopened the ITypeDescriptorContext or value (TypeConverter) don't have anything about serialized data into designer.cs file.
Thanks all, and sorry for bad language :P