TRichTextBox – A universal RichTextBox which can display animated images and more

trestan, 7 Dec 2010 CPOL

   4.81 (13 votes)
1 2 3

4

5

4.81/5 - 13 votes
μ 4.81, σa 0.91 [?]
 
Rate:
Add a reason or comment to your vote: x
Adding a comment to your rating is optional
This article describes how to insert Windows control objects into a RichTextBox and use them to host images.

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:

Hide   Copy Code
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:

Hide   Copy Code
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:

Hide   Copy Code
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:

Hide   Copy Code
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:

Hide   Copy Code
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 PictureBox controls into a TRichTextBox; 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 the TRichTextBox gains more potential to become a client side reader in a P2P forum with no centralized website.

Final result:

<a href="https://pubads.g.doubleclick.net/gampad/jump?iu=/6839/lqm.codeproject.site/Desktop-Development/Edit-Controls/General&sz=300x250&c=736511"><img src="https://pubads.g.doubleclick.net/gampad/jump?iu=/6839/lqm.codeproject.site/Desktop-Development/Edit-Controls/General&sz=300x250&c=736511" width="300px" height="250px" target="_blank"/></a>

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的更多相关文章

  1. Windows-universal-samples学习笔记系列五:Custom user interactions

    Custom user interactions Basic input Complex inking Inking Low latency input Simple inking Touch key ...

  2. 设备管理 USB ID

    发现个USB ID站点,对于做设备管理识别的小伙伴特别实用 http://www.linux-usb.org/usb.ids 附录: # # List of USB ID's # # Maintain ...

  3. QML Image Element

    QML Image Element The Image element displays an image in a declarative user interface More... Image元 ...

  4. 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 ...

  5. universal image loader自己使用的一些感受

    1.全局入口的Application定义初始化: ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Build ...

  6. Insert Plain Text and Images into RichTextBox at Runtime

    Insert Plain Text and Images into RichTextBox at Runtime' https://www.codeproject.com/Articles/4544/ ...

  7. 如何使用 App Studio 快速定制你自己的 Universal Windows App

    之前我为大家介绍过 App Studio 这只神器可以帮助大家快速制作一个 Windows Phone 8 的应用,今天之所以在写一篇关于 App Studio 的文章是因为,App Studio 经 ...

  8. 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 ...

  9. Android中Universal Image Loader开源框架的简单使用

    UIL (Universal Image Loader)aims to provide a powerful, flexible and highly customizable instrument ...

随机推荐

  1. android知识杂记(三)

    记录项目中的android零碎知识点,用以备忘. 1.android 自定义权限 app可以自定义属于自己的权限: <permission android:description="s ...

  2. WebAPI的一种单元测试方案

    大家是如何对webApi写测试的呢? 1.利用Fiddler直接做请求,观察response的内容. 2.利用Httpclient做请求,断言response的内容. 3.直接调用webApi的act ...

  3. 一次外企QQ面试

    无忧上挂了简历,让个外企的hr约好面试,今天刚面完,整理出来给大家看看.难度不是很大,基本就是Asp.net Mvc 用到的东西,没有问数据库方面的. Part I – Frontend  1. Tr ...

  4. ngModel 值不更新/显示

    angular中的$scope是页面(view)和数据(model)之间的桥梁,它链接了页面元素和model,也是angular双向绑定机制的核心. 而ngModel是angular用来处理表单(fo ...

  5. 基于zepto的移动端日期+时间选择插件

    前段时间写了两个移动端的日期选择插件:轻量级移动端日期选择器,本来是为特定的场景中使用的,结果有同学反应不够灵活和强大.虽然我的看法是移动端的界面要尽可能简洁,功能要尽可能简单,但是难免还是会有各种各 ...

  6. 如何为编程爱好者设计一款好玩的智能硬件(七)——LCD1602点阵字符型液晶显示模块驱动封装(上)

    当前进展: 一.我的构想:如何为编程爱好者设计一款好玩的智能硬件(一)——即插即用.积木化.功能重组的智能硬件模块构想 二.别人家的孩子:如何为编程爱好者设计一款好玩的智能硬件(二)——别人是如何设计 ...

  7. iOS——Core Animation 知识摘抄(一)

    本文是对http://www.cocoachina.com/ios/20150104/10814.html文章的关键段落的摘抄,有需要的看原文 CALayer和UIView的关系: CALayer类在 ...

  8. 记录排查解决Hubble.Net连接Oracle数据库建立镜像库数据丢失的问题

    起因 前几天在弄Hubble连接Oracle数据库,然后在mongodb中建立一个镜像数据库; 发现一个问题,原本数据是11W,但是镜像库中只有6w多条; 刚开始以为是没运行好,又rebuild了一下 ...

  9. EF架构~在T4模版中自定义属性的getter和setter

    回到目录 T4模版为我们在ORM操作上提供了便捷,它很方便的可以对实体进行全局性的修改,之前我介绍过通过T4来为属性加默认性,而今天我主要告诉大家如何使用T4模版将getter,setter块改为自己 ...

  10. MySQL的Grant命令[转]

    本文实例,运行于 MySQL 5.0 及以上版本. MySQL 赋予用户权限命令的简单格式可概括为: grant 权限 on 数据库对象 to 用户 一.grant 普通数据用户,查询.插入.更新.删 ...