‘ .NET开发 ’ category archive

.NET中实现自绘的ListBox

130 views 十月 30, 06 by Timothy

在平常的开发过程中,我们为了改善用户的体验,通常会在界面上花一些功夫。因为清爽的界面,通常给用户带来一些耳目一新的感觉,并且能增强用户满意度。前几周的一个项目中,就用到了ListBox,需要突出用户在ListBox中选中的项,而.NET自带的ListBox由于配色过于普通,无法满足我们的需要,这就需要我们重新对ListBox的配色进行一些修改。
在Windows的消息机制中,有一个消息叫做WM_DRAWITEM,当控件(比如:Button,ComboBox,ListBox,Menu)需要被重新绘制的时候,会向该控件的父窗口发送这个消息。父窗口通过响应这个消息,就可以实现对该控件外观的绘制,也就是说,通过响应WM_DRAWITEM消息,我们可以接管系统对控件的绘制,这样,我们就可以随心所欲,用我们喜欢的方式,来绘制这个控件了。听起来不错吧?
.NET的WinForm程序,也是基于Windows的GUI界面的窗口,而.NET追溯到最底层,其实也是对Win32 API的更高一个层次的封装。所以,.NET的WinForm程序同样也逃脱不了消息机制。于是我们就可以在程序中,通过设置控件为Owner Draw属性,然后加入对WM_DRAWITEM的处理,就可以在OnDrawItem函数中,定制控件外观了。
理论上就是这样,我们开始动手吧,打造一个具有个性的ListBox控件!需要说明的是,当我们把控件属性中的DrawMode改为OwnerDrawFixed过后,.NET以及将绘制这个控件的责任交给了我们,因此我们必须要实现DrawItem函数,否则ListBox会是一片白,什么也看不到。
1.建立一个简单的工程,在工具箱拖放ListBox到Form上,然后将ListBox属性中的DrawMode改为OwnerDrawFixed,或者在Form_Load中,设置控件的属性:
this.listBox1.DrawMode = DrawMode.OwnerDrawFixed;
2.在事件窗口中,为DrawItem添加处理函数。
3.配色方案:这里,我们设定被选中的项目有一个蓝色的外框,并且选中的项中,内容呈亮绿色显示,背景色呈灰色显示,这样就更醒目一些了。
4.最后一步,就是加入OnDrawItem的代码,如下:

private void listBox1_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
{
//定义背景色,选中为灰色,未选中为白色
Brush brNoSelectedBack=Brushes.White;
Brush brSelectedBack=Brushes.Gray;
//定义前景色,选中为亮绿色,未选中为黑色
Brush brSelectedFore=Brushes.LightGreen;
Brush brNoSelectedFore=Brushes.Black;

//定义焦点,有焦点状态下绘制蓝色外框(蓝色)
Pen penFocus=new Pen(Brushes.Blue);

e.Graphics.FillRectangle(Brushes.White,e.Bounds);

//绘制Item被选中的情况
if((e.State & DrawItemState.Selected) == DrawItemState.Selected)
{
e.Graphics.FillRectangle(brSelectedBack,e.Bounds);
//绘制焦点外框
e.Graphics.DrawRectangle(penFocus,e.Bounds.X,e.Bounds.Y,e.Bounds.Width,e.Bounds.Height-1);
e.Graphics.DrawString(this.listBox1.Items[e.Index].ToString(),
this.listBox1.Font,brSelectedFore,e.Bounds);
//e.DrawFocusRectangle();
return;
}

//绘制没有被选中的情况
e.Graphics.FillRectangle(brNoSelectedBack,e.Bounds);
e.Graphics.DrawString(this.listBox1.Items[e.Index].ToString(),
this.listBox1.Font,brNoSelectedFore,e.Bounds);
}

到这里,一个简单的自绘ListBox就实现了,不过这里的代码都是直接加载父窗体的里面,不是很美观。其实我们可以自己实现一个这样的类,并把这个ListBox编译为一个.NET控件,这样,在其他的项目中就可以很简单的重用这个控件了。:)
下面是这个自绘控件的效果,左边是我们的自绘ListBox,右边是普通的ListBox。等有时间,我会把这些封装成为一个dll控件 :)。这里提供源代码的下载,有兴趣的朋友可以再根据自己的需求进行一些自己的修改。

点击下载此文件

调试windows服务的一点经验【补充】

143 views 九月 18, 06 by Timothy

