您的位置:首页 > 其它

在.NET中调试设计时(design-time)控件

2005-01-07 14:10 495 查看
介绍
[/b]
[/b]在自定义控件开发过程中的一个重要部分,是检查和调试代码的能力。当在VS.NET的Design-Time环境中使用控件或者Windows Form时,即使在你的项目并没有运行(run)的情况下,你的代码仍能够被执行(execute)。这一点使得如何调试代码成为一个挑战。如果你曾经创建过任何会在Design-Time用到的对象,那么这篇文章将告诉你,当你在VS.NET的designer中使用这些对象时,如何发现错误并调试代码。我同时也将描述在使用设计界面操作控件时有可能发生的一系列事件。

为了展示当你改变控件的值,或者拖一个控件到设计界面时如何检查错误并调试代码,我们将创建一个基于WinForm的例子,它有一个string属性。当我们改变了属性的值时,我们将步入调试器中。

建立环境
[/b]第一步是建立一个容纳我们的代码的环境,我们将创建:
·一个空的解决方案来容纳我们的工程
·一个控件库来容纳我们的自定义控件
·一个WinForm程序来使用我们的自定义控件

建立空解决方案
[/b]1. 在File菜单中点击New,然后选择Blank Solution。
2. 将Solution命名为DesignTimeDebugging
3. 将此Solution放在c:/ DesignTimeDebugging。如下图:



创建空间库
[/b]由于我们的目标是示范如何调试而非如何创建自定义控件,因此我们不关心控件创建的细节。我们将创建一个具有一个属性的简单的Form。

建立控件工程
[/b]1. 在File菜单中点击New,然后点击Project。
2. 在左边的窗口里选Visual C# Projects。
3. 在右边的窗口里选Windows Control Library。
4. 将控件库命名为Immedient.Windows.Forms,注意我们保持与微软相同的命名规则,唯一不同的是我们用“Immedient”代替了“System”。这样就定义了一个唯一的名字空间。
5. 删除UserControl1。



添加一个新的Form
[/b]1. 右击Control Library工程,点Add,然后点Add Windows Form。
2. 将Form命名为“Form”。
3. 在类定义中添加如下代码:
C#
private string _myText = "Hello";
/// <summary>
/// A Custom property used for this silly sample
/// </summary>
/// <value>A value of no meaning</value>
[
Description("A Custom property used for this silly sample"),
DefaultValue("Hello"),
Category("Appearance")
]
public string MyText
{
get{return _myText;}
set
{
if(_myText != value)
{
_myText = value;
}
}
}
代码Description("A Custom property used for this silly sample")将在属性页的下方为你的属性添加描述。这里的文字通常与Xml注视文档中的相同。



DefaultValue("Hello")定义了我们的属性的缺省值。如果属性的值与DefaultValue不同,属性页将以黑体显示该值。同时,VS.NET也将在InitializeComponent()中生成设置该值的代码。这一点对于继承非常好用。如果没有派生类改变属性,那么它们都将继承来自基类的改变。
Category("Appearance")被用来将属性在属性页中排序。
创建应用自定义控件的WinForm工程
[/b]1. 在File菜单中点击New,然后点击Project。
2. 在左边的窗口中选择Visual C# Projects。
3. 在右边的窗口中选择Window Application(这个Application里将应用我们的例子)。
4. 将工程命名为DesignTimeDebugging。
5. 确定radio button选择的是Add to solution。

为工程添加我们的控件库的引用
[/b]1. 在我们的Host App Project中,右击References,点击Add Reference。
2. 在Projects属性页上,选择Immedient.Windows.Forms工程。



