注册 登录
编程论坛 VFP论坛

RichTextbox控件如何根据自身宽、高,以及用户设定的字体、字号和行距,计算1屏有几行?1行有几列?

cssnet 发布于 2025-09-01 23:13, 2045 次点击
看到吹版的这段代码,联想到以前我遇到的那个问题(一直都未能精确地解决):

程序代码:

        DECLARE long SendMessageW   IN user32 as SendMessageW2   long,long,long,string@
        DECLARE long SetWindowTextW IN user32 as SetWindowTextW2 long,string
        #define EM_GETLINE    0x00C4
        nBufferSize = 2048    && 要足够大
        cBuffer     = BINTOC(nBufferSize,"4rs")+REPLICATE(0h00,nBufferSize+1)
        hEdit       = thisform.hEdit1.hwnd    &&编辑框句柄
        nLineNo     = 9                  &&要取的行号(通常是从0行起)
        nWordCount  = SendMessageW2(hEdit, EM_GETLINE, nLineNo, @cBuffer)
        SetWindowTextW2(hEdit, cBuffer)   && 显示结果


如标题所示:如何计算RichTextbox一屏可完整显示的行数?以及,一列能显示的字符个数?
原因:希望在不出现垂直滚动条的前提下,能够一屏显示完整内容,让用户不必频繁地上下翻滚(注:表单另外设计有翻页按钮)。
问DC,它给出的解答异常复杂,远非类似一个SendMessageW2(hEdit, EM_GETLINE, nLineNo, @cBuffer)就能解决得了的。
想想也是,“宋体,12,N”和“微软雅黑, 16, B”,同一屏能够完整显示的字符行数与列数,结果相差非常大的!

113 回复
#2
sych2025-09-02 06:43
这个问题挑战性大,每行可显示的字符数可以变通获取,但不同的字符宽度不同,不知道会不会有所不同
#3
sych2025-09-02 07:18
Declare INTEGER SendMessage in WIN32API as sendmessage_n INTEGER hwnd , INTEGER wMsg , INTEGER wParam , INTEGER lParam
#defi EM_LINELENGTH 0xC1
MESSAGEBOX( SendMessage_n(thisform.Olecontrol5.hwnd, EM_LINELENGTH, 0,  0))
每行列数

#4
sych2025-09-02 07:20
不同的字符占用的空间不同,这个值会发生变化
#5
吹水佬2025-09-02 08:35
没用过RichTextbox,随便拖一个试试,有个自动换行的问题,有无禁止自动换行的设置?
init事件:
this.text = REPLICATE("AaBbCcDdEeFfGgHhIiJjKkLlMmNn汉字1234567890",3)
遇到数字号码框不够宽时会自动换行?
只有本站会员才能查看附件,请 登录



#6
cssnet2025-09-02 10:29
以下是引用吹水佬在2025-9-2 08:35:18的发言:

没用过RichTextbox,随便拖一个试试,有个自动换行的问题,有无禁止自动换行的设置?
init事件:
this.text = REPLICATE("AaBbCcDdEeFfGgHhIiJjKkLlMmNn汉字1234567890",3)
遇到数字号码框不够宽时会自动换行?


确实,先前看到你们讨论“获取RichTextbox第N行文字内容”的帖子,恰好我也想到了这个变通的解决方案:
RichTextbox1.init事件:
this.text = REPLICATE(0hA1A1,10000)

然后,用你那一个API得到第1--3行的字符,再分别取lenc(),合计值/3得到平均值,这就是比较精确的一行有多少个字符了吧。
至于一屏有多少列,估计仍得用比较复杂的像素值来计算……

#7
wxzd1232025-09-02 11:04
太好了,跟着楼主学习了
#8
wxzd1232025-09-02 11:05
我弄了半天也没搞明白,这回测试一下楼主的代码
#9
wxzd1232025-09-02 11:10
RichTextbox排版好,可vfp的编辑框Edit1排版不好,但能透明,我想RichTextbox排版完把每行后面加个回车符,然后用Edit1显示,可是没有实现。
#10
cssnet2025-09-02 11:17
以下是引用sych在2025-9-2 07:18:47的发言:

