Using Windowless RichEdit Controls

本文介绍如何使用Microsoft较少文档记录的无窗口RichEdit控件。通过创建一个简单的包装类CRichDrawText,实现对格式化文本的大小调整及绘制。文章详细解释了ITextHost接口的实现细节及如何通过ITextServices接口进行文本尺寸计算和绘制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概要:这是一片入门的文章,想做IM的无句柄聊天框,这只是一个很好的起步。

By David Kinder | 10 Oct 2006
 
How to use the windowless RichEdit control, one of Microsoft's less well-documented APIs.

Introduction

Windowless controls have been around since the dawn of ActiveX, and are very useful when you do not want (or cannot have) a separate HWND for every control element in your user interface. This article presents the class CRichDrawText, a simple wrapper around the windowless version of the RichEdit control, allowing formatted text to be sized and drawn on a device context.

Background

As part of an application I was working on (the Windows version of Inform 7), I had a need to draw sections of formatted text in a CScrollView derived class. As the application was already making heavy use of the RichEdit control, that seemed the perfect control to use. Experiments with the FormatRange and DisplayBand functions in CRichEditCtrl were not satisfactory: what I needed was a proper windowless control.

When I actually came to try to use the windowless RichEdit control, however, life became more interesting. The MSDN documentation in this area is very sparse. Most of the useful information I found buried away in an MSDN Knowledge Base article, Q270161, with the remainder determined by trial and error. Hopefully, if you need a windowless RichEdit control, you won't have to do as much searching as I did.

Using the code

To use the class in your code, include RichDrawText.h and declare an instance of the CRichDrawText class somewhere.

To find out how much vertical space is needed for the contents of the CRichDrawText, call SizeText, passing in a device context and a rectangle whose width is the width you want the text to be formatted for. To actually draw the text, call DrawText, passing in a device context and the bounding rectangle.

To actually set up the formatted text, CRichDrawText provides two methods. SetText replaces all the text in the windowless control with the given Unicode string, and Range returns an ITextRange COM interface pointer for a range of text in the windowless control. ITextRange is part of the TOM (Text Object Model) which is an under-appreciated part of the RichEdit control, providing faster and more functional text manipulation than is available through the usual RichEdit EM_ messages. A description of the TOM is outside of the scope of this article, but the example program provided with this article, along with the MSDN documentation, should be enough to get you started.

The ITextHost implementation

In the CRichDrawText constructor, we create a windowless RichEdit control by calling the CreateTextServices function, to which we have to pass an implementation of the ITextHost interface. The CRichDrawText class provides a minimal implementation, which does the least possible to be able to size and draw the rich text. Which methods to implement was determined simply by seeing which were called using the debugger. Most of these methods are straightforward, however:

Collapse | Copy Code
HRESULT CRichDrawText::XTextHost::TxNotify(DWORD iNotify, void *pv)
{
  return S_OK;
}
The TxNotify implementation must return S_OK, even if it does nothing with the notification messages it is passed. If it returns an error code then the sizing and drawing calls will fail.

Another catch for the unwary is TxGetCharFormat which must return a pointer to a CHARFORMATW structure, not a CHARFORMAT: the Unicode version of the structure must be used, even on Windows 95.

What CreateTextServices returns

Having successfully called CreateTextServices we now have an IUnknown COM pointer, which according to MSDN, we can now use QueryInterface on to get an ITextServices COM pointer. However, my initial attempts at this all failed: I always got E_NOINTERFACE back from the QueryInterface call.

The problem turns out to be that the IID_ITextServices linked in from riched20.lib is wrong. Thanks, Microsoft... The code linked from Knowledge Base article Q270161 contains the correct IID:

Collapse | Copy Code
const IID IID_ITextServices = {
  // 8d33f740-cf58-11ce-a89d-00aa006cadc5
  0x8d33f740, 0xcf58, 0x11ce, {0xa8, 0x9d, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5}
};
When we include this in the CRichDrawText source file (where it will be linked in preference to the one in riched20.lib), we are able to get back an ITextServices COM pointer.

Calling ITextServices methods

Having implemented ITextHost and obtained an ITextServices interface, we are now ready to actually call the methods to size and draw text.

The CRichDrawText::SizeText method calls ITextServices::TxGetNaturalSize to calculate the required height. Trial and error has had to be used here to determine what all the arguments should be:

  • hicTargetDev and ptd are both NULL, even though the documentation does not say that null values are allowed. For drawing onto a display device context, it is not easy to see what else could be passed.
  • psizelExtent is a pointer to a SIZEL structure with both member variables set to -1. MSDN claims that this argument is "currently unused" but this seems not to be the case: passing in NULL leads to an access violation in riched20.dll. Passing in zeros (or small values) seems to affect the returned sizing rectangle. Given the lack of documentation, using {-1,-1} seems a reasonable approach.

The CRichDrawText::DrawText method calls ITextServices::TxDraw to draw the rich text. The arguments to this method have proved slightly less problematic. However:

  • pvAspect is NULL, though the documentation does not say if this is allowed. Given that the argument's type is void* and the documentation for it says "pointer for information for drawing optimizations", this might be worthy of some sort of award for unhelpful documentation.
  • hicTargetDev and ptd are again both NULL.
  • lViewId is zero. MSDN claims that this argument is not used, but in the Platform SDK TextServ.h defines enum TXTVIEW as "useful values for TxDraw lViewId parameter".

Conclusion

With poorly documented interfaces, the hardest problem is usually getting any of it to work in the first place. I hope that this article will give anyone playing with the windowless RichEdit control enough of a start that they can make it do what they need. Anyway, if it was easy, what would be the fun in that?

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

David Kinder


United Kingdom

 

原文出处:http://www.codeproject.com/KB/edit/RichDrawText.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值