注册 登录
编程论坛 VFP论坛

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

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

程序代码:

        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 回复
#52
easyppt2025-09-05 10:44
LZ是想搞 行号显示吗,记得之前好像搜索到这个示例,缺陷是:放大缩小窗口,包括行高、字体的调整,这个行号 对错乱了,无法动态调整。

另: 如果能增加代码块 功能,就更好了(想法:通过代码设计出格式(排版),然后再正常书写代码)。

这样VFP就可以利用 RichEdit 来写笔记了或收藏资料 等。

#53
cssnet2025-09-05 11:54
我尝试往Richtextbox灌满Unicode字符,然后,上边讨论的获取行字符数的两个API皆阵亡,都只能返回1或2者啦——

nWordCount = SendMessageW2(hEdit, EM_GETLINE, nLineNo, @cBuffer)
nWordCount = SendMessage_n(thisform.Olecontrol5.hwnd, EM_LINELENGTH, 0,  0)

感觉上,是不是要改换SendMessageW()了?

#54
吹水佬2025-09-05 16:27
回复 53楼 cssnet
EM_GETLINE 支持 RichEdit
要注意的是接收行数据的缓冲区在发送消息之前,将此缓冲区的第一个字节设置为缓冲区的大小(以 TCHAR为单位)。
    对于 ANSI 文本,这是字节数。
    对于 Unicode 文本,这是字数。

#55
cssnet2025-09-05 17:01
以下是引用吹水佬在2025-9-5 16:27:40的发言:

EM_GETLINE 支持 RichEdit
要注意的是接收行数据的缓冲区在发送消息之前,将此缓冲区的第一个字节设置为缓冲区的大小(以 TCHAR为单位)。
    对于 ANSI 文本,这是字节数。
    对于 Unicode 文本,这是字数。


试了,不行,仍返回行字符数 = 2

参考的是此帖:
https://bbs.bc-cn.net/viewthread.php?tid=514450&page=6#pid2792983

将"11.txt"换成这一个空白字符:
只有本站会员才能查看附件,请 登录


然后统计一行的字符数,要么=1,要么=2

#56
吹水佬2025-09-05 17:20
回复 55楼 cssnet
这文件就是一行,返回1、2正常,返回2可能是0D结束符
#57
吹水佬2025-09-05 17:23
EM_GETLINE不是只取编辑框视区的行,应该是整个编辑内容,用EM_GETLINE来计算行数不准确吧?
#58
cssnet2025-09-05 17:27
以下是引用吹水佬在2025-9-5 17:23:30的发言:

EM_GETLINE不是只取编辑框视区的行,应该是整个编辑内容,用EM_GETLINE来计算行数不准确吧?


见本帖顶楼,以及:
https://bbs.bc-cn.net/viewthread.php?tid=514468&page=2#pid2793162

大佬啊,这是您亲自贴的代码啊!!!
#59
吹水佬2025-09-05 18:35
回复 58楼 cssnet
11.txt的0hEE9385是双字节“配”转UTF-8编码?

#60
cssnet2025-09-05 18:46
以下是引用吹水佬在2025-9-5 18:35:52的发言:

11.txt的0hEE9385是双字节“配”转UTF-8编码?


0hEE9385其实是51#的0hFEFE:

c1 = 0hFEFE
c2 = strconv(c1, 9)
c3 = REPLICATE(c2, 2000)
strtofile(c3, "11.txt")

只是随便选的一个空白字符。你也可以用0hA1A1的UTF-8,或者,任意可见字符的UTF-8去试,都行!

#61
sych2025-09-05 20:37
回复 3楼 吹水佬
PARAFORMAT2 STRUCT
  cbSize            DWORD    ?
  dwMask            DWORD    ?
  wNumbering        WORD     ?
  wEffects          WORD     ?
  dxStartIndent     DWORD    ?
  dxRightIndent     DWORD    ?
  dxOffset          DWORD    ?
  wAlignment        WORD     ?
  cTabCount         WORD     ?
  rgxTabs           DWORD MAX_TAB_STOPS dup(?)
  dySpaceBefore     DWORD    ?
  dySpaceAfter      DWORD    ?
  dyLineSpacing     DWORD    ?
  sStyle            WORD     ?
  bLineSpacingRule  BYTE     ?
  bOutlineLevel     BYTE     ?
  wShadingWeight    WORD     ?
  wShadingStyle     WORD     ?
  wNumberingStart   WORD     ?
  wNumberingStyle   WORD     ?
  wNumberingTab     WORD     ?
  wBorderSpace      WORD     ?
  wBorderWidth      WORD     ?
  wBorders          WORD     ?
