【问题标题】:Exposing DataGridView's columns property in UserControl doesn't work properly在 UserControl 中公开 DataGridView 的 columns 属性不能正常工作
【发布时间】:2016-02-09 06:19:03
【问题描述】:

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

public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
    }

    public DataGridViewColumnCollection MyDataGridColumns
    {
        get
        {
            return dataGridView1.Columns;
        }
    }
}

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

谁能向我解释为什么会发生这种情况?以及如何解决?

【问题讨论】:

  • 如何添加列?在设计模式下还是以编程方式?
  • 尝试为您的属性添加一个 setter MyDataGridColumns
  • @Bioukh,已经试过了,添加setter并不能解决问题。
  • @AfshinAghazadeh...你觉得下面的答案最终解决了你的问题吗?
  • @S.Akbari 那是最好的答案,因为现在我知道这个问题至少这就是我投票的原因,但我需要一种方法来正确公开内部 DataGrid.Columns 以便我可以更改它设计时的列来制作一个好的 UserControl。

标签: c# winforms datagridview user-controls


【解决方案1】:

这对我有用:我创建了一个特定的列编辑器,因为似乎不可能对任何不扩展 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;
    }
}

【讨论】:

  • 好的,这对我也适用于 DataGridView 的 Columns 集合。现在,我们如何在设计器中获取所有其他属性和事件处理程序?
  • 在使用 .NET 6 Windows 窗体的 Visual Studio 2022(版本 17.0.3)中,对话框的“添加”按钮不会引发“添加”对话框。现在添加只添加一个新的DataGridViewTextBoxColumn,并没有设计器属性来改变那个列类型。
  • @gridtrak 可以直接在Designer.cs文件中修改DataGridView的列类型。
【解决方案2】:

这是因为您没有指定列的类型。添加列时应提供列的类型(例如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);
}

【讨论】:

  • @Afshin Aghazadeh,你为什么坚持使用设计师?代码提供了比设计器更多的控制权。
【解决方案3】:

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

为了解决这个问题,我将我的 DataGridView 实例维护为 native,并在我的 UserControl 中使用公共 DataGridView 属性,并在属性设置器中执行绑定和对接。然后我将 my_UserControl 放在我的表单上,将 my_DataGridView 放在我的表单上,然后设置 my_UserControl.DataGridView = my_DataGridView。此解决方法保留了与 DataGridView 关联的本机属性和行为

在 my_UserControl 中,我有一个名为“GridPanel”的 Panel 和一个 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"
    }
  }
}

上述 sn-p 编码为 C# 10、.NET 6、Windows Forms App、UserControl 并在 Visual Studio 2022 版本 17.0.3 中测试

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-25
    • 2018-09-27
    • 2013-05-24
    • 1970-01-01
    • 2011-12-26
    • 2011-08-13
    • 2021-12-06
    相关资源
    最近更新 更多