新鲜、有趣,互联生活。令狐葱。

2007/01/04

使用Windows Live Writer更新Blogger

Windows Live Writer 最新版支持很多blog系统,当然包括google的blogger。不过设置的时候偶遇到一点点麻烦,居然在Windows Live Writer帮助里没找到解决方案,后来总算试出来了,呵呵

在此列出设置blogger 和博客园(cnblogs)的方法:

1. 设置Blogger

首先填写blog地址、用户名、密码,无甚可说,然后在“Type of  weblog that you are using”中选择“blogger”,最让人难以捉摸的是下面的“Remote posting URL for your weblog”,其实只要你把自己blogger最下面的Posts(Atom)连接地址复制过来就可以了,然后下一步,完成设置。

2. 设置CnBlogs

方法与上面差不多,首先填写blog地址、用户名、密码,然后在“Type of  weblog that you are using”中选择“Custom(Metaweblog API)”。然后“Remote posting URL for your weblog”中输入“http://www.cnblogs.com/用户名/services/metaweblog.aspx”。OK,设置完毕。

后记:1. 目前没办法直接上传图片到blogger,不过还好有插件Flickr2Writer,可以帮助你从flickr导入图片。

2. 设置blogger时看到http://www.blogger.com/feeds/< feed-id >/posts/full,可以把它替换为你的feed地址(如上面步骤),也可以如下设置:

登入你的Blogger beta的blog后台,在地址栏里查看blogID=一串数字,这串数字就是blogID,把这个blogID替换掉上面链接中的< feed-id >即可。

云与水的痴情

(点击图片查看豆瓣影评)
看了不少电影,但是也懒得去写什么评论。想要来说一下《云水谣》,是因为看过之后久久的不能平静。虽然明明知道这是假的、不可能的简单到苍白的虚构,但是每次想起还是能激起心里的那份感动。
很久没有的这份感动,看电影会情不自禁的流眼泪的感动,想不起上次有类似感觉是什么时候了,可能是在看《暖春》的时候吧!电影本身而言,演员、画面都有诸多可以令人称道的地方,不过真正让人感动的还是它的故事。导演在给观众营造一种虚拟的感觉不到真实确又希望它会真实存在的爱情,这就像信教的人总以为有一个天堂,但是却时时不得见一样。60年的执著,60年的等待,60年的爱恋,痴情如斯,也只有在电影中了!
以前我总觉得自己的思想不合时宜,总感觉自己应该生活在那种风雨飘摇的40年代的旧上海,可能是自己先前有这种想法的缘故吧,所以在看电影的时候,感觉就是那个情境的再现。那个时候的人们,简单的生活,纯真到拿一朵玫瑰花站在女孩家门口仅仅就是为了看她一眼,纯真到一句话都要守一辈子。现在的人们听来好像是东方夜谈是笑话,其实如今所谓的快餐爱情就像那快餐一样,没有了一丁点的营养成分,所以吃快餐的人是体会不到那种哪怕是"家常便饭"的味道的,更别说是"美味佳肴"了。
看电影的时候很容易把自己融入角色,我看的时候就在想,如果我是主人公,我会这样等么?答案是我也不知道。如果上天告诉我,有这么一个女孩,她就是你的另一半!那样的话我或许会一直等下去。但是我怎么确定这个女孩就是我的the One呢?不得而知。电影中陈秋水因做家庭教师而来到王家,并与王家千金王碧云一见钟情,两人很快坠入爱河并私订终身。其实从认识到分开两个人并没有在一起多长时间,到底是什么让这份爱这么的坚韧、这么的持久呢?因为这是爱,真正的爱!爱里是不掺杂任何其他成分的,有的只是信任和理解。
电影的名字"云水谣"取自两个主人公的名字,陈秋水和王碧云,"谣"字自然有唱赞歌的意思。因为这份爱的稀有而珍贵,因为珍贵所以才需要拿出来赞美。但是这份爱又像云像水一样捉摸不定如影随形,或许它只存在幻想之中。
到底爱是什么呢?这个探讨了千百年的话题还在一直被人继续拿来探讨着。其实,爱或许不像想象中的那么复杂,爱就是当你见到对方,就会不自觉地觉得心情愉快,喜形于色;爱就是"老的哪里也去不了,你还依然是我手心里的宝"。

