【问题标题】:Custom User Control Not Initialized in Auto-Generated Code自定义用户控件未在自动生成的代码中初始化
【发布时间】:2011-08-06 18:53:26
【问题描述】:

这种情况以前发生过很多次,但我从来没有费心去弄清楚为什么,现在我厌倦了:

例如,我从 RichTextBox 或 Panel 派生一个类,我重新构建我的项目以将该类添加到 VS 设计器工具箱中,然后将自定义用户控件拖放到窗体中。一切正常,我可以运行我的项目...

当我通过设计器编辑表单或自定义用户控件的属性时,问题就出现了。有时,设计器会从其代码隐藏中删除初始化行,导致设计器和可执行文件出现异常,因为控件仍未初始化。

换句话说,就是从 Form1.Designer.cs 中删除以下行:

this.customRichTextBox1=new CustomRichTextBox();

没有从代码隐藏中删除其他行,因此自定义控件的属性仍然设置,尽管变量保持未初始化状态。

我的解决方案一直是在设计器代码隐藏中手动初始化我的用户控件,但设计器最终再次将其删除。

我相信当我通过设计器构建自定义用户控件时不会发生这种情况(但我对此并不完全确定)。只有当我手动定义如下内容时才会发生这种情况:

class CustomRichTextBox:RichTextBox{}

这太烦人了。我做错了什么?


按照@Cody 的要求,这里是重现问题的步骤。我正在使用 VS2010,但我认为自 2005 年以来我就遇到了这个问题。

第 1 步。创建新的 Windows 窗体应用程序,任何框架

第 2 步。在主 Form 类下面添加以下类:(碰巧这是这次导致我出现此问题的控件。)

class CustomRichTextBox : RichTextBox
{
    Timer tt = new Timer();

    internal CustomRichTextBox()
    {
        tt.Tick += new EventHandler(tt_Tick);
        tt.Interval = 200;
    }


    protected override void OnTextChanged(EventArgs e)
    {
        tt.Stop();
        tt.Start();
    }

    void tt_Tick(object sender, EventArgs e)
    {
        System.Diagnostics.Trace.WriteLine("Hello world!");
    }
}

第 3 步。按 F6 键重建。

第 4 步。通过从工具箱中拖放,将 CustomRichTextBox 控件添加到您的表单中。

第 5 步。如果您愿意,可以按 F5 来测试应用程序,但它应该可以工作。关闭正在运行的应用程序。

第 6 步。按 F6 键重新构建,此时,设计器应崩溃并显示以下消息:“变量 'customRichTextBox1' 未声明或从未分配。” (有一种情况是整个 VS 完全崩溃,但错误通常包含在设计器内部。)

第 7 步。要更正此问题,请进入代码隐藏并初始化变量,但下次重建时,初始化行将消失。

【问题讨论】:

  • 几天前发布了类似的问题,但他们的问题和您的问题都没有提供足够的代码来实际重现问题。我花了很多时间开发自定义控件库和使用 VS 设计器,但我从未见过这种情况。我不否认它发生在你身上,但如果我自己无法重现问题,我无法帮助你找到解决方案。设计师有时很古怪,但并非完全不可救药。
  • @Cody:感谢您的评论。我测试并添加了重现问题的步骤。我希望你能重现它。你是对的,设计器是一个非常强大的工具,它很少给我带来麻烦。我想说这是我必须与之抗争的唯一一个实例......我相信,这个问题与我创建控件的方式有关,我自己输入了类。

标签: c# visual-studio user-controls custom-controls designer


【解决方案1】:

我有一个类似的问题,这个帖子帮助我解决了。我有一个扩展 ComboBox 的 CustomControl,该类包含一个内部私有类 YearItem。我试图突出显示理解问题和解决方案所需的代码。

public class YearsCbo : ComboBox //Inherits ComboBox
{
    public YearsCbo() { 
        fill();
    }
    private void fill() { // <<<=== THIS METHOD ADDED ITEMS TO THE COMBOBOX
        for(int idx = 0; idx < 25; idx++) {
            this.Items.Add(new YearItem());
        }
    }
    // Other code not shown
    private class YearItem {} // <<<=== The VS designer can't access this class and yet 
        // it generated code to try to do so.  That code then fails to compile.
        // The compiler error rightfully says it is unable to access 
        // the private class YearItem
}

