SUMTEC -- There's a thing in my bloglet.

But it's not only one. It's many. It's the same as other things but it exactly likes nothing else...

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  209 随笔 :: 19 文章 :: 1662 评论 :: 22 引用

2004年7月29日 #

很多时候,我们希望对用户输入的文字进行即时的分析,并对部分文字的字体颜色进行修改,让那些文字更加醒目。如果说要对部分的文字进行字体颜色的修改,我们很可能就会想到使用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 @ 2004-07-29 16:59 Sumtec 阅读(974) | 评论 (4)编辑

今天有一位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 @ 2004-07-29 15:11 Sumtec 阅读(814) | 评论 (15)编辑