自定义用户控件在自动生成的代码中未初始化

13
这种情况之前发生过很多次,但我从来没有费心去弄清楚为什么,现在我已经厌烦了:
例如,我从RichTextBox或Panel派生一个类,重新构建项目以将该类添加到VS设计器工具箱中,然后将自定义用户控件拖放到窗体上。一切正常,可以运行项目...
问题出在通过设计器编辑窗体或自定义用户控件的属性时。有时,设计器会从其代码后台中删除初始化行,导致设计器和可执行文件发生异常,因为控件保持未初始化状态。
换句话说,下面这行代码,例如在Form1.Designer.cs中,被删除了:
this.customRichTextBox1=new CustomRichTextBox();

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

我的解决方案一直是在设计器代码后台手动初始化用户控件,但设计器最终会再次删除它。

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

class CustomRichTextBox:RichTextBox{}

这真的很烦人。我错在哪里了?


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

步骤 1. 创建新的 Windows Forms 应用程序,任何框架

步骤 2. 在您的主窗体类下方添加以下类:(这只是这次导致我出现问题的控件。)

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

第三步。按F6重新构建。

第四步。从工具箱中拖放CustomRichTextBox控件到您的表单中。

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

第六步。按F6重新构建,此时设计器应崩溃,并显示以下消息:“变量'customRichTextBox1'未声明或未分配。”(在某些情况下,整个VS会完全崩溃,但错误通常仅限于设计器。)

第七步。为了纠正问题,请进入代码后台并初始化变量,但下次重新构建时,初始化行将消失。


几天前有一个类似的问题被发布了,但是他们的问题和你的问题都没有提供足够的代码来真正重现这个问题。我花了很多时间开发自定义控件库并与VS Designer一起工作,但我从来没有见过这种情况发生过。我不否认它已经发生在你身上,但如果我无法自己重现这个问题,那么我就无法帮助你找到解决方案。设计师有时会有些古怪,但它并不完全无可救药。 - Cody Gray
@Cody:感谢您的评论。我已经测试并添加了重现问题的步骤。希望您也能够重现它。您说得对,设计师是一个非常强大的工具,很少会给我带来麻烦。我想说这是我唯一需要与之抗争的情况...我相信问题与我自己输入类的方式有关。 - Eugenio De Hoyos
4个回答

16

感谢所有尝试回答我的问题以及发表评论帮助我诊断和解决问题的人。

当使用控件构造函数的 "internal" 关键字时,会出现问题。将其更改为 "public" 可以解决问题。这种行为出现的原因可能是设计器自己的类无法看到构造函数,因为它们不在我的类的命名空间中,除非将其标记为 public。这一切都说得通,我以后会使用 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!");
    }
}

0

我曾经遇到过类似的问题,这篇文章帮助我解决了。我有一个扩展了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 });

请注意,设计师生成的代码试图访问私有类。它不需要这样做,但出于某种原因它这样做了。
通过试错和这篇文章:如何判断.NET代码是否在Visual Studio设计器中运行,我找到了一个解决方案:
我添加了一个属性来判断我是否在设计器中运行。
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
}

这段代码是使用VS2012 Premium在Win7x64操作系统上编写的(如果有关系的话)。


0
你的构建设置是调试模式还是发布模式? 我猜测它是发布模式,因为编译器会优化代码并删除设计师生成的行。

谢谢Sonosar,但它被设置为Debug。编译器不应该删除该行,因为当其属性被设置时仍然引用控件。换句话说,即使初始化该对象的行被删除,分配其属性的行仍然留下来使用未初始化的对象。编译器删除初始化行而不删除其他行是没有任何意义的。无论如何,据我所知,编译器不会从实际源文件中删除任何内容。它只会从最终可执行文件中删除内容。 - Eugenio De Hoyos

0

你尝试过将控制代码放在自己的文件中吗?我曾经遇到过问题,即使在文件中设计器代码不是第一个类时,也会出现问题。


你可能是对的!我会尽快尝试这个。我从未想过这一点,但这很有道理,因为设计师需要自己生成的控件在文件中排在第一位。 - Eugenio De Hoyos
同样的问题。将类放在自己的文件中也没有帮助。无论如何还是谢谢。:( - Eugenio De Hoyos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接