在用户控件中公开 DataGridView 的列属性无法正常工作

本文关键字:常工作 工作 属性 控件 用户 DataGridView | 更新日期: 2023-09-27 18:33:43

我在UserControl中放了一个DataGridView,并在我的用户控件中创建了一个公共属性,该属性公开了 datagridview 的列属性。
下面是示例代码:

public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
    }
    public DataGridViewColumnCollection MyDataGridColumns
    {
        get
        {
            return dataGridView1.Columns;
        }
    }
}

然后我在窗体中添加UserControl1,然后单击属性窗口中MyDataGridColumns属性并添加 1 列或多列。当我重新生成解决方案时,就会出现问题;我刚刚添加的所有列在重建后都会消失。

谁能向我解释为什么会这样?以及如何解决它?

在用户控件中公开 DataGridView 的列属性无法正常工作

这对

我有用:我创建了一个特定的列编辑器,因为似乎不可能对任何不扩展DataGridView的控件使用默认列编辑器。

public partial class UserControl1 : UserControl, IDataGridView
{
    public UserControl1()
    {
        InitializeComponent();
    }
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Browsable(false)]
    public DataGridView DataGridView
    {
        get { return dataGridView1; }
    }
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    [Editor(typeof(ExtendedDataGridViewColumnCollectionEditor), typeof(UITypeEditor))]
    [MergableProperty(false)]
    public DataGridViewColumnCollection MyDataGridColumns
    {
        get { return dataGridView1.Columns; }
    }
}
public interface IDataGridView
{
    DataGridView DataGridView { get; }
}
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 ctr = type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)[0];
        return (Form)ctr.Invoke(new object[] { provider });
    }
    public static void SetLiveDataGridView(Form form, DataGridView grid)
    {
        var mi = form.GetType().GetMethod("SetLiveDataGridView", BindingFlags.NonPublic | BindingFlags.Instance);
        mi.Invoke(form, new object[] { grid });
    }
    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);
            //Unfortunately we had to make property which returns inner datagridview  
            //to access it here because we need to pass DataGridView into SetLiveDataGridView () method 
            var grid = ((IDataGridView)context.Instance).DataGridView;
            //we have to set Site property because it will be accessed inside SetLiveDataGridView () method 
            //and by default it's usually null, so if we do not set it here, we will get exception inside SetLiveDataGridView () 
            var oldSite = grid.Site;
            grid.Site = ((UserControl)context.Instance).Site;
            //execute SetLiveDataGridView () via reflection 
            SetLiveDataGridView(dataGridViewColumnCollectionDialog, grid);
            using (var transaction = host.CreateTransaction("DataGridViewColumnCollectionTransaction"))
            {
                if (service.ShowDialog(dataGridViewColumnCollectionDialog) == DialogResult.OK)
                    transaction.Commit();
                else
                    transaction.Cancel();
            }
            //we need to set Site property back to the previous value to prevent problems with serializing our control 
            grid.Site = oldSite;
        }
        return value;
    }
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
        return UITypeEditorEditStyle.Modal;
    }
}

这是因为您没有指定列的类型。添加列时应提供列的类型(例如DataGridViewTextBoxColumnDataGridViewCheckBoxColumn(。在Form1.cs执行以下操作:

public Form1()
{
    InitializeComponent();
    DataGridViewColumn dgViewColumn = new DataGridViewTextBoxColumn();//Or DataGridViewCheckBoxColumn
    dgViewColumn.DataPropertyName = "dgViewColumn";
    dgViewColumn.HeaderText = @"dgViewColumn";
    dgViewColumn.Name = "dgViewColumn";
    userControl11.MyDataGridColumns.Add(dgViewColumn);
}

@Bioukh答案在VS2019中有效,在VS2022中也有些有效。但是,在我的用户控件中嵌入 DataGridView 控件,然后使用答案添加和编辑列的结果不会使这些列迁移到用户控件的另一个实例。 例如:复制/粘贴用户控件,所有嵌入的 DataGridView 列将从新副本中消失。

为了解决此问题,我将我的 DataGridView 实例维护为本机实例,并在 UserControl 中使用公共 DataGridView 属性,并在属性资源库中执行绑定和停靠。 然后,我把my_UserControl放到我的窗体上,my_DataGridView放在我的窗体上,然后设置 my_UserControl.DataGridView = my_DataGridView。此解决方法可保留与 DataGridView 关联的本机属性和行为

在my_UserControl中,我有一个名为"GridPanel"的面板和一个VScrollBar。然后我添加了以下属性:

///<summary>
/// Associates a native DataGridView with this UserControl
/// then sets the DataGridView.Parent to the Panel in this UserControl
/// and sets the DataGridView.Dock to Fill the Panel
///</summary>
public DataGridView? ContainedDataGridView
{
  get
  {
    try
    {
      // if we have a DataGridView in our Panel then return it
      if ((this.GridPanel.Controls.Count == 1)
          && (this.GridPanel.Controls[0] is DataGridView view))
      {
        return view;
      }
    }
    catch (Exception ex)
    {
      //// TODO Handle "ContainedDataGridView get error"
    }
    // Return null if there is no DataGridView or there was an error checking for it.
    return null;
  }
  set
  {
    try
    {
      // Clear the panel to prevent adding more than one DataGridView
      this.GridPanel.Controls.Clear();
      if (value is not null)              
      {
        this.GridPanel.Controls.Add(value);
        value.Parent = this.GridPanel;
        value.Dock = DockStyle.Fill;
      }
      // else the panel remains cleared
    }
    catch (Exception ex)
    {
      //// TODO Handle "ContainedDataGridView set error"
    }
  }
}

上面的代码片段编码为 C# 10、.NET 6、Windows 窗体应用、用户控件,并在 Visual Studio 2022 版本 17.0.3 中进行了测试