Declare INTEGER SendMessage in WIN32API as sendmessage_n INTEGER hwnd , INTEGER wMsg , INTEGER wParam , INTEGER lParam
#defi EM_LINELENGTH 0xC1
MESSAGEBOX( SendMessage_n(thisform.Olecontrol5.hwnd, EM_LINELENGTH, 0,  0))
每行列数


这个API没问题,能准确返回一行汉字字符的个数!

[此贴子已经被作者于2025-9-2 13:30编辑过]

#11
cssnet2025-09-02 14:10
其实,计算一屏总行数,经反复实验,我有一个不太精确的解决方案:

程序代码:

*获取当前字体的行高
lnFontHeight = FONTMETRIC(1, thisform.RichTexBox1.Font.Name, thisform.RichTexBox1.Font.Size)

*计算每屏行数
lnLinesPerPage = INT(thisform.RichTexBox1.Height / lnFontHeight / 1.35)


至于那个神秘参数1.35,大致相当于行间距,是我随意设置的——有时候准,有时候不准,计算误差大约在0--2行之间。
不知诸位英雄,有没有更精确的解决方案?

#12
吹水佬2025-09-02 16:24
GDI绘制文本有设置行间距和字间距的相关函数
思路:
1..获取字串高+行间距,确定行数。
2..获取字串宽,确定一行字数。
行间距可以试试FONTMETRIC(5,.....)+n,n(1,2,.....)试试。
字串宽就有点麻烦,可尝试调用API函数GetTextExtentPoint32W()最后一个参数(SIZE结构)是返回字串输出的x,y座标,x就是字串的宽,y是字高。



#13
sych2025-09-03 07:32
这个神奇的1.35猜测是96/72=1.33
#14
吹水佬2025-09-03 09:46
试了一下:
1、GetTextExtentPoint32W()对windwos的标准控件EDIT计算结果正确。
2、GetTextExtentPoint32W()好像不直接支持富编辑(Rich Edit),不能正确获取字体对象句柄。
    尝试:
    1、看看能否获取Rich Edit的字体句柄。
    2、根据Rich Edit设置的字体创建新的字体对象。

#15
cssnet2025-09-03 11:11
以下是引用sych在2025-9-3 07:32:30的发言:

这个神奇的1.35猜测是96/72=1.33


不太清楚。我让DC猜测这个“校正参数”,我设置行距 lnlineSpacing = 行高的1.5倍,它给出的调整建议:

程序代码:

初始值:从 lc校正参数 = 0.08 开始测试
根据行间距调整:
1.0倍行距:校正参数 ≈ 0.04-0.06
1.5倍行距:校正参数 ≈ 0.08-0.12
2.0倍行距:校正参数 ≈ 0.12-0.16
根据字体大小调整:
小字体(8-10pt):减少校正参数
大字体(12-14pt+):增加校正参数

最终公式:
lnLinesPerPage = INT(thisform.RichTextBox1.Height / lnFontHeight / ;
                  (lnlineSpacing - (0.08 + (lnlineSpacing - 1) * 0.06)))


然而,结果并不理想。

#16
吹水佬2025-09-03 11:39
行间距应该是编辑控件有定义的,不同字体和大小会有不同。
最好是自己能重新定义,那就是你说了算。

[此贴子已经被作者于2025-9-3 11:41编辑过]

#17
cssnet2025-09-03 13:08
以下是引用吹水佬在2025-9-3 11:39:56的发言:

行间距应该是编辑控件有定义的,不同字体和大小会有不同。
最好是自己能重新定义,那就是你说了算。


Richtextbox可以通过API设置行距,见我以前的讨论帖,还是您老人家给出的答案,亲,您不记得啦:
https://hrss.shandong.
#18
吹水佬2025-09-03 13:55
以下是引用cssnet在2025-9-3 13:08:44的发言:

Richtextbox可以通过API设置行距,见我以前的讨论帖,还是您老人家给出的答案,亲,您不记得啦:
https://hrss.shandong.

