none
请问如何计算String对象的长度呢?单位要求用像素表示? RRS feed

  • 问题

  • 请问如何计算String对象的长度呢?单位要求用像素表示?(最好用C#实现)
    先谢谢各位同行了!
    2008年10月14日 2:41
    版主

答案

  • 你好!泉!
       感谢你热心的解答!
       1,我这样解释这个问题:当输入一个字符后,如果当前文本框的宽度不够放置这些文本,那么TextBox会根据一定的规则来调整文本的显示位置,这个规则是尽量显示更多的字符,左移右移都有可能。当然,以上这些动作是发生在TextChanged事件之前的,所以当发生TextChanged事件的时候,文本的位置已经被调整了,这时候才执行TextChanged事件的处理函数,在处理函数中将TextBox.Width属性重新设置,这以后文本框的边框重新绘制为新的值,当然这时文本的位置已经被改变,并且这时文本的位置不会被调整了,所以头几个字符看不到了,右面还有一些空白。你同意我的解释吗?
       2,我观察到一个新的想象,输入很多字符后,头几个字符已经显示出来了,但是右面的空白越来越大,这时右边的空白已经足够放下好几个字符了,而且再输入字符,文本框还会扩大,我感觉好象是MeasureString方法计算的值比我们看到的字符串的长度要大,而且好象是字符越多,这个计算误差就越大。你对这个问题怎么看?

      
      
      
      
    2008年10月20日 15:48
    版主
  • 不好意思,这几天工作比较忙一些,回复有些迟到。

    1. 基本同意,只是不能确定,呵呵。

    2. 我认为是这样的:

    比如说原先是“abc”,现在录入一个d,这时候MeasureString计算的不是“abcd”占据的长度,而是“abc”+“d”占据的长度,因此较大的字符间距被计算在了文本长度中;但是这样也解释不通文本框右侧空间越来越大的现象,因为着看起来就像是MessureString方法在计算文本长度时采用了类似单件模式的思路,就是以前计算的长度被保留了然后下一次计算时只是在前次结果的基础上加上一个字符的长度,由此我怀疑MeasureString在字符串对象动态创建后没有及时更新对文本的引用。呵呵,天马行空了,只是我的猜测而已,没有看到其实现机制因此也无从解释。

     

    问题好多啊,希望大家多关注吧。

    2008年10月23日 9:42

全部回复

  • 试试
    MeasureString 方法 MeasureCharacterRanges 方法
    2008年10月14日 2:50
    版主
  • 感谢孟老师热心的解答!
    2008年10月14日 7:08
    版主
  • 我想想作个根据输入的文本的宽度,自动增加宽度的文本框,我订阅了TextBox的TextBox事件,处理函数如下:
            private void textBox1_TextChanged(object sender, EventArgs e)
            {
                int StringLength=(int)(this.CreateGraphics().MeasureString(textBox1.Text,textBox1.Font).Width);
                if(StringLength>textBox1.Width)//字符串长度大于控件的宽度
                {
                    textBox1.Width=StringLength;
                }


            }

    问题:输入的字符串长了以后,头几个字符跑到文本框外面了,看不到了,很奇怪!

    2008年10月14日 7:31
    版主
  • 用个笨办法,你算出半角和全角字符的像素长度来计算得到。

    2008年10月14日 7:52
    版主
  • 周老师你好:

    在输入一个字符后,文本框因为宽度不够使得文本在TextChanged事件触发前被左移,而您重设文本框宽度的操作是在左移发生之后进行的,自然已经无法看到全部的字符了。

     

    我写了一段代码,在按键按下时处理文本框宽度,您看看是否符合要求。

    private void textBox2_KeyDown(object sender, KeyEventArgs e)
            {
                //这里仅仅写了字母和数字
                if (!(e.KeyCode >= Keys.D0 && e.KeyCode <= Keys.D9) && !(e.KeyCode >= Keys.A && e.KeyCode <= Keys.Z))
                {
                    return;
                }

                string chr = e.KeyCode.ToString();
                double dChrLength = Math.Ceiling(this.CreateGraphics().MeasureString(chr, textBox2.Font).Width);

                double dStrLength = Math.Ceiling(this.CreateGraphics().MeasureString(textBox2.Text, textBox2.Font).Width);
                double dTbLength = textBox2.Width;

                //当剩余空间不足以容纳后续录入的字符时,重设文本框长度
                if (dTbLength - dStrLength < dChrLength)
                {
                    textBox2.Width = Convert.ToInt32(dStrLength) + Convert.ToInt32(dChrLength);
                }
            }

     

    2008年10月14日 9:21
  • 泉泉你好!
        首先感谢你的帮助!我用你的方法实现了,有两个问题:
        1,用TextChanged事件实现的时候,为什么一开始看不到开头的字符,而再多输入一些文本,又能看到开头的字符了。
        2,TextBox右面始终有一些空白,我的还是你的方法都是。
    2008年10月14日 11:37
    版主
  • 桂老师,感谢你的解答!
       你的意思是不是先计算每个字符的宽度,然后乘以字符数,我按你的方法试了,文本框仍然不能正常显示头几个字符。
       我开了一个新的帖子,来说明这个问题!
    2008年10月14日 11:52
    版主
  • 刚开始的代码没考虑到,正在输入的字符的长度,所以左边有的字符自动隐藏了,还有就是事件顺序的问题。

    2008年10月14日 13:34
    版主
  • 建春,你好!
       我认为TextChanged事件是在Text属性改变之后除法的,所以应该是考虑到了正在输入的字符。
    2008年10月14日 13:58
    版主
  • 1. 随着录入文本的增多,在 TextChanged 事件刚触发时 StringLength 与 TextBox.Width 长度越来越接近,最终在录入足够字符后文本框原先宽度大于字符串去掉两端保留间距后的长度,此时便能够看全了。可以仔细看一下,文本应该是随着录入字数的增加而逐渐右移的。

    2. 字符与字符之间是有间距的,比如字符串长度 【"aa"的长度】应该比 【"a"的长度+"a"的长度】要大。

    你的方法主要是因为文本已经左移了所以看起来右边有空白;我的方法里则没有考虑这个差值。还有,方法里进行的类型转换也可能会损失一些精度吧。

    改进:

    private void textBox2_KeyDown(object sender, KeyEventArgs e)
            {
                //这里只写了字母和数字
                if (!(e.KeyCode >= Keys.D0 && e.KeyCode <= Keys.D9) && !(e.KeyCode >= Keys.A && e.KeyCode <= Keys.Z))
                {
                    return;
                }
                string chr = e.KeyCode.ToString();
                double dChrLength = Math.Ceiling(this.CreateGraphics().MeasureString(chr, textBox2.Font).Width);
                double dTwoChrLength = Math.Ceiling(this.CreateGraphics().MeasureString(chr + chr, textBox2.Font).Width);
                double dStrLength = Math.Ceiling(this.CreateGraphics().MeasureString(textBox2.Text + chr, textBox2.Font).Width);
                double dTbLength = textBox2.Width;
                //当剩余空间不足以容纳后续录入的字符时,重设文本框长度
                if (dTbLength - dStrLength < 0)
                {

                    textBox2.Width = Convert.ToInt32(dTbLength + dChrLength - (2 * dChrLength - dTwoChrLength));

     

                    //不要像下面这样写:间距误差将累积

                    //textBox2.Width = Convert.ToInt32(dStrLength);
                }       

            }

    2008年10月15日 6:54
  • 你好!
       泉泉,感谢你的解释,我研究了两个小时,还是有很多的疑问:
       1,你的意思是说输入一个字符并且出界后,文本就先判断TextBox的宽度不够,然后左移了,再然后TextChanged事件才触发。
          我做了如下的测试:
           private void textBox1_TextChanged(object sender, EventArgs e)          {
                int StringLength=(int)(this.CreateGraphics().MeasureString(textBox1.Text,textBox1.Font).Width);
                if(StringLength>textBox1.Width)//字符串长度大于控件的宽度
                {
                    textBox1.Width=StringLength+30;//这里我强制加了30像素
                }


            }
          我的疑问是这样的:当输入第一个字符并且刚好要出界的时候,
    文本就先判断TextBox的宽度不够,然后左移了,这时头几个字符是看不到的,再然后TextChanged事件才触发并且重新设置TextBox的宽度,这时右面已经有很多的空白了(因为我加了30像素),当输入第二个字符的时候,应该有足够的空间放置文本了,但是头几个字符还是隐藏的?
       2,而且我观察到的情况是:一开始输入字符并且出界后,文本是左移几次的,输入的足够多的字符后,文本才向右移动的,很难解释?
       3,另外,
    你说,随着输入字符的增多,StringLength越来越接近TextBox.Width,这个我没有明白是什么意思!MeasureString()方法计算的字符串长度已经包括前后的光标空白的长度了。
       这个问题似乎很难解释,能告诉我你的msn吗?我想和你详细讨论一下!
    2008年10月15日 14:40
    版主
  • 周老师你好,我已加你msn。

    这个问题深入进去好像挺复杂,如果能看看TextBox的源代码就好了。前端时间微软公布了一部分代码,不过好像没有TB。

    2008年10月20日 9:56
  • 周老师:

    1. 在进入TextChanged处理之前有两个重绘:文本重绘和TextBox边框重绘,并且先进行的是文本重绘,所以会有左移的情况发生。按照你的说法,第一次加了30后在第二次输入的时候正因为有了足够的空间了所以不会进入if判断了自然也就不会发生边框重绘了,这个你跟一下程序很容易可以看出来;

    2. 本质同上,可以跟踪一下或者让进程等待几秒钟也可以看出来;

    3. 其中一个原因:随着输入字符的增多,误差会越来越小。

     

    附:

    if (StringLength > textBox1.Width)//字符串长度大于控件的宽度
                {
                    System.Threading.Thread.Sleep(3000);
                    textBox1.Width = StringLength;
                }

    2008年10月20日 12:45
  • 你好!泉!
       感谢你热心的解答!
       1,我这样解释这个问题:当输入一个字符后,如果当前文本框的宽度不够放置这些文本,那么TextBox会根据一定的规则来调整文本的显示位置,这个规则是尽量显示更多的字符,左移右移都有可能。当然,以上这些动作是发生在TextChanged事件之前的,所以当发生TextChanged事件的时候,文本的位置已经被调整了,这时候才执行TextChanged事件的处理函数,在处理函数中将TextBox.Width属性重新设置,这以后文本框的边框重新绘制为新的值,当然这时文本的位置已经被改变,并且这时文本的位置不会被调整了,所以头几个字符看不到了,右面还有一些空白。你同意我的解释吗?
       2,我观察到一个新的想象,输入很多字符后,头几个字符已经显示出来了,但是右面的空白越来越大,这时右边的空白已经足够放下好几个字符了,而且再输入字符,文本框还会扩大,我感觉好象是MeasureString方法计算的值比我们看到的字符串的长度要大,而且好象是字符越多,这个计算误差就越大。你对这个问题怎么看?

      
      
      
      
    2008年10月20日 15:48
    版主
  • 不好意思,这几天工作比较忙一些,回复有些迟到。

    1. 基本同意,只是不能确定,呵呵。

    2. 我认为是这样的:

    比如说原先是“abc”,现在录入一个d,这时候MeasureString计算的不是“abcd”占据的长度,而是“abc”+“d”占据的长度,因此较大的字符间距被计算在了文本长度中;但是这样也解释不通文本框右侧空间越来越大的现象,因为着看起来就像是MessureString方法在计算文本长度时采用了类似单件模式的思路,就是以前计算的长度被保留了然后下一次计算时只是在前次结果的基础上加上一个字符的长度,由此我怀疑MeasureString在字符串对象动态创建后没有及时更新对文本的引用。呵呵,天马行空了,只是我的猜测而已,没有看到其实现机制因此也无从解释。

     

    问题好多啊,希望大家多关注吧。

    2008年10月23日 9:42
  • 你好!
       具体的源码我也没有看过,我的说法也是评猜测,但是我估计是这样的。
       你说的MeasureString的实现也是有可能的,但是我也不能确定。
       对了,我使用的是VS2005,你用VS2008的单不能不能进入TextBox控件的内部来看到源代码?
    2008年10月23日 11:44
    版主
  • 希望大家继续关注这个问题!
    2008年10月27日 11:21
    版主