簡體   English   中英

自定義組件在設計器中工作,但在代碼中不可見

[英]Custom component works in the Designer, but is invisible in code

我創建了一個從BindingSource派生的自定義組件,它似乎按預期工作。
當我將它放在窗體上時,我可以設置所有屬性,其他控件可以看到它,並且可以將它用作綁定的數據源。 這一切都很好。

我的問題是,當我想在代碼中訪問這個組件時,代碼編輯器一直告訴我沒有這樣的組件。
這怎么可能?

它顯示在設計器中,我可以設置屬性,我可以讓它與設計器中的其他控件交互,並且在運行時它可以完美運行。
但是代碼編輯器找不到它,它一直說:

當前上下文中不存在名稱 gttDatasource1"

什么會導致這種情況? 如何解決?

我試過清理/重建
我嘗試重新啟動VS
我嘗試重新啟動計算機

編輯
我將組件拖放到的表單的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
我按照評論中的建議添加了一個演員:

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

但這無濟於事。 將組件放在表單上時或在任何其他時間都不會調用此構造函數。

症狀
實現自定義CodeDomSerializer的自定義組件,當添加到表單容器時,會生成一個 Object,它不能從包含它的表單 class 訪問,並且應該定義和生成組件的實例(作為private字段)。

該問題與自定義CodeDomSerializer實現有關。
實現基本CodeDomSerializer的自定義序列化程序必須覆蓋Deserialize()Serialize()方法。
從文檔的備注部分(注意must , not should ):

要為某個類型實現自定義 CodeDomSerializer,您必須:

  • 定義一個派生自 CodeDomSerializer 的 class。

  • 為序列化或反序列化方法實現方法覆蓋。

  • 使用DesignerSerializerAttribute將您的自定義 CodeDomSerializer 實現與某種類型的組件相關聯。

為了讓默認序列化程序生成以標准方式配置組件/控件的代碼語句,我們必須調用組件的基本序列化程序。
否則,序列化程序不會執行完整的序列化,只是使用它可以訪問的類型創建一個本地 object。
因此,它不會創建關聯的字段,並且在 Designer 中分配的屬性值的序列化也不會遵循標准邏輯(此 object 的屬性可能分散在Designer.cs文件中)。

在問題中,不清楚此處發布的代碼是完整的CodeDomSerializer實現。
要按預期工作,自定義CodeDomSerializer必須包含Serialize()方法覆蓋並指定應序列化的類型。
然后調用基礎 class 的默認Serialize()方法,生成組件的標准序列化,現在分配給實例字段,然后可以在容器表單 class 中訪問:

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);
    }
}

組件提供接受IContainer object 的構造函數也很重要。 這用於正確處理組件,因為 Form 的基礎 class ( Control ) 的默認Dispose()方法只考慮子控件,而不考慮組件。
CodeDomSerialzier考慮了這個 Constructor 並在Designer.cs中添加:

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

如果之前沒有添加其他組件,它還會創建:

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

所以表格 class 的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 == null) { 
            throw new ArgumentNullException("container is null"); 
        }
        container.Add(this);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM