winforms 自定义组件在设计器中工作,但在代码中不可见

jljoyd4f  于 2023-03-31  发布在  其他
关注(0)|答案(1)|浏览(126)

我已经创建了一个自定义组件,从BindingSource派生,它似乎按预期工作。
当我把它放在一个窗体上时,我可以设置所有的属性,其他控件可以看到它,并可以将它用作绑定的数据源。
我的问题是,当我想在代码中访问这个组件时,代码编辑器一直告诉我没有这样的组件。
这怎么可能呢?
它显示在设计器中,我可以设置属性,我可以让它与设计器中的其他控件交互,并且在运行时它可以完美地工作。
但是代码编辑器找不到它,它一直在说:
当前上下文中不存在名称“gttDatasource 1
什么原因会导致这种情况?如何解决?
我试过清理/重建
我尝试重新启动VS
我试着重启电脑

编辑

Form I的Designer.cs部分将组件掉落在:

namespace Test_app
{
    partial class FormLogSCSSalesInvoiceList
    {
        private System.ComponentModel.IContainer components = null;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormLogSCSSalesInvoiceList));
            gttControls.gttDataSource gttDataSource1 = new gttControls.gttDataSource();

自定义组件位于Designer.cs

自定义组件部分代码

namespace gttControls
{
    internal class gttDataSourceCodeDomSerializer : CodeDomSerializer
    {
        public override object Deserialize(IDesignerSerializationManager manager, object codeObject)
        {
            CodeDomSerializer baseClassSerializer = (CodeDomSerializer)manager.GetSerializer(typeof(gttDataSource).BaseType, typeof(CodeDomSerializer));
            var Result = baseClassSerializer.Deserialize(manager, codeObject);
            ((gttDataSource)Result).CorrectTableColumns();
            return Result;
        }
    }

    public delegate void OnActiveChangedHandler(object sender, EventArgs e);
    public delegate void OnBeforeUpdateHandler(object sender, EventArgs e);

    [DesignerSerializer(typeof(gttDataSourceCodeDomSerializer), typeof(CodeDomSerializer))]
    public partial class gttDataSource : BindingSource
    {
        private readonly gttDataTable _gttDataTable;
        private GttTableProperties _gttTableProperties;
        private Collection<gttDataTableColumn> _columns = new Collection<gttDataTableColumn>();
        private bool _active = false;
        private readonly bool _refreshSchema = false;

        public event ActiveChangedHandler ActiveChanged;
        public event BeforeUpdateHandler BeforeUpdate;

        public gttDataSource()
        {
            _gttTableProperties = new GttTableProperties(this);
            _gttDataTable = new gttDataTable();
            DataSource = _gttDataTable.Table;
            _gttDataTable.BeforeUpdate += _gttDataTable_BeforeUpdate;
        }
    }
}

编辑2

当我尝试在代码中使用这个组件时,我得到了这个:

为了证明组件实际上在表单的设计器中:

编辑3

我在评论中添加了一个ctor:

public gttDataSource(IContainer container) : this() 
{ 
    if (container == null) 
    { 
        throw new ArgumentNullException("container is null"); 
    } 
    container.Add(this);
}

但这并没有帮助。当在窗体上放置组件时,或在任何其他时间,都不会调用此构造函数。

mwg9r5ms

mwg9r5ms1#

症状

实现自定义CodeDomSerializer的自定义组件,当添加到表单容器时,会生成一个无法从包含它的Form类访问的Object,并且 * 应该 * 定义和生成组件的示例(作为private字段)。
此问题与自定义CodeDomSerializer实现有关。
实现基CodeDomSerializer的自定义序列化程序必须覆盖**Deserialize()Serialize()**方法。
来自文档的备注部分(注意 * 必须 *,而不是 * 应 *):
若要为类型实现自定义CodeDomSerializer,必须:

  • 定义从CodeDomSerializer派生的类。
  • 实现序列化或反序列化方法的方法重写。
  • 使用DesignerSerializerAttribute将您的自定义CodeDomSerializer实现与某个类型的组件相关联。

为了让默认序列化程序生成以标准方式配置Component / Control的代码语句,我们必须调用该组件的基序列化程序。
否则,序列化程序不会执行完整的序列化,而只是使用它可以访问的Type创建一个局部对象。
因此,它不会创建关联的Field,并且在Designer中分配的Property值的序列化也不会遵循标准的 * 逻辑 *(此对象的Properties可能分散在Designer.cs文件中)。
在问题中,不清楚这里发布的代码是否是完整的CodeDomSerializer实现。
要按预期工作,自定义CodeDomSerializer必须包含Serialize()方法覆盖,并指定应序列化的类型。
然后调用基类的默认Serialize()方法,生成Componenent的标准序列化,该序列化现在被分配给示例Field,然后可以在Container Form类中访问:

internal class gttDataSourceCodeDomSerializer : CodeDomSerializer
{
    public override object Deserialize(IDesignerSerializationManager manager, object codeObject)
    {
        CodeDomSerializer baseClassSerializer = (CodeDomSerializer)manager.GetSerializer(typeof(gttDataSource).BaseType, typeof(CodeDomSerializer));
        var Result = baseClassSerializer.Deserialize(manager, codeObject);
        ((gttDataSource)Result).CorrectTableColumns();
        return Result;
    }

    public override object Serialize(IDesignerSerializationManager manager, object value)
    {
        var serializer = (CodeDomSerializer)manager.GetSerializer(typeof(gttDataSource).BaseType, typeof(CodeDomSerializer));
        return serializer.Serialize(manager, value);
    }
}

Components提供一个接受IContainer对象的构造函数也很重要,因为窗体基类(Control)的默认Dispose()方法只考虑子控件,而不考虑Components。
CodeDomSerialzier考虑了这个构造函数,并在Designer.cs中添加了:

this.gttDataSource1 = new gttControls.gttDataSource(this.components);
// [...]
private gttControls.gttDataSource gttDataSource1;

如果之前没有添加其他组件,则还会创建:

this.components = new System.ComponentModel.Container();

所以Form类的Dispose()覆盖可以解决这个问题:

protected override void Dispose(bool disposing)
{
    if (disposing && (components != null))
    {
        components.Dispose();
    }
    base.Dispose(disposing);
}

简单实施:

public partial class gttDataSource : BindingSource
{
    public gttDataSource() => InitializeComponent();

    public gttDataSource(IContainer container) : this()
    {
        if (container is null) { 
            throw new ArgumentNullException("container is null"); 
        }
        container.Add(this);
    }
}

相关问题