我可以将该控件 YearsCbo 拖放到表单上并且它可以正常工作,但是在我返回并编辑表单后,VS 设计器生成了无法编译的代码。有问题的代码是这样的:

Dim YearItem1 As my.ns.YearsCbo.YearItem = New my.ns.YearsCbo.YearItem()
Dim YearItem2 As my.ns.YearsCbo.YearItem = New my.ns.YearsCbo.YearItem()
// This was repeated 25 times because in my constructor I created 25 of these
Me.YearsCbo1.Items.AddRange(New Object() {YearItem1, 2, 3, ..., YearItem25 });

请注意,设计器生成的代码试图访问私有类。它不需要这样做,但出于某种原因它确实这样做了。

经过反复试验,这篇帖子:How to tell if .NET code is being run by Visual Studio designer 想出了一个解决方案:

我添加了一个属性来判断我是否在设计器中运行。

public bool HostedDesignMode
{
    get
    {
        if (System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime)
            return true;
        else
            return false;
    }
}

我还更改了构造函数,使其不调用 fill(),因此当设计器运行时,ComboBox 中没有项目,因此设计器不需要手动创建这些项目。

“固定”代码如下所示:

public class YearsCbo : ComboBox //Inherits ComboBox
{
    public YearsCbo() { 
        if ( ! HostedDesignMode ) {
            fill();
        }
    }
    private class YearItem {} // <<<=== Now the VS Designer does not try to access this
}

此代码是在 Win7x64 操作系统上使用 VS2012 Premium 编写的(以防万一)。

【讨论】:

    【解决方案2】:

    感谢所有尝试回答我的问题并发布帮助我诊断和解决问题的 cmets 的人。

    在控件的构造函数中使用“内部”关键字时会出现问题。将其更改为“公共”可以解决问题。这种行为的原因可能是设计者自己的类看不到构造函数,因为它们不在我的类的命名空间内,除非它被标记为公共。这一切都说得通,从现在开始我将使用 public 关键字。

    该类不需要在其自己的单独文件中,也不需要像其他答案所建议的那样是文件中第一个声明的类。

    以下类运行良好,因为构造函数的关键字已更改为 public。

    class CustomRichTextBox : RichTextBox
    {
        Timer tt = new Timer();
    
        public CustomRichTextBox()
        {
            tt.Tick += new EventHandler(tt_Tick);
            tt.Interval = 200;
        }
    
    
        protected override void OnTextChanged(EventArgs e)
        {
            tt.Stop();
            tt.Start();
        }
    
        void tt_Tick(object sender, EventArgs e)
        {
            System.Diagnostics.Trace.WriteLine("Hello world!");
        }
    }
    

    【讨论】:

      【解决方案3】:

      您是否尝试过将控制代码放在自己的文件中?过去,当设计器代码不在文件中的第一类时,我什至在使用表单设计器时也遇到过问题。

      【讨论】:

      • 你可能是对的!我会尽快尝试。我从来没有想过这一点,但这是有道理的,因为设计器确实需要它自己的设计器生成的控件是文件中的第一个。
      • 同样的问题。给班级自己的文件没有帮助。不管怎么说,多谢拉。 :(
      【解决方案4】:

      您的构建设置为调试还是发布? 我想它是发布,因为我认为编译器优化了代码并删除了设计器生成的行。

      【讨论】:

      • 感谢 Sonosar,但它已设置为调试。编译器不应删除该行,因为在设置其属性时仍会引用该控件。换句话说,即使删除了初始化它的行,分配其属性的行仍被留下以使用未初始化的对象。编译器删除初始化行而不删除其他行没有任何意义。无论如何,据我所知,编译器不会从实际源文件中删除内容。它只会从最终的可执行文件中删除内容。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-28
      • 1970-01-01
      • 2019-11-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多