上次写过一个调试windows服务的一点经验的日志。这段时间也在做一个和Windows Service有关的东西。又有了些经验,所以拿来和大家分享下。调试windows服务,采用的一般方法,就是设好断点,然后启动服务,在IDE里面直接通过进程列表,把Service的exe附加到IDE上面来调试,这个方法在上一个日志也提到过。其实在.NET建立的服务程序中,还有一个方法,也是之前没有想到的方法,更为简单 :)

// 进程的主入口点
static void Main()
{
System.ServiceProcess.ServiceBase[] ServicesToRun;

// 同一进程中可以运行多个用户服务。若要将
//另一个服务添加到此进程,请更改下行
// 以创建另一个服务对象。例如,
//
// ServicesToRun = New System.ServiceProcess.ServiceBase[] {new Service1(), new MySecondUserService()};
//
ServicesToRun = new System.ServiceProcess.ServiceBase[] { new Service1() };

System.ServiceProcess.ServiceBase.Run(ServicesToRun);
}

也就是在Main方法,服务程序开始的地方,如上,把默认的创建并运行服务的代码全部注释掉,然后直接生成本类的一个实例,然后调用OnStart方法就可以了,这里要注意的是OnStart方法是一个受保护的无返回值的方法,而Main函数是静态方法,所以需要把OnStart也改动一下,设置为静态函数。这样的话,服务和一般的.net应用程序就一样了,调试的时候更加方便,直接F5就可以了 :) 不过在完成调试过后,一定记得要把上面的改动还原 [cool]

改动后的代码如下:

// 进程的主入口点
static void Main()
{
Service1 obj=new Service1();
obj.OnStart();
}

这个方法虽然有点投机,不过确实能够节省很多步骤,让调试服务和调试一般的应用程序那样方便!值得推荐

微软的XNA Framework

166 views 八月 18, 06 by Timothy

今天在MSDN网站上逛,看到了醒目的XNA标题。不得不佩服MS在平台这方面的雄心。
XNA,是MS针对游戏领域开发的一个体系。现在只要谈到未来的游戏发展蓝图几乎跟XNA脱不了关系。由于XNA整合了电脑/ XBOX家族 / 手机游戏 的开发环境,甚至可说XNA是未来XBOX二代机最重要的核心,也难怪MS对XNA如此重视。这样看来,在经后的游戏开发,就有了一个统一的平台,这也可以看出MS在游戏领域的雄心。MS同时提供XNA Game Studio Express/Professional 开发环境。
XNA Framework也是一系列的托管类库,是基于.NET 2.0的,并且建立在DirectX基础之上。通过XNA Game Studio,游戏开发者、游戏爱好者,都可以根据自己的爱好来开发游戏了,这不得不是一个诱人的地方。想想自己也可以开发游戏了,不用像以前那样,用C/C++调用复杂的Direct X SDK来开发游戏的过程。把这些绘图、物理运算、音效、网络等不同范围的程序完全整合在一个工具中,并提供最完善的函数库让游戏开发人员能学会使用、全盘了解,这就是XNA Framework。也许这还不够,而在MS规划中希望XNA Framework的最终目标,能让开发一款游戏就像制作一个网页那么简单!

有关更多XNA的信息:

http://msdn.microsoft.com/directx/XNA/default.aspx

.NET中的文件监视类

126 views 八月 02, 06 by Timothy

最近做的一个项目,涉及到对某个目录的操作的监视,其实在.NET中,使用.NET中的文件监视类,就可以轻松实现对某个目录,以及其子目录的文件监视。比如新建文件、修改文件、删除文件等操作。并且能够以事件的形式通知程序。只要我们在事件委托里面,绑定相应的处理函数即可。.NET的功能果然很强大,只需要几句简单的调用,我们就可以实现这样的监视功能了。

我们以一个例子来实现吧:

static void Main(string[] args)
{
FileSystemWatcher m_Watcher=new FileSystemWatcher();
m_Watcher.Path=@"d:\test";
m_Watcher.IncludeSubdirectories=true;
m_Watcher.Filter="*.*";
m_Watcher.NotifyFilter=NotifyFilters.LastWrite | NotifyFilters.FileName;
m_Watcher.Created+=new FileSystemEventHandler(OnChanged);
m_Watcher.Changed+=new FileSystemEventHandler(OnChanged);
m_Watcher.Deleted+=new FileSystemEventHandler(OnChanged);
m_Watcher.EnableRaisingEvents=true;

Console.WriteLine("Press \'q\' to quit the sample.");
while(Console.Read()!='q');
}