PARAFORMAT2 ENDS
MAX_TAB_STOPS = 32
可以用BINTOC()转换,BINTOC(num,"xRS") x:1(BYTE)或2(WORD)或4(DWORD)
请问:结构长度188是怎么算出来的?
#62
吹水佬2025-09-05 20:47
以下是引用cssnet在2025-9-5 18:46:41的发言:



0hEE9385其实是51#的0hFEFE:

c1 = 0hFEFE
c2 = strconv(c1, 9)
c3 = REPLICATE(c2, 2000)
strtofile(c3, "11.txt")

只是随便选的一个空白字符。你也可以用0hA1A1的UTF-8,或者,任意可见字符的UTF-8去试,都行!

可能是RichEdit当前不支持这个UTF-8编码字符。
如果只是想见到的是空白的行列,不用转来转去,可以直接输入:
        richedit.SelText = REPLICATE(" ",2000)
        richedit.SelText = REPLICATE(""+0hA1A1,2000)
#63
吹水佬2025-09-05 20:57
回复 61楼 sych
程序代码:

DWORD 占4字节 4*8=32
WORD  占2字节 2*13=26
BYTE  占1字节 1*2=2
数组 DWORD MAX_TAB_STOPS dup(?) 4*32=128
32+26+2+128=188
#64
sych2025-09-05 22:06
明白了,谢谢
#65
sych2025-09-05 22:08
以下是引用吹水佬在2025-9-5 20:47:10的发言:


可能是RichEdit当前不支持这个UTF-8编码字符。
如果只是想见到的是空白的行列,不用转来转去,可以直接输入:
        richedit.SelText = REPLICATE(" ",2000)
        richedit.SelText = REPLICATE(""+0hA1A1,2000)

支持utf-8的
输入前需要sys(3101,65001)
#66
cssnet2025-09-05 22:56
以下是引用吹水佬在2025-9-5 20:47:10的发言:
可能是RichEdit当前不支持这个UTF-8编码字符。
如果只是想见到的是空白的行列,不用转来转去,可以直接输入:
        richedit.SelText = REPLICATE(" ",2000)
        richedit.SelText = REPLICATE(""+0hA1A1,2000)


大佬可能没留意前边的讨论,若采用sych帖子的那个API,ANSI版本,半角空格0h20、全角空格0hA1A1都不行,唯独0hFeFe这个特别的空白字符能够正常计算,很是邪门!
不过,一旦切换至UTF-8领空,任何字符都试过了,尽皆扑街,行字符数目全都返回1或2!

#67
吹水佬2025-09-06 05:54
回复 66楼 cssnet
是想用一个字符先填充来算行列数吗?
这样的话,要用实际的字串来填才相对准确点。
因每行的字不同而字数也不一样。
还有靠边框的字可能有不完整的绘制(边框挡住)。
#68
sych2025-09-06 06:45
以下是引用cssnet在2025-9-5 22:56:23的发言:



大佬可能没留意前边的讨论,若采用sych帖子的那个API,ANSI版本,半角空格0h20、全角空格0hA1A1都不行,唯独0hFeFe这个特别的空白字符能够正常计算,很是邪门!
不过,一旦切换至UTF-8领空,任何字符都试过了,尽皆扑街,行字符数目全都返回1或2!

估计是你代码有错误,代码贴出来看看
#69
csyx2025-09-06 08:24
感觉用不上这么多复杂的api吧?
简单的用个取插入点光标位置api,加上richtext控件本身的属性和方法,就能计算出每个字符的客户区显示坐标
有了坐标,,每行字符数和行高不就加减法吗?
#70
吹水佬2025-09-06 09:17
以下是引用csyx在2025-9-6 08:24:33的发言:

感觉用不上这么多复杂的api吧?
简单的用个取插入点光标位置api,加上richtext控件本身的属性和方法,就能计算出每个字符的客户区显示坐标
有了坐标,,每行字符数和行高不就加减法吗?