使用我们的自定义Form
[/b]1. 为了能够使用Microsoft IntelliSense,我们重新编译我们的工程。
2. 使用下面的代码改变Form1的继承关系,改由我们自定义的Form派生。
C#
namespace Immedient.Samples.HostApp.Windows
{
public class Form1 : Immedient.Windows.Forms.Form
{
3. 打开Form1,会发现我们自定义的属性出现在了属性页上。如果有错误发生,关闭所有代开的文件,然后Rebuild Solution,再重新打开Form1。

设置控件库工程的属性
[/b]通常我们希望能够确定我们是否产生了VS.NET调试我们的代码所需的信息。当开始调试时,如果在断点上开到“?”符号,说明我们没有产生合适的符号文件。这可能是由于当前正处于Release模式下,或者工程设置被偶然改变了。应当确定Generate Debugging Information属性被设为true。

Xml文档(C# Only)
[/b]下面的设置只对C#有效,在VS.NET2002中,VB.NET并不能产生Xml文档。

为了从我们在C#代码中创建的Xml注释中受益,我们应当告诉VS.NET生成一个Xml文档。将XML Documentation File属性设为与Assembly的名字加上xml后缀相同的值。

这里还用一个关于Xml文档的小窍门。你一旦设置了XML Documentation File,VS.NET会帮你为每个没有Xml注释的public interface产生警告。这些警告有时多的烦人。如果你还没有准备好处理所有public interface的文档的话,你可以将Warning Level设置为2,这样,任务列表就不会收集警告信息,直到你将所有的事情都准备妥当之后,在将它设回来即可。



开始调试
[/b]现在我们有了一些可以调试的代码,让我们开始吧。为了调试我们的代码,我们需要步入(step into)容纳我们代码的应用程序中。在我们的示例中是VS.NET。

设置调试属性
[/b]在调试过程中,我们需要改变一些调试属性。它们在VB.NET和C#中有些许不同(译注:在此谨以C#为例)。
C#工程属性页
[/b]1. 右击Control Library工程,选择Property。
2. 点击Configuration Properties。
3. 将Debug Mode改为Program。
4. 将Start Application改为Visual Studio .NET。默认的位置是:C:/Program Files/Microsoft Visual Studio .NET/Common7/IDE/devenv.exe。



设置断点
[/b]在自定义属性的set中的if表达式上设置断点



最后一件事
[/b]确保Control Library工程是启动工程。在Control Library工程上右击选择Set as Startup Project。

继续下去
[/b]1. 按F5开始调试过程。这时会有一个新的VS.NET实例被启动,我们的开发环境掌握此实例——pretty cool。
2. 分辨哪个VS.NET是我们的调试环境的一个简便方法是看调试按钮。调试环境中的start button是灰色无效的。
3. 当新的VS.NET启动之后,打开相同的solution:C:/DesignTimeDebugging/DesignTimeDebugging.sln
4. 在HostApp Windows Forms工程中,双击Form1打开之。
5. 在属性页中,改变MyText属性的值为Good bye。
6. 此时应当步入断点所在处。
7. 我们已经调试了我们的第一个.NET design-time control。现在让我们看一看发生在后台的一系列事件。

事件链条
[/b]在使用设计时控件时,当你将一个控件拖到设计界面上时,明白在它背后发生了什么是很重要的。你肯定很想知道,当你创建一个从同样是你创建的另一个Form派生的Form是发生了什么。

当一个对象被在设计环境中打开时,这个对象所继承的那个类(不是新生成的类)被VS.NET构造。记住,这个被创建的控件激活(fire)它的构造函数,构造函数又激活了它的基类的构造函数中的InitializeComponent()方法。一旦派生类被构造,那个在新建类中具有神奇的名字的方法,InitializeComponent(),将被VS.NET一行一行地解析。这个方法唯一神奇的是它的名字,因为VS.NET知道寻找这个方法。

在VB.NET中,如果VS.NET不能解析某行代码,那么这一行将被删除。这时由于VB开发小组觉得保持设计时环境更重要。而C#开发小组认为保持代码更重要,因此在C#中,如果VS.NET不能解析InitializeComponent(),你将会得到一个描述异常的文本。

当InitializeComponent()运行时,属性的值将被改成你在属性页上所设置的值。在VS.NET中,对象的属性页只是InitializeComponent()方法的图形化表示而已。如果你的基类的构造函数要完成一些特殊的功能,要小心了,这些功能也同样会被设计环境执行。

有一个方法可以使这些代码在设计时不被执行。任何派生自Component类的class都具有一个DesignMode属性。当对象在VS.NET designer中被构造时,这个属性被设为true。因此,你可以写一个if来包装你的代码,从而避免他们被执行。但是也没有更多的花招了,DesignMode不会再构造函数中设为true。记住,这里根本没有魔法。VS.NET通过解析InitializeComponent()方法来构造对象,一旦对象被构造,VS.NET将保持对对象的跟踪,并简单地设置:
newlyCreatedObject.DesignMode = true

为了增加一些乐趣,同时了事都有哪些事件会发生,以及DesignMode何时会被设置,在你的Immedient.Windows.Forms.Form中添加如下代码(译注:请用VS.NET来添加事件,这可以保证事件被正确添加到InitializeComponent方法中)。为了能够获得VS.NET发出的事件,请关闭所有的Form,重新编译Solution。

private void Form_Layout(object sender, System.Windows.Forms.LayoutEventArgs e)
{
MessageBox.Show("Layout: DesignMode = " + this.DesignMode.ToString());
}

private void Form_Load(object sender, System.EventArgs e)
{
if(this.DesignMode)
{
// Don't connected to database.
MessageBox.Show("Form load: DesignMode = " + this.DesignMode.ToString());
}
else
{
// Make a connection to database and do something.
MessageBox.Show("Form load: DesignMode = " + this.DesignMode.ToString());
}
}

private void Form_VisibleChanged(object sender, System.EventArgs e)
{
MessageBox.Show("VisibleChanged: DesignMode = " + this.DesignMode.ToString());
}

private void Form_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
MessageBox.Show("Paint: DesignMode = " + this.DesignMode.ToString());
}

总结
[/b]在这篇文章里,我们在Windows Form上使用了一个简单的属性。由此可以看出,在设计时环境中,任何东西的改变都是可以被调试的。我最初发现这个技巧,是在开发一个ASP.NET Image HREF设计时控件的时候。同样的技巧也适用于系统组件控件,比如EventLog控件和显示于系统托盘里的Data控件。VS.NET为开发控件提供了一个丰富、高效的开发环境,在这里,创建和调试控件就如同毁灭他们一样简单。
创建应用自定义控件的WinForm工程
[/b]1. 在File菜单中点击New,然后点击Project。
2. 在左边的窗口中选择Visual C# Projects。
3. 在右边的窗口中选择Window Application(这个Application里将应用我们的例子)。
4. 将工程命名为DesignTimeDebugging。
5. 确定radio button选择的是Add to solution。

为工程添加我们的控件库的引用
[/b]1. 在我们的Host App Project中,右击References,点击Add Reference。
2. 在Projects属性页上,选择Immedient.Windows.Forms工程。



使用我们的自定义Form
[/b]1. 为了能够使用Microsoft IntelliSense,我们重新编译我们的工程。
2. 使用下面的代码改变Form1的继承关系,改由我们自定义的Form派生。
C#
namespace Immedient.Samples.HostApp.Windows
{
public class Form1 : Immedient.Windows.Forms.Form
{
3. 打开Form1,会发现我们自定义的属性出现在了属性页上。如果有错误发生,关闭所有代开的文件,然后Rebuild Solution,再重新打开Form1。

设置控件库工程的属性
[/b]通常我们希望能够确定我们是否产生了VS.NET调试我们的代码所需的信息。当开始调试时,如果在断点上开到“?”符号,说明我们没有产生合适的符号文件。这可能是由于当前正处于Release模式下,或者工程设置被偶然改变了。应当确定Generate Debugging Information属性被设为true。

Xml文档(C# Only)
[/b]下面的设置只对C#有效,在VS.NET2002中,VB.NET并不能产生Xml文档。

为了从我们在C#代码中创建的Xml注释中受益,我们应当告诉VS.NET生成一个Xml文档。将XML Documentation File属性设为与Assembly的名字加上xml后缀相同的值。

这里还用一个关于Xml文档的小窍门。你一旦设置了XML Documentation File,VS.NET会帮你为每个没有Xml注释的public interface产生警告。这些警告有时多的烦人。如果你还没有准备好处理所有public interface的文档的话,你可以将Warning Level设置为2,这样,任务列表就不会收集警告信息,直到你将所有的事情都准备妥当之后,在将它设回来即可。



开始调试
[/b]现在我们有了一些可以调试的代码,让我们开始吧。为了调试我们的代码,我们需要步入(step into)容纳我们代码的应用程序中。在我们的示例中是VS.NET。

设置调试属性
[/b]在调试过程中,我们需要改变一些调试属性。它们在VB.NET和C#中有些许不同(译注:在此谨以C#为例)。
C#工程属性页
[/b]1. 右击Control Library工程,选择Property。
2. 点击Configuration Properties。
3. 将Debug Mode改为Program。
4. 将Start Application改为Visual Studio .NET。默认的位置是:C:/Program Files/Microsoft Visual Studio .NET/Common7/IDE/devenv.exe。



设置断点
[/b]在自定义属性的set中的if表达式上设置断点



最后一件事
[/b]确保Control Library工程是启动工程。在Control Library工程上右击选择Set as Startup Project。

继续下去
[/b]1. 按F5开始调试过程。这时会有一个新的VS.NET实例被启动,我们的开发环境掌握此实例——pretty cool。
2. 分辨哪个VS.NET是我们的调试环境的一个简便方法是看调试按钮。调试环境中的start button是灰色无效的。
3. 当新的VS.NET启动之后,打开相同的solution:C:/DesignTimeDebugging/DesignTimeDebugging.sln
4. 在HostApp Windows Forms工程中,双击Form1打开之。
5. 在属性页中,改变MyText属性的值为Good bye。
6. 此时应当步入断点所在处。
7. 我们已经调试了我们的第一个.NET design-time control。现在让我们看一看发生在后台的一系列事件。

事件链条
[/b]在使用设计时控件时,当你将一个控件拖到设计界面上时,明白在它背后发生了什么是很重要的。你肯定很想知道,当你创建一个从同样是你创建的另一个Form派生的Form是发生了什么。

当一个对象被在设计环境中打开时,这个对象所继承的那个类(不是新生成的类)被VS.NET构造。记住,这个被创建的控件激活(fire)它的构造函数,构造函数又激活了它的基类的构造函数中的InitializeComponent()方法。一旦派生类被构造,那个在新建类中具有神奇的名字的方法,InitializeComponent(),将被VS.NET一行一行地解析。这个方法唯一神奇的是它的名字,因为VS.NET知道寻找这个方法。

在VB.NET中,如果VS.NET不能解析某行代码,那么这一行将被删除。这时由于VB开发小组觉得保持设计时环境更重要。而C#开发小组认为保持代码更重要,因此在C#中,如果VS.NET不能解析InitializeComponent(),你将会得到一个描述异常的文本。

当InitializeComponent()运行时,属性的值将被改成你在属性页上所设置的值。在VS.NET中,对象的属性页只是InitializeComponent()方法的图形化表示而已。如果你的基类的构造函数要完成一些特殊的功能,要小心了,这些功能也同样会被设计环境执行。

有一个方法可以使这些代码在设计时不被执行。任何派生自Component类的class都具有一个DesignMode属性。当对象在VS.NET designer中被构造时,这个属性被设为true。因此,你可以写一个if来包装你的代码,从而避免他们被执行。但是也没有更多的花招了,DesignMode不会再构造函数中设为true。记住,这里根本没有魔法。VS.NET通过解析InitializeComponent()方法来构造对象,一旦对象被构造,VS.NET将保持对对象的跟踪,并简单地设置:
newlyCreatedObject.DesignMode = true

为了增加一些乐趣,同时了事都有哪些事件会发生,以及DesignMode何时会被设置,在你的Immedient.Windows.Forms.Form中添加如下代码(译注:请用VS.NET来添加事件,这可以保证事件被正确添加到InitializeComponent方法中)。为了能够获得VS.NET发出的事件,请关闭所有的Form,重新编译Solution。

private void Form_Layout(object sender, System.Windows.Forms.LayoutEventArgs e)
{
MessageBox.Show("Layout: DesignMode = " + this.DesignMode.ToString());
}

private void Form_Load(object sender, System.EventArgs e)
{
if(this.DesignMode)
{
// Don't connected to database.
MessageBox.Show("Form load: DesignMode = " + this.DesignMode.ToString());
}
else
{
// Make a connection to database and do something.
MessageBox.Show("Form load: DesignMode = " + this.DesignMode.ToString());
}
}

private void Form_VisibleChanged(object sender, System.EventArgs e)
{
MessageBox.Show("VisibleChanged: DesignMode = " + this.DesignMode.ToString());
}

private void Form_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
MessageBox.Show("Paint: DesignMode = " + this.DesignMode.ToString());
}

总结
[/b]在这篇文章里,我们在Windows Form上使用了一个简单的属性。由此可以看出,在设计时环境中,任何东西的改变都是可以被调试的。我最初发现这个技巧,是在开发一个ASP.NET Image HREF设计时控件的时候。同样的技巧也适用于系统组件控件,比如EventLog控件和显示于系统托盘里的Data控件。VS.NET为开发控件提供了一个丰富、高效的开发环境,在这里,创建和调试控件就如同毁灭他们一样简单。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: