东软的C#编程规范,与大家分享。
点击下载

引言:
  微软的新的.NET平台为开发者带来了许多新的诸如GDI+、Globalization之类的编程机制,同时还发明了一门全新的类似Java的编程语言-C#。对于这些新知识,我们应尽快了解、掌握并试图运用到实践项目中去,而通过实例学习的方法无疑是一个非常有效的途径。本文就通过一个简单的实例,向大家展示了在Visual C#中如何运用GDI+和Unsafe代码类等技术以实现简单的数字图像处理。

  一.概述:
  本文的实例是一个数字图像处理的应用程序,它完成的功能包括对图像颜色的翻转、对图像进行灰度处理和对图像进行增亮处理。该程序对图像进行处理部分的代码包含在一个专门的Filters类里面,通过调用该类里的静态成员函数,我们就可以实现相应的图像处理功能了。为实现图像处理,我们要对图像进行逐个象素处理。我们知道图像是由一个个的象素点组成的,对一幅图像的每个象素进行了相应的处理,最后整个图像也就处理好了。在这个过程中,我们只需对每个象素点进行相应的处理,在处理过程中却不需要考虑周围象素点对其的影响,所以相对来说程序的实现就变得简单多了。
  由于GDI+中的BitmapData类不提供对图像内部数据的直接访问的方法,我们唯一的办法就是使用指针来获得图像的内部数据,这时我们就得运用unsafe这个关键字来指明函数中访问图像内部数据的代码块了。在程序中,我还运用了打开文件和保存文件等选项,以使我们的辛勤劳动不付之东流。
  二.程序的实现:
  1.打开Visual Studio.net,新建一个Visual C#的项目,在模板中选择"Windows 应用程序"即可,项目名称可自定(这里为ImageProcessor)。
  2.为使窗体能显示图像,我们需要重载窗体的OnPaint()事件函数,在该函数中我们将一个图像绘制在程序的主窗体上,为了使窗体能显示不同尺寸大小的图像,我们还将窗体的AutoScroll属性设置为true。这样,根据图像的尺寸,窗体两边就会出现相应的滚动条。该函数的实现如下:
private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
Graphics g = e.Graphics;
g.DrawImage(m_Bitmap, new Rectangle(this.AutoScrollPosition.X, this.AutoScrollPosition.Y,
(int)(m_Bitmap.Width), (int)(m_Bitmap.Height)));
}
3.给主窗体添加一个主菜单,该主菜单完成了一些基本的操作,包括"打开文件"、"保存文件"、"退出"、"翻转操作"、"灰度操作"、"增亮操作"等。前面三个操作完成图像文件的打开和保存以及程序的退出功能,相应的事件处理函数如下:
private void menuItemOpen_Click(object sender, System.EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "Bitmap文件(*.bmp)|*.bmp|
Jpeg文件(*.jpg)|*.jpg|
所有合适文件(*.bmp/*.jpg)|*.bmp/*.jpg";
openFileDialog.FilterIndex = 2 ;
openFileDialog.RestoreDirectory = true ;
if(DialogResult.OK == openFileDialog.ShowDialog())
{
m_Bitmap = (Bitmap)Bitmap.FromFile(openFileDialog.FileName, false);
this.AutoScroll = true;
this.AutoScrollMinSize=new Size ((int)(m_Bitmap.Width),(int)
m_Bitmap.Height));
this.Invalidate();
}
}
  其中,m_Bitmap为主窗体类的一个数据成员,声明为private System.Drawing.Bitmap m_Bitmap;(注:因为程序中用到了相关的类,所以在程序文件的开始处应添加using System.Drawing.Imaging;)同时,在该类的构造函数中,我们必须先给它new一个Bitmap对象:m_Bitmap = new Bitmap(2,2);上述代码中的this.Invalidate();完成主窗体的重绘工作,它调用了主窗体的OnPaint()函数,结果就将打开的图像文件显示在主窗体上。
private void menuItemSave_Click(object sender, System.EventArgs e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.Filter = "Bitmap文件(*.bmp)|*.bmp|
Jpeg文件(*.jpg)|*.jpg|
所有合适文件(*.bmp/*.jpg)|*.bmp/*.jpg";
saveFileDialog.FilterIndex = 1 ;
saveFileDialog.RestoreDirectory = true ;
if(DialogResult.OK == saveFileDialog.ShowDialog())
{
m_Bitmap.Save(saveFileDialog.FileName);
}
}
  其中m_Bitmap.Save(saveFileDialog.FileName);一句完成了图像文件的保存,正是运用了GDI+的强大功能,我们只需这么一条简单的语句就完成了以前很大工作量的任务,所以合理运用.NET中的新机制一定会大大简化我们的工作的。
private void menuItemExit_Click(object sender, System.EventArgs e)
{
this.Close();
}
  接下来,三个主要操作的事件处理函数如下:
private void menuItemInvert_Click(object sender, System.EventArgs e)
{
if(Filters.Invert(m_Bitmap))
this.Invalidate();
}
private void menuItemGray_Click(object sender, System.EventArgs e)
{
if(Filters.Gray(m_Bitmap))
this.Invalidate();
}
private void menuItemBright_Click(object sender, System.EventArgs e)
{
Parameter dlg = new Parameter();
dlg.nValue = 0;
if (DialogResult.OK == dlg.ShowDialog())
{
if(Filters.Brightness(m_Bitmap, dlg.nValue))
this.Invalidate();
}
}
  三个函数中分别调用了相应的图像处理函数Invert()、Gray()、Brightness()等三个函数。这三个函数Filters类中的三个类型为public的静态函数(含有static关键字),它们的返回值类型均是bool型的,根据返回值我们可以决定是否进行主窗体的重绘工作。
  Invert()、Gray()、Brightness()等三个函数均包含在Filters类里面,Invert()函数的算法如下:
public static bool Invert(Bitmap b)
{
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte * p = (byte *)(void *)Scan0;
int nOffset = stride - b.Width*3;
int nWidth = b.Width * 3;
for(int y=0;y
  该函数以及后面的函数的参数都是Bitmap类型的,它们传值的对象就是程序中所打开的图像文件了。该函数中的BitmapData类型的bmData包含了图像文件的内部信息,bmData的Stride属性指明了一条线的宽度,而它的Scan0属性则是指向图像内部信息的指针。本函数完成的功能是图像颜色的翻转,实现的方法即用255减去图像中的每个象素点的值,并将所得值设置为原象素点处的值,对每个象素点进行如此的操作,只到整幅图像都处理完毕。函数中的unsafe代码块是整个函数的主体部分,首先我们取得图像内部数据的指针,然后设置好偏移量,同时设置nWidth为b.Width*3,因为每个象素点包含了三种颜色成分,对每个象素点进行处理时便要进行三次处理。接下来运用两个嵌套的for循环完成对每个象素点的处理,处理的核心便是一句:p[0] = (byte)(255-p[0]);。在unsafe代码块后,便可运用b.UnlockBits(bmData)进行图像资源的释放。函数执行成功,最后返回true值。注:由于是要编译不安全代码,所以得将项目属性页中的"允许不安全代码块"属性设置为true,图示如下:
http://www.yesky.com/image20010518/53286.jpg
该函数实现的程序效果如下:
http://www.yesky.com/image20010518/53287.jpg
http://www.yesky.com/image20010518/53288.jpg
 Gray()函数的算法如下:
public static bool Gray(Bitmap b)
{
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte * p = (byte *)(void *)Scan0;
int nOffset = stride - b.Width*3;
byte red, green, blue;
for(int y=0;y
  本函数完成的功能是对图像进行灰度处理,我们的基本想法可是将每个象素点的三种颜色成分的值取平均值。然而由于人眼的敏感性,这样完全取平均值的做法的效果并不好,所以在程序中我取了三个效果最好的参数:.299,.587,.114。不过在这里要向读者指明的是,在GDI+中图像存储的格式是BGR而非RGB,即其顺序为:Blue、Green、Red。所以在for循环内部一定要设置好red、green、blue等变量的值,切不可颠倒。函数执行成功后,同样返回true值。
  该函数实现的程序效果如下:
http://www.yesky.com/image20010518/53289.jpg
http://www.yesky.com/image20010518/53293.jpg
 Brightness()函数的算法如下:
public static bool Brightness(Bitmap b, int nBrightness)
{
if (nBrightness 255)
return false;
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width,
b.Height), ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
int nVal = 0;
unsafe
{
byte * p = (byte *)(void *)Scan0;
int nOffset = stride - b.Width*3;
int nWidth = b.Width * 3;
for(int y=0;y 255) nVal = 255;
p[0] = (byte)nVal;
++p;
}
p += nOffset;
}
}
b.UnlockBits(bmData);
return true;
}
  本函数完成的功能是对图像进行增亮处理,它比上面两个函数多了一个增亮参数-nBrightness,该参数由用户输入,范围为-255~255。在取得了增亮参数后,函数的unsafe代码部分对每个象素点的不同颜色成分进行逐个处理,即在原来值的基础上加上一个增亮参数以获得新的值。同时代码中还有一个防止成分值越界的操作,因为RGB成分值的范围为0~255,一旦超过了这个范围就要重新设置。函数最后执行成功后,同样得返回true值。
  该函数实现的程序效果如下:
http://www.yesky.com/image20010518/53295.jpg
三.小结:
  本文通过一个简单的实例向大家展现了用Visual C#以及GDI+完成数字图像处理的基本方法,通过实例,我们不难发现合理运用新技术不仅可以大大简化我们的编程工作,还可以提高编程的效率。不过我们在运用新技术的同时也得明白掌握基本的编程思想才是最主要的,不同的语言、不同的机制只是实现的具体方式不同而已,其内在的思想还是相通的。对于上面的例子,掌握了编写图像处理函数的算法,用其他的方式实现也应该是可行的。同时,在上面的基础上,读者不妨试着举一反三,编写出更多的图像处理的函数来,以充实并完善这个简单的实例。

一、最小化窗口

点击“X”或“Alt+F4”时,最小化窗口,
如:
protected override void WndProc(ref Message m)
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_CLOSE = 0xF060;
if (m.Msg == WM_SYSCOMMAND && (int) m.WParam == SC_CLOSE)
{
// User clicked close button
this.WindowState = FormWindowState.Minimized;
return;
}
base.WndProc(ref m);
}

二、如何让Foreach 循环运行的更快
foreach是一个对集合中的元素进行简单的枚举及处理的现成语句,用法如下例所示:
using System;
using System.Collections;
namespace LoopTest
{
class Class1
{
static void Main(string[] args)
{
// create an ArrayList of strings
ArrayList array = new ArrayList();
array.Add("Marty");
array.Add("Bill");
array.Add("George");
// print the value of every item
foreach (string item in array)
{
Console.WriteLine(item);
}
}
}
你可以将foreach语句用在每个实现了Ienumerable接口的集合里。如果想了解更多foreach的用法,你可以查看.NET Framework SDK文档中的C# Language Specification。

在编译的时候,C#编辑器会对每一个foreach 区域进行转换。IEnumerator enumerator = array.GetEnumerator();
try
{
string item;
while (enumerator.MoveNext())
{
item = (string) enumerator.Current;
Console.WriteLine(item);
}
}
finally
{
IDisposable d = enumerator as IDisposable;
if (d != null) d.Dispose();
}
这说明在后台,foreach的管理会给你的程序带来一些增加系统开销的额外代码。

三、将图片保存到一个XML文件
WinForm的资源文件中,将PictureBox的Image属性等非文字内容都转变成文本保存,这是通过序列化(Serialization)实现的,
例子://
using System.Runtime.Serialization.Formatters.Soap;
Stream stream = new FileStream("E:\\Image.xml",FileMode.Create,FileAccess.Write,FileShare.None);
SoapFormatter f = new SoapFormatter();
Image img = Image.FromFile("E:\\Image.bmp");
f.Serialize(stream,img);
stream.Close();

四、屏蔽CTRL-V
在WinForm中的TextBox控件没有办法屏蔽CTRL-V的剪贴板粘贴动作,如果需要一个输入框,但是不希望用户粘贴剪贴板的内容,可以改用RichTextBox控件,并且在KeyDown中屏蔽掉CTRL-V键,例子:

private void richTextBox1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
if(e.Control && e.KeyCode==Keys.V)
e.Handled = true;
}

一、判断文件或文件夹是否存在
使用System.IO.File,要检查一个文件是否存在非常简单:
bool exist = System.IO.File.Exists(fileName);

如果需要判断目录(文件夹)是否存在,可以使用System.IO.Directory:
bool exist = System.IO.Directory.Exists(folderName);

二、使用delegate类型设计自定义事件
在C#编程中,除了Method和Property,任何Class都可以有自己的事件(Event)。定义和使用自定义事件的步骤如下:
(1)在Class之外定义一个delegate类型,用于确定事件程序的接口
(2)在Class内部,声明一个public event变量,类型为上一步骤定义的delegate类型
(3)在某个Method或者Property内部某处,触发事件
(4)Client程序中使用+=操作符指定事件处理程序

例子: // 定义Delegate类型,约束事件程序的参数
public delegate void MyEventHandler(object sender, long lineNumber) ;

public class DataImports
{
// 定义新事件NewLineRead
public event MyEventHandler NewLineRead ;

public void ImportData()
{
long i = 0 ; // 事件参数
while()
{
i++ ;
// 触发事件
if( NewLineRead != null ) NewLineRead(this, i);
//...
}
//...
}
//...
}

// 以下为Client代码

private void CallMethod()
{
// 声明Class变量,不需要WithEvents
private DataImports _da = null;
// 指定事件处理程序
_da.NewLineRead += new MyEventHandler(this.DA_EnterNewLine) ;
// 调用Class方法,途中会触发事件
_da.ImportData();
}
// 事件处理程序
private void DA_EnterNewLine(object sender, long lineNumber)
{
// ...
}

三、IP与主机名解析
使用System.Net可以实现与Ping命令行类似的IP解析功能,例如将主机名解析为IP或者反过来: private string GetHostNameByIP(string ipAddress)
{
IPHostEntry hostInfo = Dns.GetHostByAddress(ipAddress);
return hostInfo.HostName;
}
private string GetIPByHostName(string hostName)
{
System.Net.IPHostEntry hostInfo = Dns.GetHostByName(hostName);
return hostInfo.AddressList[0].ToString();
}

In some scenarios, you may wish to ensure that a user can run only one instance of your application at a time. Besides ensuring that only a single instance of your application is running, you may also want to bring the instance already running to the front and restore it, if it is minimized.
First, to ensure that only one instance of your application is running at a time, the best method I've found is to create a mutex that is held by the operating system (thanks to Michael Covington). This will put a request to the operating system that a mutex be created if one does not already exist. Only one mutex can ever be created at a time, so if you request a new one and it cannot be created, you can safely assume that your application is already running.

using System.Threading
using System.Runtime.InteropServices;

public class Form1 : Form
{
[STAThread]
static void Main()
{
bool createdNew;

Mutex m = new Mutex(true, "YourAppName", out createdNew);

if (! createdNew)
{
// app is already running...
MessageBox.Show("Only one instance of this application is allowed at a time.");
return;
}

Application.Run(new Form1());

// keep the mutex reference alive until the normal termination of the program
GC.KeepAlive(m);
}
}

The above code will work for the vast majority of your needs. It will also run under scenarios where your code is executing with less than FullTrust permissions (see Code Access Security in MSDN for further information).

If your application can run with Full Trust permissions, we can take this a step further and find the window of the application instnace already running and bring it to the front for the user:

public class Form1 : Form
{
[STAThread]
static void Main()
{
bool createdNew;

System.Threading.Mutex m = new System.Threading.Mutex(true, "YourAppName", out createdNew);

if (! createdNew)
{
// see if we can find the other app and Bring it to front
IntPtr hWnd = FindWindow("WindowsForms10.Window.8.app3", "YourAppName");

if(hWnd != IntPtr.Zero)
{
Form1.WINDOWPLACEMENT placement = new Form1.WINDOWPLACEMENT();
placement.length = Marshal.SizeOf(placement);

GetWindowPlacement(hWnd, ref placement);

if(placement.showCmd != SW_NORMAL)
{
placement.showCmd = SW_RESTORE;

SetWindowPlacement(hWnd, ref placement);
SetForegroundWindow(hWnd);
}
}

return;
}

Application.Run(new Form1());

// keep the mutex reference alive until the normal termination of the program
GC.KeepAlive(m);
}

private const int SW_NORMAL = 1; // see WinUser.h for definitions
private const int SW_RESTORE = 9;

[DllImport("User32",EntryPoint="FindWindow")]
static extern IntPtr FindWindow(string className, string windowName);

[DllImport("User32",EntryPoint="SendMessage")]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

[DllImport("User32",EntryPoint="SetForegroundWindow")]
private static extern bool SetForegroundWindow(IntPtr hWnd);

[DllImport("User32",EntryPoint="SetWindowPlacement")]
private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);

[DllImport("User32",EntryPoint="GetWindowPlacement")]
private static extern bool GetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);

private struct POINTAPI
{
public int x;
public int y;
}

private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}

private struct WINDOWPLACEMENT
{
public int length;
public int flags;
public int showCmd;
public POINTAPI ptMinPosition;
public POINTAPI ptMaxPosition;
public RECT rcNormalPosition;
}
}

As you can see, with minimal effort, you can easily add a polished touch to your application. This might even help you avoid some extra legwork in ensuring that there are no issues with running multiple instances of your app at the same time that you might have to address.

For more information about the Platform Invoke mechanisms to call Win32 API functions, I recommend that you check out .NET Framework Solutions: In Search of the Lost Win32 API by John Mueller and Charles Petzold's seminal classic Programming Windows.

Until Longhorn comes out and more of the Windows platform becomes managed, platform invokes and interop will remain a key technology to understand and use to your advantage to fill the gaps left by the Windows Forms framework.

UPDATE:
Visual Studio .NET 2005 (aka. “Whidbey”) will let you specify Single Instance behavior for Visual Basic .NET projects through the Project Properties page. Curious as to why this option is available for VB projects and not for C# projects, as this seems to instruct the runtime or some framework library not to load more than one instance.

北理代理助手是专为北京理工大学开发的代理设置软件。使用该软件可以快速的切换、验证代理,并可随时下载网协提供的最新的代理列表。

http://www.felixwoo.com/wp-content/uploads/attachments/old/20041092068694.jpg

特色:
1、专为北理校园网设计 可以随时从网协服务器下载最新代理列表,列表数据由服务器每小时自动验证更新
2、快速切换代理 双击代理列表中的代理或在悬浮窗菜单中选择即可切换,无需重启IE
3、快速启用/禁用代理 通过主界面上选择或使用热键(Alt+F1)即可方便的启用或禁用代理,无需重启IE
4、自动启用最快代理 勾选此选项后即可在下载或验证结束后自动启用最快的代理
5、本地代理列表管理方便 除了可以从服务器下载最新的代理列表,您还可以在本地手动进行验证,验证结果按照由快到慢的顺序自动排列。您也可以手动的添加或删除代理。
6、添加代理方便 直接将代理文本拖动到悬浮窗即可添加到代理列表
7、绿色软件 免安装、不更改注册表、程序小巧(不到200K)

使用说明

http://www.felixwoo.com/wp-content/uploads/attachments/old/200410920650941.jpg

下载地址:
/download/北理代理助手.exe
.NET Framework /dotnetfx.exe (运行前请先安装)

Felix
2004.10.9

For performance test, it is very important to measure code execution time. Without measurement, there is no way to tell if we meet performance goal.

System.Environment.TickCount is not suitable for high resolution timing. Its resolution cannot be less than 500 milliseconds.

System.Datetime.Now returns the current time of type DateTime. With start datetime and end datetime, we can get the interval as a value of TimeSpan by (end - start ) . TimeSpan.TotalMilliseconds or TimeSpan.Ticks may be used to read interval. From MSDN, the resolution of System.Datetime.Now depends on the system timer.

System Approximate Resolution

Windows NT 3.5 and later 10 milliseconds

Windows 98 55 milliseconds

So it is better but not high resolution at all.

In .NET framework v1 and v1.1, we have to use P/Invoke to get high resolution reading. The class below is commonly used in performance test measurement. It is querying hardware to get high resolution performance counter. For more information (including what happens if the hardware does not support high resolution performance counter) please check MSDN for QueryPerformanceCounter and QueryPerformanceFrequency.

public class HighResolutionTimer

{

private long start;

private long stop;

private long frequency;

public HighResolutionTimer()

{

QueryPerformanceFrequency (ref frequency);

}

public void Start ()

{

QueryPerformanceCounter (ref start);

}

public void Stop ()

{

QueryPerformanceCounter (ref stop);

}

public float ElapsedTime

{

get{

float elapsed = (((float)(stop - start)) / ((float) frequency));

return elapsed;

}

}

[System.Runtime.InteropServices.DllImport("KERNEL32.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)]

private static extern bool QueryPerformanceCounter( [In, Out] ref long performanceCount);

[System.Runtime.InteropServices.DllImport("KERNEL32.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)]

private static extern bool QueryPerformanceFrequency( [In, Out] ref long frequency);

}

To illustrate the use of this class, check the code below.

HighResolutionTimer timer = new HighResolutionTimer();

timer.Start();

//Perf Test

timer.Stop();

Console.WriteLine(timer.ElapsedTime);

(This posting is provided "AS IS" with no warranties, and confers no rights. Use of included script samples are subject to the terms specified at http://www.microsoft.com/info/cpyright.htm)

作者:孙展波

来源: http://blogs.gotdotnet.com/ZHANBOS

BIT Google 3.0更新日志
=================================
1.增加悬浮窗,把您想要搜索的文本拖动到悬浮窗即可搜索,搜索时悬浮窗会自动变为红色
2.增加"搜索剪贴板"功能,即可搜索您刚刚复制的文本内容
3.增加天气预报小栏目,同步MSN的北京天气预报
4.主界面最小化时缩小到系统托盘,单击系统托盘图标或双击悬浮窗即可恢复主界面

http://www.felixwoo.com/wp-content/uploads/attachments/old/200492817266477.jpg

http://www.felixwoo.com/wp-content/uploads/attachments/old/2004928172614734.jpg

http://www.felixwoo.com/wp-content/uploads/attachments/old/2004928172621354.jpg

而完成以上所有的功能您都无需连接外网

BIT Google 2.0 更新日志
=================================
1.增加网页模式查看搜索结果
2.列表模式增加鼠标右键菜单,可以选择打开网页和复制地址
3.修改了一些小bug..

  BIT Google是专为北京理工大学校园网提供的Google搜索服务软件。使用该软件无需连接外网即可搜索Google。
  BIT Google每页显示10个搜索条目,单击条目可显示网页内容简介,双击直接打开网页。BIT Google全面支持Google的搜索表达式,如“中国+北京”、“水果-苹果”等等...
  这个软件是我第一个使用WebService的软件,实现方法就是在在校内的一台可以访问外网的服务器上架设了Google搜索的WebService,该WebService又调用了Google提供的WebService。校内的用户使用软件时远程调用校内的WebService,也就是说用校内的服务器做中转代理,从而实现了不连接外网进行Google的搜索。
  这个软件也是使用C#开发的,所以使用前需要先安装.NET Framework。

下载地址:www.felixwoo.com/download/BITGoogle.exe

http://www.felixwoo.com/wp-content/uploads/attachments/old/2004928172650486.jpg

1、添加了一个Windows窗体Form1,在Form1中添加了一个文本框textBox1
2、在textBox1的KeyDown()事件中加入了以下代码:
private void textBox1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
if ( e.KeyValue < 48 && e.KeyValue >57 ) //如果输入的字符是从 ‘0’ 到 ‘9’
{
//什么都不做
}
else
{
e.Handled=true; //如果输入的是非数字字符,则提前将这个事件结束掉,而不添加
MessageBox.Show( e.Handled.ToString() );
}
}
没写完,待续...
MSDN上的例子http://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/cpref/html/frlrfsystemwindowsformscontrolclasskeydowntopic.asp

[C#]
// Boolean flag used to determine when a character other than a number is entered.
private bool nonNumberEntered = false;

// Handle the KeyDown event to determine the type of character entered into the control.
private void textBox1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
// Initialize the flag to false.
nonNumberEntered = false;

// Determine whether the keystroke is a number from the top of the keyboard.
if (e.KeyCode < Keys.D0 || e.KeyCode > Keys.D9)
{
// Determine whether the keystroke is a number from the keypad.
if (e.KeyCode < Keys.NumPad0 || e.KeyCode > Keys.NumPad9)
{
// Determine whether the keystroke is a backspace.
if(e.KeyCode != Keys.Back)
{
// A non-numerical keystroke was pressed.
// Set the flag to true and evaluate in KeyPress event.
nonNumberEntered = true;
}
}
}
}

// This event occurs after the KeyDown event and can be used to prevent
// characters from entering the control.
private void textBox1_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
// Check for the flag being set in the KeyDown event.
if (nonNumberEntered == true)
{
// Stop the character from being entered into the control since it is non-numerical.
e.Handled = true;
}
}

  科学研究表明,人体有体力、情绪与智力三个周期,分别为23,28,33天。运行该软件输入生日后即可得出您处于体力、情绪与智力周期的哪一阶段,并预测您未来100天内的身体状态。
程序是用C#编写的,绘图使用的ZedGraph绘图组件。
下载地址:http://www.felixwoo.com/download/bodycycle.zip
/UploadFile/2004919215910946.jpg

经过了一个月的努力,FreeTalk终于算是正式发布了。这个网络电话本来是老师留的一个作业,其实网上有现成的C++写好的代码,不过为了学习C#我决定还是重新写一个。这一个月里,先是用了1/4的时间学习C#,又用了1/4的时间研究DirectX,最后1/2的时间就是开始编写代码和调试了。

要说学习编程是从小学5年级开始的,那时学的是Basic,用的是Apple II+的电脑(可不是现在的苹果,比286还差),后来初中学Pascal,不过都是Dos模式下的编程。高中转向网站制作和动画设计,就没怎么再接触编程了。虽然这个FreeTalk很简单,但却是我Windows窗体程序的处女作,应该算是我的电脑学习生涯的里程碑吧:)

FreeTalk是基于.Net环境下用C#编写的,语音的采集和播放用DirectX下的DirectSound实现。C#应该是现在最新的编程语言,他大大简化了编程的复杂度,又是基于.Net平台,因此可移植性也很好。比如用C#写网络传输不用建立麻烦的Socket对象,用UDPClient/TCPClient就可以很轻松的搞定,C#的开发效率恐怕Java望尘莫及啊……音频部分其实也可以用Windows API来实现,不过无论从功能还是可扩展行都不如DirectX,DirectX提供了一个完整的多媒体和游戏解决方案,包括DirectDraw,Direct3D,DirectSound,DirectShow等等。音频的采集和播放用DirectSound或DirectShow都可以,由于DirectX对流式数据的完美支持,使得音频的时时采集和播放能够很容易的实现。

在后续的开发过程中,我还打算对这个软件加以改进。一是增加音频压缩编码,这样能够更加节约带宽。二是采用DirectShow代替DirectSound以实现视频音频聊天。第三就就是增加中央目录服务器,可以让用户更方便的选择聊天对象……

回想整个开发过程,最难忘的就是查找资料,逛书店、搜Google、找ebook、混csdn……关于C#网络编程的资料确实不少,不过查找DirectX9 Managed的资料就像大海捞针,逛遍了海图也没发现一本相关的书籍。手头上只有DirectX SDK可以参考,又是英文的,所以感觉自己的英文水平提高了不少:)软件开发进度曾一度停滞在解决音频时时播放这个问题上,SDK中又没有详细介绍,最后很偶然的机会我在国外的一个BBS上看到了一个求助帖,其中是他一段错误的代码,而他正确的部分正是我需要的,我正确的部分也正是他需要的……:)

这一个月以来最要感谢的就是我的好朋友DigitalGhost,由于我是从对C#一窍不通开始学起,所以很多最基本的问题都解决不了,DigitalGhost给予了我很大的帮助和支持,还有从他那抢的两本书也让我受益匪浅。

在开发这个软件的过程中我也深刻的体会到,编写程序其实是很缺乏创造力的,我们只是在老美构建的程序世界中不断的Discovery而不是Invent,和使用他们早已准备好的资源。就算编写出再nb的程序,我们也只不过是IT民工罢了……

京ICP备05053527号
经过26次查询历时0.418秒终于生成了此页面
Powered by WordPress & Designed by Felix © 2012