与.gov的无关吧
#19
吹水佬2025-09-03 14:42
SendMessage(thisform.rich1.Hwnd, EM_SETPARAFORMAT, 0, @pf)
获取就 EM_GETPARAFORMAT
#20
sych2025-09-03 15:09
Rich Edit 控件 SDK 参考手册-CSDN博客  https://blog.
刚搜到的帖子,先钉一下
#21
sych2025-09-03 15:19
EM_POSFROMCHAR(&HD6或&H426)这个消息是否可以实现
取得指定字符偏移的显示位置行列号,行列是按编辑控件的客户区左上角为原点(0,0)计算的。字符偏移是相对于文本头部的偏移。

EM_CHARFROMPOS(&HD7=215),0, //取得编辑控件中指定位置的字符相对于文本头部的偏移量。使用本操作应先在第4个参数的高16位指定行号,低16位指定列号(或一个POINTAPI结构),行列是按编辑控件的客户区左上角为原点(0,0)计算的。如果指定的位置超出控件客户区则返回-1

[此贴子已经被作者于2025-9-3 15:21编辑过]

#22
吹水佬2025-09-03 15:20
官方参考:https://learn.
#23
吹水佬2025-09-03 15:26
以下是引用cssnet在2025-9-3 11:11:31的发言:



不太清楚。我让DC猜测这个“校正参数”,我设置行距 lnlineSpacing = 行高的1.5倍,它给出的调整建议:


初始值:从 lc校正参数 = 0.08 开始测试
根据行间距调整:
1.0倍行距:校正参数 ≈ 0.04-0.06
1.5倍行距:校正参数 ≈ 0.08-0.12
2.0倍行距:校正参数 ≈ 0.12-0.16
根据字体大小调整:
小字体(8-10pt):减少校正参数
大字体(12-14pt+):增加校正参数

最终公式:
lnLinesPerPage = INT(thisform.RichTextBox1.Height / lnFontHeight / ;
                  (lnlineSpacing - (0.08 + (lnlineSpacing - 1) * 0.06)))


然而,结果并不理想。

获取RichEdit字体对象句柄代码中有个转换函数:
MulDiv(cf.yHeight / 20, 96, 72)
就是(cf.yHeight / 20) * 96 / 72

程序代码:

DLLIMPORT_C HFONT GetRichEditFontHandle(HWND hRichEdit)
{
    CHARFORMAT cf;
    ZeroMemory(&cf, sizeof(CHARFORMAT));
    cf.cbSize = sizeof(CHARFORMAT);

    // 获取当前选择文本的字符格式
    SendMessage(hRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);

    // 从CHARFORMAT创建字体对象
    return CreateFont(
        -MulDiv(cf.yHeight / 20, 96, 72), // 转换单位
        0, 0, 0,
        (cf.dwEffects & CFE_BOLD) ? FW_BOLD : FW_NORMAL,
        (cf.dwEffects & CFE_ITALIC) ? TRUE : FALSE,
        (cf.dwEffects & CFE_UNDERLINE) ? TRUE : FALSE,
        (cf.dwEffects & CFE_STRIKEOUT) ? TRUE : FALSE,
        DEFAULT_CHARSET,
        OUT_DEFAULT_PRECIS,
        CLIP_DEFAULT_PRECIS,
        DEFAULT_QUALITY,
        DEFAULT_PITCH | FF_DONTCARE,
        cf.szFaceName
    );
}
#24
sych2025-09-03 17:12
获取行数,如果最后一行没有完整显示,则不计算在内
Declare INTEGER SendMessage in WIN32API as sendmessage_n INTEGER hwnd , INTEGER wMsg , INTEGER wParam , integer lParam
#defi EM_GETFIRSTVISIBLELINE 0XCE
#defi EM_LINEINDEX  0xbb
#defi EM_POSFROMCHAR  0xd6
iFirstLine = SendMessage_n(thisform.Olecontrol5.hwnd, EM_GETFIRSTVISIBLELINE,0,0)
FOR i=0 TO 100
    cpFirst = SendMessage_n(thisform.Olecontrol5.hwnd,EM_LINEINDEX,iFirstLine+i,0)
    aa=SendMessage_n(thisform.Olecontrol5.hwnd, EM_POSFROMCHAR,cpFirst,0)
    aa=BITrSHIFT(aa,16)
    IF i>0
        IF aa=1
            EXIT
        endif
        IF aa>thisform.Olecontrol5.Height
            i=iif(aa>thisform.Olecontrol5.Height+5,i-1,i)  &&判断上一行是否完整显示,这个5是预估值,也可以用获取的行间距
            EXIT
        endif
    ENDIF
next
MESSAGEBOX(i)
retu


[此贴子已经被作者于2025-9-3 18:03编辑过]

#25
cssnet2025-09-03 17:34
以下是引用吹水佬在2025-9-3 13:55:00的发言:
与.gov的无关吧


搞笑搞笑,贴错了网址,应该是这一个的:

https://bbs.bc-cn.net/viewthread.php?tid=508544&page=1#pid2758685



#26
cssnet2025-09-03 17:35
以下是引用sych在2025-9-3 17:12:05的发言:

获取行数,如果最后一行没有完整显示,则不计算在内
Declare INTEGER SendMessage in WIN32API as sendmessage_n INTEGER hwnd , INTEGER wMsg , INTEGER wParam , integer lParam
#defi EM_GETFIRSTVISIBLELINE 0XCE
#defi EM_LINEINDEX  0xbb
#defi EM_POSFROMCHAR  0xd6
iFirstLine = SendMessage_n(thisform.Olecontrol5.hwnd, EM_GETFIRSTVISIBLELINE,0,0)
lh=0
lt=0
FOR i=0 TO 100
    cpFirst = SendMessage_n(thisform.Olecontrol5.hwnd,EM_LINEINDEX,iFirstLine+i,0)
    aa=SendMessage_n(thisform.Olecontrol5.hwnd, EM_POSFROMCHAR,cpFirst,0)
    aa=BITrSHIFT(aa,16)
    IF i>0
        IF aa=1
            i=i-1
            EXIT
        endif
        lh=MAX(aa-lt,lh)
        IF lh+aa>thisform.Olecontrol5.Height
            EXIT
        endif
    ENDIF
    lt=aa
next
MESSAGEBOX(i)
retu


太牛B了!英雄!
#27
sych2025-09-03 17:58
又修改了一下
#28
cssnet2025-09-03 21:44
实战的话,其实是有一点点尴尬的:
必须填满Richtextbox1一整屏后,才能够得到正确的行、列结果。
表单初始化时,倘若客户区空白,那么只能返回0列和1行。
倒是可以设置visible = .F. 或 LockScreen = .T.,
先用垃圾将客户区填满,得到行、列值,然后再恢复现场。
不过,拖动改变表单大小,特别是拉大表单,导致Richtextbox客户区不再被填满,那么也将得到小于实际值的行的结果。

当然,解决方案也有,就是浪费一点资源:
复制一个一毛一样的Richtextbox2,
并设置Richtextbox2.left = - Richtextbox2.width - 10,再用垃圾将它填满。
计算行、列值,就用Richtextbox2;而真正用,则是Richtextbox1。

话说,我老人家是不是冰雪聪明来着?
#29
sych2025-09-03 21:49
你这是量子纠缠,牛逼plus
#30
sych2025-09-04 06:05
顺着你的思路走,可以用半角或者全角空格填满,再获取行列值,是不是可以省掉一个2
#31
cssnet2025-09-04 07:35
以下是引用sych在2025-9-4 06:05:48的发言:

顺着你的思路走,可以用半角或者全角空格填满,再获取行列值,是不是可以省掉一个2


冰雪聪明如我,早想到这个思路啦!
不行,填充500个空格,似乎全挤在了一行。得到的返回值:
列=500
行=1

#32
sych2025-09-04 08:51
全角空格
#33
cssnet2025-09-04 10:22
以下是引用sych在2025-9-4 08:51:36的发言:

全角空格


唉,冰雪聪明如我,怎么可能想不到呢?
——不行。
其实我还试过:100个【回车】,100个【全角空格+回车】,……
——不行。准确地说,不太行。
richtextbox对于不可见空白字符的解析似乎存在一些问题,【宋体】会解析错误,多出来好几行,【微软雅黑】倒可能是正确的。

#34
sych2025-09-04 11:03
只能说你一点也不像我
*thisform.Olecontrol5.font.name="宋体"
thisform.Olecontrol5.font.name="微软雅黑"
thisform.Olecontrol5.text=REPLICATE(CHR(254),2000)

[此贴子已经被作者于2025-9-4 11:11编辑过]

