|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThis article shows how you can insert images, controls and ActiveX objects into a .NET There are other related articles for inserting images and OLE objects into a 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#". BackgroundTo 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 this.IRichEditOle = SendMessage(richEditHandle, EM_GETOLEINTERFACE, 0);
With this interface, you can insert objects through the //-----------------------
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 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 Now, here is how the object is inserted into the 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 InterestAnd finally, how are the controls updated? This is the trick, you need to use a timer and call the method 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 codeTo use the code, simply add a reference to the control, put a normal MyExtRichTextBox.MyExtRichTextBox richTextBox1;
TipI update objects by creating an array of controls (buttons and progress bars) and adding a timer to the form, then calling the method 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
|
||||||||||||||||||||||