{"id":2021,"date":"2008-10-13T20:11:25","date_gmt":"2008-10-13T23:11:25","guid":{"rendered":"http:\/\/www.hoogervorst.ca\/arthur\/?p=2021"},"modified":"2008-10-13T20:19:17","modified_gmt":"2008-10-13T23:19:17","slug":"canvas","status":"publish","type":"post","link":"http:\/\/www.hoogervorst.ca\/arthur\/?p=2021","title":{"rendered":"Canvas"},"content":{"rendered":"<p><span class=\"dropcap\">D<\/span>uring my <a href=\"http:\/\/www.hoogervorst.ca\/arthur\/?attachment_id=2022\" rel=\"attachment wp-att-2022\"><img decoding=\"async\" loading=\"lazy\" src=\"http:\/\/www.hoogervorst.ca\/arthur\/wp-content\/uploads\/2008\/10\/a_scr_een_shot-150x120.jpg\" alt=\"\" title=\"Kitten Selector&trade;\" width=\"150\" height=\"120\" class=\"alignright size-thumbnail wp-image-2022\" srcset=\"http:\/\/www.hoogervorst.ca\/arthur\/wp-content\/uploads\/2008\/10\/a_scr_een_shot-150x120.jpg 150w, http:\/\/www.hoogervorst.ca\/arthur\/wp-content\/uploads\/2008\/10\/a_scr_een_shot-300x240.jpg 300w, http:\/\/www.hoogervorst.ca\/arthur\/wp-content\/uploads\/2008\/10\/a_scr_een_shot.jpg 648w\" sizes=\"(max-width: 150px) 100vw, 150px\" \/><\/a> programming career, I&#8217;ve ran into several cases where I absolutely had to use owner-draw to accomplish customized drawing. Originally, as a Delphi programmer, this required knowledge of the specific graphic wrappers around the Windows GDI (and GDI+) functions which Borland (appropriately) called TCanvas. The C#\/.Net equivalent is called &#8216;Graphics&#8217;, which (admittingly) does not sound as fancy as Canvas.\n<\/p>\n<p>That said: in my never-ending quest to fill a niche craving, I decided to look into the basics of photo-editing; that is, on a much smaller scale. The first step was to create a component that (given a specific image), drops a frame on it, which you can use to crop a photo (by either moving it and\/or resizing it). Additionally, I always liked how some photo-editors integrate the <em>Rule Of Thirds<\/em> during cropping of photos, so, that had to be part of the custom-draw routine too.\n<\/p>\n<p>There are couple of common tasks that need to be taken care of when doing own-draw stuff, all in C# (however, should be similar in Delphi): <\/p>\n<ul>\n<li>The first thing is to decide <em>which<\/em> control you&#8217;re going to &#8216;descend&#8217; from, or rather, which control is going to be your base-class\n<li>If your control requires user-interaction (i.e. mouse\/key input), you should probably override the control&#8217;s MouseUp\/Down and MouseMove events. Most likely you&#8217;ll need a couple of (private) flags that track down if a mouse button is still &#8216;pressed&#8217;. Add to that a couple of variables that track down the <em>last<\/em> positions clicked on the screen.\n<li>Separate the drawing routines and call these routines from an overridden OnPaint event.\n<li>Debugging (owner-draw) graphical routines is extremely painful, so <em>think through your drawing routines<\/em>.\n<\/ul>\n<\/p>\n<p>Boring sample code is about to follow.\n<\/p>\n<p><!--more--><\/p>\n<p>The order of processing:\n<\/p>\n<p>\n<ul>\n<li>Calculate the actual needed rectangle (deflate it by one pixel)\n<li>Draw the main rectangle (2 points thick)\n<li>Draw the &#8216;Rule of Thirds&#8217; lines.\n<li>Draw the text for size and location of the current frame.\n<li>Draw the &#8216;size block&#8217; in the bottom-right corner of the frame (we do a hittest for this region in the OnMouseDown event).\n<\/ul>\n<\/p>\n<pre>       protected virtual void DrawFrameBox(PaintEventArgs pe) {\r\n            Graphics pes = pe.Graphics;\r\n            Pen pen = new Pen(this.bordercolor);\r\n            Pen lpen = new Pen(this.bordercolor);\r\n            \r\n            try {\r\n                Rectangle nrect = Rectangle.Inflate(this.ClientRectangle, -1, -1);\r\n                pen.DashStyle = \r\n                    Functions.ConvertFrameStyleToDashStyle(this.framestyle);\r\n                pen.Width = 2f;\r\n               \r\n                pes.DrawRectangle(pen, nrect);\r\n\r\n                lpen.DashStyle =\r\n                    Functions.ConvertFrameStyleToDashStyle(this.framestyle);\r\n                lpen.Width = 1f;\r\n\r\n\r\n                int x1 = (int) ((1f \/ 3f) * (float)nrect.Width);\r\n                int x2 = (int)((2f \/ 3f) * (float)nrect.Width);\r\n                int y1 = (int) ((1f \/ 3f) * (float)nrect.Height);\r\n                int y2 = (int) ((2f \/ 3f) * (float)nrect.Height);\r\n                \r\n                \/\/Draw lines...\r\n                pes.DrawLine(lpen, x1, nrect.Top, x1, nrect.Bottom);\r\n                pes.DrawLine(lpen, x2, nrect.Top, x2, nrect.Bottom);\r\n                pes.DrawLine(lpen, nrect.Left, y1, nrect.Width, y1);\r\n                pes.DrawLine(lpen, nrect.Left, y2, nrect.Width, y2);\r\n\r\n                String s = String.Format(\"Loc:{0},{1}\", this.Top, this.Left);                \r\n                pes.DrawString(s, new Font(\"Arial\", 8), new SolidBrush(this.bordercolor), new PointF(0, 0));\r\n                \r\n                s = String.Format(\"Sz: {0}x{1}\", this.Width, this.Height);\r\n                pes.DrawString(s, new Font(\"Arial\", 8), new SolidBrush(this.bordercolor), new PointF(0, 10));\r\n\r\n                \/\/ Draw sizeblock...\r\n                Rectangle sizerect = new Rectangle(nrect.Width - 10, nrect.Height - 10, 10, 10);\r\n                pes.DrawRectangle(lpen, sizerect);\r\n\r\n            } finally {\r\n                lpen.Dispose();\r\n                pen.Dispose();                \r\n            }\r\n        }\r\n<\/pre>\n<p>Setting the size and position of the selection frame happens in the appropriate mouse and key-events: If you make sure that the proper flags and variables are set in place and the calculations are correct, you should not have a problem having your control draw itself like you want it to.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>During my programming career, I&#8217;ve ran into several cases where I absolutely had to use owner-draw to accomplish customized drawing. Originally, as a Delphi programmer, this required knowledge of the specific graphic wrappers around the Windows GDI (and GDI+) functions &hellip; <a href=\"http:\/\/www.hoogervorst.ca\/arthur\/?p=2021\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[4],"tags":[56,88,55,176,109],"_links":{"self":[{"href":"http:\/\/www.hoogervorst.ca\/arthur\/index.php?rest_route=\/wp\/v2\/posts\/2021"}],"collection":[{"href":"http:\/\/www.hoogervorst.ca\/arthur\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.hoogervorst.ca\/arthur\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.hoogervorst.ca\/arthur\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.hoogervorst.ca\/arthur\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2021"}],"version-history":[{"count":0,"href":"http:\/\/www.hoogervorst.ca\/arthur\/index.php?rest_route=\/wp\/v2\/posts\/2021\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.hoogervorst.ca\/arthur\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2021"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.hoogervorst.ca\/arthur\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2021"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.hoogervorst.ca\/arthur\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2021"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}