广告欣赏+一句话


"为什么我们看到这么多的烂广告?
因为他们烂,所以不得不经常出现以便混个眼熟。"
我想中国人大概比较容易体会这一点,因为一打开电视就是"盖中钙"或者"脑白金"、"黄金搭档"。无论怎么说,他们的效果达到了,大街小巷,妇孺皆知!
从字体的颜色变化就可以看出,血库确实是缺血了!
那还不快鲜血去?号召之意,跃然纸上。
上面这一组,把椅子拟人化,突出主题:物品也像人一样有感情。

2007/01/02

猪年快乐:〉

{/ o o /} {/ . . /} {/ ︿︿ /}
( (oo) ) ( (oo) ) ( (oo) )
︶ ︶︶ ︶ ︶ ︶ ︶ ︶ ︶
标准猪 小眼猪 眉开眼笑的猪
╭︿︿︿╮ ╭︿︿︿╮ ╭︿︿︿╮
{/ $ $ /} {/ @ @ /} {/-■■-/}
( (oo) ) ( (oo) ) ( (oo) )
︶ ︶ ︶ ︶︶︶ ︶︶︶
财迷心窍猪 头晕目眩猪 酷酷猪

╭︿︿︿╮ ╭︿︿︿╮ ╭︿︿︿╮
{/ 0 0 /} {/ X o /} {/ ·· /}
( (qp) ) ( (oo) ) ( (00))
︶︶︶ ︶︶︶ ︶︶︶
生气猪 独眼龙猪 张大鼻孔猪

