Click here to Skip to main content
12,070,171 members (30,024 online)
Click here to Skip to main content
Add your own
alternative version

Stats

258.3K views
6.2K downloads
95 bookmarked
Posted

Inserting images into a RichTextBox control (the OLE way)

, 1 Nov 2005
Rate this:
Please Sign up or sign in to vote.
This article shows how you can insert images, controls and ActiveX objects into a .NET RichTextBox control by using the OLE way. There are several samples about how it could be done, but all of them are in C++ and I needed it for managed code (C#).

Sample Image - MyExtRichTextBox.jpg

Introduction

This article shows how you can insert images, controls and ActiveX objects into a .NET RichTextBox control by using the OLE way, like explained in the Microsoft site. Unfortunately, it covers only the sample with a C++ source code, so I need to implement a similar solution in managed code (C#).

There are other related articles for inserting images and OLE objects into a RichTextBox, but they are using RTF codes, and I need a more specialized control suitable to be used for chat and to provide a way to insert emoticons, progress bars and images, and finally, recover them by getting their OLE handles or any object attribute.

Special thanks to Khendys Gordon for the article: "Insert Plain Text and Images into RichTextBox at Runtime" and John Fisher for his article: "Use IRichEditOle from C#".

Background

To achieve the solution, I need to use the P/Invoke (Platform Invoke) methods. I got a lot of information from pinvoke.net.

The first step to insert an OLE object into a RichTextBox is to get its IRichEditOle interface. It could be done, by sending the message EM_GETOLEINTERFACE to the control:

this.IRichEditOle = SendMessage(richEditHandle, EM_GETOLEINTERFACE, 0);

With this interface, you can insert objects through the REOBJECT struct. It is important to note the you can specify the insertion point, the aspect and the dwUser variable to store flags or any related information for this object, so you can recover it at any time for update.

//-----------------------
REOBJECT reoObject=new REOBJECT();
reoObject.cp = this._richEdit.TextLength;
reoObject.clsid = guid;
reoObject.pstg = pStorage;
reoObject.poleobj = Marshal.GetIUnknownForObject(control);
reoObject.polesite = pOleClientSite;
reoObject.dvAspect = (uint)(DVASPECT.DVASPECT_CONTENT);
reoObject.dwFlags = (uint)(REOOBJECTFLAGS.REO_BELOWBASELINE);
reoObject.dwUser = 1;
 
this.IRicEditOle.InsertObject(reoObject);
//-----------------------

For inserting images, you need to implement the interface IDataObject, in this case I named it myDataObject. This class is an OLE callback object that uses the FORMATETC and the STGMEDIUM structures to display the image, telling to the OLE container that this object is a GDI medium (TYMED_GDI) with a bitmap clipboard format (CF_BITMAP).

public class myDataObject : IDataObject
{
    private Bitmap mBitmap;
    public FORMATETC mpFormatetc;

    #region IDataObject Members

    private const uint S_OK = 0;
    private const uint E_POINTER = 0x80004003;
    private const uint E_NOTIMPL = 0x80004001;
    private const uint E_FAIL = 0x80004005;

    public uint GetData(ref FORMATETC pFormatetc, 
                        ref STGMEDIUM pMedium)
    {
        IntPtr hDst = mBitmap.GetHbitmap();

        pMedium.tymed = (int)TYMED.TYMED_GDI;
        pMedium.unionmember = hDst;
        pMedium.pUnkForRelease = IntPtr.Zero;

        return (uint)S_OK;
    }

    ...

    #endregion

    public myDataObject()
    {
        mBitmap = new Bitmap(16, 16);
        mpFormatetc = new FORMATETC();
    }

    public void SetImage(string strFilename)
    {
        try
        {
            mBitmap = (Bitmap)Bitmap.FromFile(strFilename, true);

            // Clipboard format = CF_BITMAP
            mpFormatetc.cfFormat = CLIPFORMAT.CF_BITMAP;
            // Target Device = Screen
            mpFormatetc.ptd = IntPtr.Zero;
            // Level of detail = Full content
            mpFormatetc.dwAspect = DVASPECT.DVASPECT_CONTENT;
            // Index = Not applicaple
            mpFormatetc.lindex = -1;
            // Storage medium = HBITMAP handle
            mpFormatetc.tymed = TYMED.TYMED_GDI;
        }
        catch
        {
        }
    }

    public void SetImage(Image image)
    {
        try
        {
            mBitmap = new Bitmap(image);

            // Clipboard format = CF_BITMAP
            mpFormatetc.cfFormat = CLIPFORMAT.CF_BITMAP;
            // Target Device = Screen
            mpFormatetc.ptd = IntPtr.Zero;
            // Level of detail = Full content
            mpFormatetc.dwAspect = DVASPECT.DVASPECT_CONTENT;
            // Index = Not applicaple
            mpFormatetc.lindex = -1;
            // Storage medium = HBITMAP handle
            mpFormatetc.tymed = TYMED.TYMED_GDI;
        }
        catch
        {
        }
    }
}

Take a look at how the member method SetImage creates a Bitmap object to use its handle when the GetData is called.

Now, here is how the object is inserted into the RichEditBox creating a shared global memory and getting a pointer to it (IStorage) and using the OleClientSite interface from the IRichEditOle.

public void InsertMyDataObject(myDataObject mdo)
{
    if (mdo == null)
        return;

    //-----------------------
    ILockBytes pLockBytes;
    int sc = CreateILockBytesOnHGlobal(IntPtr.Zero, 
                             true, out pLockBytes);

    IStorage pStorage;
    sc = StgCreateDocfileOnILockBytes(pLockBytes, (uint)
        (STGM.STGM_SHARE_EXCLUSIVE|STGM.STGM_CREATE|
                                   STGM.STGM_READWRITE), 
        0, out pStorage);
    
    IOleClientSite pOleClientSite;
    this.IRichEditOle.GetClientSite(out pOleClientSite);
    //-----------------------

    Guid guid = Marshal.GenerateGuidForType(mdo.GetType());

    Guid IID_IOleObject = 
      new Guid("{00000112-0000-0000-C000-000000000046}");
    Guid IID_IDataObject = 
      new Guid("{0000010e-0000-0000-C000-000000000046}");
    Guid IID_IUnknown = 
      new Guid("{00000000-0000-0000-C000-000000000046}");

    object pOleObject;

    int hr = OleCreateStaticFromData(mdo, ref IID_IOleObject, 
        (uint)OLERENDER.OLERENDER_FORMAT, ref mdo.mpFormatetc,
        pOleClientSite, pStorage, out pOleObject);

    if (pOleObject == null)
        return;
    //-----------------------

    
    //-----------------------
    OleSetContainedObject(pOleObject, true);

    REOBJECT reoObject = new REOBJECT();

    reoObject.cp = this._richEdit.TextLength;

    reoObject.clsid = guid;
    reoObject.pstg = pStorage;
    reoObject.poleobj = Marshal.GetIUnknownForObject(pOleObject);
    reoObject.polesite = pOleClientSite;
    reoObject.dvAspect = (uint)(DVASPECT.DVASPECT_CONTENT);
    reoObject.dwFlags = (uint)(REOOBJECTFLAGS.REO_BELOWBASELINE);
    reoObject.dwUser = 0;

    this.IRichEditOle.InsertObject(reoObject);
    //-----------------------

    //-----------------------
    Marshal.ReleaseComObject(pLockBytes);
    Marshal.ReleaseComObject(pOleClientSite);
    Marshal.ReleaseComObject(pStorage);
    Marshal.ReleaseComObject(pOleObject);
    //-----------------------
}

There are other methods to insert controls and ActiveX objects, they look very similar to the above method, so please review the source code.

Points of Interest

And finally, how are the controls updated?

This is the trick, you need to use a timer and call the method UpdateObjects. This method performs a search for all objects in the RichTextBox and if they are marked as special (in my case I use the dwUser variable), they will be updated:

public void UpdateObjects()
{
    int k = this.IRichEditOle.GetObjectCount();

    for (int i = 0; i < k; i++)
    {
        REOBJECT reoObject = new REOBJECT();

        this.IRichEditOle.GetObject(i, reoObject, 
          GETOBJECTOPTIONS.REO_GETOBJ_ALL_INTERFACES);

        if (reoObject.dwUser == 1)
        {
            Point pt = this._richEdit.GetPositionFromCharIndex(reoObject.cp);
            Rectangle rect = new Rectangle(pt, reoObject.sizel);

            this._richEdit.Invalidate(rect, false); // repaint
        }
    }
}

There is a lot of work required for optimizing this control but for now, any suggestion is appreciated.

Using the code

To use the code, simply add a reference to the control, put a normal RichTextBox into the form and then replace the type for MyExtRichTextBox:

MyExtRichTextBox.MyExtRichTextBox richTextBox1;

Tip

I update objects by creating an array of controls (buttons and progress bars) and adding a timer to the form, then calling the method UpdateObjects like this:

private void timer1_Tick(object sender, System.EventArgs e)
{
    for (int i = 0; i < ar.Count; i++)
    {
        itimer++;
        if (itimer > 100)
            itimer = 0;

        object obj = ar[i];
        if (obj is Button)
        {
            Button bt = (Button) obj;

            if (bt.Text != "Clicked")
                bt.Text = "button " + i.ToString() + 
                          " - " + itimer.ToString();
        }
        else
        {
            ProgressBar pb = (ProgressBar) obj;

            if (pb.Value + 1 > 100)
                pb.Value = 0;

            pb.Value = pb.Value + 1;
        }
    }

    richTextBox1.UpdateObjects();
}

History

  • Version 1.0 - Nov. 1 / 2005

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

Share

About the Author

Oscar Londono
Software Developer (Senior) Kinecor Ltee
Canada Canada
I have been working for 16 years as Analyst Programmer in several companies.

I love the Object Oriented Programming paradigm and now, I love C#. Currently, I works with X++ in Microsoft Dynamics AX systems.

Also, I like to perform my work by using methodologies like Rational Unified Process (RUP).

Please, take a look to my last project: Meetgate

You may also be interested in...

Comments and Discussions

 
QuestionHow to find an image and replace it with a string? Pin
omarbasha1-Oct-14 4:55
memberomarbasha1-Oct-14 4:55 
QuestionHow to store OLE objects in RichTextBox into SQL Database Pin
wintecharivu8-Aug-14 22:01
memberwintecharivu8-Aug-14 22:01 
QuestionWhy the DragOver event is not available? Pin
Member 161654211-Oct-13 21:03
memberMember 161654211-Oct-13 21:03 
QuestionShowing red cross for custom control Pin
Member 161654213-Sep-13 8:19
memberMember 161654213-Sep-13 8:19 
Questionbutton with one click Pin
seckin durgay29-Jun-12 3:08
memberseckin durgay29-Jun-12 3:08 
Questionhow to print? Pin
sbryu916@naver.com9-Mar-11 16:03
membersbryu916@naver.com9-Mar-11 16:03 
GeneralNot able to access COM activex control's property before inserting it to myExtRichTextBOx control Pin
bibhucodeproject3-May-10 2:16
memberbibhucodeproject3-May-10 2:16 
Generalimage (-object) is not below the baseline Pin
chand00513-Mar-10 4:34
memberchand00513-Mar-10 4:34 
GeneralObject and Memory Management Pin
CLoUdYvIsIoN2-Dec-09 10:33
memberCLoUdYvIsIoN2-Dec-09 10:33 
QuestionAnimated gifs Pin
Member 39617224-Aug-09 6:34
memberMember 39617224-Aug-09 6:34 
AnswerRe: Animated gifs Pin
CLoUdYvIsIoN2-Dec-09 10:42
memberCLoUdYvIsIoN2-Dec-09 10:42 
GeneralUnable to cast COM object of type 'System.__ComObject' to interface type 'MyExtRichTextBox.IRichEditOle' Pin
chenyonghao30-Mar-09 16:01
memberchenyonghao30-Mar-09 16:01 
GeneralRemoving Ole Object from RTB Pin
The Innovator24-Mar-09 10:10
memberThe Innovator24-Mar-09 10:10 
QuestionHow could the OLE content be saved in SQL Server? Pin
Radox26-Jan-09 4:18
memberRadox26-Jan-09 4:18 
AnswerRe: How could the OLE content be saved in SQL Server? Pin
codemachine99911-Nov-09 8:24
membercodemachine99911-Nov-09 8:24 
GeneralRe: How could the OLE content be saved in SQL Server? Pin
Radox19-Nov-09 7:58
memberRadox19-Nov-09 7:58 
GeneralSomething goes wrong Pin
Aleksei Krassovskikh19-Sep-08 3:52
memberAleksei Krassovskikh19-Sep-08 3:52 
GeneralInsertMyDataObject Pin
alex_tver29-May-08 13:35
memberalex_tver29-May-08 13:35 
QuestionAnyone done this with VB? Pin
PsychUK25-Sep-07 3:15
memberPsychUK25-Sep-07 3:15 
AnswerRe: Anyone done this with VB? Pin
Takanashi Canon21-Apr-11 2:33
memberTakanashi Canon21-Apr-11 2:33 
GeneralPlease Reply! Pin
mohadese4-Aug-07 22:39
membermohadese4-Aug-07 22:39 
QuestionProblem with insert Textbox? Pin
mohadese4-Aug-07 6:52
membermohadese4-Aug-07 6:52 
AnswerRe: Problem with insert Textbox? Pin
Oscar Londoño5-Aug-07 4:14
memberOscar Londoño5-Aug-07 4:14 
GeneralRe: Problem with insert Textbox? Pin
mohadese5-Aug-07 23:32
membermohadese5-Aug-07 23:32 
GeneralRe: Problem with insert Textbox? Pin
Oscar Londoño6-Aug-07 6:28
memberOscar Londoño6-Aug-07 6:28 
GeneralHaving problem whit MouseEventHandler Pin
mohadese4-Aug-07 6:48
membermohadese4-Aug-07 6:48 
Generalsomething wrong when debug in vs2005 Pin
GIS_Developer25-Jun-07 21:19
memberGIS_Developer25-Jun-07 21:19 
GeneralRe: something wrong when debug in vs2005 Pin
GIS_Developer25-Jun-07 21:59
memberGIS_Developer25-Jun-07 21:59 
GeneralRe: something wrong when debug in vs2005 Pin
borkpio24-Jul-07 3:48
memberborkpio24-Jul-07 3:48 
GeneralFreeing memory Pin
powermcpet21-May-07 9:22
memberpowermcpet21-May-07 9:22 
QuestionVB.NET 2005 Pin
clarkwgriswald21-Apr-07 8:24
memberclarkwgriswald21-Apr-07 8:24 
QuestionHow Can I Get my Pic Pin
czlc13-Nov-06 23:12
memberczlc13-Nov-06 23:12 
AnswerRe: How Can I Get my Pic Pin
Oscar Londoño5-Aug-07 4:24
memberOscar Londoño5-Aug-07 4:24 
Questionhow to input text in a textbox which have been inserted into a RichTextBox control Pin
shgyl1-Nov-06 5:08
membershgyl1-Nov-06 5:08 
AnswerRe: how to input text in a textbox which have been inserted into a RichTextBox control Pin
Oscar Londoño5-Aug-07 4:25
memberOscar Londoño5-Aug-07 4:25 
QuestionInserting linked object Pin
SanjaySSK8-Oct-06 19:51
memberSanjaySSK8-Oct-06 19:51 
AnswerRe: Inserting linked object Pin
Oscar Londoño5-Aug-07 4:32
memberOscar Londoño5-Aug-07 4:32 
GeneralPerhaps easier.... Pin
objm22-Aug-06 6:04
memberobjm22-Aug-06 6:04 
GeneralRe: Perhaps easier.... Pin
Oscar Londoño5-Aug-07 4:27
memberOscar Londoño5-Aug-07 4:27 
GeneralRe: Perhaps easier.... Pin
zmrcic16-Aug-07 22:54
memberzmrcic16-Aug-07 22:54 
QuestionHow controls in richtextbox can use contextmenu & Resize them? Pin
mansoureh14-Aug-06 2:52
membermansoureh14-Aug-06 2:52 
AnswerRe: How controls in richtextbox can use contextmenu &amp; Resize them? [modified] Pin
Oscar Londoño5-Aug-07 4:26
memberOscar Londoño5-Aug-07 4:26 
GeneralFailed to save the inserted ActiveX controls Pin
yangzw_cn1-Jul-06 6:00
memberyangzw_cn1-Jul-06 6:00 
GeneralRe: It wants converting with the image which is different object. Pin
Oscar Londoño30-Jun-06 5:23
memberOscar Londoño30-Jun-06 5:23 
Generalcopy / paste problem Pin
catialin8-Jun-06 3:05
membercatialin8-Jun-06 3:05 
GeneralRe: copy / paste problem Pin
Oscar Londoño30-Jun-06 5:21
memberOscar Londoño30-Jun-06 5:21 
GeneralRe: copy / paste problem Pin
dennisV1-Nov-06 17:16
memberdennisV1-Nov-06 17:16 
GeneralConverting images into HTML Pin
Cyrus Chan20-May-06 6:28
memberCyrus Chan20-May-06 6:28 
GeneralRe: Converting images into HTML Pin
Oscar Londoño30-Jun-06 5:07
memberOscar Londoño30-Jun-06 5:07 
Questionpopulate the Control inside the richtextbox Pin
huangxw10-May-06 5:04
memberhuangxw10-May-06 5:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160208.1 | Last Updated 1 Nov 2005
Article Copyright 2005 by Oscar Londono
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid