当前位置:C++技术网 > 资讯 > [MFC] CEdit控件如何实现单行文本垂直居中

[MFC] CEdit控件如何实现单行文本垂直居中

更新时间:2016-08-06 15:50:49浏览次数:1+次

百度的答案都是派生CEdit类,在绘图过程中实现文本垂直居中,但只能多行,而且绘图过程中编辑框会出灰色条纹,这不是我想要的;有什么好的方法更加完美的实现单行文本垂直居中

C++技术网解答:

在MSDN中解释说,Edit编辑框控件只有一个居中样式,而且这个居中样式只是水平居中,不管是单行文本编辑框还是多行文本编辑框,都可以直接设置文本对齐样式实现。在资源视图界面中,点击编辑框控件,然后右击,选择属性,然后可以看到如下图所示:

    在箭头处,选择“Center”即可实现文本的水平居中。没有任何选项设置文本的垂直居中。所以不要幻想直接通过设置属性来实现垂直居中,单行文本和多行文本编辑框都不可以。

    设置此属性,实际上在内部是设置了编辑框控件的控件风格ES_CENTER。你可以在RC资源文件中,也可以直接添加这个风格。当然,你喜欢动态创建编辑框控件,你也可以这样:


CEdit edit;
edit.Create(ES_CENTER|WS_CHILD|WS_VISIBLE|WS_TABSTOP|WS_BORDER, CRect(0,301,250,800), this, 1000);
     其中第一个参数的第一个风格就是设置文本水平居中的。如果你已经是创建好的控件,或者说是直接拖控件得到的编辑框控件,反正就是,没有水平居中的控件已经存在,且是水平左对齐,你想修改为水平居中,怎么办?看下面的代码:


CEdit edit;
DWORD dwStyle = GetWindowLong(edit.m_hWnd,GWL_STYLE);
dwStyle |= ES_CENTER;
SetWindowLong(edit.m_hWnd,GWL_STYLE,dwStyle);
     这里的编辑框是动态创建的,也可以是拖好控件然后绑定变量edit。我们从直接在界面上修改属性、创建时设置和中途修改的水平居中都是很顺利的。我们看看水平居中的效果:

    但是垂直方向上并没有这样的设置。为什么呢?

    对于单行文本来说,只能显示一行文本,所以,你要垂直居中,只要调好编辑框高度就可以了。也就是高度不要太高,这样就看上去垂直居中的。如果你将高度调到 很高,如上图所示,这明显不符合单行编辑框的设计了。所以,此时微软认为不存在这个需求,所以没有直接支持这样的单行垂直居中。

    像上图所示的编辑框,看上去应该是多行编辑框。那么多行编辑框是不是可以实现一行显示在垂直居中的位置呢?当然可以啦。你如果只是展示,那么前几行和后几 行都留空行不就行了。这样也就有了单行垂直居中的效果。所以,上面的单行垂直居中的效果是可以通过多行模拟出来的。但是,这仅限于简单的展示。如果要编 辑,这无疑就暴露了问题了。因为多行编辑框可以自由的换行,这不是让人感觉,这不是单行编辑框了。

    我们先来看多行编辑框本身的垂直居中问题。微软考虑到这个问题了,允许让多行文本编辑框自由调节编辑框文本显示的矩形位置。这么一来,多行编辑框就可以垂直居中,只要你算好垂直居中的位置,然后设置一下内容显示的矩形局域就行了。

    下图是我画的多行文本编辑框垂直居中的文本开始应该出现的位置计算示意图:

多行文本编辑框垂直居中的文本开始应该出现的位置计算示意图

    设置垂直居中的代码如下:


edit.GetClientRect(&rt);//获取编辑框客户区高度,edit是绑定变量的编辑框控件
CFont * pFont = edit.GetFont();//获取编辑框用的字体
LOGFONT logFont;
pFont->GetLogFont(&logFont);//获取逻辑字体
rt.top = rt.bottom/2-logFont.lfHeight;//logFont.lfHeight竟然是负数
edit.SetRect(rt);
     这样就设置了多行编辑框的垂直居中。但是,实际上,这个效果并不是真正的垂直居中。你想想,多行编辑框可以不断地增加文本,增加行数,还怎么保证离底部的 高度和离顶部的高度一致呢,如果不能一致,那怎么能叫做垂直居中呢?所以,多行文本编辑框没有固定设置一个值就可以保证垂直居中的。不过你可以实时计算, 实时调整多行文本显示的矩形的位置,保持上下距离相等,从而确保多行文本的垂直居中。此时你要考虑到多行文本的高度,然后可以替换上面示意图中间表示单行 文本高度。这样得到的就是真正的多行文本垂直居中。

    实际上,上面的示意图表示的是多行编辑框只显示单行文本的单行文本垂直居中效果。在不重载CEdit控件的情况下,能够完美做到单行文本垂直居中呢?

    答案是,可以的。下面看看效果图:

单行文本编辑框垂直居中。

    我们使用原理是:用多行文本编辑框来模拟单行文本编辑框实现垂直居中。而且支持输入编辑,而且不能回车换行。也就是说,我们将多行文本编辑框定制为了单行文本编辑框了。这样我们根本就不需要利用重载CEdit来实现这个效果。只要转动你的小脑袋,就可以轻松搞定。

    在设置多行文本编辑框垂直居中开始位置的时候,我们用了CEdit的SetRect函数,我们通过计算,修改了原本的编辑框文字的位置。所以,现在需要用这个函数来实现这个效果。

    攻克的技术点:

1.多行编辑框可以回车换行,我们要阻止。

2.单行编辑时保证光标位置正确。

3.单行显示。

4.水平输入文字可以支持超越一行的长度,也就是单行编辑框自动水平滚动,支持更多的输入。

    第一个问题:如果你想到用屏蔽按键WM_KEYDOWN或WM_CHAR的方法,那可不太好。标准的编辑框不能直接处理这个消息。所以我们可以利用文本改 变的控件消息。我们在文本内容发生改变时,及时的去掉换行符,就可以实现回车键按下后没有换行效果。处理的控件消息为EN_CHANGE。代码如下:


CString str;
edit.GetWindowText(str);
str.Remove(''''\r'''');
str.Remove(''''\n'''');
edit.SetWindowText(str);
//下面是设置插入符光标的位置
int l=edit.GetWindowTextLength();
edit.SetSel(l,l,FALSE);
edit.SetFocus();
     当然这里在移除回车换行符的时候,插入符光标会自动跳到最开始,因为我们重新设置了编辑框的文字。我们获取编辑框的内容,移除回车换行符,然后设置回去,就解决了换行问题。

    第二个问题就是,在移除换行符后,因为重新设置了编辑框内容,所以导致编辑框的插入符光标跳回了最开始。所以我们需要矫正。我们可以通过获取文字的长度, 然后将光标设置回去。然后再设置焦点,让光标恢复闪烁。如果你在文字的末尾追加文字,上述就OK了。但是如果你是在文字中间打字插入,那么光标每次都被定 位到末尾,那也是不行的。此时,你需要获取当前选中的文本的位置,然后再设置回去。所以我们改进代码如下:


CString str;
edit.GetWindowText(str);
str.Remove(''''\r'''');
str.Remove(''''\n'''');
int sel = edit.GetSel();
edit.SetWindowText(str);
edit.SetSel(sel);
edit.SetFocus();
     这样,光标原来在哪里,现在还是恢复到哪里。所以,在末尾追加的情况也解决了。非常完美。但是注意,要在设置编辑框文本之前获取之前的光标位置哦。因为设置之后,光标位置变为了0.

    然后第三个问题就是单行显示。前面我们说了SetRect函数,可以修改文本的有效矩形。这样编辑文本和显示文本都是在新的有效矩形呢。那么我们将矩形的 开始和高度限定为一行文本的开始和高度,不就完成了单行文本的编辑框功能了嘛。而且看上去是多行,实际就是单行。这就是我们要的效果。

    我们只需要在前面设置多行文本垂直居中开始位置的代码加上一句代码即可,即:


rt.bottom = rt.top + 18;//增加的数值一定要大于字体全部高度
     将文字有效矩形的底部限制一下,就得到了最后的搞多,也就是18.当然你应该通过计算字体的全部高度,包括字体高度和字体外边距高度(行间距)。这样就实现了单行效果。

    此时三个问题都解决了。最后一个问题就是,如果我们要支持水平上输入超过一行的文字,那么就使用ES_AUTOHSCROLL编辑框风格。直接在界面设置属性即可,属性名为Auto HScroll。但是你要注意,要让这个属性生效,文本水平不能是居中对齐的。

    到此,一个完美的单行垂直居中编辑框就实现了,借助的是多行文本编辑框,加上我们的一点小辅助,就大功告成了。重载CEdit固然也是可以实现这个效果的,但是比这个麻烦多了。其实很多时候,只要稍微转变下思维,就可以将本来要复杂技术实现的问题轻松巧妙实现。