private static void OnChanged(object sender, FileSystemEventArgs e)
{
Console.WriteLine(DateTime.Now.ToString()+" {0} {1}",e.ChangeType,e.FullPath);
}

程序比较简单,先是生成FileSystemWatcher的实例,然后进行相应的设置。我在这里设置要监控的目录
为D:\Test,监控所有文件,也即*.*。然后通过事件,添加响应事件的函数OnChanged到Created、Changed以及Deleted事件中。这样的话,当文件被创建、修改、或者被删除的时候,就能调用我们的OnChanged函数。此函数中的FileSystemEventArgs包含了具体事件的一些信息。比如被修改的文件名、被出发的事件类型等等。通过在回调函数的判断,我们就可以实现响应的操作了。

程序运行效果如下图:

【代码下载】
点击下载此文件

使用NUnit对.NET程序进行单元测试

135 views 七月 24, 06 by Timothy

每一个开发出来的软件,在发布之前,都需要进行测试。因为没有程序员敢保证自己的代码没有BUG。而测试又分为很多种,今天所要介绍的单元测试,属于众多测试手段中的一种。我的观点,单元测试是应该由开发人员在编码过程结束后,所进行的一种自我测试手段,是测试过程的第一步。

单元测试的目的,就是针对要测试的模块或者方法,写一段代码用来测试,看是否达到预期的结果。不同的开发语言,单元测试的工具也不一样。JAVA语言开发系统:目前比较流行的是JUnit;.Net语言开发系统:目前比较流行的是:NUnit;C/C++语言开发系统:目前比较流行的是CPPUnit

今天要介绍的是NUnit,NUnit的工具,大家可以到www.nunit.org的网站去下载。下载后双击安装,在桌面可以找到一个NUnit的GUI程序快捷方式。NUnit提供GUI和CUI的两种界面。

首先,我们要构建一个被测试的类。在这里,我们假定有一个类 MyClass:

namespace NUnitTest
{
public class MyClass
{

private int m_Value;
public MyClass()
{
m_Value=0;
}

public void Add(int nAmount)
{
m_Value+=nAmount;
}

public int CurrentValue
{
get{return m_Value;}
}

}
}

类提供一个Add方法和一个CurrentValue的属性。Add方法,用于对成员变量m_Value值进行加法运算。
CurrentValue属性用于获取当前m_Value的值。

在完成这个类之后,我们就需要对这个类进行单元测试了。具体的方法,是建立另外一个类库,在这个工程里面,引用我们的MyClass类所在的程序集。并且在NUnit的安装目录下的bin子目录,添加对NUnit.Framework.dll的引用。我们的测试类构建如下:

using System;
using NUnit.Framework;
using NUnitTest;

namespace NUnitTest
{
[TestFixture]
public class MyClassTest
{
public MyClassTest()
{
}

[Test]
public void TestMethod()
{
MyClass obj=new MyClass();
obj.Add(50);
Assert.AreEqual(50,obj.CurrentValue);
}
}
}

注意,其中的[TestFixture]和[Test]两个Attribute为NUnit所规定必须要添加的,这样,测试框架就可以知道哪些类或者方法需要进行测试。并且根据NUnit在线文档的规定,测试的方法之前,必须加上Test特性。并且改方法必须返回void,还不能带输入参数。

在这个方法种,就是我们针对Add方法的具体测试代码了,代码比较简单,就是调用Add方法,改变m_Value的值为50。然后调用Assert类的静态方法AreEqual进行测试,比较m_Value当前的值是否是我们所预期的值:50。

保存好工程后,编译该测试的程序集。然后打开NUnit GUI程序。加载这个测试的程序集到工具中。选中工程,然后点击右边的Run按钮。如果左边全部变为绿色,表示测试通过。

加入我们将上面的
Assert.AreEqual(50,obj.CurrentValue);
改为:
Assert.AreEqual(60,obj.CurrentValue);

显然,我们期望的值是60,而实际值并不是60。则测试不会通过,并且会提示:
NUnitTest.MyClassTest.TestMethod :
expected: <60>
but was: <50>

在下面的输出窗口,会提示发生错误的具体的代码位置:

at NUnitTest.MyClassTest.TestMethod() in d:\mycode\c# projects\nunittest\myclasstest\myclasstest.cs:line 24

这样,我们能够方便的找到出错的地方,并进行分析和改正。NUnit对单元测试提供了很多的方便,它还提供了很多方便和功能强大的类。具体的用法,大家可以参考NUnit官方网站的在线文档。

Page 5 of 512345