#35
cssnet2025-09-04 11:36
以下是引用sych在2025-9-4 11:03:36的发言:

只能说你一点也不像我
*thisform.Olecontrol5.font.name="宋体"
thisform.Olecontrol5.font.name="微软雅黑"
thisform.Olecontrol5.text=REPLICATE(CHR(254),2000)


想不到还有0hFEFE这个空白汉字!我老人家墙都不扶就服你!
#36
cssnet2025-09-04 11:44
其实,冰雪聪明的我,还想过,要不要插入N行模样像Word行尾那一个“带弯弯的小箭头”(U+21B5),以冒充回车符,蒙骗用户的……只可惜,GBK编码表中没有对应的字符。
#37
吹水佬2025-09-04 14:41
对于一个字串,从头开始每行显示多少个字,这个问题可以算出。
显示多少行,要适应不同的字体和大小还要多方测试。通过获取字高加行距来算好像不准确,对不同的字体和大小差别也不一样。
#38
cssnet2025-09-04 16:02
以下是引用sych在2025-9-4 11:03:36的发言:

只能说你一点也不像我
*thisform.Olecontrol5.font.name="宋体"
thisform.Olecontrol5.font.name="微软雅黑"
thisform.Olecontrol5.text=REPLICATE(CHR(254),2000)


其实,实战中仍会有一个小瑕疵的:

假设,richtextbox1中原有两三行文字(注:我老人家一般习惯是thisform.richtextbox1.text = REPLICATE("一二三四五", 7),这样方便数一行有几个字);
现在,需计算一屏有几多行——按照前边的讨论,我们可能会这么做:

* --------------
* 保存现场
* --------------
_screen.LockScreen = .t.
with thisform.richtextbox1
   lnSelstart = lo1.Selstart
   lcRTF = .textRTF
   lcText = .text + REPLICATE(CHR(254), 2000)
   .visible = .f.
   .text = lcText
* 统计一屏总行数(见上边帖子,略。)
* ...
* --------------
* 恢复现场
* --------------
   .textRTF = lcRTF
   .Selstart = lnSelstart
   .visible = .T.
endwith
_screen.LockScreen = .F.

该死的屏幕,执行至此,总会闪一下!
无论如何设置,都会闪一下!
那么,聪明的,如何教他不要闪,只做一个安静的美男子呢?
#39
吹水佬2025-09-04 17:08
用text方法输入会清除用EM_SETPARAFORMAT设置的字体属性,也就是说设置的行间距无效。
#40
cssnet2025-09-04 17:19
以下是引用吹水佬在2025-9-4 17:08:41的发言:

用text方法输入会清除用EM_SETPARAFORMAT设置的字体属性,也就是说设置的行间距无效。


确实,我也发现了:Richtextbox的行距有些“飘忽不定”,想得到的一个折中的解决方案是:
在.text = "xxxx"后,重设一遍行距。
直觉行。

#41
吹水佬2025-09-04 17:31
回复 40楼 cssnet
重新设置后生效
试试选定之前输入的块再重新设置看看有效否
#42
sych2025-09-04 20:08
*_screen.LockScreen = .t.
with thisform.richtextbox1
   lnSelstart = lo1.Selstart
   lcRTF = .textRTF
   lcText = .text + REPLICATE(CHR(254), 2000)
*   .visible = .f.
   .text = lcText
* 统计一屏总行数(见上边帖子,略。)
* ...
* --------------
* 恢复现场
* --------------
   .textRTF = lcRTF
   .Selstart = lnSelstart
*   .visible = .T.
endwith
*_screen.LockScreen = .F.

注释掉这几句,我测试没有闪一下
*_screen.LockScreen = .t.
*   .visible = .f.
*   .visible = .T.
*_screen.LockScreen = .F.
#43
cssnet2025-09-04 20:52
以下是引用sych在2025-9-4 20:08:10的发言:
注释掉这几句,我测试没有闪一下
*_screen.LockScreen = .t.
*   .visible = .f.
*   .visible = .T.
*_screen.LockScreen = .F.


大致明白了,谢谢!不过,设置thisform.LockScreen = .t.应无坏处,理论上。


#44
吹水佬2025-09-04 23:57
参考示例,没有精准测试。
只有本站会员才能查看附件,请 登录

