TRichTextBox – A universal RichTextBox which can display animated images and more
TRichTextBox – A universal RichTextBox which can display animated images and more
Summary
Displaying images in a RichTextBox is a common requirement, with limited solutions available. Pasting through Clipboard or embedding into RTF content only supports static images. This article describes how to insert Windows control objects into a RichTextBox and use them to host images. It is a straightforward -- but flexible and usable -- solution. An attached demo project shows a simple example of the result:
Introduction
Quite often here at EE, I come across questions about how to insert images into a RichTextBox. Especially for developers working with Instant Messaging (IM) projects, emotion icons are inevitable elements besides handling chat text messages. A crying picture sounds much louder than pale text like "I'm crying", "I want to cry". However, RichTextBox is basically the only choice left for programmers, unless you write your own reader or browser. Using a web browser control is actually not a bad option. We may discuss it in the following articles (but not this one).
There are two "standard" ways of displaying static images in a RichTextBox. The CodeProject article, Insert Plain Text and Images into RichTextBox at Runtime, describes two ways of implementing in much detail:
- Copying an image to the Clipboard and pasting the data into the target 
RichTextBox. - Reading the RTF specification and inserting image data through a metafile wrapped with RTF tags.
 
Molesting the content of Clipboard is really annoying, and not good programming practice. For example, I'm used to copying words as a backup while typing, and I'll definitely copy the whole article again before I post it to a forum. In case anything goes wrong, I have the Clipboard as an emergency backup tool.
Actually, it's trivial to restore the original Clipboard data after pasting the image, and a responsible developer should do it:
public void InsertImage()
{
...
string lstrFile = fileDialog.FileName;
Bitmap myBitmap = new Bitmap(lstrFile); //keep the clipboard data before set image in it.
object orgData = Clipboard.GetDataObject Clipboard.SetDataObject(myBitmap); DataFormats.Format myFormat = DataFormats.GetFormat (DataFormats.Bitmap); if(NoteBox.CanPaste(myFormat)) {
NoteBox.Paste(myFormat);
}
else {
MessageBox.Show("The data format that you attempted site" +
" is not supportedby this control.");
}
//Restore the original data.
Clipboard.SetDataObject(orgData )
...
}
The fatal problem of both of the above solutions is that they disable the animation of a GIF image. An animated GIF file contains multiple frames of images. "Copy & Paste" just reads the first frame of the image so the animation stops. While "RTF tagging" transforms the image data into a huge string, and the RTF specification does not extend enough to support getting the next active frame from a string.
Insert Windows Controls
Picture boxes can display animated images. And a user control can contain other controls. So there is nothing unconventional about inserting controls into a RichTextBox for any possible purpose. There are two issues that need to be addressed before it would be working:
- First, an inner control stays still in the parent control, and...
 - Second, the input text will go behind the control instead of wrapping around it.
 
We need to make inner controls move along with the text while the RichTextBox is scrolling, and we need to leave enough space for the inner controls to prevent overlapping -- and covering up the nearby text as shown here:
To resolve the above issues, the start point of the text can be used as a reference point to decide the control's position relative to the text. When the content moves from PosA to PosB, the PictureBox must move to a new position as well, which is easily calculated:
thePictureBox.newPosition = PosB - PosA + thePictureBox.oldPosition = PosB - Delta
The RichTextBox.GetPositionFromCharIndex method allows us to retrieve the beginning position (PosA) of the text with a character index of zero. We need to store this value and, in the VScroll event, use the same method to retrieve the new start position (PosB) of the text. Then we can set the new position of the inner controls.
The call to GetPositionFromCharIndex is a little expensive. Consider that tens or even hundreds of images could be inserted into the RichTextBox during a session of chat, so frequent calls to the function would make scrolling very sluggish.
An alternative call to get the new position is a Win32 API GetScrollPos(). In most cases, the value obtained from GetScrollPos() is almost exactly the negated value to the one coming from GetPositionFromCharIndex... with a couple of points differentiation, which makes no difference to the naked eye. However, further tests reveal that the number returned from GetScrollPos() does not change if the user clicks on the slider bar and drags. The new position value can only be retrieved after the user releases the mouse button. So the pictures do not move while scrolling, and then they fly to the new positions only after the scrolling stops. We have to send messages to notify the position change while dragging. So we must add some more coding logic. Finally, the call to get the position in VScroll is:
private void TRichTextBox_VScroll(object sender, EventArgs e)
{
Point pt = new Point();
SendMessage(this.Handle, EM_GETSCROLLPOS, 0, ref pt); foreach (MetaInfo one in ControlList)
{
one.TheControl.Location =
new Point(one.TheControl.Location.X, -pt.Y - one.DeltaY);
}
}
Using the code
All the deductive procedure is to make using the technique easy and simple. Firstly, derive your RichTextBox from TRichTextBox in Form1.Designer.cs:
private Trestan.TRichTextBox richTextBox1;
this.richTextBox1 = new Trestan.TRichTextBox();
Secondly, wherever you need to use a PictureBox to display an image, simply call richTextBox1.AddControl to add it in the list:
PictureBox thePic = new PictureBox();
thePic.Image = theImage;
thePic.Size = theImage.Size;
this.richTextBox1.AddControl(thePic);
That's it! TRichTextBox takes care of everything else for you.
Bonus / Notes
- GDI+ seems more delicate than GDI functions. Some images can be loaded by GDI functions, but will crash in GDI+. So a simple sanity check is called before setting the image to 
PictureBox, in which multiple frames are read one by one, and the image will be discarded if any exception is caught. - A self-management function 
RemoveSome(), which will automatically truncate messages received in earlier time using the length of the text and the number of images received as thresholds. - Most interestingly, you are not limited to adding only 
PictureBoxcontrols into aTRichTextBox; you can insert virtually any kind of control into it. And they will all scroll with the text as an integrated part of the content. As illustrated in the following picture, a button is affixed to each line of the message. Thus theTRichTextBoxgains more potential to become a client side reader in a P2P forum with no centralized website. 
Final result:
License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
TRichTextBox – A universal RichTextBox which can display animated images and more的更多相关文章
- Windows-universal-samples学习笔记系列五:Custom user interactions
		
