随笔 - 59, 评论 - 580, 引用 - 50

导航

关于


微软提供的免费计数器

山不在高,有仙则名。水不在深,有龙则灵。斯是陋室,唯吾德馨。苔痕上阶绿,草色入帘青。谈笑有鸿儒,往来无白丁。可以调素琴,阅金经。无丝竹之乱耳,无案牍之劳形。南阳诸葛庐,西蜀子云亭。
子曰∶“何陋之有?”


My name is BEN.
This is my master's "doghouse". hehe...

标签

每月存档

最新留言

广告

【第1页/共2页,16条】
首页
前页
1

难道我的英文真的很差?

最近我同事的朋友替公司做题目,这个题目是网上的,好像是一个什么Symbol公司出的题,是Symbol认证。因为这些题目都是关于.NET方面的,所以我同事让我帮她的朋友做。可是这些题目实在是太难了,我给一个比较经典的吧:

What’s true about types in C#?

a.

The CTS only appropriate operations can be performed on a particular type

b.

The program will not compile

c.

The program will cause run-time error

d.

Every type in CTS is controlled

e.

A&B

f.

A&B&C

g.

A&B&D

h.

A&D

i.

B&C

可惜我是丈二金刚摸不着头脑,types in C# 和 The program will not compile 有什么关系呢?注意这里没有任何的上下文,types in C# 不知道具体指的是哪一个范畴的,program也不知道是什么东西。更可怕的是,我觉得这里夹杂着一大堆的语法错误,我根本就看不懂。就像这里所说的program will not compile,感觉program是主语,也就是说program应该是指cs.exe这个compiler。那么宾语又是什么?

还没有看完这一份卷子,我就已经晕掉了。还有类似什么:

Which of the following is not a Boxing and unboxing rule?

a.

An object can only be accessed in C# by its interface.

b.

The interface defines the rules how it can be accessed.

c.

The System.Object base class interface only accepts reference types.

d.

The System.Object base class interface accepts all available types of the CTS.

e.

Any variable of type value has to be converted to type reference. This process is called BOXING.

f.

Boxing happens automatically

 

大家说说什么是System.Object base class interface?四个名词连在一起的效果就像我说“飞机花朵骨头云只能够看”一样,不知道是什么意思。难道应该有逗号在这几个词当中?也不像啊?也许这里的base是形容词,可还是看不懂什么叫做class interface。谁看得懂的教教我,说不定是我太长时间没有接触英语退化了。

还有下面这个题目也让我觉得很模棱两可:

Which of the following languages support objects?
 
 a.  C++

 
 b.  Microsoft Visual Basic

 
 c.  C#

 
 d.  A&B

 
 e.  A&B&C

 
 f.  A&C

 
 g.  No answer is correct

严格的说VB不支持OO,那是否supports objects就值得商酌了。大家觉得应该选择 e 还是 f 呢?

posted on 2004-07-30 14:41:00 by sumtec  评论(12) 阅读(2050)

吊胃口 V part I -- 用RichTextBox进行无闪烁的着色

很多时候,我们希望对用户输入的文字进行即时的分析,并对部分文字的字体颜色进行修改,让那些文字更加醒目。如果说要对部分的文字进行字体颜色的修改,我们很可能就会想到使用RichTextBox这个控件,因为这个控件至少能够让部分文字的字体颜色变得和其他部分不一样。然而实际上对于现实静态的东西,RichTextBox也许是适合的,但是对于想这种的“动态着色”则不见得有效率。最为头痛的一个问题就是,通过SelectionStart、SelectionLength的设置,然后再修改SelectionFont和SelectionColor,必然有一个短暂的时间会有文字被选中,结果就是你要加亮的文字会在一瞬间闪现蓝色背景的白色文字。这个现象并不一定每次都能够看到,但是如果在不停的进行输入的话,就一定会看到这个情况了。

这种不停的闪烁的情景比较让人感到讨厌,怎么解决呢?在没有试验之前,我想到了三种办法:
1、在进行着色的时候,让焦点从当前的RichTextBox上面转移到别的地方,同时HideSelection设置成Ture;
2、用一个后台RichTextBox进行着色,然后把着色的结果复制到前台;
3、在进行着色的时候暂时屏蔽RichTextBox的画面更新,着色完毕之后再允许画面更新。

首先说第一个,这个方法看起来很好,但事实上首先效率有问题,其次很多时候并不能够成功的转移焦点。尤其是牵扯到一个后面将会提到的快速输入引起问题的时候,为了让行为正确,设计将会变得非常复杂和麻烦,所以我放弃了这个方案。
而第二个则是我在NfaGen1里面所使用的方案,因为这个方案甚至连RichTextBox都不需要改造就能够实现,开发起来最为简单易行。代码类似于:

private void rtxEdit_TextChanged()
{
    
if (busy)
    
{
        busy 
= false;
        
return;
    }

    busy 
= true;
    
int oriStart, oriLength;
    rtxBuffer.Text 
= rtxEdit.Text;
    oriStart 
= rtxEdit.SelectionStart;
    oriLength 
= rtxEdit.SelectionLength;
    AnalyzeBuffer();
    rtxEdit.Rtf 
= rtxBuffer.Rtf;
    rtxEdit.SelectionStart 
= oriStart;
    rtxEdit.SelectionLength 
= oriLength;
}


上面的rtxEdit是用户输入区,rtxBuffer则是一个不可见的后台处理区。由于处理的时候在另外一个RichTextBox里面,要闪也是在那个看不见的后台空间内部“闪”,所以基本上解决了“选中”-“闪烁”的问题。但是这个方案也不见得很明智,因为需要不断的更新前台输入区的内容,需要通过busy变量来避免更新前台输入的时候引发TextChanged事件,进而引起一个死循环。而这个方案实际上并不能够完全杜绝“闪烁”问题,因为当输入的内容超过文本框的大小范围,就会引起“更新”-“闪烁”问题。因为当你从后台拷贝Rtf到前台的时候,会使得SelectionStart变成0,也就是回到了最前面的位置,而很多时候原本光标的位置里最前面的位置相差有一个RichTextBox的大小,因此画面就会短暂停留在和原来显示内容不相同的位置上,然后通过后面恢复SelectionStart来恢复光标位置以及原来显示内容的位置。这个时候就会闪现整个文本最开始的内容的画面,如果有滚动条,你还会看到滚动条的位置也会不断的来回跳跃。但是对于内容非常有限的,能够保证输入内容不会超过文本框大小的情况下,用这个方法的确是比较合适的。

实际上第三种方法才是最好的,因为不需要将文字来回的搬动,就不应该出现任何的闪烁,同时效率也应该相对较高。可这么好的方法,为什么在NfaGen1里面使用呢?因为我遇到了困难,不过幸运的是现在我已经有解决方法了。困难就是:首先override OnPaint似乎不起作用,其次截获WM_PAINT不传递给基类的WndProc函数来阻止重画会引起系统不断发送WM_PAINT消息,非常地占用CPU。而且事实上被截获的消息最后还是要发出去,也就是说本来你想阻止不让画的东西似乎还是要画出来的,或者至少背负了一些不必要的消息处理花费。解决问题的办法就是绕过去!

我猜测这个WM_PAINT的重发并不是操作系统引起的,因为在MSDN里面说,如果这个窗口处理这个消息,返回0就OK了。如果WM_PAINT不断重发,那么很可能是因为.NET Framework的底层代码发出了这个WM_PAINT来达到重画画面的目的,但是他发现并没有执行相应的操作,因此就不断的重发。当然,这个也是我的无理猜测而已,因为至少这部分代码在Managed部分找不到。既然有这个猜测,我就进行了相应的测试。不过大家想想,如果允许RichTextBox接受WM_PAINT消息固然可能不让消息重发,但是也不可能达到暂停重画的目的。在我分析过RichTextBox、TextBoxBase、Control等部分的WndProc函数之后,发现WM_PAINT的消息处理是通过非托管的代码来完成的,并且很可能是根据托管对象的类型来确定具体的行为方式,因此我就尝试对.NET进行“欺骗”。

首先我建立一个派生自Control的类,然后在这个类里面添加一个public函数,调用base.WndProc(ref m),然后所有WM_PAINT消息就转发到这个类的对象上面,结果竟然成功了!我猜测也许hWnd确实是RichTextBox的Handle,所以系统认为那个WM_PAINT消息已经被处理了。但是同时由于这个时候的处理对象并不是一个RichTextBox,因此就不会用RichTextBox的画面更新代码来进行更新,而是什么也不做,因此达到了暂时屏蔽画面显示的效果。

public class SuperBox : RichTextBox
{
    
private class paintHelper : Control
    
{
        
public void DefaultWndProc(ref Message m)
        
{
            
this.DefWndProc(ref m);
        }

    }


    
private const int WM_PAINT = 0x000F;
    
private int lockPaint;
    
private bool needPaint;
    
private paintHelper pHelp = new paintHelper();

    
public void BeginUpdate()
    
{
        lockPaint
++;
    }


    
public void EndUpdate()
    
{
        lockPaint
--;
        
if (lockPaint <= 0)
        
{
            lockPaint 
= 0;
            
if (needPaint)
            
{
                
this.Refresh();
                needPaint 
= false;
            }

        }

    }


    
protected override void WndProc(ref Message m)
    
{
        
switch (m.Msg)
        
{
            
case WM_PAINT:
                
if (lockPaint <= 0)
                
{
                    
base.WndProc(ref m);
                }

                
else
                
{
                    needPaint 
= true;
                    pHelp.DefaultWndProc(
ref m);
                }

                
return;
        }


        
base.WndProc (ref m);
    }

}

posted on 2004-07-29 17:11:00 by sumtec  评论(12) 阅读(7349)

Hey, everybody!

今天有一位MVP朋友问我是否想做MVP,我想了半天,还是觉得有机会就要尝试,失败了也没什么大不了的。所以最后决定要参加这么一次活动,虽然是自不量力,也要搏一搏啦!

如果说大家都能够来推荐我一下多好啊,可惜只能够由MVP来推荐。目前有两位MVP朋友推荐我,我已经感到非常的高兴了,但是我这个人不容易满足,所以特地来做一下广告,推销一下自己,看看会不会有其他MVP也来推荐我呢?

先来一个三句话的简短介绍:
1、我叫郑毅帆。
2、我之所以有勇气参选MVP,是因为我在.NET CF方面有一定的经验。
3、我参选MVP的目的是推销自己,如果当选了我一定发扬乐于助人的精神。

下面是我在MVP申请表格里面填写的自我简介:

大家好,我是郑毅帆,英文代号sumtec。无论在哪里看到sumtec,那么多半就是我本人了,尤其在中文网站里面。"Sum" for summary and "tec" for technology. Sumtec是我在初二的时候给自己命名的“代号”,为什么说是代号呢?因为这不是一个英文的人名,没有人叫这样奇怪的名字,不过我个人很喜欢这个代号。

在过去的一年多时间里面,由于公司开发一个PDA上面的行业应用,而我在这一家处于创业阶段的公司里面任研发部主管,因此该任务由我来负责。在进行一轮比较之后,操作系统决定选用PPC系列,并且用.NET CF进行开发。该方案的好处是,在PPC2k2到PPC2k3之间能够进行完全无缝的“升级”,有利于日后选择不同厂家的硬件,使得我们在选择上游厂家的时候,处于更有利的地位。在几个月之前该项目的开发已经完成,目前已经转入市场销售阶段。在整个开发过程当中,对于.NET CF的不少细节问题都有了比较深入的了解。例如在SP2之前,应用程序启动速度比较慢,这是由于.NET CF设计疏漏造成的效率问题。而这个问题可以通过将部分源代码修改成另外一种等价形式来解决,但是手动修改任务非常繁重,为此我还专门开发了一个自动修改源代码的工具,用于专门解决这一个问题。

当然,我在接触.NET CF之前,对于.NET也已经非常的熟悉了。跟.NET的接触可以说是从VS2k2的Alpha版就开始了,当时由于国外黑客泄露了一份Alpha版,有幸接触到这个产品。但正是接触是在Beta1开始的,甚至在这个版本上面已经写了不少个人的应用(非商业的),因此当年在CSDN上面解答其他人问题的时候,经常能够判断出别人使用的书籍是根据Beta1版本来编写的,标志就是某些命名空间有区别,例如Winform这个命名空间。

而更早之前则是使用的QB/VB系列,这些工具时常能勾起我往事的回忆。我总难以忘怀用QBX开发一个“淡入淡出”效果的库,更难以忘记当年试图在QB上面模拟消息机制以及Windows那样的UI。不过我这个人对于不常使用的东西忘记得也特别快,以便于记住最新的知识。因此如果你现在问我VB的问题,那么恐怕我已经不能够给你很精确的答案了。VB至少有四五年没有使用了,QB就更不要提了。

大家也许发现了,我从很早之前就已经开始学习编写计算机程序了。确切的说,我是从小学4年级下学期的时候就开始了这个漫长的计算机学习历程。并且在中学的时候参加了NOI,也就是全国奥林匹克信息学竞赛,除了第一次拿了市三等奖之外,每次都拿到市一等奖,甚至有省一等以及全国一等。不过这些都是分区联赛的成绩,而说到竞选省队参加全国决赛则一次都没有,每次都是差一名进不去。我对这个感到些许遗憾,但是并不后悔,因为那是我的性格使然——我对于不解决实际问题的东西非常不感兴趣,竞选之前不愿意做充分的准备也就是一件必然的事情了。

上面提到了CSDN,这个社区什么时候加入的我都很模糊了,大概是在2000年左右吧。后来还申请当上了VB.NET的版主,为很多朋友解答过不少问题。到目前为止我在CSDN上面已经累积到15112分的专家分,其中14486分都是属于.NET板块的记分。当然这个分数并不高,想比许多高手,我这个分数也许只有几分之一到几十分之一,但至少表明我也帮助过不少的朋友们。更何况我对于分数这样的东西并不在意,能帮助别人才是最重要的。不过最近到CSDN上面的次数已经比较少了,因为我最近有一些私人事务需要处理,此外还着迷于另外一种媒体——Blog。现在维护着两个个人的Blog——http://blog.joycode.com/sumtec以及http://www.cnblogs.com/sumtec。这两个Blog实际上内容基本上一致,因为有幸受到双方的邀请,所以两边都留下来了。

说到Blog,其实这也是一个能够帮助别人的地方。把自己的心得体会写下来,供他人参考,有时候比回答别人的问题更加有效果,因为回答问题的方式也许只有提问的人才能够得到答案,但是在Blog上面所有对他感兴趣的人都能够获得满意的答案。我个人就在blog上面得到了很多帮助,同时现在我也在尽量利用Blog记录下个人的一些经验知识以及成果,希望能够帮助其他人。欢迎大家经常光顾我个人的Blog,并提出您宝贵的建议。对于我个人来说,是否能够当上MVP并不重要,我更希望能够通过这次活动展现自我。当然,如果能够当选,我会感到无限荣幸。谢谢大家对我的支持!

posted on 2004-07-29 15:21:00 by sumtec  评论(23) 阅读(1791)

我的NfaGen2到底是用来干什么的?

Ninputer问我这个是干什么的。实际上很多人都问过我这个问题,甚至dudu也问过,甚至还有人因此对这个东西有误会。这个怪我,没有说清楚。今天我就来给大家说明白这个事情。

这个NfaGen2是用来产生复杂正则表达式的,比如如果我在NfaGen2里面这么写:

ws:\s
#_SemiColon:;
#_IdHead:a-zA-Z_
#_IdTail:0-9<IdHead>
_Id:<IdHead><IdTail:any>
Type:<Id>
Name:<Id>
SimpleDefine:<Type><ws:many><Name><SemiColon>

那么SimpleDefine就会生成:

(?<SimpleDefine>(?<Type>[a-zA-Z_][0-9a-zA-Z]*)\s+(?<Name>[a-zA-Z_][0-9a-zA-Z]*))

你觉得是上面那种写法(NfaGen2)比较清晰呢,还是下面这种写法(Regex)清晰?我觉得如果我看到
SimpleDefine:<Type><ws:many><Name><SemiColon>
我就知道SimpleDefine匹配的是一个类似
类型  名称;
这样的字符串,至于类型(Type)是什么东西,我再找Type的定义好了。现在给出的这个例子已经足够简单了,如果我们需要产生比较复杂的正则表达式的时候,区别就更大了。

如果让你直接看或者写标准的正则表达式,估计光是括号对齐就已经让你头痛不已,甚至有时候你需要搞清楚某一个“捕获组”到底捕获了一些什么,这个时候会非常头痛。如果这个表达式本身需要匹配各种括号,那就更加麻烦了,比如我要匹配类似(a+(a+a)+(a))这样的表达式,你觉得用Regex要怎么写呢?
(?>(?<=\(|[+]|\A)(?<left>\()(?=[a-zA-Z_]|\()|(?<=[0-9a-zA-Z_]|\))(?<right-left>\))(?=[+]|\)|\z)|(?<=\(|[+]|\A)(?<var>(?<id>[a-zA-Z_](?:[0-9a-zA-Z_])*))(?=\)|[+]|\z)|(?<=\)|[0-9a-zA-Z_])(?<operator>[+])(?=\(|[a-zA-Z_]))+

如果我不告诉你这个正则表达式就是用来匹配这样的字符串的,你自己能够看得懂这个表达式的作用吗?如果你声称能够看得懂,我再来问你,你说说看捕获组left捕获的内容是什么?var组呢?还有它能够接受那些运算符呢?上述这个式子你如果自己来写的话,要花多少时间才能够写正确呢?如果我现在给你这么一个正则表达式,我要求你修改成能够接受加减乘除这四个运算符,你要花多少时间来修改这样一个表达式呢?

大家再来看看,下面这些就是我用于产生上面那个正则表达式的NfaGen1的源代码(NfaGen2的格式跟这个差不多):

_root:(?><_leftPart>|<_rightPart>|<_varPart>|<_operatorPart>)+

_leftPart:<_left_start><left><_left_end>
_rightPart:<_right_start><right-left><_right_end>
_varPart:<_var_start><var><_var_end>
_operatorPart:<_operator_start><operator><_operator_end>

_left_start:(?<=<_left>|<_operator>|\A)
left:<_left>
_left_end:(?=<_id_head>|<_left>)

_right_start:(?<=<_id_tail>|<_right>)
right-left:<_right>
_right_end:(?=<_operator>|<_right>|\z)

_var_start:(?<=<_left>|<_operator>|\A)
var:<id>
_var_end:(?=<_right>|<_operator>|\z)

_operator_start:(?<=<_right>|<_id_tail>)
operator:<_operator>
_operator_end:(?=<_left>|<_id_head>)

id:<_id_head><_id_tail>*
_id_head:[a-zA-Z_]
_id_tail:[0-9a-zA-Z_]

_left:\(
_right:\)
_operator:[+]

虽然长了一点,不过看完第一行已经知道大概是什么样的了,而要接受加减乘除四个运算符,只需要修改最下面那一个_operator,变成[-+*/]就可以了。如果你直接修改正则表达式,你可能就会漏掉了检测左括号前面的合法字符的那部分,以及其它的你没有注意到的地方,结果没有办法完全正确的匹配,甚至你不知道问题出在什么地方。

用上述方法定义的时候还有一个好处,就是你可以进行局部的匹配测试,看看局部的正则表达式是否书写正确。例如你可以测试_leftPart,测试的内容包括:
(
(a
a(a
a(+
+(a
+(+
+()
((a
)(a
看看对于左括号的匹配是否正确。如果你直接从标准的正则表达式上面进行类似的提取,是非常困难的一件事情,你必须数一下有多少个"("以及")",中间还得提防"\("和"\)"这样的被转义了的字符。

上面这个表达式还是很简单的,如果你看看NfaGen2用于进行语法加亮的表达式,你会剧烈的头痛得,像那种几K级别的正则表达式,要对某一个局部提取,进行上面那种局部测试,简直就是异想天开!

好了,说到这里不知道大家是否明白了NfaGen2的作用了呢?如果还是不明白,那表明我表达得不够清晰,或者漏了什么内容,我会再找时间来给大家解释的。谢谢大家的支持。

posted on 2004-07-28 14:28:00 by sumtec  评论(6) 阅读(1713)

NfaGen2 的自动着色功能概念演示版本

点击这里下载(请首先看完下面的说明后再继续使用)
预览画面:



说明:这个只是一个概念演示版本,仅仅演示“自动着色”功能,目前没有任何的存盘功能和智能提示等功能。并且还有一部分的着色没有完成,没有完成的部分将在下一个版本里面看到。目前大家可以做的是,看看这个自动着色器的速度和效果。关于速度,日后会有一定的优化,不过可能并不会比目前快超过一个数量级。
在这个压缩文件包当中有一个后缀为NGARPT的文件,这个文件是必须的,不能够删除,并且必须和EXE文件放在同一个目录下面。该文件的内容就是对上面那个文本框内部输入的字符进行分析的正则表达式内容,其中第一行(不包括自动换行)的内容是语法分析,第二行是用于找出“语句”的分隔点,以便进行局部分析(而不是耗费时间的全局分析)。
上图中,右边的方框用于演示如何“暂停”RichTextBox的刷新,点击Begin之后将会暂停(用其它窗口覆盖在这个文本框上面再拿开就可以看到效果了),点击End的次数等于点击Begin的次数将会恢复刷新。下面这部分是计划当中的“头文件”,有兴趣可以看一下:

/ File name:?? Version2.txn
/ Create time: 2004-07-23
/ Creator:???? sumtec

/ This file defines the version of the TXN file to version 2.
/ This will enable the version 2 syntax.

/ These lines defines the version.
@ifdef version

@warning "Version is already defined in file {1} at line {2} as Version \"{0}\"." version version.File version.Line

@endif /version

@version 2.0


/ File name:?? Version.txn
/ Create time: 2004-07-23
/ Creator:???? sumtec

/ This file defines the version of the TXN file to version 1.
/ \ will turns into

@ifdef version

@ifdef version

@warning "Version is already defined in file {1} at line {2} as Version \"{0}\"." version version.File version.Line

@endif /version

@version 1.0

/ File name:?? Options.txn
/ Create time: 2004-07-23
/ Creator:???? sumtec
@option trace >
@option capture ?
@option regex "is-x" / This is incorrect!


/ File name:?? Symbols.txn
/ Create time: 2004-07-23
/ Creator:???? sumtec

@ifndef _Symbols_Defined
@define _Symbols_Defined

@sealcapture begin

#_cBacklash:\
#_cRegexEscape:
#_cLeftRoundBracket:(
#_cRightRoundBracket:)
#_cLeftSquareBracket:[
#_cRightSquareBracket:]
#_cLeftPointBracket:<
#_cRightPointBracket:>
#_cLeftCurlyBracket:{
#_cRightCurlyBracket:}
#_cPipeline:|
#_cColon::
#_cSemiColon:;
#_cComma:,
#_cDot:.
#_cExcalmatoryMark:!
#_cQuestionMark:?
#_cAsterisk:*
#_cPlus:+
#_cMinus:-
#_cEqual:=
#_cUnderline:_
#_cEmailAt:@
#_cSharp:#
#_cDollar:$
#_cPercentage:%
#_cUpPoint:^
#_cAndMark:&
#_cSlash:/
#_cDoubleQuote:"
#_cSingleQuote:'

@sealcapture end

@endif / _Symbols_Defined


/ File name:?? Operators.txn
/ Create time: 2004-07-23
/ Creator:???? sumtec

@ifndef _Operators_Defined
@define _Operators_Defined

@include Symbols.txn

@sealcapture begin

_m_cStart:
_m_cEnd:
_m_cInverse:

_m_gStart:
_m_gEnd:
_m_gOr:

_m_dStart:
_m_dDefine:
_m_dEnd:

_m_rFrom:
_m_rTo:
_m_Repeat:
_m_somethingLazy:
_m_lazyRepeat:<_m_Repeat><_m_somethingLazy>

_m_any:
_m_many:
_m_one:

_m_lazyAny:<_m_any><_m_somethingLazy>
_m_lazyMany:<_m_many><_m_somethingLazy>
_m_lazyOne:<_m_one><_m_somethingLazy>

@sealcapture end

@endif / _Operators_Defined

/ File name:?? Escapes.txn
/ Create time: 2004-07-23
/ Creator:???? sumtec


#_escape_Operator:
#_escape.Char:<_escape_Operator><_escape_Operator>

@ifndef _Escapes_Defined
@define _Escapes_Defined

@include Symbols.txn

@sealcapture begin

#_Escape:
#_EscapeMark:<_Escape><_Escape>
#_lrBracket:<_Escape>
#_rrBracket:<_Escape>
#_lsBracket:<_Escape>
#_rsBracket:<_Escape>
#_lpBracket:<_Escape>
#_rpBracket:<_Escape>
#_Dot:<_Escape><_cDot>
#_Asterisk:<_Escape><_cAsterisk>
#_PlusSign:<_Escape><_cPlus>
#_Question:<_Escape><_cQuestion>
#_Pipeline:<_Escape><_cPipeline>
#_MinusSign:<_Escape><_cMinus>
#_UpPoint:<_Escape><_cUpPoint>

@sealcapture end

@endif


/ File name:?? Base.txn
/ Create time: 2004-07-23
/ Creator:???? sumtec

@include Version2.txn
@include options.txn
@include operators.txn
@include escapes.txn
@include spaces.txn

posted on 2004-07-28 13:07:00 by sumtec  评论(3) 阅读(1405)

吊胃口V——预告篇

这个Post只是预告一下“吊胃口V”可能发布的内容,是一个预告。

首先发扬一下吊胃口的精神,给大家看看目前NfaGen的工作成果:



上面这个图是NfaGen2的雏形,上面的代码着色是程序自动完成的。不过目前着色也没有完全昨晚,比如定义后面部分的字符就没有进行着色。目前NfaGen2的语法分析用的正则表达式基本上已经稳定下来了,一般来说错误已经很少了,但是还有一些已知的错误存在,也有一些不足的地方需要调整。这个正则表达式的长度目前已经达到了惊人的5,009个字符了,估计还要继续增加一点。

那么我准备在“吊胃口V”里面给大家说些什么呢?就讲讲我是怎么进行这个着色工作的。其时不要小看这个着色步骤,如果你用RichTextBox就会发现有各种各样的问题:
1、要对某部分的颜色字体进行改变,就需要设置SelectionStart、SelectionLength,然后修改SelectionColor、SelectionFont。但是在此过程当中,你会发现编辑框会闪烁——你选中的部分会变蓝然后再变回来。
2、如果你打算使用一个RichTextBox作为后台着色的工具,那么你会发现如果文本内容太多,也会出现闪烁——首先闪现最开始部分的内容,然后再显示你正在编辑的部分。
3、你还会发现,如果你输入的速度太快了,就可能出现错误,比如你输入"asdf",结果却显示"asfd",并且光标停留在f和d之间,或者出现其他类型的错误(尤其是你打算用多线程的话)
4、你打算怎么进行代码分析着色呢?把文本框里面的所有内容都重新分析并着色一遍?如果是这样的话,你会发现内容多到一定程度的时候就会非常的迟钝了。假如你采取只分析最近修改的那一两行的办法,那么如果你Paste一段文字进去的话,你打算怎么处理呢?

大家觉得“吊胃口V”说这个内容OK不OK啊?我希望在正式发布“吊胃口V”之前先把大部分的Bug修正了,并且能够完全着色。好,不多说了,继续改Bug,早改完早发布。

posted on 2004-07-27 14:58:00 by sumtec  评论(3) 阅读(1670)

我好像又找到MS的一个BUG了,关于正则表达式的

[abc] 表示匹配abc这三个字符当中的任意一个;
[^abc] 表示匹配abc这三个字符之外的其它字符;
a|b 表示匹配a或者b;

我来请教大家:
[^a]|[b]
应该会匹配什么呢?
[d]|[^d]
又应该匹配些什么呢?

请大家先不要测试,直接写出答案,然后再把测试之后的结果也写出来。大家分析一下,这是不是一个Bug呢?

其实我这个问题是因为,我想匹配除了“符号”和“空白字符”之外的所有字符,但是同时允许匹配下划线"_"(这个也属于字符),因此我不得不这么写:
[_]|[^\s\p{S}\p{P}]

结果却发现完全不是这么一回事,我觉得这个实际的匹配情况不符合逻辑,好像是一个Bug。当然,上面那一个例子也许应该写成:
[_\S\P{S}\P{P}]
这样更加正确。

(顺便透露一下,上次那个1725字符的表达式,先经过几次缩减变成14xx,然后再经过几次迅速膨胀,现在变成3xxx了,估计还要稍微小胀一点点。这个用于分析词法的正则表达式已经差不多构造好了,虽然似乎没有人用正则表达式匹配来分析词法的(基本上用DFA的比较多),不过我觉得用Regex也不是完全不可能的,就让我来证明一下吧。)

posted on 2004-07-25 13:15:00 by sumtec  评论(2) 阅读(1371)

吊胃口 IV —— 让你的窗口成为“控件”

我记得第二次的时候给大家透露过可以把窗口变成“控件”一样,放到某个窗口“里面”。其实要变成“控件”那样,在某个空间里面其实很简单的,只要在构造函数里面添加一句:SetTopLevel(false); 或者TopLevel = false;。这两句话的作用几乎就是一样的,这两句话的作用都是指定这个空间不是顶层控件。

我们现在假设主窗口类的名称是Form1,我们准备变成“控件”窗口类的名称是frmInner。经过上面的修改,我们还是没有办法在ToolBox上面找到frmInner这个“控件”可以供我们拖到Form1上面,没办法,我们只好手动“拖”这个“控件”到Form1上面。在Form1里面随便找一个地方添加一个定义:
private frmInner fInner;
然后到InitializeComponent()函数里面最后添加下面几句:
fInner = new frmInner();
fInner.Dock = DockStyle.Right;
fInner.Parent = this;
然后在构造函数调用完InitializeComponent()函数之后添加下面这一句话(不要写到InitializeComponent里面):
fInner.Visible = true;

然后我们打开Form1的设计窗口,看:

现在fInner就乖乖的跑到Form1里面了。不过不要高兴得太早,运行看一下,你就发现有好几个问题存在:
1、标题栏永远都是灰色的。
2、fInner竟然可以移动,甚至还可以改变高度,要知道这个时候他应该是Dock在右边的。
3、如果Form1是MdiContainer,并且fInner.Dock 不是None也不是Fill,按照下面的步骤操作:
   (1) 把fInner变宽。
   (2) 最大化Form1(或者改变它的大小)
   (3) 把fInner变窄。
   你就会发现这个时候Form1里面有一个凹下去方框,这个方框里面应该是MdiChild窗口的区域,现在这个方框的大小没有修正,因此你可以很清晰地看到Form1右边fInner左边有一条很明显的白线。这一来不雅观,二来不符合逻辑。
4、最小化fInner竟然不见掉了!

前面三个问题我已经解决了,就剩下最后一个问题还没有解决。其实还有好多其他的问题没有解决,这里权当给大家抛砖引玉,这里就说说前面三个问题的解决方法。

首先标题栏永远是灰色的问题其实就是要你在适当的时候用Msg = 0x86的消息,调用WndProc函数。那么什么时候是恰当的呢?有两个主要的消息需要我们捕获,一个是WM_MOUSEACTIVATE 0x0021,还有一个是WM_COMMAND 0x111。
当你用鼠标点中了fInner(不是fInner里面的其他任何控件比如TextBox),就会获得WM_MOUSEACTIVATE,这个时候你需要做一件事情:this.Focus();
上面的那个this实际上是fInner,不是Form1,不要理解错了,这么做的作用后面会说到。当我们收到这个消息,并且执行完上面的那句话,就需要用Msg = 0x0086的消息调用base.WndProc函数,引起标题的变化。
而当你点中了fInner里面的其他任何控件比如TextBox,你会获得WM_COMMAND,并且WPARAM的高16位的值是0x0100(或者0x200,如果这个控件失去焦点,并且焦点离开了fInner)。当我们捕获到了这个消息,需要判断WPARAM高16位是否为0x100或者0x200,如果是那么也需要用Msg = 0x0086的消息调用base.WndProc函数,引起标题的变化。

引起标题变化的时候,需要首先判断this.ContainsFocus,如果包含,那么就应该让WPARAM = 1,否则为0。因此当我们接受到WM_MOUSEACTIVATE的时候,一定要首先让自己获得焦点,否则无法正确引起标题变化。到这里,第一个问题解决了,下面解决另外一个问题。

经过测试,我已经确认了改变窗口位置和大小的行为完全受操作系统而不是.NET Framework的掌握,也就是说如果我们截获WM_MOVE、WM_SIZE等消息,并做出相应修改,根本就不起任何作用。这个时候需要看鼠标点击标题栏和边框的时候发生了些什么事情。经过分析,可以知道首先发生的一件事情就是获得WM_NCHITTEST消息,这个消息查MSDN的帮助就可以知道是操作系统希望窗口告诉它,用户鼠标所点击到的地方是什么部位(比如标题栏、可以改变大小的边框部分或者工作区等等)。这个时候我们可以这样写:

base.WndProc(ref m);
if (this.Dock != DockStyle.None)
{
   
int mask = 0;
   
switch(this.Dock)
   
{
      
case DockStyle.Left:
         mask 
= 11// HTRIGHT
         break;
      
// 10 = HTLEFT, 15 = HTBOTTOM,  12 = HTTOP
   }

   
int i = m.Result.ToInt32();
   
if (i == 2 || (i > 9 && i < 18 && i != mask)
   
{
      m.Result 
= new IntPtr(1)
   }

}

return// 避免调用WndProc最后的那句base.WndProc

也就是说,如果处于Docking状态,那么无论如何点击标题栏都要视为点击普通的工作区(就是除了标题栏和边框之外的,可以摆放控件的地方),同时根据具体的Docking情况,不应该允许改变大小的边框位置也应该视之为普通区域。当然,你还可以做得更好一点,比如如果是左上角或者左下角,并且Dock右边,则mask=11。到这里,第二个问题也部分解决了,实际上还是有一些其他的Bug,比如说:
(1) 最大化fInner
(2) 最大化Form1
你就会发现不对劲的地方了,具体怎么解决现在我还没有试出来,大家自己思考一下吧。

下面解决第三个问题。
第三个问题其实很好解决,只要在获得WM_SIZING 0x0214 的时候,判断parent是否为一个MdiContainer的Form,如果是,则调用Parent的PerformLayout(this, "Bounds"),具体一点说:
case 0x214:
    
if (client.Dock != DockStyle.None)
    
{
        Form host 
= client.Parent as Form;
        
if (host != null && host.IsMdiContainer)
        
{
            host.PerformLayout(client, 
"Size");
        }

    }

    
break;

好了这个问题也解决了。需要我给大家一个完整的代码吗?没问题,待会儿我整理好了就贴出来,不过仍然有各种各样的问题,不过至少给你带来了一个可以Docking的Form“控件”的解决方案了。稍后在这里面给出链接(就在这句话的下一行),敬请关注。

posted on 2004-07-22 14:30:00 by sumtec  评论(4) 阅读(4411)

对博客堂的几点建 :P

如果可能的话:

1、页面上应该显示阅读次数,好跟别人比较一下,看看自己的差距在哪里。

2、那个自动转变表情的功能是不是可以关掉?感觉有点不方便,经常在贴代码的时候会被自动转换成一个笑脸,一瓶啤酒,只好到Html里面修改,非常不方便。

3、管理页面的“随笔”、“文章”和“反馈”等,应该能够点击标题,直接进入该页面,例如:

吊胃口 III (关键词:正则表达式,正则表达式构造器,保证精彩!) True 1 1 查看 修改 删除

这样发布/修改好了,可以直接点击进去看看效果,或者直接从“反馈”那个页面,点击想要回应的回复标题,就可以进入相应的Post直接回复,等等。

4、是不是可以增加一个限制文章长度的功能,太长了部显示在首页,只显示一个“详情点击查看”的超链接,大多数的超长Post都不会写摘要的,懒惰是Developer的天性。

5、登录的时候,可不可以直接按回车进去啊。现在的方法要多按一下TAB或者用鼠标,不太符合习惯。

6、管理页面最上面那个.TEXT的图标是不是可以换成博客堂的?链接也接到博客堂的首页吧!不然从管理界面想要到博客堂首页,中间要经过一个自己的首页才能到达(或者手动在地址栏里面填写),哎呀,没办法,懒是我们的天性啊。

我也是随便说说的,老大抽空升个级已经不容易了,上面那些简直就是奢求啊,忙的话就算了。本来我想回复到开心的那个提问题的Post里面去的,可是那里面似乎都是类似Bug之类的东西,好像有点不搭界……

posted on 2004-07-21 13:59:00 by sumtec  评论(1) 阅读(1303)

吊胃口 III (关键词:正则表达式,正则表达式构造器,保证精彩!)

不好意思啊各位,不是存心给大家卖关子的。因为我的确还没有完成,只是阶段性的成果,所以只能够一点一点给大家挤牙膏了。

我记得第一次最后给大家看了一个图片,左边那部分可以从大量的“单词”当中,找出符合前面已经输入的那一部分的那些单词。这个是一个自己写的类(从头到脚都是自己写的,除了书组和基本的类型,没有用其它的集合类型),在上一次已经初步完成了,但是今天不准备给大家放出来(还得继续吊着),因为:1、还没有完成(测试);2、功能还有待调整。其实很明显,第二次吊胃口所放出来的代码,就是为了解决将搜索到的单词,以“智能提示”下拉框的方式显示出来并允许选择的问题。

那么这一切又一切的工作又是为了什么呢?呵呵,为了写一个超强的正则表达式构造器!嗯,大家一定记得上一次我给大家提供过一个alpha版的正则表达式构造器吧?虽然说挺好用的,但是里面有一些隐藏得我都不知道在哪里的Bug存在,还有一些结构不合理的地方(包括代码),以及还是不是非常顺手。所以我决定推倒重来,做一个全新的,非常好用的正则表达式构造器!

目前这个东西还处于非常核心的地方,没有任何可以看得见的效果存在,但是我可以给大家模拟一下主编辑区的效果,主要是给大家看一下我设计的语法:

/ Comment: This is a sample code acceptable by NfaGen2
/ Attn: This is only a SAMPLE, might not be acceptable in final release version.
/ Sample create: 2004-07-21
/ Creator: Sumtec

/ Option Syntax:
/ @OPTIONNAME OPTIONAME...
/
/ The following option means (?>...|...)
/ The opposite option should be: (which means (?:...|...) )
/ @option trace on
/ This option might not be supported
@option trace off

/ Define Syntax:
/ (>|:)?#?(\?|_)?NAME\.ALIAS:CONTEXT
/ >|:  means (?>CONTEXT) or (?:CONTEXT), it will ignore the @option trace.
/ #    means the definition here is a character set, or we say it's “[CONTEXT]”
/ ?|_ means it is a “captured replace” or “pure replace”,  or we say:
/ (?<NAME>CONTEXT) or CONTEXT
/ If (?|_) is omitted,  it will determine by @option capture which defaults to “caputure replace”
/ .ALIAS   is not a part of capture name in RegEx, but a part of definition name in NfaGen2
/ Some times we need to capture different context by the same capture groupname, .ALIAS provide
/ a way to get two or more different captures a same name.

/ Here gives you a full sample:
/ This sample will match the following text:
/ ABCD      0123  0123

_ws:\s
_EndString:\Z
#_word:a-zA-Z
#_number:\d
#Invalid.Word:^<_word>
#Invalid.Number:^<_number>
Word:(?><_word>|<Invalid.Word>)+
Number:(?><_number>|<Invalid.Number>)+
_Root:<Word>(?><_ws:many><Number@FirstNumber>(?><_ws:many><Number>)*)?

/ NfaGen2 will compile _Root into the following RegEx expression:
/ (?<Word>(?>[a-zA-Z]|(?<Invalid>[^a-zA-Z]))+)(?>\s+(?<FirstNumber>(?>\d|(?<Invalid>[^\d])))
/ (?>\s+(?<Number>(?>\d|(?<Invalid>[^\d])))*)?
/
/ I inserted line break for reading more easier.
/ There should not be any line breaks in the NfaGen2 generated expressions.
/ You might find optimization, for example: [\d] is optimized into \d
/ And you can see that <Number@FirstNumber> is redirected to (?<FirstNumber>...).
/ It is an example of the @ operator which can only be used in Context part.

呵呵!是不是比较酷呢?如果对格式有什么疑问或者建议,大家尽可以回复,谢谢支持!(看在我这么辛苦的进行手动着色,没有功劳也有苦劳的份上,多多支持哦!)

最后还是忍不住要说一下,上一次的Post里面提到我用那个alpha版的正则表达式构造器所产生的最长的表达式有七百多个字符,今天早上被我打破了,而且仅仅是一个上午的时间!这一次为了进行上面那个语法分析,目前已经有1,725个字符了,而且很可能要继续变长,因为目前对于CONTEXT部分还没有任何的分析。

我把这个正则表达式给大家,大家可以试一下匹配上面的Example,看看是否能够正确匹配:
(?>(?<Comment>/(?<CommentText>[^\n]*))|(?<Option>@(?<OptionName>(?>(?<![\f\t\v\x85\p{Z}])[>:\.@](?<InComplete>(?=[>:\.@\r\n]))|(?<OptionNameHead>[^0-9\s\p{S}\p{Pe}\p{Pd}\p{Pf}\p{Pi}\p{Ps}\p{Po}]|(?<OptionNameReject>[^>:\.@\r\n\s]))(?:[^\s\p{S}\p{Pe}\p{Pd}\p{Pf}\p{Pi}\p{Ps}\p{Po}]|(?<OptionNameReject>[^>:\.@\r\n\s]))*(?:(?<![\f\t\v\x85\p{Z}])[>:\.@](?<InComplete>(?=[>:\.@\r\n])))?))(?>[\f\t\v\x85\p{Z}]+(?<OptionName>(?>(?<![\f\t\v\x85\p{Z}])[>:\.@](?<InComplete>(?=[>:\.@\r\n]))|(?<OptionNameHead>[^0-9\s\p{S}\p{Pe}\p{Pd}\p{Pf}\p{Pi}\p{Ps}\p{Po}]|(?<OptionNameReject>[^>:\.@\r\n\s]))(?:[^\s\p{S}\p{Pe}\p{Pd}\p{Pf}\p{Pi}\p{Ps}\p{Po}]|(?<OptionNameReject>[^>:\.@\r\n\s]))*(?:(?<![\f\t\v\x85\p{Z}])[>:\.@](?<InComplete>(?=[>:\.@\r\n])))?))?)*[\f\t\v\x85\p{Z}]*)|(?<Define>(?<DefineGroupName>(?<TraceSelect>(?<ForceTrace>:)|(?<ForceNotTrace>>)race>)?(?<Char>#)?(?<GroupCapturePrefix>(?<ForceCapture>\?)|(?<ForceNotCapture>_))?(?<Groupname>(?>(?<![\f\t\v\x85\p{Z}])[>:\.@](?<InComplete>(?=[>:\.@\r\n]))|(?<NameHead>[^0-9\s\p{S}\p{Pe}\p{Pd}\p{Pf}\p{Pi}\p{Ps}\p{Po}]|(?<NameReject>[^>:\.@\r\n]))(?:[^\s\p{S}\p{Pe}\p{Pd}\p{Pf}\p{Pi}\p{Ps}\p{Po}]|(?<NameReject>[^>:\.@\r\n]))*(?:(?<![\f\t\v\x85\p{Z}])[>:\.@](?<InComplete>(?=[>:\.@\r\n])))?))(?:\.(?<Alias>(?>(?<![\f\t\v\x85\p{Z}])[>:\.@](?<InComplete>(?=[>:\.@\r\n]))|(?<NameHead>[^0-9\s\p{S}\p{Pe}\p{Pd}\p{Pf}\p{Pi}\p{Ps}\p{Po}]|(?<NameReject>[^>:\.@\r\n]))(?:[^\s\p{S}\p{Pe}\p{Pd}\p{Pf}\p{Pi}\p{Ps}\p{Po}]|(?<NameReject>[^>:\.@\r\n]))*(?:(?<![\f\t\v\x85\p{Z}])[>:\.@](?<InComplete>(?=[>:\.@\r\n])))?)))?(?<RejectDefineName>>[^:\r\n]*)?)(?>(?<DefineInComplete>(?<!\G)(?=[\f\t\v\x85\p{Z}]*[\r\n]|\Z))|:(?<DefineLine>[^\n]*)))|(?<RejectLine>[^\n]+)|)[\f\t\v\x85\p{Z}]*(?>[\r\n]|\Z)

注意:中间没有任何的空格和回车!最后也没有回车(这个千万注意)!

posted on 2004-07-21 13:21:00 by sumtec  评论(5) 阅读(3689)

我来钓大家胃口 II

上一次提到了一个浮动的ListBox,实现得到方法是创建另外一个窗口。这一次呢,我就来给大家带来我的代码,希望大家喜欢。

不过在这之前,我先说明一下原理。一般情况下,我的原则是能够利用.NET本身提供的东西就尽量少使用API,原因见仁见智,我不罗嗦了。也就是说呢,这里并没有用到任何的API。
有人说了,得到Desktop的hWnd然后在上面画——我对于这个想法感到恐惧,因为你要手动画ListBox,手动处理一切的鼠标键盘事件。还有人说了,parent指向桌面,这个想法没有验证过,只是觉得如果这样仍然无法避免点击到这个ListBox的时候,“激活”转移到“桌面”(相当于你点击到桌面的任何一部分),从而使你的窗口失去“激活”,标题就变成绘画的了。当然,这也只是猜想。还有人说,获得WM_NCACTIVATE之后再创建相应的窗口……这个不好理解,不过似乎也没有解决“激活窗口”被转移造成的标题变色问题。

看起来这个问题很复杂,其实很简单,给大家看看重点部分的代码是什么样的:

        public Form1()
        
{
            InitializeComponent();
            fcManager = new FloatingClientManager(this);
            fcManager.AddClient(fLs);
        }


        
private frmList fLs = new frmList();
        
private FloatingClientManager fcManager;

        
protected override void WndProc(ref Message m)
        
{
            Message tm 
= fcManager.WinProc(ref m);
            
if (tm.Msg != m.Msg)
            
{
                
base.WndProc(ref tm);
            }

            
base.WndProc(ref m);
        }


也就是说,首先你需要一个FloatingClientManager(这个东西后面介绍,先吊一下胃口),构造的时侯指明“宿主”是哪一个Form,然后把所有“客户”窗口通过AddClient注册一下,最后在WndProc里面添加一小段代码,那么FloatingClientManager就会自动帮你处理所有事情了。很简单吧?

FloatingClientManager的原理是,通过截获WM_NCACTIVATE消息阻止“宿主窗口”的标题变色。大家如果看一下WM_NCACTIVATE的描述,就知道这个消息是用来通知这个窗口ACTIVATE状态改变,需要重绘窗口的标题(以及ICON)。看MSDN的描述,如果在DEACTIVATE(失去“激活”状态)的时候,返回FALSE就可以阻止标题重画,然而事实上并不是这样的,.NET在这里搞了点鬼——至于什么鬼不知道,那些是Native的代码,用Reflector看不到。如果你真的只是打算再调用完毕之后让m.Result = IntPtr.Zero,那么你会发现标题的颜色变了,并且还有一个更坏的消息:那个ListBox无法获得焦点了(甚至那个客户窗口也没办法获得焦点)。

那怎么做呢?呵呵,我的办法很奇特,就是在DEACTIVATE的时候,让.NET Framework误以为实际上是ACTIVATE,让他把标题画成“激活状态”的样子。其实我也说不清楚为什么这样能够成功,我也是在前面一个尝试失败的情况下,胡乱试试的——死马且当活马医,没想到还真的医活了!在看FloatingClientManager的代码之前,可以看看下面这个“另类”的效果图:


好了,废话不多说,赶紧看代码吧:
using System;
using System.Collections;
using System.Windows.Forms;

namespace NFAGEN2
{
    
/// <summary>
    
/// FloatingClientManager 浮动客户窗口管理器
    
/// </summary>

    public class FloatingClientManager
    
{
        
/// <summary>
        
/// 构造函数
        
/// </summary>
        
/// <param name="HostForm">宿主窗口</param>

        public FloatingClientManager(Form HostForm)
        
{
            hostForm 
= HostForm;
            
if (HostForm == null)
            
{
                
throw new ArgumentException("HostForm must not be null");
            }

        }


        
/// <summary>
        
/// 宿主窗口
        
/// </summary>

        private Form hostForm;
        
/// <summary>
        
/// 客户窗口 哈希表
        
/// </summary>

        private Hashtable ht = new Hashtable();
        
/// <summary>
        
/// 整个程序程序是否处于“激活”状态
        
/// </summary>

        private bool appActive;

        
private readonly IntPtr pFalse      = IntPtr.Zero;
        
private readonly IntPtr pTrue       = new IntPtr(1);
        
private const int WM_ACTIVATEAPP    = 0x1C;
        
private const int WM_NCPAINT        = 0x85;
        
private const int WM_NCACTIVATE     = 0x86;

        
/// <summary>
        
/// 添加在这个宿主窗口上浮动的客户窗口
        
/// </summary>
        
/// <param name="Client">客户窗口</param>

        public void AddClient(Form Client)
        
{
            
if (Client.TopLevel == false)
            
{
                
throw new ArgumentException("Client.TopLevel must be true");
            }

            ht.Add(Client, Client.Owner);
            Client.Owner 
= hostForm;
        }


        
/// <summary>
        
/// 删除客户窗口
        
/// </summary>
        
/// <param name="Client">客户窗口</param>

        public void RemoveClient(Form Client)
        
{
            Client.Owner 
= (Form) ht[Client];
            ht.Remove(Client);
        }


        
/// <summary>
        
/// 检测是否已经注册客户窗口
        
/// </summary>
        
/// <param name="Client">客户窗口</param>
        
/// <returns>包含则返回true</returns>

        public bool ContainsClient(Form Client)
        
{
            
return ht.Contains(Client);
        }


        
/// <summary>
        
/// 获得宿主窗口
        
/// </summary>

        public Form HostForm
        
{
            
get
            
{
                
return hostForm;
            }

        }


        
/// <summary>
        
/// 获得客户窗口数组
        
/// </summary>

        public Form[] Clients
        
{
            
get
            
{
                Form[] clients;

                clients 
= new Form[ht.Count];
                ht.Keys.CopyTo(clients, 
0);
                
return clients;
            }

        }


        
/// <summary>
        
/// 辅助处理函数,用于简化宿主窗口的WndProc函数的代码。
        
/// </summary>
        
/// <param name="m">消息m,可能会被部分修改</param>
        
/// <returns>如果需要首先“处理”另外一个消息,则返回需要首先处理的消息,否则返回消息m。</returns>

        public Message WinProc(ref Message m)
        
{
            
// 不是宿主窗口,避免错误处理情况
            if (m.HWnd != hostForm.Handle)
            
{
                
return m;
            }

            
// 程序激活状态改变
            if (m.Msg == WM_ACTIVATEAPP)
            
{
                appActive 
= (m.WParam != pFalse);
                Message tm 
= m;
                tm.Msg 
= WM_NCACTIVATE;
                
return tm;
            }

            
else if (appActive) // 如果应用程序处于激活状态
            {
                
switch (m.Msg)
                
{
                    
case WM_NCPAINT: // NC区域需要重绘,此时需要手动重绘标题,标题状态错误。
                        Message tm = m;
                        tm.Msg 
= WM_NCACTIVATE;
                        
if ( Form.ActiveForm == hostForm || 
                            (Form.ActiveForm 
!= null && ht.Contains(Form.ActiveForm)) )
                        
{
                            tm.WParam 
= pTrue;
                        }

                        
else
                        
{
                            tm.WParam 
= pFalse;
                        }

                        
return tm;
                    
case WM_NCACTIVATE: // NC标题需要重绘,
                        
// 让.NET Framework 以为宿主窗口是激活的(如果需要的话)。
                        if ( Form.ActiveForm == hostForm || 
                            (Form.ActiveForm 
!= null && ht.Contains(Form.ActiveForm)) )
                        
{
                            m.WParam 
= pTrue;
                        }

                        
else
                        
{
                            m.WParam 
= pFalse;
                        }

                        
break;
                }

            }

            
return m;
        }


    }

}


呵呵,这里的代码有点过于“简陋”了,可以说不是很完美,很安全。如果你觉得不爽,尽管修改好了。最后再来发扬一下“吊胃口”精神,给大家出示另外一个图片:


需要说明一下,这个“frmI...”窗口实际上是Form1上面的一个“控件”,Dock = DockStyle.Right。让一个窗口能够放到另外一个窗口上面,就像Label/TextBox一样,其实很简单,就是在构造函数里面添加一句:
SetTopLevel(false);

但是你会发现标题栏是灰色的!

我这里的标题栏可使蓝色的哦!

现在我可以非常简单的模拟VS.NET的那些能够Docking的窗口了!下一次再来公布这部分的代码,如果感兴趣的人比较多的话,感兴趣就回复哦!谢谢支持!

posted on 2004-07-19 16:41:00 by sumtec  评论(12) 阅读(2211)

我来钓大家胃口 :P

我想大家用VS.NET的时候一定对那个智能提示印象深刻,如果你仔细看看,你就会发现那个智能提示的下拉框实际上是在整个VS.NET IDE的外面的。比如说,如果你把VS.NET IDE变成不是最大/小化的时候,在编辑区的最下方调出只能提示下拉框,并且在屏幕下方留有足够的空间,你就会发现那个下拉框竟然申到VS.NET IDE的外面了!

第一印象你一定会觉得那个是一个FormBorderStyle为None的一个Form上面放了一个ListBox所实现的效果,可是如果你自己来试一下,就会发现有一个大问题:
就是当你选中ListBox上面的内容的时候,主窗口就会失去焦点,标题就会灰化!感觉很不爽吧!

你可以尝试SetTopLevel、Owner、Parent……结果都不行吧!呵呵,不要说上面那几个公开的东西了,甚至我用Reflection手动设置state,并且使得Owner和Parent同时指向主窗口,都无济于事(主窗口甚至变成无效的)。

然后大概你会想到,菜单好像也可以延伸到所属窗口的外边,可是很不幸的是,至少我没有找到可以把一个控件放到菜单上面的方法。我甚至考虑过自己去OwnerDraw,但是有一点让我放弃了:
如果你在VS.NET IDE探出智能提示下拉框的时候,移动主窗口,就会发现那个下拉框也会跟着移动。可是菜单,对不起,点击非菜单部分就会使菜单消失,跟不要说去移动它了。

最后,千辛万苦(也就半天时间),终于给我搞定了,看图:


哈哈!我要卖关子喽,怎么做的现在先不说,回复的人多了我再说(其实是现在还不是做得很通用,我想做的通用了再发布出来)。呵呵!

不过我可以透露一点点:实际上确实是用FormBorderStyle为None的Form来实现的,不过需要主窗口截获一个WM_NCACTIVATE消息,进行一定的处理,就能够得到这个效果了。

(不知道有没有人注意到左边的ListBox显示的是什么呢?还要卖一个关子……)

posted on 2004-07-18 11:24:00 by sumtec  评论(18) 阅读(2021)

如果你身边有这样的人,赶紧让他说点什么,然后反过来买彩票

贝利战绩: 90年,他看好南斯拉夫队当黑马,第一场就被德国干了1:4。 92年,他继续看好南斯拉夫队,结果南被禁赛。 94年,他看好哥伦比亚当黑马,结果哥伦比亚小组被淘汰,后卫被枪杀。 96年, 说过土耳其看好,结果土耳其立马就被淘汰。 98年,决赛前他看好巴西队,巴西队以历史最大惨败输了世界杯。 98年,小组赛结束狂看好尼日利亚, 结果好好的尼日利亚,淘汰赛第一轮就回家了。 98年,贝利看好西班牙成为黑马,结果西班牙小组都没出线。 02年,他看好阿根廷和法国队,结果双双小组毙命。 02年,英格兰3:0干掉丹麦之后,贝利预言英格兰拿冠军,结果下一场被巴西干掉。 04年,他在葡英赛前极力看好鲁尼和英格兰,结果鲁尼立刻受伤下场,英格兰点球落败。 最狠的是,20多年前,贝利访问中国,说你们很快就能进入世界杯,结果中国队苦苦挣扎了20年。

posted on 2004-07-05 13:29:00 by sumtec  评论(4) 阅读(1464)

最近才想通的一个问题……

不知道大家有没有想过这一个弱弱的问题?反正我想过了,并且在一段时间里面百思不得其解。

那些成对的运算符,为什么非要成对的进行定义呢?eg:
A>B 难道不应该等于 !(A<=B)吗?A==B难道不应该等于!(A!=B)吗?
也就是说,为什么不能够简单的只定义<、= 两个符号就能够得到其他几个符号的定义呢?
>      ->   !(< || ==)
>=    ->   !(<眨眼笑脸
<=    ->   < || ==
!=     ->   !(==)

如果觉得要引入||不好,顶多再定义一个<=就够了。也许多了一个非操作,影响效率。但是一个非操作在普通情况下并不会造成多大的性能影响,至少如果我只定义了<和==没有定义>和!=,编译器也应该能够自动产生相应的函数啊。产生一个编译错误是在让人头痛,因为不得不做一些重复性的劳动。

最后我在float.IsNaN函数里面找到答案了:原来判断a是不是NaN的办法是通过 a<0 || a>=0 来得到的,如果这个式子为真,那么就不是NaN,否则就是NaN,真是非常让我感到意外。也就是说,a>=0 并不等价于 !(a<0)  ,对于NaN来说,a<0是false,a>=0也应该是false,但是!(a<0)却得到true了,而不是a>=0的false。

不过再怎么说,我还是认为如果我没有成对的定义某些运算符,系统应该尽可能帮我自动生成,然后给出一个警告。比如定义了<、==两个符号,就应该可以得到其他的>、>=、!=、<=了,然后给出一个警告,这样比编译错误要舒服许多,有时候根本不想追求性能,只想尽快完成的时候,就可以偷个小懒了,何况一个“非”运算根本就不是什么大问题,甚至很可能被优化掉。

posted on 2004-07-05 13:20:00 by sumtec  评论(5) 阅读(1378)

这个算不算是Bug?

先问大家一个问题:objA == objB 和 objA.Equals(objB) 是否等价?答案显然是要看具体情况,或者说很可能是不等价的。

可是如果我这么问呢?
对于定义float  A, B; 
A == B 是否应该和 A.Equals啤酒杯  完全等价呢?应该是的吧?也许大家的答案有下列几个之一:
1、Equals通常用于在==运算符重载的时候,比较引用是否相等。
2、Equals通常用于比较引用类型的引用是否相等。

float没有运算符重载,也不是一个引用类型,甚至我也看不出有什么必要让Equals和==表现得不一样。然而我却发现这两个方法是不完全等价的!注意,我这里讨论的是A、B都是float的情况下。那么什么情况下不一样呢?当A和B都是float.NaN的情况下,==会得出false的结论,Equals会得出是true的结论。

这下子我就懵了,为什么要这样呢?按照数学定义来说,NaN之间的比较应该是false才对,因为“非数字”之间的比较是无意义的,更不可能相等啊。也许Equals想要表达的是,他们从二进制的角度上来说是相等的——两者都是非数字。

posted on 2004-07-05 12:53:00 by sumtec  评论(6) 阅读(1454)

【第1页/共2页,16条】
首页
前页
1

Powered by: Joycode.MVC引擎 0.5.2.0