这也是要先填满一框才能操作,只是算法不同,要面对的问题都差不多。
#71
吹水佬2025-09-06 09:30
vfp好像只能用richedit的1.0版本(riched32.dll),对UTF-8不好整。
2.0/3.0(riched20.dll)、4.0( msftedit.dll),vfp好像用不了?
MS有提到动态创建方法和VS开发平台控件

#72
吹水佬2025-09-06 10:00
以下是引用sych在2025-9-5 22:08:28的发言:


支持utf-8的
输入前需要sys(3101,65001)

这样正常
        sys(3101,65001)
        this.edit.SelText = STRCONV("abc字字123",9)
        sys(3101,0)
试试下面的,好像不行?
        STRTOFILE(0hC58B,"utf8.txt")
        sys(3101,65001)
        this.edit.SelText = FILETOSTR("utf8.txt")
        sys(3101,0)
或者:
        sys(3101,65001)
        this.edit.SelText = 0hC58B  &&或者 ""+0hC58B
        sys(3101,0)

#73
sych2025-09-06 10:02
只有本站会员才能查看附件,请 登录

不知道这个是哪个版本
#74
吹水佬2025-09-06 10:25
回复 73楼 sych
richtx32.ocx
2003的产品
只有本站会员才能查看附件,请 登录

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



#75
吹水佬2025-09-06 10:29
UTF-8的编码有多种,这个richtx32.ocx可能不兼容一些UTF-8的编码。

#76
sych2025-09-06 10:33
以下是引用吹水佬在2025-9-6 10:00:30的发言:


这样正常
        sys(3101,65001)
        this.edit.SelText = STRCONV("abc字字123",9)
        sys(3101,0)
试试下面的,好像不行?
        STRTOFILE(0hC58B,"utf8.txt")
        sys(3101,65001)
        this.edit.SelText = FILETOSTR("utf8.txt")
        sys(3101,0)
或者:
        sys(3101,65001)
        this.edit.SelText = 0hC58B  &&或者 ""+0hC58B
        sys(3101,0)

这就是我以前的帖子说到的richtext的局限性,可以在richtext里复制、粘贴、但不能在控件外处理UNICODE字符
STRTOFILE(0hC58B,"utf8.txt")
sys(3101,65001)
this.edit.SelText = FILETOSTR("utf8.txt")
sys(3101,0)
改为下面的代码,只能用loadfile方法处理
STRTOFILE(0hc58b,"utf8.txt")
sys(3101,65001)
this.edit.loadFile("utf8.txt",1)
sys(3101,0)

#77
吹水佬2025-09-06 11:02
回复 76楼 sych
这样也可以了,有点像从文件流输入。
但我这测试文件要带“BOM”才正常。STRTOFILE(0hC58B,"utf8.txt")不正常,STRTOFILE(0hC58B,"utf8.txt",4)正常。
#78
sych2025-09-06 11:11
STRTOFILE(0hC58B,"utf8.txt")
sys(3101,65001)
这两句顺序颠倒一下就可以了,这样就不需要写文件头了(不知道是不是这样,我这边不存在顺序和文件头的问题)

另外如果用字符串,可以用消息传递
Declare INTEGER SendMessage in WIN32API as sendmessage_c2 INTEGER hwnd , INTEGER wMsg , INTEGER wParam , string lParam
#defi EM_REPLACESEL 0xC2
SendMessage_c2(this.edit.hwnd, EM_REPLACESEL, 1, 0hefbbbf+0hc58b)  &&这里要带上标记

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

#79
吹水佬2025-09-06 12:09
我这测试带“BOM”可不用sys(3101,65001),不带“BOM”用sys(3101,65001)也不正常。
这个richtext是不是有点老了
只有本站会员才能查看附件,请 登录
#80
cssnet2025-09-06 13:48
只是随手测试,没有刻意保存、记录,现在还原不了啦。
应该是我代码的问题,可能搞着搞着就给搞乱了!太乱了,代码就不贴了。
之前以为0hA1A1无法正确计算行数,今天一查,可能是行距的问题。
吹版提醒过的:Richtextbox1.text = "xxx",会冲掉之前的一切设置,结果我不小心给忽略了,弄得乱七八糟。
修正一下:填充0hA1A1跟0hFEFE,效果是一样的。个人推荐用前者,因绝无风险!
至于行数统计,不知是否错觉,当拉大拉小表单,Anchor=15,导致Richtextbox1控件高度改变,半行有可能也会被计入行数。
#81
吹水佬2025-09-06 14:20
以下是引用sych在2025-9-6 11:11:27的发言:

另外如果用字符串,可以用消息传递
Declare INTEGER SendMessage in WIN32API as sendmessage_c2 INTEGER hwnd , INTEGER wMsg , INTEGER wParam , string lParam
#defi EM_REPLACESEL 0xC2
SendMessage_c2(this.edit.hwnd, EM_REPLACESEL, 1, 0hefbbbf+0hc58b)  &&这里要带上标记

UTF-8字串输入在C测试正常,偷点懒贴代码,很简单的。
vfp测试代码:
SetTextUtf8(this.edit.hWnd, STRCONV(0h0D0A+"abc字字123"+0h0D0A+"si",9)+0hC58B)
C代码:
程序代码:

//输入UTF-8字串
DLLIMPORT_C void SetTextUtf8(HWND hRichEdit, LPVOID* pTextUtf8)
{
    SETTEXTEX stx;                                                          //SETTEXTEX {DWORD flags; UINT codepage}
    stx.flags    = ST_SELECTION;                                            //#define ST_SELECTION  2
    stx.codepage = CP_UTF8;                                                 //#define CP_UTF8       65001
    SendMessage(hRichEdit, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)pTextUtf8);  //#define EM_SETTEXTEX  0x0461
}


#82
吹水佬2025-09-06 14:52
顺便也试一下保存UTF-8字串,再偷点懒贴代码,也很简单的。
vfp测试代码:
程序代码:

        textUtf8 = REPLICATE(0h00, 10000)    &&要足够大
        len = GetTextUtf8(this.edit.hWnd, @textUtf8, 10000)
        STRTOFILE(LEFT(textUtf8,len), "utf8.txt", 4)

C代码
程序代码:

//保存为UTF-8字串
DLLIMPORT_C UINT GetTextUtf8(HWND hRichEdit, LPVOID* pBuffer, UINT bufferSize)
{
    GETTEXTEX gtx;  //GETTEXTEX {DWORD cb; DWORD flags; UINT codepage; LPCSTR lpDefaultChar; LPBOOL lpUsedDefChar;}
    gtx.cb            = bufferSize;
    gtx.flags         = GT_USECRLF; //#define GT_USECRLF    1
    gtx.codepage      = CP_UTF8;    //#define CP_UTF8       65001
    gtx.lpDefaultChar = NULL;
    gtx.lpUsedDefChar = NULL;
    return SendMessage(hRichEdit, EM_GETTEXTEX, (WPARAM)&gtx, (LPARAM)pBuffer); //#define EM_GETTEXTEX  0x045E
}


#83
吹水佬2025-09-06 17:35
完善一下上面保存的过程,增加获取缓冲字节数的函数,不用去乱猜。
vfp代码:
程序代码:

        ** 保存UTF-8字串
        size = GetTextLenGthex(this.edit.hWnd)
        textUtf8 = REPLICATE(0h00, size)
        len = GetTextUtf8(this.edit.hWnd, @textUtf8, size)
        STRTOFILE(LEFT(textUtf8,len), "utf8.txt", 4)

C代码
程序代码:

//获取RichEdit编辑的UTF-8字节数
DLLIMPORT_C UINT GetTextLenGthex(HWND hRichEdit)
{
    GETTEXTLENGTHEX gtl;            //GETTEXTLENGTHEX {DWORD flags; UINT  codepage;}
    gtl.flags    = GTL_NUMBYTES;    //#define GTL_NUMBYTES  16      返回字节数
    gtl.codepage = CP_UTF8;         //#define CP_UTF8       65001   UTF-8编码
    return SendMessage(hRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0); //#define EM_GETTEXTLENGTHEX    0x045F
}

