Designer doesn't serialzie Columns of a DataGridView property of a UserControl

Viewed 104

I'm trying to create a custom control that contains a label, a toolstrip and a Datagridview.

When the test form generates the Design code, it saves correctely the toolstrip items but it doesn't save the Datagridview columns. Code DGrid.cs

...
    [DesignerAttribute(typeof(MultiDesigner))]
    public partial class FDGrid : Panel
    {
        ....
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Category("Bars"), Description("Barra dei comandi.")]
        public ToolStrip Barra
        {
            get { return _barra; }
            set { _barra = value; }
        }
        
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Category("Grid"), Description("Griglia dati.")]
        public DataGridView Griglia
        {
            get { return _griglia; }
            set { _griglia = value; }
        }
        ....
        
    }
    
    [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = 
        "FullTrust")]
    public class MultiDesigner : System.Windows.Forms.Design.ControlDesigner
    {
        public override void Initialize(IComponent c)
        {
            base.Initialize(c);
            FDGrid ctl = (FDGrid)c;
            EnableDesignMode(ctl.Griglia, "Griglia");
            EnableDesignMode(ctl.Barra, "Barra");
        }
    }
...

Code FDGrid.Designer.cs

FDGrid.Designer.cs ....

private FDLabeledImage _titolo;
private ToolStrip _barra;
private DataGridView _griglia;
    

private void InitializeComponent()
{
    this._barra = new System.Windows.Forms.ToolStrip();
    this._griglia = new System.Windows.Forms.DataGridView();
    this._titolo = new FDControl.FDLabeledImage();
    ((System.ComponentModel.ISupportInitialize)(this._griglia)).BeginInit();
    this.SuspendLayout();
    ....
    ((System.ComponentModel.ISupportInitialize)(this._griglia)).EndInit();
    this.ResumeLayout(false);
}

....

Code Form1.Designer.cs

....    
            
            private void InitializeComponent()
            {
                this.fdGrid1 = new FDControl.FDGrid();  // OK
                this.fdGrid1.Column1 = new System.Windows.Forms.DataGridViewTextBoxColumn(); **// Error** 
                this.toolStripLabel1 = new System.Windows.Forms.ToolStripLabel(); //OK
                
            }
            
            ...
            private FDControl.FDGrid fdGrid1; //OK
            private System.Windows.Forms.ToolStripLabel toolStripLabel1; //OK
            private System.Windows.Forms.DataGridViewTextBoxColumnfdGrid1.Column1;//Error
...

help, I would like to understand where am I wrong?

1 Answers

Not the most elegant, but a working solution:

  1. Create a custom MyDataGridView which derives from DataGridView:

     using System;
     using System.ComponentModel;
     using System.ComponentModel.Design;
     using System.Drawing.Design;
     using System.Reflection;
     using System.Windows.Forms;
     using System.Windows.Forms.Design;
     public class MyDataGridView : DataGridView
     {
         [Editor(typeof(ExtendedDataGridViewColumnCollectionEditor), typeof(UITypeEditor))]
         public new DataGridViewColumnCollection Columns { get => base.Columns; }
    
         private class ExtendedDataGridViewColumnCollectionEditor : UITypeEditor
         {
             private Form dataGridViewColumnCollectionDialog;
             private ExtendedDataGridViewColumnCollectionEditor() { }
             private static Form CreateColumnCollectionDialog(IServiceProvider provider)
             {
                 var assembly = Assembly.Load(typeof(ControlDesigner).Assembly.ToString());
                 var type = assembly.GetType("System.Windows.Forms.Design.DataGridViewColumnCollectionDialog");
                 var ctor = type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)[0];
                 return (Form)ctor.Invoke(new object[] { provider });
             }
             public static void SetLiveDataGridView(Form form, DataGridView dgv)
             {
                 var method = form.GetType().GetMethod("SetLiveDataGridView", BindingFlags.NonPublic | BindingFlags.Instance);
                 method.Invoke(form, new object[] { dgv });
             }
             public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
             {
                 if (provider != null && context != null)
                 {
                     var service = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
                     if (service == null || context.Instance == null)
                         return value;
    
                     var host = (IDesignerHost)provider.GetService(typeof(IDesignerHost));
                     if (host == null)
                         return value;
    
                     if (dataGridViewColumnCollectionDialog == null)
                         dataGridViewColumnCollectionDialog = CreateColumnCollectionDialog(provider);
    
                     var dgv = ((MyDataGridView)context.Instance);
                     var oldSite = dgv.Site;
                     dgv.Site = ((MyDataGridView)context.Instance).Parent?.Site;
                     SetLiveDataGridView(dataGridViewColumnCollectionDialog, dgv);
    
                     using (var transaction = host.CreateTransaction("DataGridViewColumnCollectionTransaction"))
                     {
                         if (service.ShowDialog(dataGridViewColumnCollectionDialog) == DialogResult.OK)
                             transaction.Commit();
                         else
                             transaction.Cancel();
                     }
                     dgv.Site = oldSite;
                 }
                 return value;
             }
             public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
             {
                 return UITypeEditorEditStyle.Modal;
             }
         }
     }
    
  2. Build the solution.

  3. Create your UserControl and drop an instance of MyDataGridView on it.

  4. Expose the DataGridView property:

     public partial class MyUserControl : UserControl
     {
         public MyUserControl()
         {
             InitializeComponent();
         }
         [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
         public DataGridView DataGridView { get => dataGridView1; }
     }
    
  5. Build the solution.

  6. Drop an instance of MyUserControl on your form, then in property editor window, find the DataGridView property, expand it and edit Columns and Save the form.

The columns are serialized now. There you go.

Note: The custom UITypeEditor is based on this post with some changes.

The result looks like:

private void InitializeComponent()
{
    ...
    ...

    this.Column1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
    this.Column2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
    ((System.ComponentModel.ISupportInitialize)(this.myUserControl1.DataGridView)).BeginInit();
    this.SuspendLayout();
    // 
    // myUserControl1
    // 
    // 
    // 
    // 
    this.myUserControl1.DataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
    this.myUserControl1.DataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
    this.Column1,
    this.Column2});

    ...
    ...

    // 
    // Column1
    // 
    this.Column1.HeaderText = "Column1";
    this.Column1.MinimumWidth = 6;
    this.Column1.Name = "Column1";
    this.Column1.Width = 125;
    // 
    // Column2
    // 
    this.Column2.HeaderText = "Column2";
    this.Column2.MinimumWidth = 6;
    this.Column2.Name = "Column2";
    this.Column2.Width = 125;
    
    ...
    ...

    ((System.ComponentModel.ISupportInitialize)(this.myUserControl1.DataGridView)).EndInit();
    this.ResumeLayout(false);

}

...

private System.Windows.Forms.DataGridViewTextBoxColumn Column1;
private System.Windows.Forms.DataGridViewTextBoxColumn Column2;

...
Related