只有本站会员才能查看附件,请 登录

程序代码:

CLEAR
CLEAR ALL

PUBLIC cDefPath
cDefPath = ADDBS(JUSTPATH(SYS(16)))
SET DEFAULT TO (cDefPath)

**    int GetRichEditCharsW(HWND hRichEdit, LPCWSTR text, char* cFontName, int nFontSize)
DECLARE integer GetRichEditCharsW IN TextMetric long,string@,string@,integer

**    int SetRichEditLineHeight(HWND hRichEdit, int nPixels)
DECLARE integer SetRichEditLineHeight IN TextMetric long,integer

**    int GetRichEditLineCount(HWND hRichEdit, char* pFontName, int nFontSize)
DECLARE integer GetRichEditLineCount IN TextMetric long,string@,integer

of = CREATEOBJECT("form1")
of.show(1)
CLEAR ALL
RETURN

DEFINE CLASS form1 as Form
    width       = 420
    height      = 420
    AllowOutput = .f.
    AutoCenter  = .t.
    ADD OBJECT edit as OleControl WITH left=10,top=10,width=400,height=400,;
        OleClass="RICHTEXT.RichtextCtrl.1"
   
    PROCEDURE init
        #if 1
            this.edit.font.name = "宋体"   
        #else
            this.edit.font.name = "微软雅黑"
        #endif
        this.edit.font.size = 12
        
        ** 行高 = 字高 + 行间距
        nLineHeight = FONTMETRIC(1,this.edit.font.name, MTON(this.edit.font.size)) + 5
        SetRichEditLineHeight(this.edit.hWnd, nLineHeight)    && 设置行高
        
        ** 获取总行数

        nLineCount = GetRichEditLineCount(this.edit.hWnd, this.edit.font.name, MTON(this.edit.font.size))

        this.Caption = "    FontName: "+this.edit.font.name;
                     + "    FontSize: "+TRANSFORM(MTON(this.edit.font.size));
                     + "    LineCount: "+TRANSFORM(nLineCount)

        ** 显示一页字(测试只显示一页)
        cText = REPLICATE("AaBbCcDdEeFfGgHhIiJjKk字字字字1234567890",100)
        cStr = ""    && 一页字串
        nCount = 0   && 行计数
        DO WHILE !EMPTY(cText) AND nCount < nLineCount
            ** 获取一行字串
            nChars = GetRichEditCharsW(this.edit.hWnd, STRCONV(cText,5), this.edit.font.name, MTON(this.edit.font.size))
            IF nChars < LENC(cText)    && 如果不是最后一行
                nChars = nChars - 1    && 减一个字,忽略边际不完整的字
            ENDIF
            cStr  = cStr + LEFTC(cText,nChars) + 0h0D0A && 添加一行
            cText = SUBSTRC(cText, nChars+1)            && 下一行开始
            nCount = nCount + 1                         && 行计数
        ENDDO
        
        ** 显示输出
        this.edit.text = ""                                    && test方法会清除字体设置信息
        SetRichEditLineHeight(this.edit.hWnd, nLineHeight)     && 重新设置字体信息
        this.edit.SelText  = cStr
        this.edit.selstart = 0
    ENDPROC
ENDDEFINE

程序代码:

/*
    库文件 gdi32.lib
*/
#define DLLIMPORT_C extern "C" __declspec(dllexport)
#include <windows.h>
#include <richedit.h>

//获取 Rich Edit 字体信息并创建字体对象句柄
HFONT GetRichEditFontHandle(HWND hRichEdit, char* pFontName, int nFontSize)
{
    HDC hdc = CreateDCW(L"DISPLAY", NULL, NULL, NULL);

    CHARFORMAT cf;
    ZeroMemory(&cf, sizeof(CHARFORMAT));
    cf.cbSize = sizeof(CHARFORMAT);

    // 获取当前选择文本的字符格式
    SendMessage(hRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);

    // 从CHARFORMAT创建字体对象
    HFONT hFont = CreateFont(
        -MulDiv(nFontSize, GetDeviceCaps(hdc,LOGPIXELSY), 72),   //转换单位
        0, 0, 0,
        (cf.dwEffects & CFE_BOLD) ? FW_BOLD : FW_NORMAL,
        (cf.dwEffects & CFE_ITALIC) ? TRUE : FALSE,
        (cf.dwEffects & CFE_UNDERLINE) ? TRUE : FALSE,
        (cf.dwEffects & CFE_STRIKEOUT) ? TRUE : FALSE,
        DEFAULT_CHARSET,
        OUT_DEFAULT_PRECIS,
        CLIP_DEFAULT_PRECIS,
        DEFAULT_QUALITY,
        DEFAULT_PITCH | FF_DONTCARE,
        pFontName
    );

    DeleteDC(hdc);
    return hFont;
}