Custom user interactions Basic input Complex inking Inking Low latency input Simple inking Touch key ...
 - 设备管理 USB ID
		
发现个USB ID站点,对于做设备管理识别的小伙伴特别实用 http://www.linux-usb.org/usb.ids 附录: # # List of USB ID's # # Maintain ...
 - QML Image Element
		
QML Image Element The Image element displays an image in a declarative user interface More... Image元 ...
 - Building Applications with Force.com and VisualForce (DEV401) (二二):Visualforce Componets (Tags) Library Part II
		
Dev401-023:Visualforce Pages: Visualforce Componets (Tags) Library Part II Apex:pageBlockTable1.A ...
 - universal image loader自己使用的一些感受
		
1.全局入口的Application定义初始化: ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Build ...
 - Insert Plain Text and Images into RichTextBox at Runtime
		
Insert Plain Text and Images into RichTextBox at Runtime' https://www.codeproject.com/Articles/4544/ ...
 - 如何使用 App Studio 快速定制你自己的 Universal Windows App
		
之前我为大家介绍过 App Studio 这只神器可以帮助大家快速制作一个 Windows Phone 8 的应用,今天之所以在写一篇关于 App Studio 的文章是因为,App Studio 经 ...
 - Animated Scroll to Top
		
Due to a number of requests, I'm writing a detail tutorial on how to create an animated scroll to to ...
 - Android中Universal Image Loader开源框架的简单使用
		
UIL (Universal Image Loader)aims to provide a powerful, flexible and highly customizable instrument ...
 
随机推荐
- Android 开发必备知识:我和 Gradle 有个约会
			
腾讯Bugly特约作者:霍丙乾 0.讲个故事 0.1 Ant,我还真以为你是只蚂蚁 真正开始近距离接触编程其实是在2012年,年底的时候带我的大哥说,咱们这个 app 发布的时候手动构建耗时太久,研究 ...
 - java生成压缩文件
			
在工作过程中,需要将一个文件夹生成压缩文件,然后提供给用户下载.所以自己写了一个压缩文件的工具类.该工具类支持单个文件和文件夹压缩.放代码: import java.io.BufferedOutput ...
 - 一种另类的解决URL中文乱码问题--对中文进行加密、解密处理
			
情景:在资源调度中,首先用户需要选择工作目标,然后跟据选择的工作目标不同而选择不同的账号和代理ip.处理过程如下:点击选择账号,在js中获取工作目标对工作目标进行两次编码(encodeURI(enco ...
 - Comet实现的网页聊天程序
			
“上一篇”介绍了我在c/s程序中用了那些技术,如今只谈c/s不谈b/s那未免out了,势必要写一写b/s的程序与大家共勉. 回忆做技术这些年,06年每天盯着“天轰穿”的视频不亦乐乎,估计那是一代程序员 ...
 - Smack Message扩展,添加自定义元素(标签)经验分享
			
Smack框架对XMPP协议进行了封装,从而方便与Openfire即时通信服务器做交互.说白了,Smack框架可以通过对象构造符合XMPP协议的XML字符串,避免手动拼接字符串. XMPP协议基本XM ...
 - 绝对干货:自定义msi安装包的执行过程
			
有时候我们需要在程序中执行另一个程序的安装,这就需要我们去自定义msi安装包的执行过程. 比如我要做一个安装管理程序,可以根据用户的选择安装不同的子产品.当用户选择了三个产品时,如果分别显示这三个产品 ...
 - try catch 与 throw拾遗
			
今天在微软虚拟学院看到的代码: var b = 0; try{ if (b == 0) { throw('NO NO!!'); } else { alert('OK OK'); }} catch(e) ...
 - java代码效率优化
			
[转载于http://blog.163.com/user_zhaopeng/blog/static/16602270820122105731329/] 1. 尽量指定类的final修饰符 带有fina ...
 - MVVM架构~Knockoutjs系列之对象与对象组合
			
返回目录 在面向对象的程序设计里,对象是核心,一切皆为对象,对象与对象之间的关系可以表现为继承和组合,而在Knockoutjs或者JS里,也存在着对象的概念,今天主要说一下JS里的对象及对象的组合. ...
 - 拥抱cnpm
			
在国内由于墙的原因,使用NPM安装模块经常会失败,要或在速度上会慢得跟蜗牛一样,这时候我们其实可以选择国内淘宝的NPM镜像,使用下面的命令来进行安装: npm install -g cnpm --re ...
 
			
		