╭︿︿︿╮ ╭︿︿︿╮ ╭︿︿︿╮
{/ # # /} {/-◎◎-/} {/ - - /}
( (oo) ) ( (oo) ) ( (..) )
︶︶︶ ︶︶︶ ︶︶︶
茫然猪 戴眼镜猪 悠闲自在猪

╭︿︿︿╮ ╭︿︿︿╮ ╭︿︿︿╮
{/-●●-/} {/-★★-/} {/-⊙⊙-/}
( (oo) ) ( (oo) ) ( (oo) )
︶︶︶ ︶︶︶ ︶︶︶
墨镜猪 时髦猪 目瞪口呆猪

祝大家猪猪平安 ,猪年快乐!!


PS1:最早有这种字符构图创意的估计是以前的Dos用户了,记得以前见过水木清华bbs的telnet登陆时显示的画面,全是这种,创意不错:〉(上面是因为blogger空格显示的问题,所以猪都变丑了:《)
PS2:确切的说现在还不算是猪年(应该以农历/阴历来算才合适),不过一提猪,想想到偶本命年喽:〉呵呵但愿一切顺利!

TellBody:聊天工具集成


TellBody 自称是国内首款可与MSN、ICQ、QQ、YAHOO、GoogleTalk、AIM/AOL等网络聊天工具互联互通的新型聊天软件,可以把你几个聊天软件的好友都添加到Tellbaby一个软件里面,同时支持多国语言实时翻译,让您更可畅快的享受Tellbaby带来聊天无国界的享受。

其实类似的在线webIM 也出现了不少,不过总感觉用起来不是很爽。TellBody在一开始就把自己定位在PC 和手机两个版本上,而且还有类似于Skype 的PC2Phone的功能,看来真是一个大大的杂烩,至于用起来如何,自己来看吧:〉

1tie:网络及时贴


1tie是一个及时贴的在线版本,现在我用的桌面及时贴工具是 FreeNote,一个很不错的工具,推荐一下。

1tie 使用起来十分简单,请看说明:
在地址栏输入任意以http://1tie.cn/开头的网址,比如:http://1tie.cn/xxx,将xxx替换为任意你所想要的名字(可由字母、数字、下划线组成。不支持中文字符 ),回车,你所打开的页面就是属于你自己的在线记事本。 你可以从不同的计算机登录这个由你设定的网址,对该记事本进行编辑和拷贝。

2007/01/01

BLOG改版,新年新气象

2007年的第一天,教研室居然就我和另一个小伙,真是清静啊!闲来无事把这个blog的样子重新调整了一下,这才发现,原来google的blogger是这样的好用,呵呵,不过最值得称道的一点是它可以直接使用Email 发Post,真是不错。

现在的样子看上去好多了,毕竟,新年要有新气象阿!在侧边栏也加了好多东西:〉

Donews的blog就暂时停止更新吧,破服务器还不够人烦的呢!

记住:本blog地址:http://linghucong.blogspot.com
          RSS订阅地址: http://feeds.feedburner.com/Linghucong

2006,又一年这样过去了


一不小心就走进了2007年,原来打算做的对2006年的总结也不小心变成了现在对去年的回顾。一年的时间说长不长,说短又不短,感觉什么事情都还没来得及做,就过完了。Time flies,确也。总的来说,这一年不太成功,想要去总结的时候才发现其实自己需要做而且能够做但是没有做的事情确实不少,该是到自我反省的时候了,去年这个时候在回顾2005的时候我 用到的词是"光阴虚度、一事无成",现在如果想形容一下刚刚过去的一年,用四个字可以是:庸碌无为,用八个字那就是:庸庸碌碌、无所作为。看来自己变化不是很大,确切的说是进步不是很大。

1. 所做的事。上半年基本上是在应付课程。现在发现,当时学的基本上没什么能用的,需要用的,当时居然一点都没有学,不是自己不去学,是课程里压根没有(呵呵,没有推卸责任的意思,只是觉得现在研究生的课程也像是为了应付上面的检查一样,打扮得冠冕堂皇,其实却没有多少价值可言)。下半年就进入本部904的教研室,开始正式的实习。一学期下来,也就是作了一个项目,不可以说没有收获,只是感觉离自己的期望还有很大的距离。毕竟,项目和科研还是有点风马牛不及的。通过兰州的项目,(还有四人小组的渭南项目)让我感受最深的是个互需要的就是一个能用的软件而已,至于可用性以及美观什么的倒是其次。这就是Joel 所称的专有软件吧。只有那些面向大众的软件才需要在可用性以及界面等方面下大功夫。

2. 所看所思。这个就不多说了,只是要发几个感慨:

    所看到的不一定就是事实,就比如开始如何讨厌一个人,很可能后来这个人对你帮助很大。

    要觉得自己无知但是不要觉得自己很笨,也就是说认识到自己的不足但是也要有自信,因为能认识到自己的不足了,就说明你很聪明。

    做事情之前多考虑别人的感受,不要冒冒失失的出现在别人面前,这样只能适得其反。

    喜欢一个人就要告诉他/她,不要以为默默无闻他/她就会知道。

    珍惜身边的一切,因为只有当你失去的时候,你才知道原来以前自己是多么的幸福。

    你认为好的事情别人未必认为就是好事,所以不要把自己的喜好强加于人,这就像狗拿着骨头给猫献殷勤一样,未必会有效果。另外,更应该记住:己所不欲,勿施于人。

    记住一句话:祸兮福所倚,福兮祸所伏(老子),这样才能做到不以物喜,不以己悲,才能把握住重点,不至于以小失大。    


3. 个人生活。其实自己倒是很习惯一个人的生活,不会觉得有什么不自在。但是还是很怀念以前的日子,那种心有所系、牵肠挂肚的感觉。现在自己也不敢奢望太多,因为我怕自己一旦有所奢望,那就连眼前的默契都不在了,只是祝福:都一路走好!如以前所想,我还是趁这段时间好好的充实自己吧!或许可以拿一句话来安慰自己,"得知我幸,不得我命",将决定权交给上天,也是一种无奈吧,这或许是"缘分"的注释?记得以前听人说过,"爱上一个人之需要一秒,而忘记一个人却需要一辈子",似乎挺对的,如果一撒手就忘记了,那说明压根就没有真心爱过。我承认或许还会傻傻的心存幻想,但是不会去妨碍别人,也不会耽误自己的正事。相信时间是可以解释一切的。(阿弥陀佛,我是典型的唯物主义者,阿门!)

4. 缺点总结。一个人要真正的了解自己真不是件容易的事情,所以也只有慢慢的发现,慢慢的改进。如果觉得自己没有缺点,我不会说那是自信,呵呵,可能是自恋了。

    交际欠缺!这也不是一时半会才有的毛病了,想来不过两个原因,一个是不怎么会说话,再一个就是在陌生人面前懒得喜笑颜开,感觉特傻!其实自己但是很高兴看到那些第一次简单你就笑得看不见黑眼珠的人,至少首先给人一种亲切感:〉

    有目的的作为。这一点有很多值得我学习的人,我感觉他们做什么事都有一个很明确的目的,而且往往把握的很准确。而自己,就有点随兴而为的意思了,未必是什么坏事,但是也要把握好一个度字,兴趣和学业(事业)要找到一个很好的平衡点。

    做事犹豫不决。人生大多在走十字路口,向左走了就不要去想向右走的好处,专心走好就是。自己有时候就会站在十字路口,东张西望,最后发现,等确定了已经赶不上末班车了:〉

5. 给07年的几句话

相信"功不唐捐"。

分清重要的和不重要的,紧急的和非紧急的事情。

不以物喜,不以己悲。 

保护好"革命的本钱"。That they lose their health to make money and then lose their money to restore their health.

开心一点。

[附注:总结的比较仓促,所以肯定会有很多事情没有提到,或者还有一些别的需要记下的感悟:〉等以后再继续补上吧。感觉上面这些不光是在对自己说,或许对某些或者某个人还有点借鉴意义,呵呵,不要笑话偶就好了:〉]

2006/12/31

C#中using的用法

上一篇文章中有用到using,上网查了一下,索性把using的用法列出来。
 
1. using指令。using + 命名空间名字,这个应该算是最常见的了,这样可以在程序中直接用命令空间中的类型,而不必指定类型的详细命名空间。
例:using System;
 
2. using别名。using + 别名 = 包括详细命名空间信息的具体的类型。 这个不经常使用,比如当同一个cs引用了两个不同的命名空间,但两个命名空间都包括了一个相同名字的类型的时候,这种特性就显得很好用了:〉
例:using aClass = NameSpace1.MyClass;
   
using bClass = NameSpace2.MyClass;
 
3. using语句,定义一个范围,在范围结束时处理对象。

当在某个代码段中使用了类的实例,而希望无论因为什么原因,只要离开了这个代码段就自动调用这个类实例的Dispose。当然,用try...catch来捕捉异常也可以实现,但是不如using方便。
例:
using  (Class1 cls1 = new Class1(), cls2  = new Class1())
{
  
//  the code using cls1, cls2


}
 // call the Dispose on cls1 and cls2

WinForm程序启动时不显示主窗体的方法(C#)

看了两篇文章(1和2),然后把文中的代码整理了以下,文中所述WinForm程序启动时不显示主窗体的实现方法主要有以下5种,个人觉得第五种最简单,而且效果也不错,第四种方法也值得推荐。
 
实现代码及简短解释如下:
//隐藏窗体的方法1/5:不指定任何窗体为主窗体
//注意:通常,在一个程序中,关闭主窗体,就可以关闭应用程序。
//但是在没有主窗体中,不行。
//只能使用Application.Exit()才能关闭应用程序。
//using可以保证Application结束前,关闭MyMainForm
using (new Form1())
{
    Application.Run();
};
//Application.Run(new Form1());

//隐藏窗体的方法2/5:
//通过close()关闭主窗口同时可以关闭应用程序
protected override CreateParams CreateParams
{
    get
    {
        Hide();
        return base.CreateParams;
    }
}

//隐藏窗体的方法3/5:
//这种方法仍然不能用Close主窗口的方式来关闭应用程序, 还得使用Application.Exit。
protected override void SetVisibleCore( bool value)
{
    base.SetVisibleCore(false);
}

//注意:方法2和3 使用Show好像没办法调出主窗口,比较郁闷。

//隐藏窗体的方法4/5 part1/2: 推荐使用!!!
//ApplicationContext实质上就是一个Application与主窗体之间的连接器,
//掌管着二者之间的互动关系。其中最主要的,就是负责在主窗体
//关闭时结束线程。既然如此,我们只要根据需要自定义一个ApplicationContext就可以了
internal class HideOnStartupApplicationContext : ApplicationContext
{
    private Form mainFormInternal;

   
// 构造函数,主窗体被存储在mainFormInternal
    public HideOnStartupApplicationContext( Form mainForm)
    {
        this.mainFormInternal = mainForm;

        this.mainFormInternal .Closed += new EventHandler(mainFormInternal_Closed);
    }

   
// 当主窗体被关闭时,退出应用程序
    void mainFormInternal_Closed(object sender, EventArgs e )
    {
        Application.Exit();
    }
}
//在Main中作如下修改:
HideOnStartupApplicationContext context = new HideOnStartupApplicationContext( new Form1());
Application.Run(context );

//隐藏窗体的方法5/5: 推荐使用!!!
//在构造函数中或者直接设置form属性
this.ShowInTaskbar = false;
this.WindowState = FormWindowState.Minimized;
 
程序中配合notifyIcon 控件一起使用,效果很好!
以上代码在VS2005下编译通过。

使应用程序只运行单个实例(C#)

代码如下:(在类中)
public partial class AppSingleton : Form
    {
        public AppSingleton()
        {
            InitializeComponent();
        }
         static Mutex m_Mutex;

        public static void Run()
        {
            if (IsFirstInstance()) {
                 Application.ApplicationExit += new EventHandler(OnExit);
                Application .Run();
            }

         }
        public static void Run (ApplicationContext context)
        {
            if (IsFirstInstance()) {
                Application.ApplicationExit += new EventHandler(OnExit);
                Application.Run(context );
            }
        }
        public static void Run(Form mainForm)
        {
             if (IsFirstInstance()) {

                Application.ApplicationExit += new EventHandler(OnExit);

                Application.Run(mainForm);


            } else {

                MessageBox.Show("应用程序已启动,请在任务管理中关闭后再启动!" , "系统提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }
         static bool IsFirstInstance()
        {
            m_Mutex = new Mutex(false, "SingletonApp Mutext" );
            bool owned = false;
            owned = m_Mutex .WaitOne(TimeSpan.Zero, false );
            return owned;
        }
        static void OnExit(object sender, EventArgs args )
        {
            m_Mutex.ReleaseMutex ();
            m_Mutex.Close();
         }
    }

主程序中(main)需要做如下修改:
AppSingleton.Run(new AppSingleton());
//Application.Run(new AppSingleton());

2006/12/30

C#中使用BackgroundWorker 实现后台操作窗体

编程的时候常常遇到需要处理很长时间的函数等,这时可能会导致用户界面 (UI) 似乎处于停止响应状态。在VS2005里可以使用 BackgroundWorker 类来杜绝这种UI假死的现象。

DoWork 事件:处理程序中调用耗时的操作。
RunWorkerAsync 事件:若要启动该操作。
ProgressChanged 事件:收到进度更新通知。
RunWorkerCompleted 事件在操作完成时收到通知。

下面通过一个程序说明,程序来自msdn(Link),有所删减。
 
public partial class FormFibonacci : Form
    {
        private int numberToCompute = 0;
         private int highestPercentageReached = 0 ;

        public FormFibonacci ()
        {
            InitializeComponent ();
         }

        
// 占时计算函数
        long ComputeFibonacci (int n, BackgroundWorker worker , DoWorkEventArgs e)
        {
            
// The parameter n must be >= 0 and <= 91.
             // Fib(n), with n > 91, overflows a long.
             if ((n < 0) || (n > 91 )) {
                 throw new ArgumentException (
                    "value must be >= 0 and <= 91", "n");
            }

             long result = 0;

            
// Abort the operation if the user has canceled.
             // Note that a call to CancelAsync may have set
             // CancellationPending to true just after the
             // last invocation of this method exits, so this
             // code will not have the opportunity to set the
             // DoWorkEventArgs.Cancel flag to true. This means
             // that RunWorkerCompletedEventArgs.Cancelled will
             // not be set to true in your RunWorkerCompleted
            // event handler. This is a race condition.
            if (worker .CancellationPending ) {
                e.Cancel = true;
            }
else {
                if (n < 2) {
                    result = 1;
                 } else {
                    result = ComputeFibonacci(n - 1, worker, e) +
                              ComputeFibonacci(n - 2, worker, e);
                }

                
// 报告进度
                 int percentComplete =
                    ( int)((float) n / ( float )numberToCompute * 100 );
                if (percentComplete > highestPercentageReached) {
                     highestPercentageReached = percentComplete;
                     worker.ReportProgress (percentComplete);
                }
             }

            return result;
         }

         private void backgroundWorker1_DoWork (object sender, DoWorkEventArgs e)
        {
            
// Get the BackgroundWorker that raised this event.
             BackgroundWorker worker = sender as BackgroundWorker;

            
// Assign the result of the computation
             // to the Result property of the DoWorkEventArgs
            // object. This is will be available to the
            // RunWorkerCompleted eventhandler.
            e.Result = ComputeFibonacci ((int)e.Argument , worker, e );
         }

        private void btnStart_Click( object sender, EventArgs e)
        {
            numberToCompute = (int)numericUpDown1 .Value ;
            highestPercentageReached = 0;
            
//Start the asynchronous operation.
             //注意:参数从这里传过去
            backgroundWorker1.RunWorkerAsync (numberToCompute);
         }

         private void btnCancel_Click (object sender, EventArgs e)
        {
            
// Cancel the asynchronous operation.
            this.backgroundWorker1 .CancelAsync();
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            
// First, handle the case where an exception was thrown.
             if (e. Error != null) {
                MessageBox.Show(e.Error. Message);
             } else if (e .Cancelled) {
                
// Next, handle the case where the user canceled
                 // the operation.
                 // Note that due to a race condition in
                 // the DoWork event handler, the Cancelled
                 // flag may not have been set, even though
                 // CancelAsync was called.
                resultLabel .Text = "Canceled" ;
            } else {
                
// Finally, handle the case where the operation
                // succeeded.
                resultLabel.Text = e.Result. ToString();
             }

        }

         private void backgroundWorker1_ProgressChanged (object sender, ProgressChangedEventArgs e)
        {
            
//设置进度条
             progressBar1.Value = e.ProgressPercentage ;
        }
    }

 
 
在程序中需要注意以下几点:
 
1. 必须确保在 DoWork 事件处理程序中不操作任何用户界面对象。而应该通过 ProgressChanged 和 RunWorkerCompleted 事件与用户界面进行通信。
 
2. 如果后台操作需要参数,要在调用 RunWorkerAsync 时给出参数。在 DoWork 事件处理程序内部,可以从 DoWorkEventArgs.Argument 属性中提取该参数。
 
3. 报告进度函数 ReportProgress有一个参数(或者两个,参见msdn):
percentProgress 已完成的后台操作所占的百分比,范围从 0% 到 100%。
 
4. 注意取消操作和进度条的设置。
 
5. 可以使用 IsBusy 属性可以确定 BackgroundWorker 线程是否仍在运行。如果代码在主 UI 线程上(对于 Click 事件处理程序即是如此),请务必调用 System.Windows.Forms.Application.DoEvents 方法以使用户界面能够响应用户操作。在btnClick中可以有:
while (this.backgroundWorker1.IsBusy)
    {
        Application.DoEvents();
    }
 
6. 在试图访问 System.ComponentModel.RunWorkerCompletedEventArgs.Result 属性或可能已受 DoWork 事件处理程序影响的任何其他对象之前,一定要在 RunWorkerCompleted 事件处理程序中检查 System.ComponentModel.AsyncCompletedEventArgs.Error 属性。

C#中的事件和委托(原)

事件在VS中使用起来很方便,拖一个按钮上去,然后双击,就会看到OnClick(...)的响应函数。但是真要深究,还真有点学问。今天看了MSDN上的几个关于事件和委托的介绍,感觉��里��唆,废了半天劲才看了个半懂,暂时记下。
 
相关知识点
 
1. 事件是对象发送的消息,以发信号通知操作的发生。操作可能是由用户交互(例如鼠标单击)引起的,也可能是由某些其他的程序逻辑触发的。引发事件的对象称为事件发送方。捕获事件并对其作出响应的对象叫做事件接收方。

2. 在事件通信中,事件发送方类不知道哪个对象或方法将接收到(处理)它引发的事件。所需要的是在源和接收方之间存在一个媒介(或类似指针的机制)。.NET Framework 定义了一个特殊的类型(Delegate),该类型提供函数指针的功能。

3. 委托是可保存对方法的引用的类。与其他的类不同,委托类具有一个签名,并且它只能对与其签名匹配的方法进行引用。这样,委托就等效于一个类型安全函数指针或一个回调。虽然委托具有许多其他的用途,但这里只讨论委托的事件处理功能。一个委托声明足以定义一个委托类。声明提供委托的签名,公共语言运行库提供实现。

4. 只有当事件生成事件数据时才需要自定义事件委托。许多事件,包括一些用户界面事件(例如鼠标单击)在内,都不生成事件数据。在这种情况下,类库中为无数据事件提供的事件委托 System.EventHandler 便足够了。
delegate void EventHandler(object sender, EventArgs e);

5. 事件功能是由三个互相联系的元素提供的:提供事件数据的类、事件委托和引发事件的类。.NET Framework 具有命名与事件相关的类和方法的约定。如果希望您的类引发一个名为 EventName 的事件,您需要以下元素:

包含事件数据的类,名为 EventNameEventArgs。该类必须从 System.EventArgs 导出。

事件的委托,名为 EventNameEventHandler。

引发事件的类。该类必须提供事件声明 (EventName) 和引发事件 (OnEventName) 的方法。

6. .NET Framework 类库或第三方类库中可能已经定义了事件数据类和事件委托类。在这种情况下,您就不需要定义这些类了。例如,如果您的事件不使用自定义数据,可以使用 System.EventArgs 作为事件数据并使用 System.EventHandler 作为委托。

7. 若要订阅事件,接收器必须创建一个与事件具有相同类型的委托,并使用事件处理程序作为委托目标。然后,接收器必须使用加法赋值运算符 (+=) 将该委托添加到源对象的事件中。
clock.Alarm += new AlarmEventHandler(w.AlarmRang);
若要取消订阅事件,接收器可以使用减法赋值运算符 (-=) 从源对象的事件中移除事件处理程序的委托。
 
源代码
 
第一个例子使用系统自己的System.EventArgs 作为事件数据,如下所示(这就相当于直接拖控件,双击实现的那个:〉)其中,Clock 类定义公共事件 Alarm,并提供引发该事件的方法。WakeMeUp 类定义处理 Alarm 事件的 AlarmRang 方法。Program类一起使用类,将使用 WakeMeUp 的 AlarmRang 方法设置为处理 Clock 的 Alarm 事件。
using System;

namespace testEvent
{
    public delegate void AlarmEventHandler(object sender, EventArgs e);

    public class Clock
    {
        public event AlarmEventHandler Alarm;

        
// The protected OnAlarm method raises the event by invoking
        
// the delegates. The sender is always this, the current instance
        
// of the class.
        
//
         protected virtual void OnAlarm(EventArgs e)
        {
            if (Alarm != null) {
                
// Invokes the delegates.
                Alarm( this, e);
             }
        }

        internal void Start()
        {
            OnAlarm(EventArgs.Empty );
        }
    }

    public class WakeMeUp
    {
        public void AlarmRang( object sender, EventArgs e) {
            System. Console.WriteLine("Event has been invoked!");
        }
    }

     class Program
    {
         static void Main(string[] args)
        {
            
// Instantiates the event receiver.
            WakeMeUp w = new WakeMeUp ();

            
// Instantiates the event source.
            Clock clock = new Clock();

            
// Wires the AlarmRang method to the Alarm event.
            clock.Alarm += new AlarmEventHandler(w.AlarmRang);

            clock.Start();
        }
    }
}
 
我们再把上面的例子扩展一下,实现一个简单的AlarmEventArgs 类定义 Alarm 事件特定的数据。
namespace testEvent
{
    public class AlarmEventArgs : EventArgs
     {
        public string AlarmText;

         public AlarmEventArgs(string alarmText)
        {
             AlarmText = alarmText;
        }
    }

     public delegate void AlarmEventHandler(object sender, AlarmEventArgs e);

    public class Clock
    {
        public event AlarmEventHandler Alarm;
        public string state = "the clock is ringing...";

         protected virtual void OnAlarm(AlarmEventArgs e)
        {
             if (Alarm != null) {
                
// Invokes the delegates.
                 Alarm(this, e);
            }
        }

         internal void Start()
        {
            System .Threading.Thread.Sleep(300);
            AlarmEventArgs e = new AlarmEventArgs("Please wake up!" );
            OnAlarm(e);
  
        
}
    }

    public class WakeMeUp
    {
        public void AlarmRang( object sender, AlarmEventArgs e)
        {
            Console .WriteLine(e.AlarmText);
            Console.WriteLine ("The state of clock:");
            Console. WriteLine(((Clock)sender).state);

            Console .ReadLine();
        }
    }

    class Program
    {
        ......
    }
}


参考和延伸阅读(需有VS2005 MSDN)
 

2006/12/29

C#实现.net组件和COM组件的互操作

.net 框架自微软推出以来受到了很高的评价,但是无论如何,以前的东西诸如COM还是没办法完全丢弃的。本文试图通过两个小的例子,来测试一下.net组件和COM组件是如何相互调用和操作的,由于例子比较简单,估计会遗漏不少知识点。
 
COM组件中调用.net编写的组件
 
首先使用C#写一个组件(其实就是实现一个DLL),代码如下(保存为hello.cs):
using System;
using System.Collections.Generic;
using System.Text;

namespace Hello
{
    public class Hello
    {
        public void sayHello()
        {
             System.Console.WriteLine("Hello World!");
        }
    }
}
 
在命令行中执行
C:\>csc /t:library /out:Hello.dll hello.cs
即可生成一个dll库文件 Hello.dll。但是想要在现有的COM组件中使用这个组件,还需要进行如下操作:
C:\>regasm hello.dll /regfile hello.reg
上面的命令行是注册我们的.NET组件,并且产生了一个备用的注册表文件。其实就相当于在win9x/NT/2000下面注册COM组件使用的命令regsvr32 c:\test.dll 。在.NET下面,注册.NET组件的命令是regasm,注意,这个方法仅仅是为了可供COM组件来调用,.NET本身之间相互调用组件是不需要任何注册。
下面需要产生一个.NET组件的类型库,目的是为了我们在COM组件中进行提前绑定操作:
C:\>tlbexp hello.dll /out:hello.tlb
 
接下来就可以在自己的基于目前COM技术的代码中使用上面我们使用C#编写的.NET组件了。在这里,我们使用VB6.0编写一个小小的测试代码,在开始之前我们需要在VB的集成环境中使用菜单中的"引用"选项,选择我们刚才产生的类型库文件Hello.tlb。

VB代码如下:
Private Sub Form_Load()
Dim test As New Hello.hello
Dim str As String

str = test.sayHello()
MsgBox str
End Sub
 
.NET组件中使用目前存在的COM组件
 
对于.NET来讲,使用COM组件就要简单一些。..NET提供了大量的类库来方便的实现同COM的相互操作,其中很重要的一个名称空间就是:System.Runtime.InteropServices。通过这个名称空间的名字我们也可以从字面上看出,"互操作服务"。System.Runtime.InteropServices这个名称空间提供了一系列的类来对COM对象进行操作。

下面的例子中,我们来调用一下系统自带的Win32函数MessageBoxA, 这个函数位于系统的COM组件user32.dll当中,我们调用的代码如下:

using System;
using System.Runtime.InteropServices;

class Test
{
    [DllImport ("user32.dll")]
    public static extern int MessageBoxA( int hWnd,string strMsg,string strCaption, int nType);

    public static void Main()
    {
        int myMsg;
        myMsg =MessageBoxA(0,"Hello!" ,"test",0);
    }
}
 
切换到命令行,运行:
C:\>csc Test.cs
编译完毕我们的C#应用程序之后,直接运行就可以看到对话框了!同样的,也可以使用这种方法来调用我们自己使用VB/VC编写的COM组件。

需要注意的是,在调用COM组件之前,我们需要在.NET程序中引用名称空间:System.Runtime.InteropServices 。因为我们需要使用这个名称空间所提供的一个方法:DllImport。