//获取 Rich Edit 在客户区一行字串的字数
DLLIMPORT_C int GetRichEditCharsW(HWND hRichEdit, LPCWSTR text, char* pFontName, int nFontSize)
{
    //获取控件尺寸和边距
    RECT rect;
    GetClientRect(hRichEdit, &rect);
    int editWidth = rect.right - rect.left;

    DWORD margins = SendMessage(hRichEdit, EM_GETMARGINS, 0, 0);
    editWidth -= (LOWORD(margins) + HIWORD(margins));

    HDC hdc = GetDC(hRichEdit);

    //获取当前字体
    HFONT hFont    = GetRichEditFontHandle(hRichEdit, pFontName, nFontSize);
    HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);

    int len    = lstrlenW(text);
    int nChars = 0;
    SIZE sz;

    //计算一行的字数
    for (int i = 1; i <= len; i++) {
        if (!GetTextExtentPoint32W(hdc, text, i, &sz)) break;
        if (sz.cx > editWidth) break;
        nChars = i;
    }

    SelectObject(hdc, hOldFont);
    ReleaseDC(hRichEdit, hdc);
    return nChars;
}

//设置 Rich Edit 行高
DLLIMPORT_C int SetRichEditLineHeight(HWND hRichEdit, int nPixels)
{
    //获取当前设备DPI
    HDC hdc = CreateDCW(L"DISPLAY", NULL, NULL, NULL);
    int dpi = GetDeviceCaps(hdc, LOGPIXELSY);
    DeleteDC(hdc);
   
    //设置行高
    PARAFORMAT2 pf;
    ZeroMemory(&pf, sizeof(PARAFORMAT2));
    pf.cbSize           = sizeof(PARAFORMAT2);
    pf.dwMask           = PFM_LINESPACING;
    pf.bLineSpacingRule = 4;                                        //精确行高
    pf.dyLineSpacing    = (nPixels * 20 * 72) / dpi;                //像素转换为缇
    return SendMessage(hRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
}

//获取 Rich Edit 客户区的行数
DLLIMPORT_C int GetRichEditLineCount(HWND hRichEdit, char* pFontName, int nFontSize)
{
    HDC hdc = GetDC(hRichEdit);

    // 强制重新获取当前字体
    HFONT hFont    = GetRichEditFontHandle(hRichEdit, pFontName, nFontSize);
    HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);

    //获取当前设备DPI
    HDC hdc2 = CreateDCW(L"DISPLAY", NULL, NULL, NULL);
    int dpi  = GetDeviceCaps(hdc2, LOGPIXELSY);

    // 获取行高
    PARAFORMAT2 pf;
    ZeroMemory(&pf, sizeof(PARAFORMAT2));
    pf.cbSize = sizeof(PARAFORMAT2);
    pf.dwMask = PFM_LINESPACING;
    SendMessage(hRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
    int lineHeight = 0;
    if (pf.dyLineSpacing > 0)
    {
        lineHeight = (pf.dyLineSpacing * dpi) / (20 * 72); // 转换为像素值
    }

    DeleteDC(hdc2);
    SelectObject(hdc, hOldFont);
    ReleaseDC(hRichEdit, hdc);
   
    // 计算客户区行数
    RECT rc;
    GetClientRect(hRichEdit, &rc);
    return rc.bottom / lineHeight;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
{
    return TRUE;
}

只有本站会员才能查看附件,请 登录


#45
cssnet2025-09-05 00:13
以下是引用吹水佬在2025-9-4 23:57:43的发言:
参考示例,没有精准测试。


哇!牛逼 Turbo!牛逼 Ultra!
有C代码就好办啦,我想想办法,看能否翻译成VFP原生代码(估计DC能帮忙搞定一大半!最后自己再适当地修补一下下。)
有界面的东东,还是习惯赤条条不带个拖油瓶。

#46
吹水佬2025-09-05 07:06
回复 45楼 cssnet
主要是调用windows api,转vfp应该无问题。
测试淘个方便才用C写给vfp用。



[此贴子已经被作者于2025-9-5 07:25编辑过]

#47
吹水佬2025-09-05 07:36
以下是引用cssnet在2025-9-4 16:02:43的发言:

该死的屏幕,执行至此,总会闪一下!
无论如何设置,都会闪一下!
那么,聪明的,如何教他不要闪,只做一个安静的美男子呢?

LockScreen 是 Lock vfp窗口,对 richedit 可能无效。
试试:
SendMessage(hRichEdit, WM_SETREDRAW, 0, 0)    && Lock
SendMessage(hRichEdit, WM_SETREDRAW, 1, 0)    && UnLock

#48
sych2025-09-05 08:43
以下是引用吹水佬在2025-9-5 07:36:15的发言:


LockScreen 是 Lock vfp窗口,对 richedit 可能无效。
试试:
SendMessage(hRichEdit, WM_SETREDRAW, 0, 0)    && Lock
SendMessage(hRichEdit, WM_SETREDRAW, 1, 0)    && UnLock

这个方法好用,这样随便在后台折腾,获取行数和列数后在刷新一下就OK了

[此贴子已经被作者于2025-9-5 08:45编辑过]

#49
schtg2025-09-05 09:44
回复 44楼 吹水佬
学习啦,谢谢!
#50
吹水佬2025-09-05 10:13
下面代码可以禁止 RichEdit 自动换行
#define WM_USER               0x0400
#define EM_SETTARGETDEVICE    WM_USER + 72   
SendMessage(hRichEdit, EM_SETTARGETDEVICE, 0, 1)  && 禁止自动换行

#51
sych2025-09-05 10:43
Declare INTEGER SendMessage in WIN32API as sendmessage_n INTEGER hwnd , INTEGER wMsg , INTEGER wParam , integer lParam
Declare INTEGER SendMessage in WIN32API as sendmessage_c2 INTEGER hwnd , INTEGER wMsg , INTEGER wParam , string lParam
#defi WM_SETREDRAW 0x0b
SendMessage_n(thisform.Olecontrol5.hwnd, WM_SETREDRAW, 0, 0)  && Lock
#defi EM_REPLACESEL 0xC2
SendMessage_c2(thisform.Olecontrol5.hwnd, EM_REPLACESEL, 1, REPLICATE(CHR(254),2000))  &&填充新字符串,且可撤销
#defi EM_GETFIRSTVISIBLELINE 0XCE
#defi EM_LINEINDEX  0xbb
#defi EM_POSFROMCHAR  0xd6
iFirstLine = SendMessage_n(thisform.Olecontrol5.hwnd, EM_GETFIRSTVISIBLELINE,0,0)
FOR i=0 TO 100
    cpFirst = SendMessage_n(thisform.Olecontrol5.hwnd,EM_LINEINDEX,iFirstLine+i,0)
    aa=SendMessage_n(thisform.Olecontrol5.hwnd, EM_POSFROMCHAR,cpFirst,0)
    aa=BITrSHIFT(aa,16)
    IF i>0
        IF aa=1
            EXIT
        endif
        IF aa>thisform.Olecontrol5.Height
            i=iif(aa>thisform.Olecontrol5.Height+5,i-1,i)  &&判断上一行是否完整显示,这个5是预估值,也可以用获取的行间距
            EXIT
        endif
    ENDIF
next
#defi EM_LINELENGTH 0xC1
MESSAGEBOX( SendMessage_n(thisform.Olecontrol5.hwnd, EM_LINELENGTH, 0,0))
MESSAGEBOX(i)
#defi EM_UNDO  0xc7
SendMessage_n(thisform.Olecontrol5.hwnd, EM_UNDO, 0, 0)  &&撤销填充
SendMessage_n(thisform.Olecontrol5.hwnd, WM_SETREDRAW, 1, 0)  && UnLock
通过这个话题的讨论,学到很多新东西
123