#84
cssnet2025-09-06 22:54
回到顶楼“1行有几列?”的原始问题。
在UTF-8领空,我遇到了好些莫名其妙的意外状况,挺随机的,也无法复现:
有时会返回1,有时会返回1000(可能因为REPLICATE(0hA1, 1000)填充了1000次的缘故吧)。
退一步想想,其实我的需求,也并非要那么精确,精确到“X列 * Y行 = N个字符”的地步;权衡再三,最终决定放弃治疗,还是用回自己先前不那么精确的“神秘参数”版本罢……

这一次的讨论过程,确实是学到了不少新知识,在此特别感谢吹版和sych二位英雄的精彩展示!
尤其下边这一帖,属实是意外惊喜!——

以下是引用吹水佬在2025-9-5 07:36:15的发言:
LockScreen 是 Lock vfp窗口,对 richedit 可能无效。
试试:
SendMessage(hRichEdit, WM_SETREDRAW, 0, 0)    && Lock
SendMessage(hRichEdit, WM_SETREDRAW, 1, 0)    && UnLock



#85
吹水佬2025-09-06 23:10
回复 84楼 cssnet
用一个字符来填充来算不太精确,除非字体固定用等宽字体。

#86
吹水佬2025-09-07 10:02
这样试试:
输入要显示的字串-->取第一屏字数-->显示一屏字
下一屏就是去掉上屏字数的字串继续上面的步骤。
程序代码:

oRichEdit = this.edit
hRichEdit = oRichEdit.hWnd

DECLARE long SendMessage   IN user32 long,long,long,long
DECLARE long SendMessage   IN user32 as SendMessage2 long,long,long,string@
DECLARE long SendMessage   IN user32 as SendMessage3 long,long,string@,string@
DECLARE long SendMessageW  IN user32 long,long,long,string@
DECLARE long GetClientRect IN user32 long,string@
#define WM_SETREDRAW        0x000B
#define EM_SETPARAFORMAT    0x0447    &&(WM_USER + 71)
#define ST_SELECTION        2
#define CP_UTF8             65001
#define EM_SETTEXTEX        0x0461
#define WM_SETTEXT          0x000C
#define EM_GETLINE          0x00C4

SendMessage(hRichEdit, WM_SETREDRAW, 0, 0)        && LockScreen
oRichEdit.text = ""
**用户区高
rc = REPLICATE(0h00,16)
GetClientRect(hRichEdit, @rc)
nClientHeight = CTOBIN(RIGHT(rc,4),"4rs")
**行高
nLineHeight = FONTMETRIC(1,oRichEdit.font.name, MTON(oRichEdit.font.size)) + 5
&&设置行高
pf = BINTOC(188,"4RS") + BINTOC(256,"4RS") + REPLICATE(0h00,156);
   + BINTOC(nLineHeight*15,"4RS") + 0h0000 + BINTOC(4,"1RS") + REPLICATE(0h00,17)
