Listing 2 Implementing IHTMLPainter
01234567890123456789012345678901234567890123456789012345678901234567890123456789 namespace BinBehaviors { [ ComVisible(true), ClassInterface(ClassInterfaceType.AutoDispatch), Guid("70F8ECDA-3869-4586-958E-0914547E9984"), ProgId("BinBehaviors.line") ] public class Line : mshtml2.IElementBehavior, mshtml2.IHTMLPainter { // . // IHTMLPainter method. void mshtml2.IHTMLPainter.Draw(tagRECT rcBounds, tagRECT rcUpdate, int lDrawFlags, System.IntPtr hdc, System.IntPtr pvDrawObject) { if (this.bitmap != null) { Graphics g = Graphics.FromHdc(hdc); g.CompositingMode = CompositingMode.SourceOver; // Apply any scaling, etc. to the output. mshtml._HTML_PAINT_DRAW_INFO info; this.paintsite.GetDrawInfo( (int) _HTML_PAINT_DRAW_INFO_FLAGS.HTMLPAINT_DRAWINFO_XFORM | (int) _HTML_PAINT_DRAW_INFO_FLAGS.HTMLPAINT_DRAWINFO_UPDATEREGION, out info); Matrix xform = new System.Drawing.Drawing2D.Matrix( info.xform.eM11, info.xform.eM12, info.xform.eM21, info.xform.eM22, info.xform.eDx - rcBounds.left, info.xform.eDy - rcBounds.top); g.Transform = xform; // Update clipping region. Region clip = new Region(); if (info.hrgnUpdate != System.IntPtr.Zero) { Region updateclip = Region.FromHrgn(info.hrgnUpdate); clip.Intersect(updateclip); clip.Translate(rcBounds.left, rcBounds.top); } g.SetClip(clip, CombineMode.Replace); g.DrawImage(this.bitmap, rcUpdate.left, rcUpdate.top, new Rectangle(rcUpdate.left - rcBounds.left, rcUpdate.top - rcBounds.top, rcUpdate.right - rcUpdate.left, rcUpdate.bottom - rcUpdate.top), GraphicsUnit.Pixel); g.Dispose(); } } // IHTMLPainter method. void mshtml2.IHTMLPainter.onresize(tagSIZE size) { DoLineDraw(); } // IHTMLPainter method. void mshtml2.IHTMLPainter.GetPainterInfo(out _HTML_PAINTER_INFO pInfo) { pInfo.lFlags = (int) (_HTML_PAINTER.HTMLPAINTER_TRANSPARENT | _HTML_PAINTER.HTMLPAINTER_NOPHYSICALCLIP | _HTML_PAINTER.HTMLPAINTER_SUPPORTS_XFORM); // Possibly also: | _HTML_PAINTER.HTMLPAINTER_HITTEST. pInfo.lZOrder = (int) _HTML_PAINT_ZORDER.HTMLPAINT_ZORDER_REPLACE_ALL; pInfo.iidDrawObject = Guid.Empty; // No drawing object; using GDI+. pInfo.rcExpand.left = pInfo.rcExpand.right = pInfo.rcExpand.top = pInfo.rcExpand.bottom = 0; } // IHTMLPainter method. // Not called unless pInfo.lFlags // included _HTML_PAINTER.HTMLPAINTER_HITTEST. void mshtml2.IHTMLPainter.HitTestPoint( tagPOINT pt, out int pbHit, out int plPartID) { pbHit = 0; plPartID = 0; } /// <summary> /// Prepare bitmap image to be drawn. /// </summary> private void Compose() { // The element origin is at top left always. // This bitmap drawing is relative to the origin. int width = Math.Abs(point2.X - point1.X); int height = Math.Abs(point2.Y - point1.Y); // The end points relative to the rectangle enclosing the line: int x1, y1, x2, y2; if (point2.X < point1.X) { x1 = point1.X - point2.X; x2 = 0; } else { x1 = 0; x2 = point2.X - point1.X; } if (point2.Y < point1.Y) { y1 = point1.Y - point2.Y; y2 = 0; } else { y1 = 0; y2 = point2.Y - point1.Y; } // Element/bitmap is offset left by penwidth to give room // for line, so line x1, x2 start at +penwidth. x1 += this.xoffset; x2 += this.xoffset; y1 += this.yoffset; y2 += this.yoffset; // IMPORTANT: Free the bitmap by force or // else large bitmaps remain allocated. // GC permits increasing amounts of // memory to be left allocated between each // successive collection, until physical RAM is consumed. if (this.bitmap != null) this.bitmap.Dispose(); if (width == 0 && height == 0) // Skip bitmap if zero; invalid size. return; // Add room to bitmap to correspond to element size and to avoid clipping. width += 2*this.xoffset; height += 2*this.yoffset; this.bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(this.bitmap); g.CompositingMode = CompositingMode.SourceOver; g.DrawLine(new Pen(pencolor, penwidth), x1, y1, x2, y2); g.Dispose(); } /// <summary> /// Construct the drawing on its bitmap and adjust the containing element. /// </summary> private void DoLineDraw() { Compose(); UpdateElement(); // Invalidate so entire element must be redrawn. this.paintsite.InvalidateRegion(IntPtr.Zero); } /// <summary> /// Update element position and dimensions to fit the drawing. /// </summary> private void UpdateElement() { // Position line element at min of point1 and point2 coords. this.element.style.left = Math.Min(point1.X, point2.X) - this.xoffset; this.element.style.top = Math.Min(point1.Y, point2.Y) - this.yoffset; this.element.style.width = Math.Abs(point2.X - point1.X) + 2*this.xoffset; this.element.style.height = Math.Abs(point2.Y - point1.Y) + 2*this.yoffset; } } }