SendMessage2(hRichEdit, EM_SETPARAFORMAT, 0, @pf)
**用户区行数
nClientLines = INT(nClientHeight / nLineHeight)
**填字串(UTF-8
cText = REPLICATE("abc字字123",1000)
textUtf8 = STRCONV(cText,9)
stx = BINTOC(ST_SELECTION,"4rs") + BINTOC(CP_UTF8,"4rs")
SendMessage3(hRichEdit, EM_SETTEXTEX, stx, textUtf8)
oRichEdit.selstart = 0
**算计一屏
nSize   = 2048    && 要足够大
cBuffer = BINTOC(nSize,"4rs") + REPLICATE(0h00,nSize)
i = 0
nWordCount = 0
nWordLine  = SendMessageW(hRichEdit, EM_GETLINE, i, @cBuffer)
DO WHILE (nWordLine > 0) AND (i < nClientLines)
    nWordCount = nWordCount + nWordLine
    i = i+1
    nWordLine = SendMessageW(hRichEdit, EM_GETLINE, i, @cBuffer)
ENDDO
**显示一屏
oRichEdit.text = ""
SendMessage3(hRichEdit, EM_SETTEXTEX, stx, STRCONV(LEFTC(cText,nWordCount),9))
oRichEdit.selstart = 0
SendMessage(hRichEdit, WM_SETREDRAW, 1, 0)        && UnLockScreen



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

#87
吹水佬2025-09-07 10:19
这样做总觉得有点另类
通常都是控制字串的屏首位置字符(或自动翻屏,好像按一下PgDn)

#88
cssnet2025-09-07 14:59
其实,最初对RichEdit/RichTextbox发生兴趣,还是因为该死的 Markdown。

对于 Markdown,我有一个大胆想法。试考虑这个闭环:

假设日后我们能找到一组相对完美的可逆的双向转换代码库:

RTF --> Markdown
Markdown --> RTF

那么,我们便可以利用 RichEdit 来编辑/显示 Markdown 格式文档了!

让我来分析一下流程:

===============
1、打开一个 .MD 文档;
2、调用“Markdown --> RTF”,将 Markdown 标记转换为 RTF 标记;
3、利用RichEdit,WYSIWYG 编辑此 RTF 文档,以类似 Typora 的方式!而最终用户根本不可能知晓:此时此刻,我们内部编辑处理的,其实是 RTF 标记文档;
4、编辑结束,存档。此时调用“RTF --> Markdown”,将最终文档保存为 .MD 格式。
===============

整个文档编辑、修改的过程,我们的编辑器,根本无需真的去解析、渲染 Markdown 格式文档,一直都处在最古老的 RTF 领空!然而却能够实现近乎完美的 Markdown 实时 WYSIWYG 编辑!

这是最初的构想。

当然,这一切取决于如下必要前提——

在这个星球上,存在一组相对完美的、可逆的“Markdown <--> RTF”双向转换代码库。

可惜目前暂未能找到合适的转换库。Pandoc 据说可以实现双向转换,还是开源的,可惜太大啦,100MB+,且是64位,我试了一个多星期,都无办法将其编译成32位、小于10MB的DLL,以供VFP代码调用。

以上。就是我琢磨 RichEdit 的深层的原因所在啊!
#89
吹水佬2025-09-07 16:01
回复 88楼 cssnet
你这是想大工程搞小动作。
如果你首贴是这个也许我不会去理会RICH EDIT控件。
还以为只是简单的几行几列的小事通过EDIT控件解释一下就算。
到头来还是要花了一个晚上通读RICH EDIT的消息和结构,看来是白花了。
#90
cssnet2025-09-07 16:23
以下是引用吹水佬在2025-9-7 16:01:45的发言:

你这是想大工程搞小动作。
如果你首贴是这个也许我不会去理会RICH EDIT控件。
还以为只是简单的几行几列的小事通过EDIT控件解释一下就算。
到头来还是要花了一个晚上通读RICH EDIT的消息和结构,看来是白花了。


什么“大工程”?哦,明白了,吹版可能误解了。我的意思是:

如果能找到一个DLL库,只需包含这两个函数:MD2RTF 和 RTF2MD,且此二函数可逆;那么,现有的Richtextbox控件,无需任何额外改造,就能够支持在VFP中显示与编辑 Markdown 格式的文档了!
“RTF 《==》 MD”转换的库,大把,网上是现成的啊,只是大多不是C,也不可逆,难以编译成32位DLL而已。
话说,那也只不过私下里随便那么一想,并无什么雄心壮志。

RichEdit是目前仍在维护的古早工程里,实际用到的控件;而这一个行、列计算的遗留Bug,也是一直未得到精确解决方案的实际遗留问题。


[此贴子已经被作者于2025-9-7 17:46编辑过]

#91
cssnet2025-09-07 17:01
程序代码主动地往Richtextbox的客户区堵塞额外字符,讲真,一直都有些心理排斥——主要这样子处理,可能不太安全。

嗯……在表单看不见的负X值区域,复制出一份孖崽,一个不可见的影子控件,反倒是可以为所欲为:反正永远不会影响正常的界面显示,唯,多浪费一份系统资源就是啦。

在影子控件中,可随意加载任意中、英、数字符,这也更贴近实战需求;且,不需[保存/恢复]现场,这也是基于性价比方面考量的一大优势。

故而,这种直接、粗暴的方式,可能是相对合理、方便、快捷的方式。
#92
cssnet2025-09-08 10:08
以下是引用cssnet在2025-9-7 16:23:13的发言:
如果能找到一个DLL库,只需包含这两个函数:MD2RTF 和 RTF2MD,且此二函数可逆;那么,现有的Richtextbox控件,无需任何额外改造,就能够支持在VFP中显示与编辑 Markdown 格式的文档了!
“RTF 《==》 MD”转换的库,大把,网上是现成的啊,只是大多不是C,也不可逆,难以编译成32位DLL而已。
话说,那也只不过私下里随便那么一想,并无什么雄心壮志。


这星球上,原本最有希望实现“RTF 《==》 MD 双向转换”的代码库,是微软原厂出品的开源的文档转 Markdown 工具“Markitdown”:
https://

结果你猜怎么着?微软似乎自己已放弃了古老的 RTF 格式!微软自己出品的“万能 Markdown 转换工具”,什么阿猫阿狗都能支持,唯独不支持 RTF !
嘤嘤嘤。
#93
吹水佬2025-09-08 11:00
不能逆向说明标记没有完全对等,转换也不能是100%的样子。
既然是开源的东西,文件格式应该是公开的。自己应该可以写转换代码,文件结构确定了就是对号入座。


#94
cssnet2025-09-08 11:20
以下是引用吹水佬在2025-9-8 11:00:27的发言:

不能逆向说明标记没有完全对等,转换也不能是100%的样子。
既然是开源的东西,文件格式应该是公开的。自己应该可以写转换代码,文件结构确定了就是对号入座。


自己写转换代码,难度太大!且,我对于Python几乎是“文盲”。

我曾经“盯”上一个开源代码:
https://
国内访问时常被墙,也不知现在还能否访问?
若能看到先前的简介——意不意外?惊不惊喜?
那其实是我老人家向作者提出的一系列建议。
只可惜收到了最终代码后,用Python编译出DLL,一测试:

完犊子!这东东并不支持中文!

Python的东西,其实至少有80%以上,是你抄我,我抄你的,真正原创转换算法的极少,大多无非拿人家大厂的算法代码,再外边套一层壳,取个漂亮名字,就可以重新放上github了。
最后一封电邮,我不无遗憾地回复:

Since my specific need was for lossless, reversible conversion between the formats, and current solutions inevitably introduce significant discrepancies, I've concluded this approach isn't viable for my use case.

#95
cssnet2025-09-09 12:34
RTF 标记,远比 Markdown 标记复杂,在 RTF 的领空处理 Markdown ,只要严格限定代码可用的格式标记,将其圈牢在最基本、最通用、最常用的若干 Markdown 标记子集以内——理论上,这方案似乎是可行的。

不过,深入接触过 RTF 内部标记后,我们将发现:这种“投机取巧”的可行性,微乎其微。

事关,二者非常难以一一对应,除非微软等大厂出手。

还有,RTF内部字符编码是ANSI,而 Markdown 则是UTF-8,这二者之间的相互转换,可能会有损。

此外,不得不说,RTF标记,其实是一种相当丑陋、臃肿、恶心、落后的标记语言,早该人道毁灭淘汰掉啦!
#96
wxzd1232025-09-14 19:08
cssnet老师你好,最后用的什么代码,能自动设置字号显示整个可见区域不出滚动条,麻烦贴出代码,谢谢
#97
cssnet2025-09-15 07:37
Thisform.Richtexbox1.ScrollBars = 0

只是在计算行、列字数时,一直存在误差(其实这本身也正常的:一个回车右边就会少很多字,一个纯空行能少更多!若真要逐字符地抠字数,似乎也无必要!),计算时会主动地一屏减掉了一些字符数目。
#98
wxzd1232025-09-15 09:19
cssnet你好,没有明白你的意思,大体上显示即可,不必太精确,不知用的那段代码
#99
cssnet2025-09-15 10:17
以下是引用wxzd123在2025-9-15 09:19:00的发言:

cssnet你好,没有明白你的意思,大体上显示即可,不必太精确,不知用的那段代码


51#和86#两位英雄的代码,都行啊!
#100
wxzd1232025-09-15 18:40
都运行不了
#101
cssnet2025-09-15 23:10
以下是引用wxzd123在2025-9-15 18:40:10的发言:

都运行不了


造谣!那是两位英雄精心调配的得意佳作!我老人家读了都真心景仰的!
你要将代码中的对象名改为自己表单中的实际名称,倘若一字不漏地照抄,那当然得出错!
123