Cyotek Development Bloghttps://devblog.cyotek.com/tag/csharp/atom.xml2022-03-07T19:21:27ZPainting the borders of a custom control using WM_NCPAINTurn:uuid:ff756232-014e-47c0-bfa6-238d258eeaa82022-03-07T19:21:27Z2022-03-07T19:21:27Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ncpaint-01.png" class="gallery" title="A demonstration program showing borders painted via WM_NCPAINT" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ncpaint-01.png" alt="A demonstration program showing borders painted via WM_NCPAINT" decoding="async" loading="lazy" /></a><figcaption>A demonstration program showing borders painted via WM_NCPAINT</figcaption></figure>
<p>Over the years I've created a number of controls that require
borders. Sometimes, I'll draw the borders manually as part of
the normal user paint sequence. Other times I'll apply the
<code>WS_EX_CLIENTEDGE</code> or <code>WS_BORDER</code> styles and let Windows handle
it for me.</p>
<p>The advantage of the latter approach is that that means there is
nothing I need to do; the borders will automatically paint, and
as they are excluded from the normal client region of the
control, I don't need to account for them when performing my own
painting or positioning of child controls such as scroll bars.</p>
<p>The disadvantage is that these borders will be painted in the
classic Windows 95 style without any theming.</p>
<p>While working on a control recently, I went with applying window
styles for ease, but then decided I wanted to draw themed
borders. This time I decided to try something new, and have
Windows still manage the borders, but I would override its
default painting with my own, using the
<a href="https://docs.microsoft.com/en-us/windows/win32/gdi/wm-ncpaint" rel="external nofollow noopener"><code>WM_NCPAINT</code></a> message.</p>
<h2 id="sidequest-applying-window-styles">Sidequest: Applying window styles</h2>
<p>If your custom controls use <code>UserControl</code> as a base, this
already has a <code>BorderStyle</code> property which creates a non-client
frame. Most of the controls I create don't need the extra
functionality of <code>UserControl</code> and so I mostly inherit from
<code>Control</code>. By default this does not create a frame; the code
below adds a <code>BorderStyle</code> property and sets the appropriate
style when the window handle is created.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">const</span> <span class="keyword">int</span> WS_BORDER <span class="symbol">=</span> <span class="number">0x00800000</span><span class="symbol">;</span>

<span class="keyword">const</span> <span class="keyword">int</span> WS_EX_CLIENTEDGE <span class="symbol">=</span> <span class="number">0x00000200</span><span class="symbol">;</span>

<span class="keyword">private</span> BorderStyle _borderStyle<span class="symbol">;</span>

<span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Appearance&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="symbol">[</span>DefaultValue<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>BorderStyle<span class="symbol">)</span><span class="symbol">,</span> <span class="string">&quot;Fixed3D&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> BorderStyle BorderStyle
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">=&gt;</span> _borderStyle<span class="symbol">;</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_borderStyle <span class="symbol">!=</span> value<span class="symbol">)</span>
 <span class="symbol">{</span>
 _borderStyle <span class="symbol">=</span> value<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>UpdateStyles<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> CreateParams CreateParams
<span class="symbol">{</span>
 <span class="keyword">get</span>
 <span class="symbol">{</span>
 CreateParams createParams<span class="symbol">;</span>

 createParams <span class="symbol">=</span> <span class="keyword">base</span><span class="symbol">.</span>CreateParams<span class="symbol">;</span>
 createParams<span class="symbol">.</span>ExStyle <span class="symbol">&amp;=</span> <span class="symbol">~</span>WS_EX_CLIENTEDGE<span class="symbol">;</span>
 createParams<span class="symbol">.</span>Style <span class="symbol">&amp;=</span> <span class="symbol">~</span>WS_BORDER<span class="symbol">;</span>

 <span class="keyword">switch</span> <span class="symbol">(</span>_borderStyle<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> BorderStyle<span class="symbol">.</span>Fixed<span class="number">3</span>D<span class="symbol">:</span>
 createParams<span class="symbol">.</span>ExStyle <span class="symbol">|=</span> WS_EX_CLIENTEDGE<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>

 <span class="keyword">case</span> BorderStyle<span class="symbol">.</span>FixedSingle<span class="symbol">:</span>
 createParams<span class="symbol">.</span>Style <span class="symbol">|=</span> WS_BORDER<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> createParams<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Here we have a fairly basic property definition, the only aspect
you might not normally see is the call to <code>UpdateStyles</code> - this
will cause the window styles to be reapplied via our overridden
<code>CreateParams</code> method.</p>
<p>The <code>CreateParams</code> property is also something you might not see
or need to use very often, and is used to set the underlying
Win32 attributes of the window (remember that as far as Windows
is concerned, your forms, controls, etc are all &quot;windows&quot;). I'm
using it here to set the border styles, but you could also use
it to specify the name of an existing class such as <code>EDIT</code>,
although that doesn't come up as often - a topic for another
day, perhaps.</p>
<p>In our override we first <em>remove</em> any existing border styles.
There shouldn't be any set, but better to be sure. We then apply
either a basic or an extended style depending on our property
value. And with that done, Windows will create an appropriate
frame and paint it for us, allowing me to move on with the rest
of this article.</p>
<h2 id="introducing-wm_ncpaint">Introducing WM_NCPAINT</h2>
<p>The <code>WM_NCPAINT</code> message is sent to a window when its frame must
be painted. We can intercept this message via the <code>WndProc</code>
method of our control and then perform the desired painting.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">const</span> <span class="keyword">int</span> WM_NCPAINT <span class="symbol">=</span> <span class="number">0x0085</span><span class="symbol">;</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> WndProc<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>Msg <span class="symbol">==</span> WM_NCPAINT<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>WmNcPaint<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>WndProc<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> WmNcPaint<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
<span class="symbol">{</span> 
 <span class="keyword">base</span><span class="symbol">.</span>WndProc<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// Just going back to Windows, for now</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="getting-the-device-context">Getting the device context</h3>
<p>Regardless of if we're going to using managed or unmanaged
painting we need to start by getting the device context (DC).
According to the documentation for <code>WM_NCPAINT</code>, I should have
been able to use <a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdcex" rel="external nofollow noopener"><code>GetDCEx</code></a> to do this, but in
practice I found it always returned a null handle <a id="fnref:1" href="#fn:1" class="footnote-ref"><sup>1</sup></a>.
Normally, I might use <code>GetDC</code> but this returns a DC for the
client, and we need it for the non-client area. For this
technique, I will instead call <a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowdc" rel="external nofollow noopener"><code>GetWindowDC</code></a> -
the DC returned by this API will allow painting in both the
client and non-client areas. Once we have finished with a DC, it
needs to be released via <a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-releasedc" rel="external nofollow noopener"><code>ReleaseDC</code></a>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> IntPtr GetWindowDC<span class="symbol">(</span>IntPtr hWnd<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">bool</span> ReleaseDC<span class="symbol">(</span>IntPtr hWnd<span class="symbol">,</span> IntPtr hDc<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">private</span> <span class="keyword">void</span> WmNcPaint<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
<span class="symbol">{</span> 
 IntPtr hdc<span class="symbol">;</span>

 hdc <span class="symbol">=</span> GetWindowDC<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// TODO: Paint something</span>

 ReleaseDC<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> hdc<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="painting-in-a-mostly-managed-way">Painting in a mostly-managed way</h3>
<p>Once we've got our DC, we can begin to paint. The easiest way is
use use the managed API, e.g. the <code>Graphics</code> object. We can
create an instance of a <code>Graphics</code> instance from a Win32 DC via
the static <code>FromHdc</code> method.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">using</span> <span class="symbol">(</span>Graphics g <span class="symbol">=</span> Graphics<span class="symbol">.</span>FromHdc<span class="symbol">(</span>hdc<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// TODO: Paint</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>If we immediately start to paint here however, we'll run into
two issues - firstly, the DC is for the entire window, so we
could accidentally paint over the client area. This shouldn't
matter too much as client painting will follow but if that
itself is only partial, artefacts may be left behind (plus in my
testing there was obvious flicker). The second issue is that
properties such as <code>Control.Size</code> may not have been update at
the point this message is received and so could return
inaccurate values.</p>
<p>To resolve these issues we need to do a little more work to both
determine the correct client region, and also to exclude this
region from painting.</p>
<p>First, we use <a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getclientrect" rel="external nofollow noopener"><code>GetClientRect</code></a> to get a
<a href="https://docs.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect" rel="external nofollow noopener"><code>RECT</code></a> describing the client. Next, we will get the
window rectangle via <a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowrect" rel="external nofollow noopener"><code>GetWindowRect</code></a>. Note that
the former always has a location of zero, whilst the latter
includes the position of the window. Also note that unlike the
.NET <code>Rectangle</code> structure, the Win32 rect is comprised of
<em>left</em>, <em>top</em>, <em>right</em> (left + width) and <em>bottom</em> (top +
height) values, <strong>not</strong> the more convenient <em>width</em> and <em>height</em>
you may be used to from working solely with .NET.</p>
<p>With these values in hand, I calculate the position of the
client area with the assumption that the horizontal and vertical
margins are equidistant. Save storing the actual values
retrieved or calculated via <code>WM_NCCALCSIZE</code> (which I will
briefly cover later), I don't actually know how you'd get them
another way<a id="fnref:2" href="#fn:2" class="footnote-ref"><sup>2</sup></a>.</p>
<p>The new painting implementation is a little longer, but more
robust.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32.dll&quot;</span><span class="symbol">,</span> SetLastError <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">bool</span> GetClientRect<span class="symbol">(</span>IntPtr hWnd<span class="symbol">,</span> <span class="keyword">out</span> RECT lpRect<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32.dll&quot;</span><span class="symbol">,</span> SetLastError <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">bool</span> GetWindowRect<span class="symbol">(</span>IntPtr hWnd<span class="symbol">,</span> <span class="keyword">out</span> RECT lpRect<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>StructLayout<span class="symbol">(</span>LayoutKind<span class="symbol">.</span>Sequential<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">struct</span> RECT
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">int</span> left<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> top<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> right<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> bottom<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> WmNcPaint<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
<span class="symbol">{</span> 
 <span class="keyword">int</span> w<span class="symbol">;</span>
 <span class="keyword">int</span> h<span class="symbol">;</span>
 Rectangle clip<span class="symbol">;</span>
 IntPtr hdc<span class="symbol">;</span>

 GetClientRect<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> <span class="keyword">out</span> RECT clientRect<span class="symbol">)</span><span class="symbol">;</span>
 GetWindowRect<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> <span class="keyword">out</span> RECT windowRect<span class="symbol">)</span><span class="symbol">;</span>

 w <span class="symbol">=</span> windowRect<span class="symbol">.</span>right <span class="symbol">-</span> windowRect<span class="symbol">.</span>left<span class="symbol">;</span>
 h <span class="symbol">=</span> windowRect<span class="symbol">.</span>bottom <span class="symbol">-</span> windowRect<span class="symbol">.</span>top<span class="symbol">;</span>

 clip <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span><span class="symbol">(</span>w <span class="symbol">-</span> clientRect<span class="symbol">.</span>right<span class="symbol">)</span> <span class="symbol">/</span> <span class="number">2</span><span class="symbol">,</span> <span class="symbol">(</span>h <span class="symbol">-</span> clientRect<span class="symbol">.</span>bottom<span class="symbol">)</span> <span class="symbol">/</span> <span class="number">2</span><span class="symbol">,</span> clientRect<span class="symbol">.</span>right<span class="symbol">,</span> clientRect<span class="symbol">.</span>bottom<span class="symbol">)</span><span class="symbol">;</span>

 hdc <span class="symbol">=</span> GetWindowDC<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Graphics g <span class="symbol">=</span> Graphics<span class="symbol">.</span>FromHdc<span class="symbol">(</span>hdc<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 g<span class="symbol">.</span>SetClip<span class="symbol">(</span>clip<span class="symbol">,</span> CombineMode<span class="symbol">.</span>Exclude<span class="symbol">)</span><span class="symbol">;</span>

 g<span class="symbol">.</span>FillRectangle<span class="symbol">(</span>Brushes<span class="symbol">.</span>SeaGreen<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> w<span class="symbol">,</span> h<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 ReleaseDC<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> hdc<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>And with this in place, we now have a control that has a green
border.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ncpaint-02.png" class="gallery" title="A default paint example, the non-client area is green" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ncpaint-02.png" alt="A default paint example, the non-client area is green" decoding="async" loading="lazy" /></a><figcaption>A default paint example, the non-client area is green</figcaption></figure><h3 id="sidequest-regions">Sidequest - Regions</h3>
<p>In the above code, I calculated the clip region manually.
However, Windows does provide a paint region as part of the
message. The <em>wParam</em> parameter is a handle to an update region.
This doesn't always seem to be the case - the first call always
seems to be <code>1</code> which isn't a valid handle. When it isn't <code>1</code>,
you can use <code>Region.FromHrgn</code> to convert this handle into a
managed object, leaving you with code something similar to the
below.</p>
<p>While trying to work find out what the seemly undocumented <code>1</code>
meant, I came across this <a href="https://stackoverflow.com/a/50135792/148962" rel="external nofollow noopener">Stack Overflow answer</a>
and in turn this <a href="https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/a407591a-4b1e-4adc-ab0b-3c8b3aec3153/the-evil-wmncpaint" rel="external nofollow noopener">MSDN post</a> which first made me
realise why the calls to <code>GetDCEx</code> were failing but also made me
realise I should probably ignore that parameter and continue
doing it the way I was. As a bonus it also pointed me in the
direction of the <code>MapWindowPoints</code> call which may be the missing
piece I needed for offsetting the client rectangle, something to
investigate another day now though. (It also made me question if
I <em>really</em> should be using <code>WM_NCPAINT</code> or if I should just do
it all manually, but I'm halfway through the article now so I
may as well finish it!).</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="comment">// this works but needs more investigation and probably shouldn&#39;t be used</span>
<span class="keyword">using</span> <span class="symbol">(</span>Graphics g <span class="symbol">=</span> Graphics<span class="symbol">.</span>FromHdc<span class="symbol">(</span>hdc<span class="symbol">)</span><span class="symbol">)</span>
<span class="keyword">using</span> <span class="symbol">(</span>Region region <span class="symbol">=</span> m<span class="symbol">.</span>WParam <span class="symbol">!=</span> <span class="keyword">new</span> IntPtr<span class="symbol">(</span><span class="number">1</span><span class="symbol">)</span>
 <span class="symbol">?</span> Region<span class="symbol">.</span>FromHrgn<span class="symbol">(</span>m<span class="symbol">.</span>WParam<span class="symbol">)</span>
 <span class="symbol">:</span> <span class="keyword">new</span> Region<span class="symbol">(</span>clip<span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 g<span class="symbol">.</span>SetClip<span class="symbol">(</span>region<span class="symbol">,</span> CombineMode<span class="symbol">.</span>Exclude<span class="symbol">)</span><span class="symbol">;</span>

 g<span class="symbol">.</span>FillRectangle<span class="symbol">(</span>Brushes<span class="symbol">.</span>SeaGreen<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> w<span class="symbol">,</span> h<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="painting-using-the-themes-api">Painting using the themes API</h3>
<p>Although I could probably just use the built in Visual Styles,
using the native theme API is a nice way of demonstrating the
pure unmanaged approach.</p>
<p>It starts of similar to the previous code, except I manipulate
the results of <code>GetWindowRect</code> to be client based, otherwise the
painting would done at the wrong location. In this example, I'm
using the themes for the <code>EDIT</code> control, e.g. a <code>TextBox</code>.</p>
<p>As I don't have a <code>Graphics</code> object to call <code>SetClip</code> on, I call
<a href="https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-excludecliprect" rel="external nofollow noopener"><code>ExcludeClipRect</code></a> instead.</p>
<p>For the actual painting, first I get a handle to the theme via
<a href="https://docs.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-openthemedata" rel="external nofollow noopener"><code>OpenThemeData</code></a>. Next I check to see if any the
theme is partially transparent via
<a href="https://docs.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-isthemebackgroundpartiallytransparent" rel="external nofollow noopener"><code>IsThemeBackgroundPartiallyTransparent</code></a>
and if so I draw the background via
<a href="https://docs.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-drawthemeparentbackground" rel="external nofollow noopener"><code>DrawThemeParentBackground</code></a>. In
this case it isn't transparent, but I suppose it is good
practice to do these checks. After which I draw the theme
background via <a href="https://docs.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-drawthemebackground" rel="external nofollow noopener"><code>DrawThemeBackground</code></a>
which will give me my nice themed borders. And to wrap it up, I
close the handle I opened earlier using
<a href="https://docs.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-closethemedata" rel="external nofollow noopener"><code>CloseThemeData</code></a>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">const</span> <span class="keyword">int</span> EP_EDITTEXT <span class="symbol">=</span> <span class="number">1</span><span class="symbol">;</span>
<span class="keyword">const</span> <span class="keyword">int</span> ETS_NORMAL <span class="symbol">=</span> <span class="number">1</span><span class="symbol">;</span>
<span class="keyword">const</span> <span class="keyword">int</span> ETS_DISABLED <span class="symbol">=</span> <span class="number">4</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;uxtheme.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">int</span> CloseThemeData<span class="symbol">(</span>IntPtr hTheme<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;uxtheme.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">int</span> DrawThemeBackground<span class="symbol">(</span>IntPtr hTheme<span class="symbol">,</span> IntPtr hdc<span class="symbol">,</span> <span class="keyword">int</span> iPartId<span class="symbol">,</span> <span class="keyword">int</span> iStateId<span class="symbol">,</span> <span class="keyword">ref</span> RECT pRect<span class="symbol">,</span> IntPtr pClipRect<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;uxtheme.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">int</span> DrawThemeParentBackground<span class="symbol">(</span>IntPtr hWnd<span class="symbol">,</span> IntPtr hdc<span class="symbol">,</span> <span class="keyword">ref</span> RECT pRect<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;gdi32.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">int</span> ExcludeClipRect<span class="symbol">(</span>IntPtr hdc<span class="symbol">,</span> <span class="keyword">int</span> nLeftRect<span class="symbol">,</span> <span class="keyword">int</span> nTopRect<span class="symbol">,</span> <span class="keyword">int</span> nRightRect<span class="symbol">,</span> <span class="keyword">int</span> nBottomRect<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;uxtheme.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">int</span> IsThemeBackgroundPartiallyTransparent<span class="symbol">(</span>IntPtr hTheme<span class="symbol">,</span> <span class="keyword">int</span> iPartId<span class="symbol">,</span> <span class="keyword">int</span> iStateId<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;uxtheme.dll&quot;</span><span class="symbol">,</span> CharSet <span class="symbol">=</span> CharSet<span class="symbol">.</span>Unicode<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> IntPtr OpenThemeData<span class="symbol">(</span>IntPtr hWnd<span class="symbol">,</span> <span class="keyword">string</span> classList<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">private</span> <span class="keyword">void</span> WmNcPaint<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> w<span class="symbol">;</span>
 <span class="keyword">int</span> h<span class="symbol">;</span>
 IntPtr hdc<span class="symbol">;</span>
 IntPtr hTheme<span class="symbol">;</span>
 <span class="keyword">int</span> partId<span class="symbol">;</span>
 <span class="keyword">int</span> stateId<span class="symbol">;</span>

 GetClientRect<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> <span class="keyword">out</span> RECT clientRect<span class="symbol">)</span><span class="symbol">;</span>
 GetWindowRect<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> <span class="keyword">out</span> RECT windowRect<span class="symbol">)</span><span class="symbol">;</span>

 w <span class="symbol">=</span> windowRect<span class="symbol">.</span>right <span class="symbol">-</span> windowRect<span class="symbol">.</span>left<span class="symbol">;</span>
 h <span class="symbol">=</span> windowRect<span class="symbol">.</span>bottom <span class="symbol">-</span> windowRect<span class="symbol">.</span>top<span class="symbol">;</span>

 windowRect<span class="symbol">.</span>right <span class="symbol">=</span> w<span class="symbol">;</span>
 windowRect<span class="symbol">.</span>bottom <span class="symbol">=</span> h<span class="symbol">;</span>
 windowRect<span class="symbol">.</span>left <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 windowRect<span class="symbol">.</span>top <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>

 hdc <span class="symbol">=</span> GetWindowDC<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">)</span><span class="symbol">;</span>
 hTheme <span class="symbol">=</span> OpenThemeData<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> <span class="string">&quot;EDIT&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 partId <span class="symbol">=</span> EP_EDITTEXT<span class="symbol">;</span>

 stateId <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Enabled
 <span class="symbol">?</span> ETS_NORMAL
 <span class="symbol">:</span> ETS_DISABLED<span class="symbol">;</span>

 ExcludeClipRect<span class="symbol">(</span>hdc<span class="symbol">,</span> <span class="symbol">(</span>w <span class="symbol">-</span> clientRect<span class="symbol">.</span>right<span class="symbol">)</span> <span class="symbol">/</span> <span class="number">2</span><span class="symbol">,</span> <span class="symbol">(</span>h <span class="symbol">-</span> clientRect<span class="symbol">.</span>bottom<span class="symbol">)</span> <span class="symbol">/</span> <span class="number">2</span><span class="symbol">,</span> clientRect<span class="symbol">.</span>right<span class="symbol">,</span> clientRect<span class="symbol">.</span>bottom<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>IsThemeBackgroundPartiallyTransparent<span class="symbol">(</span>hTheme<span class="symbol">,</span> partId<span class="symbol">,</span> stateId<span class="symbol">)</span> <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 DrawThemeParentBackground<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> hdc<span class="symbol">,</span> <span class="keyword">ref</span> windowRect<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 DrawThemeBackground<span class="symbol">(</span>hTheme<span class="symbol">,</span> hdc<span class="symbol">,</span> partId<span class="symbol">,</span> stateId<span class="symbol">,</span> <span class="keyword">ref</span> windowRect<span class="symbol">,</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span><span class="symbol">;</span>

 CloseThemeData<span class="symbol">(</span>hTheme<span class="symbol">)</span><span class="symbol">;</span>

 ReleaseDC<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> hdc<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ncpaint-03.png" class="gallery" title="An example of painting using the themes API" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ncpaint-03.png" alt="An example of painting using the themes API" decoding="async" loading="lazy" /></a><figcaption>An example of painting using the themes API</figcaption></figure>
<p>Although it is probably much more concise to use the built-in
<code>VisualStyleRenderer</code> with a <code>Graphics</code> instance than the above,
this serves the purpose and will give us a control with a nice
themed border.</p>
<blockquote>
<p>Themes shouldn't be used blindly, they might be disabled by
the operating system or by the current process. You should
always check to see if themes are enabled (for example via
<code>Application.RenderWithVisualStyles</code>) before attempting to
render them, and fall back to another paint mode if not.</p>
</blockquote>
<h2 id="bonus-chatter-the-wm_nccalcsize-message">Bonus Chatter - the WM_NCCALCSIZE message</h2>
<p>When I started this article, I intended to end it with the
preceding section. However, given that themes don't have to
follow expected sizing conventions I thought I ought not do a
half job and should include some details on how you can define
the size of the non-client area yourself.</p>
<p>The <a href="https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize" rel="external nofollow noopener"><code>WM_NCCALCSIZE</code></a> message is sent when the
size and position of a window's client area must be calculated.
If you are just relying on painting over standard borders
without using themes then you may not need to use this message,
but if you are using themes them it is probably better to handle
it manually.</p>
<p>This message is slightly peculiar in my experience as it
contains different data at different times. If <em>wParam</em> is
<strong>TRUE</strong>, then <em>lParam</em> points to a
<a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-nccalcsize_params" rel="external nofollow noopener"><code>NCCALCSIZE_PARAMS</code></a> structure, otherwise it
points to a <code>RECT</code> instead. This makes the code slightly more
complicated, but not excessively.</p>
<blockquote>
<p>In my testing, it seemed a <code>RECT</code> value only happened once
when first created, thereafter a <code>NCCALCSIZE_PARAMS</code> value was
always provided.</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">const</span> <span class="keyword">int</span> WM_NCCALCSIZE <span class="symbol">=</span> <span class="number">0x0083</span><span class="symbol">;</span>

<span class="symbol">[</span>StructLayout<span class="symbol">(</span>LayoutKind<span class="symbol">.</span>Sequential<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">struct</span> NCCALCSIZE_PARAMS
<span class="symbol">{</span>
 <span class="symbol">[</span>MarshalAs<span class="symbol">(</span>UnmanagedType<span class="symbol">.</span>ByValArray<span class="symbol">,</span> SizeConst <span class="symbol">=</span> <span class="number">3</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> RECT<span class="symbol">[</span><span class="symbol">]</span> rgrc<span class="symbol">;</span>
 <span class="keyword">public</span> WINDOWPOS lppos<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="symbol">[</span>StructLayout<span class="symbol">(</span>LayoutKind<span class="symbol">.</span>Sequential<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">struct</span> WINDOWPOS
<span class="symbol">{</span>
 <span class="keyword">public</span> IntPtr hwnd<span class="symbol">;</span>
 <span class="keyword">public</span> IntPtr hwndInsertAfter<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> x<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> y<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> cx<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> cy<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">uint</span> flags<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> WndProc<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>Msg <span class="symbol">==</span> WM_NCPAINT<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>WmNcPaint<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>Msg <span class="symbol">==</span> WM_NCCALCSIZE<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>WmNcCalcSize<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>WndProc<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> WmNcCalcSize<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>WParam <span class="symbol">==</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span>
 <span class="symbol">{</span>
 RECT clientRect<span class="symbol">;</span>

 clientRect <span class="symbol">=</span> <span class="symbol">(</span>RECT<span class="symbol">)</span>Marshal<span class="symbol">.</span>PtrToStructure<span class="symbol">(</span>m<span class="symbol">.</span>LParam<span class="symbol">,</span> <span class="keyword">typeof</span><span class="symbol">(</span>RECT<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 clientRect<span class="symbol">.</span>left <span class="symbol">+=</span> leftOffset<span class="symbol">;</span>
 clientRect<span class="symbol">.</span>top <span class="symbol">+=</span> topOffset<span class="symbol">;</span>
 clientRect<span class="symbol">.</span>right <span class="symbol">-=</span> rightOffset<span class="symbol">;</span>
 clientRect<span class="symbol">.</span>bottom <span class="symbol">-=</span> bottomOffset<span class="symbol">;</span>

 Marshal<span class="symbol">.</span>StructureToPtr<span class="symbol">(</span>clientRect<span class="symbol">,</span> m<span class="symbol">.</span>LParam<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>

 _clientRect <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>leftOffset<span class="symbol">,</span> topOffset<span class="symbol">,</span> clientRect<span class="symbol">.</span>right <span class="symbol">-</span> clientRect<span class="symbol">.</span>left<span class="symbol">,</span> clientRect<span class="symbol">.</span>bottom <span class="symbol">-</span> clientRect<span class="symbol">.</span>top<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 NCCALCSIZE_PARAMS parameters<span class="symbol">;</span>
 RECT clientRect<span class="symbol">;</span>

 parameters <span class="symbol">=</span> <span class="symbol">(</span>NCCALCSIZE_PARAMS<span class="symbol">)</span>Marshal<span class="symbol">.</span>PtrToStructure<span class="symbol">(</span>m<span class="symbol">.</span>LParam<span class="symbol">,</span> <span class="keyword">typeof</span><span class="symbol">(</span>NCCALCSIZE_PARAMS<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 clientRect <span class="symbol">=</span> parameters<span class="symbol">.</span>rgrc<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span><span class="symbol">;</span>
 clientRect<span class="symbol">.</span>left <span class="symbol">+=</span> leftOffset<span class="symbol">;</span>
 clientRect<span class="symbol">.</span>top <span class="symbol">+=</span> topOffset<span class="symbol">;</span>
 clientRect<span class="symbol">.</span>right <span class="symbol">-=</span> rightOffset<span class="symbol">;</span>
 clientRect<span class="symbol">.</span>bottom <span class="symbol">-=</span> bottomOffset<span class="symbol">;</span>

 parameters<span class="symbol">.</span>rgrc<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span> <span class="symbol">=</span> clientRect<span class="symbol">;</span>
 Marshal<span class="symbol">.</span>StructureToPtr<span class="symbol">(</span>parameters<span class="symbol">,</span> m<span class="symbol">.</span>LParam<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>

 _clientRect <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>leftOffset<span class="symbol">,</span> topOffset<span class="symbol">,</span> clientRect<span class="symbol">.</span>right <span class="symbol">-</span> clientRect<span class="symbol">.</span>left<span class="symbol">,</span> clientRect<span class="symbol">.</span>bottom <span class="symbol">-</span> clientRect<span class="symbol">.</span>top<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The first thing I do is check if <em>wParam</em> is <code>IntPtr.Zero</code>, and
if so I extract the <code>RECT</code> structure from <em>lParam</em> using
<code>Marshal.PtrToStructure</code>. I then adjust this rectangle
accordingly by increasing <em>left</em> and <em>top</em>, and reducing <em>right</em>
and <em>bottom</em> to account for how large I want the client area to
be. I then store the modified rectangle back into <em>lParam</em> using
<code>Marshal.StructureToPtr</code>. Finally, I also store the new
rectangle for future use in painting.</p>
<p>If <em>wParam</em> is non-zero, I instead extract a <code>NCCALCSIZE_PARAMS</code>
structure from <em>lParam</em>. This has two members, a <code>WINDOWPOS</code>
describing the window position and an array containing 3 <code>RECT</code>
values. The first rectangle in this array is the rectangle is
the one we want to modify as per the previous paragraph. Once
we've replaced the first value in the array with our modified
version, we store the entire structure back into <em>lParam</em>, again
using <code>Marshal.StructureToPtr</code>.</p>
<p>When painting in response to <code>WM_NCPAINT</code>, I use the
<code>_clientRectangle</code> value captured earlier to define the clipping
region.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ncpaint-04.png" class="gallery" title="Using WM_NCCALCSIZE to specify a non-equidistant client rectangle" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ncpaint-04.png" alt="Using WM_NCCALCSIZE to specify a non-equidistant client rectangle" decoding="async" loading="lazy" /></a><figcaption>Using WM_NCCALCSIZE to specify a non-equidistant client rectangle</figcaption></figure>
<p>Processing this message in order to handle visual styles is
reasonably straight forward - as with painting, we need to open
a DC, open a theme, and then use
<a href="https://docs.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-getthemebackgroundcontentrect" rel="external nofollow noopener"><code>GetThemeBackgroundContentRect</code></a>
to get the client rectangle instead of calculating it ourselves.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;uxtheme.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">extern</span> <span class="keyword">static</span> <span class="keyword">int</span> GetThemeBackgroundContentRect<span class="symbol">(</span>IntPtr hTheme<span class="symbol">,</span> IntPtr hdc<span class="symbol">,</span> <span class="keyword">int</span> iPartId<span class="symbol">,</span> <span class="keyword">int</span> iStateId<span class="symbol">,</span> <span class="keyword">ref</span> RECT pBoundingRect<span class="symbol">,</span> <span class="keyword">out</span> RECT pContentRect<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">private</span> <span class="keyword">void</span> WmNcCalcSize<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
<span class="symbol">{</span>
 IntPtr hdc<span class="symbol">;</span>
 IntPtr hTheme<span class="symbol">;</span>
 <span class="keyword">int</span> partId<span class="symbol">;</span>
 <span class="keyword">int</span> stateId<span class="symbol">;</span>

 hdc <span class="symbol">=</span> GetWindowDC<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">)</span><span class="symbol">;</span>
 hTheme <span class="symbol">=</span> OpenThemeData<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> <span class="string">&quot;EDIT&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 partId <span class="symbol">=</span> EP_EDITTEXT<span class="symbol">;</span>

 stateId <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Enabled
 <span class="symbol">?</span> ETS_NORMAL
 <span class="symbol">:</span> ETS_DISABLED<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>WParam <span class="symbol">==</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span>
 <span class="symbol">{</span>
 RECT clientRect<span class="symbol">;</span>

 clientRect <span class="symbol">=</span> <span class="symbol">(</span>RECT<span class="symbol">)</span>Marshal<span class="symbol">.</span>PtrToStructure<span class="symbol">(</span>m<span class="symbol">.</span>LParam<span class="symbol">,</span> <span class="keyword">typeof</span><span class="symbol">(</span>RECT<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 GetThemeBackgroundContentRect<span class="symbol">(</span>hTheme<span class="symbol">,</span> hdc<span class="symbol">,</span> partId<span class="symbol">,</span> stateId<span class="symbol">,</span> <span class="keyword">ref</span> clientRect<span class="symbol">,</span> <span class="keyword">out</span> RECT adjustedClientRect<span class="symbol">)</span><span class="symbol">;</span>

 Marshal<span class="symbol">.</span>StructureToPtr<span class="symbol">(</span>adjustedClientRect<span class="symbol">,</span> m<span class="symbol">.</span>LParam<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>

 _clientRect <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>adjustedClientRect<span class="symbol">.</span>left <span class="symbol">-</span> clientRect<span class="symbol">.</span>left<span class="symbol">,</span> adjustedClientRect<span class="symbol">.</span>top <span class="symbol">-</span> clientRect<span class="symbol">.</span>top<span class="symbol">,</span> <span class="symbol">(</span>adjustedClientRect<span class="symbol">.</span>right <span class="symbol">-</span> adjustedClientRect<span class="symbol">.</span>left<span class="symbol">)</span> <span class="symbol">-</span> <span class="symbol">(</span>adjustedClientRect<span class="symbol">.</span>left <span class="symbol">-</span> clientRect<span class="symbol">.</span>left<span class="symbol">)</span><span class="symbol">,</span> <span class="symbol">(</span>adjustedClientRect<span class="symbol">.</span>bottom <span class="symbol">-</span> adjustedClientRect<span class="symbol">.</span>top<span class="symbol">)</span> <span class="symbol">-</span> <span class="symbol">(</span>adjustedClientRect<span class="symbol">.</span>top <span class="symbol">-</span> clientRect<span class="symbol">.</span>top<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 NCCALCSIZE_PARAMS parameters<span class="symbol">;</span>
 RECT clientRect<span class="symbol">;</span>

 parameters <span class="symbol">=</span> <span class="symbol">(</span>NCCALCSIZE_PARAMS<span class="symbol">)</span>Marshal<span class="symbol">.</span>PtrToStructure<span class="symbol">(</span>m<span class="symbol">.</span>LParam<span class="symbol">,</span> <span class="keyword">typeof</span><span class="symbol">(</span>NCCALCSIZE_PARAMS<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 clientRect <span class="symbol">=</span> parameters<span class="symbol">.</span>rgrc<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span><span class="symbol">;</span>

 GetThemeBackgroundContentRect<span class="symbol">(</span>hTheme<span class="symbol">,</span> hdc<span class="symbol">,</span> partId<span class="symbol">,</span> stateId<span class="symbol">,</span> <span class="keyword">ref</span> clientRect<span class="symbol">,</span> <span class="keyword">out</span> RECT adjustedClientRect<span class="symbol">)</span><span class="symbol">;</span>

 parameters<span class="symbol">.</span>rgrc<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span> <span class="symbol">=</span> adjustedClientRect<span class="symbol">;</span>
 Marshal<span class="symbol">.</span>StructureToPtr<span class="symbol">(</span>parameters<span class="symbol">,</span> m<span class="symbol">.</span>LParam<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>

 _clientRect <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>adjustedClientRect<span class="symbol">.</span>left <span class="symbol">-</span> clientRect<span class="symbol">.</span>left<span class="symbol">,</span> adjustedClientRect<span class="symbol">.</span>top <span class="symbol">-</span> clientRect<span class="symbol">.</span>top<span class="symbol">,</span> <span class="symbol">(</span>adjustedClientRect<span class="symbol">.</span>right <span class="symbol">-</span> adjustedClientRect<span class="symbol">.</span>left<span class="symbol">)</span> <span class="symbol">-</span> <span class="symbol">(</span>adjustedClientRect<span class="symbol">.</span>left <span class="symbol">-</span> clientRect<span class="symbol">.</span>left<span class="symbol">)</span><span class="symbol">,</span> <span class="symbol">(</span>adjustedClientRect<span class="symbol">.</span>bottom <span class="symbol">-</span> adjustedClientRect<span class="symbol">.</span>top<span class="symbol">)</span> <span class="symbol">-</span> <span class="symbol">(</span>adjustedClientRect<span class="symbol">.</span>top <span class="symbol">-</span> clientRect<span class="symbol">.</span>top<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 CloseThemeData<span class="symbol">(</span>hTheme<span class="symbol">)</span><span class="symbol">;</span>

 ReleaseDC<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> hdc<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ncpaint-05.png" class="gallery" title="Although the border style is set to the 3D style that is two pixels in size, the painted area is only a single pixel, matching the theme setting" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ncpaint-05.png" alt="Although the border style is set to the 3D style that is two pixels in size, the painted area is only a single pixel, matching the theme setting" decoding="async" loading="lazy" /></a><figcaption>Although the border style is set to the 3D style that is two pixels in size, the painted area is only a single pixel, matching the theme setting</figcaption></figure><h2 id="bonus-chatter-forcing-a-redraw-of-the-non-client-area">Bonus Chatter - Forcing a redraw of the non-client area</h2>
<p>If you use this approach, you will find the <code>WM_NCPAINT</code> message
isn't called all that often - you'll have a lot more <code>WM_PAINT</code>
messages for painting the actual client area. But what happens
if you <em>need</em> to refresh the non-client area? For example, you
might have design time properties that control the appearance,
or perhaps at runtime you want to change the border when the
control has focus.</p>
<p>Built in methods like <code>Control.Invalidate</code> or <code>Control.Refresh</code>
won't have any impact as they only invalidate the client area.</p>
<p>The solution is to use the <a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-redrawwindow" rel="external nofollow noopener"><code>RedrawWindow</code></a> API,
which allows us to control which aspects of the window are
refreshed. By using the <code>RDW_FRAME</code> flag, we tell Windows to
send a <code>WM_NCPAINT</code> message as appropriate, and the
<code>RDW_INVALIDATE</code> flag to repaint the window. By not including a
<code>RECT</code> or <code>HRGN</code> describing the area to repaint, it will paint
the full window.</p>
<blockquote>
<p>It would probably be better to try to define a region that
includes the non-client area and excludes the client area, but
this is an area I haven't touched for some time (if ever) so
I'm fuzzy on the details - I may post a follow up if I find it
becomes necessary.</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">const</span> <span class="keyword">int</span> RDW_FRAME <span class="symbol">=</span> <span class="number">0x400</span><span class="symbol">;</span>
<span class="keyword">const</span> <span class="keyword">int</span> RDW_INVALIDATE <span class="symbol">=</span> <span class="number">0x1</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">bool</span> RedrawWindow<span class="symbol">(</span>IntPtr hWnd<span class="symbol">,</span> IntPtr lprcUpdate<span class="symbol">,</span> IntPtr hrgnUpdate<span class="symbol">,</span> <span class="keyword">int</span> flags<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">private</span> <span class="keyword">void</span> InvalidateAll<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 RedrawWindow<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">,</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">,</span> RDW_FRAME <span class="symbol">|</span> RDW_INVALIDATE<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnEnter<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnEnter<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>InvalidateAll<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnLeave<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnLeave<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>InvalidateAll<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="closing-thoughts">Closing thoughts</h2>
<p>This article turned out a little longer than I was expecting. As
is often the case, I wrote the article in tandem with creating
the demonstration and jumped back and forth whilst exploring
different ideas or encountering issues. As a result of this it's
possible that there are errors in the code embedded within the
article or with the article content itself. If you spot any,
please let me know!</p>
<p>A sample project can be downloaded from our <a href="https://github.com/cyotek/ncpaintDemo" rel="external nofollow noopener">GitHub</a>
page.</p>
<p>Would you follow this approach or would you do something
different?</p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:1">
<p>The reason this call was failing appears to be two-fold.
Firstly, I was passing in an invalid HRGN whenever <em>wParam</em>
was <code>1</code>. In addition, when calling in response to
<code>WM_NCPAINT</code> apparently you need to use an undefined
<code>DCX_USESTYLE</code> (<code>0x00010000</code>) flag too.<a href="#fnref:1" class="footnote-back-ref">&#8617;</a></p>
</li>
<li id="fn:2">
<p>Potentially we can use
<a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mapwindowpoints" rel="external nofollow noopener"><code>MapWindowPoints</code></a> for this, but at the
time of writing this article I have not investigated this
further.<a href="#fnref:2" class="footnote-back-ref">&#8617;</a></p>
</li>
</ol>
</div>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/painting-the-borders-of-a-custom-control-using-wm-ncpaint .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCyotek Historical Date Libraryurn:uuid:f4876be6-ae46-4760-bfc9-5c3bea8203fd2021-09-17T05:12:22Z2021-09-17T05:12:22Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/cyotek.historicaldate.demo.png" class="gallery" title="A screenshot of the demonstration application" ><img src="https://images.cyotek.com/image/thumbnail/devblog/cyotek.historicaldate.demo.png" alt="A screenshot of the demonstration application" decoding="async" loading="lazy" /></a><figcaption>A screenshot of the demonstration application</figcaption></figure>
<p>Some months ago I was trying to create a timeline of British
pre-history and needed to be able to store dates. The .NET
<code>DateTime</code> or <code>DateOnly</code> structures are completely unsuitable
for these as their ranges are far too small, nor do they allow
for partial dates.</p>
<p>I did spent a little time poking around to see if there was some
existing code, but there doesn't seem to be a lot of detail out
there. I found a malformed <a href="http://flipbit.co.uk/2009/03/representing-large-ad-and-bc-dates-in-c" rel="external nofollow noopener">blog post</a> which was
missing the bulk of the source code, and
<a href="https://github.com/cerinman/HistoricalDate/blob/master/HistoricalDate/HistoricalDate/HistoricalDate.cs" rel="external nofollow noopener">HistoricalDate</a> class which was more fully fleshed,
but still not fully suitable. So as typical, I went my own way
and wrote my own.</p>
<h2 id="getting-the-library">Getting the library</h2>
<p>The easiest way of obtaining the library is via <a href="https://www.nuget.org/packages/Cyotek.HistoricalDate/" rel="external nofollow noopener">NuGet</a>.</p>
<blockquote>
<p><code>Install-Package Cyotek.HistoricalDate</code></p>
</blockquote>
<p>If you don't use NuGet, pre-compiled binaries can be obtained
from the <a href="https://github.com/cyotek/Cyotek.HistoricalDate/releases" rel="external nofollow noopener">GitHub Releases page</a>.</p>
<p>Of course, you can always grab <a href="https://github.com/cyotek/Cyotek.HistoricalDate" rel="external nofollow noopener">the source</a> and build it
yourself!</p>
<h2 id="about-this-library">About this library</h2>
<p>The library currently offers two read-only structs, <code>JulianDate</code>
and <code>HistoricalTimeSpan</code>.</p>
<h3 id="juliandate">JulianDate</h3>
<p>The <code>JulianDate</code> structure can represent a partial date between
2147483647 BP and 2147483647 AD. By partial, I mean that a year
and the era are always required, but the month and/or day is
optional. After all, a date such as the Battle of Hastings may
be documented but the start of the Mesolithic is a little more
nebulous!</p>
<h3 id="historicaltimespan">HistoricalTimeSpan</h3>
<p>The <code>HistoricalTimeSpan</code> is a cut down version of the more
familiar <code>TimeSpan</code> and is currently mainly used by the
<code>Add</code> and <code>Subtract</code> methods of a <code>JulianDate</code> instance.</p>
<h2 id="using-the-library">Using the library</h2>
<h3 id="constructing-instances">Constructing instances</h3>
<p>There are several constructors for specifying fully qualified or
partial dates.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
JulianDate<span class="symbol">(</span><span class="keyword">int</span> year<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// assumes AD</span>
JulianDate<span class="symbol">(</span><span class="keyword">int</span> year<span class="symbol">,</span> JulianEra era<span class="symbol">)</span><span class="symbol">;</span>
JulianDate<span class="symbol">(</span><span class="keyword">int</span> year<span class="symbol">,</span> <span class="keyword">int</span> month<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// assumes AD</span>
JulianDate<span class="symbol">(</span><span class="keyword">int</span> year<span class="symbol">,</span> <span class="keyword">int</span> month<span class="symbol">,</span> JulianEra era<span class="symbol">)</span><span class="symbol">;</span>
JulianDate<span class="symbol">(</span><span class="keyword">int</span> year<span class="symbol">,</span> <span class="keyword">int</span> month<span class="symbol">,</span> <span class="keyword">int</span> day<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// assumes AD</span>
JulianDate<span class="symbol">(</span><span class="keyword">int</span> year<span class="symbol">,</span> <span class="keyword">int</span> month<span class="symbol">,</span> <span class="keyword">int</span> day<span class="symbol">,</span> JulianEra era<span class="symbol">)</span><span class="symbol">;</span>

<span class="comment">// fully known</span>
<span class="keyword">var</span> fk <span class="symbol">=</span> <span class="keyword">new</span> JulianDate<span class="symbol">(</span><span class="number">2021</span><span class="symbol">,</span> <span class="number">9</span><span class="symbol">,</span> <span class="number">5</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// September 5th, 2021 AD</span>

<span class="comment">// partial</span>
<span class="keyword">var</span> pd <span class="symbol">=</span> <span class="keyword">new</span> JulianDate<span class="symbol">(</span><span class="number">13000</span><span class="symbol">,</span> JulianEra<span class="symbol">.</span>Bc<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// 130,000 BC</span>
</pre>
</figure>
<p>You can also use an explicit operator to convert the date
portion of a <code>DateTime</code> instance into a <code>JulianDate</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">var</span> now <span class="symbol">=</span> <span class="symbol">(</span>JulianDate<span class="symbol">)</span>DateTime<span class="symbol">.</span>UtcNow<span class="symbol">;</span>
</pre>
</figure>
<h3 id="parsing-strings">Parsing strings</h3>
<p>You can also try and parse a string into a <code>JulianDate</code>
instance.</p>
<blockquote>
<p>Note: String parsing (and formatting) is somewhat basic (and
potentially confusing) and will be improved in future updates
to the library. Except with regards to month names, parsing is
not culture-aware.</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">static</span> JulianDate Parse<span class="symbol">(</span><span class="keyword">string</span> s<span class="symbol">)</span><span class="symbol">;</span>
<span class="keyword">static</span> <span class="keyword">bool</span> TryParse<span class="symbol">(</span><span class="keyword">string</span> s<span class="symbol">,</span> <span class="keyword">out</span> JulianDate result<span class="symbol">)</span>

<span class="comment">// fully known</span>
<span class="keyword">var</span> fk <span class="symbol">=</span> JulianDate<span class="symbol">.</span>Parse<span class="symbol">(</span><span class="string">&quot;2021-09-17&quot;</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// September 17, 2021 AD</span>

<span class="comment">// partial</span>
<span class="keyword">var</span> pd <span class="symbol">=</span> JulianDate<span class="symbol">.</span>Parse<span class="symbol">(</span><span class="string">&quot;40000 BP&quot;</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// 40,000 BC</span>
</pre>
</figure>
<h3 id="partial-dates">Partial Dates</h3>
<p><code>JulianDate</code> supports partial dates, where only part of a date
is known. The year and era are always required, but month and
day are optional.</p>
<p>If day is specified, then the month is also available. The
<code>HasDay</code> and <code>HasMonth</code> properties allow you to query what
partial components are set, or if you just want to know if a
date is fully known or partial, the <code>IsFullyKnown</code> and
<code>IsPartial</code> properties can be used.</p>
<p>Accessing the <code>Month</code> or <code>Day</code> properties will return <code>0</code> if the
component has not been set.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">var</span> pd <span class="symbol">=</span> JulianDate<span class="symbol">.</span>Parse<span class="symbol">(</span><span class="string">&quot;40000 BP&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

pd<span class="symbol">.</span>HasMonth<span class="symbol">;</span> <span class="comment">// false</span>
pd<span class="symbol">.</span>HasDay<span class="symbol">;</span> <span class="comment">// false</span>

<span class="keyword">var</span> pd <span class="symbol">=</span> JulianDate<span class="symbol">.</span>Parse<span class="symbol">(</span><span class="string">&quot;09 2021&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

pd<span class="symbol">.</span>HasMonth<span class="symbol">;</span> <span class="comment">// true</span>
pd<span class="symbol">.</span>HasDay<span class="symbol">;</span> <span class="comment">// false</span>
</pre>
</figure>
<h2 id="leap-years">Leap Years</h2>
<p>The static <code>IsLeapYear</code> method will return if a given year is a
leap year. This is calculated according to Scaliger.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
JulianDate<span class="symbol">.</span>IsLeapYear<span class="symbol">(</span><span class="number">42</span><span class="symbol">,</span> JulianEra<span class="symbol">.</span>Bc<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// true</span>
JulianDate<span class="symbol">.</span>IsLeapYear<span class="symbol">(</span><span class="number">2021</span><span class="symbol">,</span> JulianEra<span class="symbol">.</span>Ad<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// false</span>
</pre>
</figure>
<h2 id="things-to-improve">Things to improve</h2>
<p>Currently, I'm unhappy with the string parsing as the list of
accepted formats is too vague, and I haven't yet built in
culture support.</p>
<p>The <code>Subtract</code> method could have serious performance issues when
used with massive AD dates.</p>
<h2 id="requirements">Requirements</h2>
<p>.NET Framework 2.0 or later.</p>
<p>Pre-built binaries are available via a signed <a href="https://www.nuget.org/packages/Cyotek.HistoricalDate/" rel="external nofollow noopener">NuGet
package</a> containing the following targets.</p>
<ul>
<li>.NET 3.5</li>
<li>.NET 4.0</li>
<li>.NET 4.5.2</li>
<li>.NET 4.6.2</li>
<li>.NET 4.7.2</li>
<li>.NET 4.8</li>
<li>.NET 5.0</li>
<li>.NET Standard 2.0</li>
<li>.NET Standard 2.1</li>
<li>.NET Core 2.1</li>
<li>.NET Core 3.1</li>
</ul>
<p>Is there a target not on this list you'd like to see? Raise an
<a href="https://github.com/cyotek/Cyotek.Drawing.HistoricalDate/issues" rel="external nofollow noopener">issue</a>, or even better, a <a href="https://github.com/cyotek/Cyotek.Drawing.HistoricalDate/pulls" rel="external nofollow noopener">pull request</a>.</p>
<h2 id="acknowledgements">Acknowledgements</h2>
<ul>
<li>Inspiration gleaned from <a href="http://flipbit.co.uk/2009/03/representing-large-ad-and-bc-dates-in-c" rel="external nofollow noopener">Representing Large AD and BC Dates
in C#</a> and <a href="https://github.com/cerinman/HistoricalDate/blob/master/HistoricalDate/HistoricalDate/HistoricalDate.cs" rel="external nofollow noopener">cerinman/HistoricalDate</a></li>
<li>The NuGet package icon is from the <a href="https://www.iconfinder.com/icons/5402415/history_recent_repeat_replay_refresh_reload_icon" rel="external nofollow noopener">Multimedia Solid 24px icon
set</a> by <a href="https://www.iconfinder.com/amoghdesign" rel="external nofollow noopener">amoghdesign</a>. Licensed under
Attribution-NonCommercial 3.0 Unported (CC BY-NC 3.0).</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/cyotek-historical-date-library .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a custom single-axis scrolling control in WinFormsurn:uuid:fd062dc5-b1d0-4bb7-a436-a5441249e2542020-12-28T13:22:50Z2020-12-28T13:22:50Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/scroll-demo-many-items.png" class="gallery" title="Neither hot nor cold, but just right" ><img src="https://images.cyotek.com/image/thumbnail/devblog/scroll-demo-many-items.png" alt="Neither hot nor cold, but just right" decoding="async" loading="lazy" /></a><figcaption>Neither hot nor cold, but just right</figcaption></figure>
<p>Over the years, I have written several custom controls that
supporting scrolling. However, almost invariably they have some
flaw that meant scrolling was sub-optimal. For example, in our
<a href="https://www.cyotek.com/cyotek-gif-animator">Gif Animator</a> software, the frame reel can cut off the
last frame. In other programs, scrolling goes beyond visible
items and thus presents an empty control at worst or a
smattering of items at best.</p>
<p>As it seems each control had its own different flaws, I created
a dedicated demonstration program to iron out scrolling issues
in my code, concentrating on single axis scrolling as most of my
controls only scroll in one direction. This article covers the
key points in creating a custom scroll control.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/scroll-demo-under-scroll.png" class="gallery" title="Exhibit A: Here, the scrollbar is as far along as it allows, but the last frame is only partially visible" ><img src="https://images.cyotek.com/image/thumbnail/devblog/scroll-demo-under-scroll.png" alt="Exhibit A: Here, the scrollbar is as far along as it allows, but the last frame is only partially visible" decoding="async" loading="lazy" /></a><figcaption>Exhibit A: Here, the scrollbar is as far along as it allows, but the last frame is only partially visible</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/scroll-demo-over-scroll.png" class="gallery" title="Exhibit B: In this example, the maximum value of the scrollbar allows over scroll for an almost empty display" ><img src="https://images.cyotek.com/image/thumbnail/devblog/scroll-demo-over-scroll.png" alt="Exhibit B: In this example, the maximum value of the scrollbar allows over scroll for an almost empty display" decoding="async" loading="lazy" /></a><figcaption>Exhibit B: In this example, the maximum value of the scrollbar allows over scroll for an almost empty display</figcaption></figure><h2 id="setting-the-scene">Setting the scene</h2>
<p>I'm making the assumption that the scrolling will be of rows of
tiles, either where each tile is the full width of the control
(a list), or where there are multiple columns of a fixed size
that are either related (a grid) or not (a multi column list).</p>
<p>The <code>DemoScrollControl</code> featured in this sample probably isn't
directly usable in your projects but should make an excellent
starting point.</p>
<p>I've added <code>ItemCount</code>, <code>ItemHeight</code> and <code>Columns</code> properties
which can be used to simulate a list or grid. In a real control
you might have <code>Items</code> or <code>Columns</code> collections, but having a
simple count property allows me to simulate different control
types for testing. It is also good for creating virtual lists,
although that is a topic for another post.</p>
<p>The <code>Padding</code> property influences the client area of the control
(i.e. the part where the list contents are drawn) and there is
also a <code>Gap</code> property to control spacing between items, again
mostly for simulation purposes.</p>
<p>I'm using a <code>VScrollBar</code> control to provide the scrolling
interface as this is far simpler than applying the <code>WS_VSCROLL</code>
style and going native.</p>
<p>As this is a demonstration control, it simply paints the index
of each &quot;item&quot;. In addition, this article is about scrolling so
I'm not going to covering painting, hit testing or anything
unrelated to the scrolling aspects. The <a href="https://github.com/cyotek/ScrollDemo" rel="external nofollow noopener">source code
example</a> demonstrates a complete implementation.</p>
<h2 id="defining-the-number-of-rows">Defining the number of rows</h2>
<p>When the contents of control changes, we need to calculate the
number of rows, as this directly influences the scrollbar. For a
list or grid, that would be a simple item count. For a
multi-column list, it would be the number of items divided by
the column count.</p>
<p>We also need to determine how many rows are at least partially
visible in the control, which we use for painting and hit
testing. Finally, the number of <em>fully</em> visible rows is used to
define the page size.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> DefineRows<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_itemCount <span class="symbol">&gt;</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> _columns <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> height<span class="symbol">;</span>

 _rows <span class="symbol">=</span> _itemCount <span class="symbol">/</span> _columns<span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_itemCount <span class="symbol">%</span> _columns <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _rows<span class="symbol">++</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 height <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>InnerClient<span class="symbol">.</span>Height<span class="symbol">;</span>

 _fullyVisibleRows <span class="symbol">=</span> height <span class="symbol">/</span> <span class="symbol">(</span>_itemHeight <span class="symbol">+</span> _gap<span class="symbol">)</span><span class="symbol">;</span>
 _visibleRows <span class="symbol">=</span> _fullyVisibleRows<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_fullyVisibleRows <span class="symbol">==</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// always make sure there is at least one row, otherwise you can&#39;t scroll</span>
 _fullyVisibleRows <span class="symbol">=</span> <span class="number">1</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_rows <span class="symbol">&gt;</span> _visibleRows <span class="symbol">&amp;&amp;</span> height <span class="symbol">%</span> <span class="symbol">(</span>_itemHeight <span class="symbol">+</span> _gap<span class="symbol">)</span> <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// account for a partially visible row</span>
 _visibleRows<span class="symbol">++</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>We calculate these values whenever a property changes that could
affect the display, for example <code>Size</code>, <code>Font</code> and <code>Padding</code> for
built-in properties, and <code>ItemCount</code>, <code>ItemHeight</code>, <code>Gap</code> and
<code>Columns</code> from the custom.</p>
<h2 id="updating-the-scrollbar-properties">Updating the scrollbar properties</h2>
<p>With our row count and visible row count defined, we can now
update our scrollbar by setting the <code>Maximum</code> and <code>LargeChange</code>
properties respectively.</p>
<blockquote>
<p>This is one of the mistakes I kept making, as I would always
set <code>LargeChange</code> to be an arbitrary value for the number of
items to scroll, but I think I was getting thrown by the naming
of the property (perhaps it is named LargeChange for
compatibility with ancient VB6?). Remember that the scrollbar
control wraps a Win32 scroll control, and the <code>SCROLLINFO</code>
structure describes <code>LargeChange</code> as <code>Page</code>. Thinking of it in
these terms let me realise I should be setting this to the
number of visible items and solved an overflow issue.</p>
</blockquote>
<p>If all items can fit without the need for scrolling, I disable
and hide the scrollbar.</p>
<p>The <code>SetScrollValue</code> helper function updates the value of a
scrollbar, ensuring that it fits within the minimum and maximum
range. However, it also adjusts the range to be the <code>Maximum</code>
minus the <code>LargeChange</code> which prevents another overflow issue
when using the mouse wheel.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> DefineRows<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_itemCount <span class="symbol">&gt;</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> _columns <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// snip</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_scrollBar <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _scrollBar<span class="symbol">.</span>LargeChange <span class="symbol">=</span> _fullyVisibleRows<span class="symbol">;</span>
 _scrollBar<span class="symbol">.</span>Maximum <span class="symbol">=</span> _rows <span class="symbol">-</span> <span class="number">1</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>SetScrollValue<span class="symbol">(</span>_scrollBar<span class="symbol">.</span>Value<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_scrollBar <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _scrollBar<span class="symbol">.</span>Enabled <span class="symbol">=</span> _rows <span class="symbol">&gt;</span> _fullyVisibleRows<span class="symbol">;</span>
 _scrollBar<span class="symbol">.</span>Visible <span class="symbol">=</span> _rows <span class="symbol">&gt;</span> _fullyVisibleRows<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> SetScrollValue<span class="symbol">(</span><span class="keyword">int</span> value<span class="symbol">)</span>
<span class="symbol">{</span>
 value <span class="symbol">=</span> Math<span class="symbol">.</span>Min<span class="symbol">(</span>value<span class="symbol">,</span> _scrollBar<span class="symbol">.</span>Maximum <span class="symbol">-</span> <span class="symbol">(</span>_scrollBar<span class="symbol">.</span>LargeChange <span class="symbol">-</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>value <span class="symbol">&lt;</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 value <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 _scrollBar<span class="symbol">.</span>Value <span class="symbol">=</span> value<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Note that this means the control can host a maximum of
<code>2,147,483,647</code> rows. If you need more than this then you'd
need to rethink all scrollbar interactions, or your entire UI
for that matter... I don't think I'd want to use such an
interface!</p>
<h2 id="setting-the-first-visible-item">Setting the first visible item</h2>
<p>I choose not to use &quot;smooth scrolling&quot; in most of my controls as
it is much easier to always have a partially displayed item at
the bottom than at the top. Being able to get and set the first,
or &quot;top&quot;, item is a core part of making scrolling work.</p>
<p>With the control set up the way it is, the first item is the
current scrollbar position multiplied by the number of columns.
This also means that when <em>setting</em> the first item, we set the
scrollbar position to be the new value divided by the column
count. You only need to do this for multi column lists, for
lists or grids you can simply apply the value as is.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="symbol">[</span>DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">int</span> TopItem
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _topItem<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>value <span class="symbol">&lt;</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 value <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>value <span class="symbol">&gt;</span> _itemCount<span class="symbol">)</span>
 <span class="symbol">{</span>
 value <span class="symbol">=</span> _itemCount<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_topItem <span class="symbol">!=</span> value<span class="symbol">)</span>
 <span class="symbol">{</span>
 _topItem <span class="symbol">=</span> value<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_columns <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>SetScrollValue<span class="symbol">(</span>value <span class="symbol">/</span> _columns<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">this</span><span class="symbol">.</span>OnTopItemChanged<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Knowing the first item allows us to easily perform hit testing
and painting without having to store bounds information.</p>
<h2 id="scrolling-with-the-keyboard">Scrolling with the keyboard</h2>
<p>As the <code>SetScrollValue</code> method keeps the new value within the
range of the scrollbar, this time around I tried something new
and <em>all</em> scroll actions were performed by line count. Usually I
have a special case for the start and end of the list, but if I
tell it to scroll by the negative item count and positive item
count, then I can achieve the same result without special cases.</p>
<p>Due to using the arrow keys for scrolling, first I need to
intercept <code>IsInputKey</code> so that I can tell our control we want to
process them, and I include the other keys I'll use for
scrolling for good measure.</p>
<p>Then, in <code>OnKeyDown</code>, I call our <code>ProcessScrollKeys</code> method
which looks at the incoming key and scrolls the control
accordingly.</p>
<table>
<thead>
<tr>
<th>Key</th>
<th>Rows to scroll</th>
</tr>
</thead>
<tbody>
<tr>
<td>Up</td>
<td>-1</td>
</tr>
<tr>
<td>Down</td>
<td>1</td>
</tr>
<tr>
<td>Home</td>
<td>-item count</td>
</tr>
<tr>
<td>End</td>
<td>item count</td>
</tr>
<tr>
<td>Page Up</td>
<td>-visible rows</td>
</tr>
<tr>
<td>Page Down</td>
<td>visible rows</td>
</tr>
</tbody>
</table>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">bool</span> IsInputKey<span class="symbol">(</span>Keys keyData<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> keyData <span class="symbol">==</span> Keys<span class="symbol">.</span>Up
 <span class="symbol">||</span> keyData <span class="symbol">==</span> Keys<span class="symbol">.</span>Down
 <span class="symbol">||</span> keyData <span class="symbol">==</span> Keys<span class="symbol">.</span>Home
 <span class="symbol">||</span> keyData <span class="symbol">==</span> Keys<span class="symbol">.</span>End
 <span class="symbol">||</span> keyData <span class="symbol">==</span> Keys<span class="symbol">.</span>PageUp
 <span class="symbol">||</span> keyData <span class="symbol">==</span> Keys<span class="symbol">.</span>PageDown
 <span class="symbol">||</span> <span class="keyword">base</span><span class="symbol">.</span>IsInputKey<span class="symbol">(</span>keyData<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnKeyDown<span class="symbol">(</span>KeyEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnKeyDown<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>e<span class="symbol">.</span>Handled<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ProcessScrollKeys<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> ProcessScrollKeys<span class="symbol">(</span>KeyEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">switch</span> <span class="symbol">(</span>e<span class="symbol">.</span>KeyCode<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> Keys<span class="symbol">.</span>Up<span class="symbol">:</span>
 <span class="keyword">this</span><span class="symbol">.</span>ScrollControl<span class="symbol">(</span><span class="symbol">-</span><span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>

 <span class="keyword">case</span> Keys<span class="symbol">.</span>Down<span class="symbol">:</span>
 <span class="keyword">this</span><span class="symbol">.</span>ScrollControl<span class="symbol">(</span><span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>

 <span class="keyword">case</span> Keys<span class="symbol">.</span>PageUp<span class="symbol">:</span>
 <span class="keyword">this</span><span class="symbol">.</span>ScrollControl<span class="symbol">(</span><span class="symbol">-</span>_fullyVisibleRows<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>

 <span class="keyword">case</span> Keys<span class="symbol">.</span>PageDown<span class="symbol">:</span>
 <span class="keyword">this</span><span class="symbol">.</span>ScrollControl<span class="symbol">(</span>_fullyVisibleRows<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>

 <span class="keyword">case</span> Keys<span class="symbol">.</span>Home<span class="symbol">:</span>
 <span class="keyword">this</span><span class="symbol">.</span>ScrollControl<span class="symbol">(</span><span class="symbol">-</span>_itemCount<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>

 <span class="keyword">case</span> Keys<span class="symbol">.</span>End<span class="symbol">:</span>
 <span class="keyword">this</span><span class="symbol">.</span>ScrollControl<span class="symbol">(</span>_itemCount<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> ScrollControl<span class="symbol">(</span><span class="keyword">int</span> lines<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> value<span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 value <span class="symbol">=</span> <span class="keyword">checked</span><span class="symbol">(</span>_scrollBar<span class="symbol">.</span>Value <span class="symbol">+</span> lines<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>OverflowException<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>lines <span class="symbol">&lt;</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 value <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 value <span class="symbol">=</span> _itemCount<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">this</span><span class="symbol">.</span>SetScrollValue<span class="symbol">(</span>value<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>Note: While writing this article, I found that jumping to the
end of the list didn't work correctly if the sum of the
current position plus the increment was above <code>int.MaxValue</code>
due to integer overflow. I changed the <code>ScrollControl</code> method
to wrap the increment in a <code>checked</code> statement to throw in
this scenario, then choose a new min/max accordingly. This
should be a pretty rare scenario so you can always remove the
<code>try</code> ... <code>catch</code> block and the <code>checked</code> statement.</p>
</blockquote>
<h2 id="scrolling-with-the-mouse-wheel">Scrolling with the mouse wheel</h2>
<p>In previous controls, I might have done something similar to the
below. While this works, I have received <a href="https://github.com/cyotek/Cyotek.Windows.Forms.ImageBox/issues/8" rel="external nofollow noopener">reports</a>
that this isn't always reliable but it is something I have never
been able to reproduce.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseWheel<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// naive implementation</span>
 <span class="keyword">this</span><span class="symbol">.</span>ScrollControl<span class="symbol">(</span><span class="symbol">-</span><span class="symbol">(</span>e<span class="symbol">.</span>Delta <span class="symbol">/</span> SystemInformation<span class="symbol">.</span>MouseWheelScrollDelta<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnMouseWheel<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>This time, I decided to use a different solution. Martin Mitáš
wrote <a href="https://www.codeproject.com/articles/1042516/custom-controls-in-win-api-scrolling" rel="external nofollow noopener">Custom Controls in Win32 API: Scrolling</a> on
Code Project which has a helper function for accumulating wheel
deltas. Unfortunately I still don't have a mouse that reports a
non-standard delta so I am unable to test that this resolves
those issues, but certainly the code works well with my
hardware.</p>
<p>I converted the original C++ code into a C# class that I can
reuse with other projects.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">static</span> <span class="keyword">class</span> WheelHelper
<span class="symbol">{</span>
 <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> <span class="keyword">int</span><span class="symbol">[</span><span class="symbol">]</span> _accumulator <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">int</span><span class="symbol">[</span><span class="number">2</span><span class="symbol">]</span><span class="symbol">;</span>

 <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> <span class="keyword">uint</span><span class="symbol">[</span><span class="symbol">]</span> _lastActivity <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">uint</span><span class="symbol">[</span><span class="number">2</span><span class="symbol">]</span><span class="symbol">;</span>

 <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> <span class="keyword">object</span> _lock <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">object</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">private</span> <span class="keyword">static</span> IntPtr _hwndCurrent <span class="symbol">=</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">;</span>

 <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> WheelScrollLines<span class="symbol">(</span>IntPtr hwnd<span class="symbol">,</span> <span class="keyword">int</span> delta<span class="symbol">,</span> <span class="keyword">int</span> pageSize<span class="symbol">,</span> <span class="keyword">bool</span> isVertical<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">uint</span> now<span class="symbol">;</span>
 <span class="keyword">int</span> scrollSysParam<span class="symbol">;</span>
 <span class="keyword">int</span> linesPerWheelDelta<span class="symbol">;</span>
 <span class="keyword">int</span> dirIndex <span class="symbol">=</span> isVertical <span class="symbol">?</span> <span class="number">0</span> <span class="symbol">:</span> <span class="number">1</span><span class="symbol">;</span>
 <span class="keyword">int</span> lines<span class="symbol">;</span>

 now <span class="symbol">=</span> GetTickCount<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>pageSize <span class="symbol">&lt;</span> <span class="number">1</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 pageSize <span class="symbol">=</span> <span class="number">1</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 scrollSysParam <span class="symbol">=</span> isVertical
 <span class="symbol">?</span> SPI_GETWHEELSCROLLLINES
 <span class="symbol">:</span> SPI_GETWHEELSCROLLCHARS<span class="symbol">;</span>

 linesPerWheelDelta <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>SystemParametersInfo<span class="symbol">(</span>scrollSysParam<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="keyword">ref</span> linesPerWheelDelta<span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 linesPerWheelDelta <span class="symbol">=</span> <span class="number">3</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>linesPerWheelDelta <span class="symbol">==</span> WHEEL_PAGESCROLL<span class="symbol">)</span>
 <span class="symbol">{</span>
 linesPerWheelDelta <span class="symbol">=</span> pageSize<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>linesPerWheelDelta <span class="symbol">&gt;</span> pageSize<span class="symbol">)</span>
 <span class="symbol">{</span>
 linesPerWheelDelta <span class="symbol">=</span> pageSize<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">lock</span> <span class="symbol">(</span>_lock<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>hwnd <span class="symbol">!=</span> _hwndCurrent<span class="symbol">)</span>
 <span class="symbol">{</span>
 _hwndCurrent <span class="symbol">=</span> hwnd<span class="symbol">;</span>
 _accumulator<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 _accumulator<span class="symbol">[</span><span class="number">1</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>now <span class="symbol">-</span> _lastActivity<span class="symbol">[</span>dirIndex<span class="symbol">]</span> <span class="symbol">&gt;</span> SystemInformation<span class="symbol">.</span>DoubleClickTime <span class="symbol">*</span> <span class="number">2</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _accumulator<span class="symbol">[</span>dirIndex<span class="symbol">]</span> <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">(</span>_accumulator<span class="symbol">[</span>dirIndex<span class="symbol">]</span> <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">)</span> <span class="symbol">==</span> <span class="symbol">(</span>delta <span class="symbol">&lt;</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _accumulator<span class="symbol">[</span>dirIndex<span class="symbol">]</span> <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>linesPerWheelDelta <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _accumulator<span class="symbol">[</span>dirIndex<span class="symbol">]</span> <span class="symbol">+=</span> delta<span class="symbol">;</span>

 lines <span class="symbol">=</span> _accumulator<span class="symbol">[</span>dirIndex<span class="symbol">]</span> <span class="symbol">*</span> linesPerWheelDelta <span class="symbol">/</span> WHEEL_DELTA<span class="symbol">;</span>

 _accumulator<span class="symbol">[</span>dirIndex<span class="symbol">]</span> <span class="symbol">-=</span> lines <span class="symbol">*</span> WHEEL_DELTA <span class="symbol">/</span> linesPerWheelDelta<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 lines <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 _accumulator<span class="symbol">[</span>dirIndex<span class="symbol">]</span> <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 _lastActivity<span class="symbol">[</span>dirIndex<span class="symbol">]</span> <span class="symbol">=</span> now<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> isVertical <span class="symbol">?</span> <span class="symbol">-</span>lines <span class="symbol">:</span> lines<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Our <code>OnMouseWheel</code> override now asks the helper class how many
lines to scroll by and acts accordingly.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseWheel<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnMouseWheel<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_fullyVisibleRows <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ScrollControl<span class="symbol">(</span>WheelHelper<span class="symbol">.</span>WheelScrollLines<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> e<span class="symbol">.</span>Delta<span class="symbol">,</span> _fullyVisibleRows<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="mouse-wheel-scrolling-on-older-versions-of-windows">Mouse wheel scrolling on older versions of Windows</h2>
<p>In versions of Windows prior to Windows 10, the <code>WM_MOUSEWHEEL</code>
and <code>WM_MOUSEHWHEEL</code> messages were only sent to the window with
focus. Windows 10 (or at least recent versions of it) changed
this behaviour so the wheel messages would be sent even if the
window didn't have focus.</p>
<p>Therefore, if you want your control to be scrollable via the
mouse wheel regardless of if it has focus or not on older
versions of Windows, you'd need to intercept the messages
yourself. The easiest way of doing this is via a <a href="/post/using-message-filters-in-windows-forms-applications">message
filter</a>.</p>
<p>The following class can be used to intercept the mouse wheel
messages and forward them to the control under the mouse. We do
this by checking for the <code>WM_MOUSEWHEEL</code> and <code>WM_MOUSEHWHEEL</code>
and on receiving these, if the window under the mouse is an
instance of our control we forward the message onto the control
and prevent it from being sent to the original window. For all
other cases, we let the message pass though and be handled
normally.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">sealed</span> <span class="keyword">class</span> MouseWheelMessageFilter<span class="symbol">&lt;</span>T<span class="symbol">&gt;</span> <span class="symbol">:</span> IMessageFilter
 <span class="keyword">where</span> T <span class="symbol">:</span> Control
<span class="symbol">{</span>
 <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">bool</span> _enabled<span class="symbol">;</span>

 <span class="keyword">private</span> <span class="keyword">static</span> MouseWheelMessageFilter<span class="symbol">&lt;</span>T<span class="symbol">&gt;</span> _instance<span class="symbol">;</span>

 <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">bool</span> Enabled
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _enabled<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_enabled <span class="symbol">!=</span> value<span class="symbol">)</span>
 <span class="symbol">{</span>
 _enabled <span class="symbol">=</span> value<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_enabled<span class="symbol">)</span>
 <span class="symbol">{</span>
 Interlocked<span class="symbol">.</span>CompareExchange<span class="symbol">(</span><span class="keyword">ref</span> _instance<span class="symbol">,</span> <span class="keyword">new</span> MouseWheelMessageFilter<span class="symbol">&lt;</span>T<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>

 Application<span class="symbol">.</span>AddMessageFilter<span class="symbol">(</span>_instance<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>_instance <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Application<span class="symbol">.</span>RemoveMessageFilter<span class="symbol">(</span>_instance<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">bool</span> IMessageFilter<span class="symbol">.</span>PreFilterMessage<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>

 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>Msg <span class="symbol">==</span> WM_MOUSEWHEEL <span class="symbol">||</span> m<span class="symbol">.</span>Msg <span class="symbol">==</span> WM_MOUSEHWHEEL<span class="symbol">)</span>
 <span class="symbol">{</span>
 IntPtr hControlUnderMouse<span class="symbol">;</span>

 hControlUnderMouse <span class="symbol">=</span> WindowFromPoint<span class="symbol">(</span><span class="keyword">new</span> Point<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>m<span class="symbol">.</span>LParam<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>hControlUnderMouse <span class="symbol">!=</span> m<span class="symbol">.</span>HWnd <span class="symbol">&amp;&amp;</span> Control<span class="symbol">.</span>FromHandle<span class="symbol">(</span>hControlUnderMouse<span class="symbol">)</span> <span class="keyword">is</span> T<span class="symbol">)</span>
 <span class="symbol">{</span>
 SendMessage<span class="symbol">(</span>hControlUnderMouse<span class="symbol">,</span> m<span class="symbol">.</span>Msg<span class="symbol">,</span> m<span class="symbol">.</span>WParam<span class="symbol">,</span> m<span class="symbol">.</span>LParam<span class="symbol">)</span><span class="symbol">;</span>

 result <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>While the above filter will work just as well on newer versions
of Windows (for example the <a href="https://github.com/cyotek/Cyotek.Windows.Forms.ImageBox" rel="external nofollow noopener">ImageBox</a> control still
currently always applies it), there is no point in running extra
code if the OS will handle it, so consider not enabling it for
Windows 10 or above.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">static</span> DemoScrollControl<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 OperatingSystem os<span class="symbol">;</span>

 os <span class="symbol">=</span> Environment<span class="symbol">.</span>OSVersion<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>os<span class="symbol">.</span>Platform <span class="symbol">==</span> PlatformID<span class="symbol">.</span>Win<span class="number">32</span>NT <span class="symbol">&amp;&amp;</span> os<span class="symbol">.</span>Version<span class="symbol">.</span>Major <span class="symbol">&lt;</span> <span class="number">10</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 MouseWheelMessageFilter<span class="symbol">&lt;</span>DemoScrollControl<span class="symbol">&gt;.</span>Enabled <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>Important! If the application using your control does not
include a manifest that is explicit about which Windows
versions it supports, it highly likely that Windows will lie
about the version and report an earlier version</p>
</blockquote>
<h2 id="final-words">Final words</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/scroll-demo-columns.png" class="gallery" title="Scrolling with multiple columns" ><img src="https://images.cyotek.com/image/thumbnail/devblog/scroll-demo-columns.png" alt="Scrolling with multiple columns" decoding="async" loading="lazy" /></a><figcaption>Scrolling with multiple columns</figcaption></figure>
<p>Sitting down and properly thinking about the issues in a sample
dedicated purely to that one aspect certainly worked, and
hopefully scroll issues will be a thing of the past in my
programs. Or at least once I update them.</p>
<h2 id="getting-the-source">Getting the source</h2>
<p>The demonstration control is available from our <a href="https://github.com/cyotek/ScrollDemo" rel="external nofollow noopener">GitHub
repository</a>.</p>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-a-custom-single-axis-scrolling-control-in-winforms .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comkbd Markdig Pluginurn:uuid:c68e9b9c-da02-4d1f-a90d-cda314fe834d2020-12-21T06:11:02Z2020-12-21T06:11:02Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/markdig.keyboard-demo.png" class="gallery" title="A basic demonstration showing the output" ><img src="https://images.cyotek.com/image/thumbnail/devblog/markdig.keyboard-demo.png" alt="A basic demonstration showing the output" decoding="async" loading="lazy" /></a><figcaption>A basic demonstration showing the output</figcaption></figure>
<p>I try to keep my markdown documents plain, avoiding HTML where
possible. The core cyotek.com website is mostly powered by
markdown, with embedded HTML and arcane constructs with assorted
regular expression processors. The <a href="https://devblog.cyotek.com/">new blog</a> uses pure
markdown and although currently still uses an arcane construct
or two for compatibility, these are provided via <a href="https://github.com/lunet-io/markdig/" rel="external nofollow noopener">Markdig</a>
extensions rather than regular expressions.</p>
<p>However, one area I still keep writing HTML is for <code>kbd</code> tags,
mostly in the documentation for our products but also in some
blog articles. I finally decided to write another extension
library to handle this and made it available for download.</p>
<p>The structure of this extension follows the same approach as in
my original article on <a href="/post/writing-custom-markdig-extensions">writing custom Markdig
extensions</a>, so I won't describe the code here.</p>
<h2 id="getting-the-library">Getting the library</h2>
<p>The easiest way of obtaining the library is via <a href="https://www.nuget.org/packages/Cyotek.Markdig.Keyboard/" rel="external nofollow noopener">NuGet</a>.</p>
<blockquote>
<p><code>Install-Package Cyotek.Markdig.Keyboard</code></p>
</blockquote>
<h2 id="using-the-library">Using the library</h2>
<p>When you build your Markdig pipeline, call <code>UseKeyboard()</code>. Any
text wrapped in double angled brackets will be converted to
<code>kbd</code> tags.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
_markdownPipeline <span class="symbol">=</span> <span class="keyword">new</span> MarkdownPipelineBuilder<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>UseAdvancedExtensions<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>UseKeyboard<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>Build<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="options">Options</h2>
<p>There is also a <code>KeyboardOptions</code> class you can use to control
the output. Currently it allows you either to assign a CSS class
to generated elements via the <code>ClassName</code> property, or specify a
different HTML tag via the <code>TagName</code> property.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
_markdownPipeline <span class="symbol">=</span> <span class="keyword">new</span> MarkdownPipelineBuilder<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>UseAdvancedExtensions<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>UseKeyboard<span class="symbol">(</span><span class="keyword">new</span> KeyboardOptions
 <span class="symbol">{</span>
 ClassName <span class="symbol">=</span> <span class="string">&quot;my-class&quot;</span><span class="symbol">,</span>
 TagName <span class="symbol">=</span> <span class="string">&quot;code&quot;</span>
 <span class="symbol">}</span><span class="symbol">)</span>
 <span class="symbol">.</span>Build<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="examples">Examples</h2>
<p>This first example uses default options, which output <code>kbd</code> tags
with no further processing.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">var</span> markdownPipeline <span class="symbol">=</span> <span class="keyword">new</span> MarkdownPipelineBuilder<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>UseAdvancedExtensions<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>UseKeyboard<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>Build<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">var</span> output <span class="symbol">=</span> Markdown<span class="symbol">.</span>ToHtml<span class="symbol">(</span><span class="string">@&quot;### File Menu

| Description | Shortcut Keys |
| ----------- | ------------- |
| New | &lt;&lt;Ctrl+N&gt;&gt; |
| Open | &lt;&lt;Ctrl+O&gt;&gt; |
| Save | &lt;&lt;Ctrl+S&gt;&gt; |
| Save As | |
| Export | |
| Exit | &lt;&lt;Alt+F4&gt;&gt; |
&quot;</span><span class="symbol">,</span> markdownPipeline<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<figure class="lang-html highlight"><figcaption><span>html</span></figcaption><pre class="code">
<span class="literal">&lt;</span><span class="name">h3</span> <span class="name">id</span><span class="symbol">=</span><span class="attribute">&quot;file-menu&quot;</span><span class="literal">&gt;</span>File Menu<span class="literal">&lt;/</span><span class="name">h3</span><span class="literal">&gt;</span>
<span class="literal">&lt;</span><span class="name">table</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">thead</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">tr</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">th</span><span class="literal">&gt;</span>Description<span class="literal">&lt;/</span><span class="name">th</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">th</span><span class="literal">&gt;</span>Shortcut Keys<span class="literal">&lt;/</span><span class="name">th</span><span class="literal">&gt;</span>
 <span class="literal">&lt;/</span><span class="name">tr</span><span class="literal">&gt;</span>
 <span class="literal">&lt;/</span><span class="name">thead</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">tbody</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">tr</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">td</span><span class="literal">&gt;</span>New<span class="literal">&lt;/</span><span class="name">td</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">td</span><span class="literal">&gt;</span><span class="literal">&lt;</span><span class="name">kbd</span><span class="literal">&gt;</span>Ctrl+N<span class="literal">&lt;/</span><span class="name">kbd</span><span class="literal">&gt;</span><span class="literal">&lt;/</span><span class="name">td</span><span class="literal">&gt;</span>
 <span class="literal">&lt;/</span><span class="name">tr</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">tr</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">td</span><span class="literal">&gt;</span>Open<span class="literal">&lt;/</span><span class="name">td</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">td</span><span class="literal">&gt;</span><span class="literal">&lt;</span><span class="name">kbd</span><span class="literal">&gt;</span>Ctrl+O<span class="literal">&lt;/</span><span class="name">kbd</span><span class="literal">&gt;</span><span class="literal">&lt;/</span><span class="name">td</span><span class="literal">&gt;</span>
 <span class="literal">&lt;/</span><span class="name">tr</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">tr</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">td</span><span class="literal">&gt;</span>Save<span class="literal">&lt;/</span><span class="name">td</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">td</span><span class="literal">&gt;</span><span class="literal">&lt;</span><span class="name">kbd</span><span class="literal">&gt;</span>Ctrl+S<span class="literal">&lt;/</span><span class="name">kbd</span><span class="literal">&gt;</span><span class="literal">&lt;/</span><span class="name">td</span><span class="literal">&gt;</span>
 <span class="literal">&lt;/</span><span class="name">tr</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">tr</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">td</span><span class="literal">&gt;</span>Save As<span class="literal">&lt;/</span><span class="name">td</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">td</span><span class="literal">&gt;</span><span class="literal">&lt;/</span><span class="name">td</span><span class="literal">&gt;</span>
 <span class="literal">&lt;/</span><span class="name">tr</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">tr</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">td</span><span class="literal">&gt;</span>Export<span class="literal">&lt;/</span><span class="name">td</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">td</span><span class="literal">&gt;</span><span class="literal">&lt;/</span><span class="name">td</span><span class="literal">&gt;</span>
 <span class="literal">&lt;/</span><span class="name">tr</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">tr</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">td</span><span class="literal">&gt;</span>Exit<span class="literal">&lt;/</span><span class="name">td</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">td</span><span class="literal">&gt;</span><span class="literal">&lt;</span><span class="name">kbd</span><span class="literal">&gt;</span>Alt+F4<span class="literal">&lt;/</span><span class="name">kbd</span><span class="literal">&gt;</span><span class="literal">&lt;/</span><span class="name">td</span><span class="literal">&gt;</span>
 <span class="literal">&lt;/</span><span class="name">tr</span><span class="literal">&gt;</span>
 <span class="literal">&lt;/</span><span class="name">tbody</span><span class="literal">&gt;</span>
<span class="literal">&lt;/</span><span class="name">table</span><span class="literal">&gt;</span>
</pre>
</figure>
<p>This second example uses custom options to change the output tag
to <code>code</code> and apply a custom class.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">var</span> markdownPipeline <span class="symbol">=</span> <span class="keyword">new</span> MarkdownPipelineBuilder<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>UseAdvancedExtensions<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>UseKeyboard<span class="symbol">(</span><span class="keyword">new</span> KeyboardOptions
 <span class="symbol">{</span>
 ClassName <span class="symbol">=</span> <span class="string">&quot;keyboard&quot;</span><span class="symbol">,</span>
 TagName <span class="symbol">=</span> <span class="string">&quot;code&quot;</span>
 <span class="symbol">}</span><span class="symbol">)</span>
 <span class="symbol">.</span>Build<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">var</span> output <span class="symbol">=</span> Markdown<span class="symbol">.</span>ToHtml<span class="symbol">(</span><span class="string">&quot;Press &lt;&lt;Alt+F4&gt;&gt; to exit.&quot;</span><span class="symbol">,</span> markdownPipeline<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<figure class="lang-html highlight"><figcaption><span>html</span></figcaption><pre class="code">
<span class="literal">&lt;</span><span class="name">p</span><span class="literal">&gt;</span>Press <span class="literal">&lt;</span><span class="name">code</span> <span class="name">class</span><span class="symbol">=</span><span class="attribute">&quot;keyboard&quot;</span><span class="literal">&gt;</span>Alt+F4<span class="literal">&lt;/</span><span class="name">code</span><span class="literal">&gt;</span> to exit.<span class="literal">&lt;/</span><span class="name">p</span><span class="literal">&gt;</span>
</pre>
</figure>
<h2 id="requirements">Requirements</h2>
<p>.NET Framework 3.5 or later.</p>
<p>Pre-built binaries are available via a signed <a href="https://www.nuget.org/packages/Cyotek.Markdig.Keyboard/" rel="external nofollow noopener">NuGet package</a>
containing the following targets.</p>
<ul>
<li>.NET 4.8</li>
<li>.NET Standard 2.0</li>
<li>.NET Standard 2.1</li>
<li>.NET Core 2.1</li>
<li>.NET Core 3.1</li>
<li>.NET 5.0</li>
</ul>
<h2 id="source">Source</h2>
<p>Source code is available from the <a href="https://github.com/cyotek/Markdig.Keyboard" rel="external nofollow noopener">GitHub repository</a>.</p>
<h2 id="implementation-note">Implementation Note</h2>
<p>The main library and the test library projects are in the SDK
style. However, the WinForms demo is in the classic csproj
format - originally it was SDK using .NET 5, but the WinForms
designer in .NET 5 is shockingly bad. I had numerous issues with
it, ranging from the designer frequently unable to load, event
handlers getting unbound, and functionality like inline editing
of <code>MenuStrip</code> controls just don't work at all.</p>
<p>In the end I converted it back to classic .NET as it is
completely unusable in its current state, in my view.</p>
<h2 id="license">License</h2>
<p>This source is licensed under the MIT license. See <code>LICENSE.txt</code>
for the full text.</p>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/kbd-markdig-plugin .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comAn introduction to using Windows Image Acquisition (WIA) via C#urn:uuid:dd18ab48-d762-4134-9758-e255eaec629b2021-04-17T18:47:35Z2020-11-06T07:37:13Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/wia-crash-course-demo.png" class="gallery" title="Screenshot of the sample application" ><img src="https://images.cyotek.com/image/thumbnail/devblog/wia-crash-course-demo.png" alt="Screenshot of the sample application" decoding="async" loading="lazy" /></a><figcaption>Screenshot of the sample application</figcaption></figure>
<p>In this post I'm going to cover a basic crash course for using
the Microsoft Windows Image Acquisition library (WIA) in a C#
WinForms application. It's mostly based around using the various
built-in dialogues in order to easily add image acquisition and
printing to your application, I don't go into deeper
functionality such as command execution.</p>
<p>I started writing this post in September 2019 before burning out
in a fairly large fashion. Is it normally my policy that I don't
write a new development blog post until the previous is
finished, even if not published, but after staring blankly at it
for 9 months whenever I opened it, I had to shelve it and move
on. It is now approaching 14 months and so I'm pushing it out as
is.</p>
<h2 id="getting-started">Getting started</h2>
<h3 id="adding-a-reference-to-wia">Adding a reference to WIA</h3>
<ul>
<li>Open the <em>Add Reference</em> dialogue</li>
<li>Select <em>COM</em> from the left-hand sidebar</li>
<li>Find <em>Microsoft Windows Image Acquisition Library 2.0</em> from
the list and check it (you may find it easier just to type
<code>image</code> into the search box)</li>
<li>Click OK to add the reference</li>
</ul>
<h3 id="using-com-interop">Using COM Interop</h3>
<p>If you aren't used to using COM references in .NET, it may be
worth reading my previous article <a href="/post/resolving-compile-error-interop-type-cannot-be-embedded-use-the-applicable-interface-instead">Resolving compile error
&quot;Interop type cannot be embedded. Use the applicable interface
instead&quot;</a> to avoid some potential pitfalls.</p>
<h3 id="a-note-on-collections">A note on collections</h3>
<p>WIA offers various collection interfaces, such as <code>DeviceInfos</code>,
<code>Vector</code> and <code>Properties</code>. All of these collections are
<strong>one-based</strong> just like Visual Basic of old, not the zero-based
collections of .NET.</p>
<h3 id="a-note-on-image-formats">A note on image formats</h3>
<p>Although several functions allow you to specify an image format,
it is not guaranteed that the result will use that format. For
example, when scanning an image I always ask for an image in PNG
format, but the result I get is always BMP on both the old Canon
flatbed I used for most of this article and on a more recent
Brother MFC-L2710DW.</p>
<h2 id="working-with-devices">Working with devices</h2>
<p>Before you can do anything with WIA, you need a device. We can
use the <code>DeviceManager</code> interface to enumerate and access
devices.</p>
<h3 id="listing-devices">Listing Devices</h3>
<p>The <code>DeviceManager</code> interface has a <code>DeviceInfos</code> property which
can be used to enumerate devices. Each device is returned via
the <code>DeviceInfo</code> interface which allows us to query the device
type and enumerate its properties.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
DeviceManager deviceManager<span class="symbol">;</span>
DeviceInfos devices<span class="symbol">;</span>

deviceManager <span class="symbol">=</span> <span class="keyword">new</span> DeviceManager<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
devices <span class="symbol">=</span> deviceManager<span class="symbol">.</span>DeviceInfos<span class="symbol">;</span>

<span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">1</span><span class="symbol">;</span> i <span class="symbol">&lt;=</span> devices<span class="symbol">.</span>Count<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
<span class="symbol">{</span>
 DeviceInfo device<span class="symbol">;</span>

 device <span class="symbol">=</span> devices<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>device<span class="symbol">.</span>Type <span class="symbol">==</span> WiaDeviceType<span class="symbol">.</span>ScannerDeviceType<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// found a scanner device, do something with it</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>WIA supports 3 different types of devices - Scanners, Cameras
and Video Cameras. I have tested WIA with a flatbed scanner, a
DLSR camera, and a mobile phone. Although the latter two can both
take pictures and record video, they appear in WIA as the Camera
type. I do not have a dedicated video device I was able to test
with.</p>
<p>In the months that this article has been sitting in draft form I
also acquired a Brother laser printer that includes both a
flatbed and an automated document feeder (ADF) scanner, I have
briefly tested the flatbed aspect with WIA but not the ADF.</p>
<blockquote>
<p>The WIA device type enumeration is not flag based. Therefore,
isn't possible to mix and match device types when using WIA
functions that take a type - you either have the choice of
listing all devices, or devices belong to a single type.</p>
</blockquote>
<h3 id="connecting-to-a-device">Connecting to a device</h3>
<p>While the <code>DeviceInfo</code> allows you to query the information
related to a specific device, to actually use it we first need
to obtain a <code>Device</code> instance. This can be done via the
<code>Connect</code> method of a given <code>DeviceInfo</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
DeviceInfo deviceInfo<span class="symbol">;</span>
Device device<span class="symbol">;</span>

deviceInfo <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetSelectedDeviceInfo<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
device <span class="symbol">=</span> deviceInfo<span class="symbol">.</span>Connect<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

<span class="comment">// do something with the device instance</span>
</pre>
</figure>
<h2 id="using-wia-dialogues">Using WIA dialogues</h2>
<p>Common dialogues in Windows are a fantastic piece of
functionality - who wants to have to implement the same file or
folder pickers in every application? WIA also provides a number
of common dialogues via the <code>CommonDialog</code> interface.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Device device<span class="symbol">;</span>
CommonDialog dialog<span class="symbol">;</span>

device <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetSelectedDevice<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
dialog <span class="symbol">=</span> <span class="keyword">new</span> CommonDialog<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

<span class="comment">// display a common a dialogue</span>
</pre>
</figure>
<h3 id="selecting-a-device">Selecting a device</h3>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/wia-crash-course-select-device-dialog.png" class="gallery" title="Selecting a device" ><img src="https://images.cyotek.com/image/thumbnail/devblog/wia-crash-course-select-device-dialog.png" alt="Selecting a device" decoding="async" loading="lazy" /></a><figcaption>Selecting a device</figcaption></figure>
<p>The <code>ShowSelectDevice</code> method displays a dialogue box for
choosing a device. By default it will show all devices, but you
can limit it to showing devices of a specific type.</p>
<p>There is one caveat with this method however - if no devices are
currently present, it will throw an exception. If you plan on
using this method you should add a handler for a hResult of
<code>WIA_S_NO_DEVICE_AVAILABLE</code> with value of <code>0x80210015</code> or
<code>-2145320939</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Device device<span class="symbol">;</span>

<span class="keyword">try</span>
<span class="symbol">{</span>
 device <span class="symbol">=</span> dialog<span class="symbol">.</span>ShowSelectDevice<span class="symbol">(</span>AlwaysSelectDevice<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
<span class="keyword">catch</span> <span class="symbol">(</span>COMException ex<span class="symbol">)</span> when <span class="symbol">(</span>ex<span class="symbol">.</span>ErrorCode <span class="symbol">==</span> WIA_S_NO_DEVICE_AVAILABLE<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// handle no devices available</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Alternatively, if you wanted to select a device of a specific
type, then you can use the optional <code>DeviceType</code> parameter.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="comment">// error handling omitted for brevity</span>
device <span class="symbol">=</span> dialog<span class="symbol">.</span>ShowSelectDevice<span class="symbol">(</span>DeviceType<span class="symbol">:</span> WiaDeviceType<span class="symbol">.</span>ScannerDeviceType<span class="symbol">,</span> AlwaysSelectDevice<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h3 id="displaying-device-properties">Displaying device properties</h3>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/wia-crash-course-device-properties-dialog-scanner.png" class="gallery" title="Scanner device properties" ><img src="https://images.cyotek.com/image/thumbnail/devblog/wia-crash-course-device-properties-dialog-scanner.png" alt="Scanner device properties" decoding="async" loading="lazy" /></a><figcaption>Scanner device properties</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/wia-crash-course-device-properties-dialog-camera.png" class="gallery" title="Camera device properties" ><img src="https://images.cyotek.com/image/thumbnail/devblog/wia-crash-course-device-properties-dialog-camera.png" alt="Camera device properties" decoding="async" loading="lazy" /></a><figcaption>Camera device properties</figcaption></figure>
<p>Although this is probably something you are unlikely to need to
call very often, it is possible to display the native
properties dialogue box for a given device via the
<code>ShowDeviceProperties</code> method. The dialogue displayed by this
method will vary by device.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
dialog<span class="symbol">.</span>ShowDeviceProperties<span class="symbol">(</span>device<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h3 id="selecting-an-item">Selecting an item</h3>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/wia-crash-course-select-items-camera.png" class="gallery" title="Item selection via a camera" ><img src="https://images.cyotek.com/image/thumbnail/devblog/wia-crash-course-select-items-camera.png" alt="Item selection via a camera" decoding="async" loading="lazy" /></a><figcaption>Item selection via a camera</figcaption></figure>
<p>The <code>ShowSelectItems</code> method displays a UI for selecting one or
more items from a device. In the case of a camera, it will
display a selection dialogue box that allows you choose one or
more photographs. For a scanner, it allows you to scan a
document or photo and for a camera it allows selection of a
previous taken photograph (as noted, I have no dedicated video
device to test with). It returns an<code>Items</code> collection describing
the user's selection, or <code>null</code> if cancelled.</p>
<p>The <code>SingleSelect</code> parameter defaults to <code>true</code> which only
allows the selection of a single item. Setting it to <code>false</code>
allows multiple selection for the Get Pictures dialogue, but has
no effect for the Scan dialogue.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Items items<span class="symbol">;</span>

items <span class="symbol">=</span> dialog<span class="symbol">.</span>ShowSelectItems<span class="symbol">(</span>device<span class="symbol">,</span> SingleSelect<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h3 id="acquiring-an-image">Acquiring an image</h3>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/wia-crash-course-select-items-scanner-advanced.png" class="gallery" title="Selecting an item from a device with multiple sources" ><img src="https://images.cyotek.com/image/thumbnail/devblog/wia-crash-course-select-items-scanner-advanced.png" alt="Selecting an item from a device with multiple sources" decoding="async" loading="lazy" /></a><figcaption>Selecting an item from a device with multiple sources</figcaption></figure>
<p>The <code>ShowAcquireImage</code> will display one or more dialogues for
selecting an image. If successful, it will return an <code>ImageFile</code>
object containing the acquired data, otherwise <code>null</code>.</p>
<p>Calling it without any parameters means it will allow an image
to be selected from any supported device, or you can use the
<code>DeviceType</code> parameter to constrain the acquisition to a
specific category.</p>
<p>Regardless of if you allow for all device types or limit to a
single type, if multiple devices exist it will first prompt for
a device as described in <a href="#selecting-a-device">Selecting a Device</a> above. If only
a single device is present it will automatically use that device
without presenting a UI.</p>
<p>Once a device has been selected, the appropriate <a href="#selecting-an-item">Select
Item</a> dialogue is displayed allowing an existing image to be
selected (in the case of a camera) or a new image created (in
the case of a scanner).</p>
<p>After selecting/creating an image, the image details are
returned wrapped in an <code>ImageFile</code>.</p>
<p>One other parameter which may be of interest is <code>FormatID</code>. If
you specify this, it will try and return the image result in
that format. In practice however, I haven't found this to work -
I always get a Windows Bitmap back regards of what I actually
ask for.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
ImageFile image<span class="symbol">;</span>

image <span class="symbol">=</span> dialog<span class="symbol">.</span>ShowAcquireImage<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">if</span><span class="symbol">(</span>image <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// do something with the image</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>See <a href="#converting-wia.imagefile-into-a-bitmap">Converting WIA.ImageFile into a Bitmap</a> below for how to
get a .NET <code>Image</code> or <code>Bitmap</code> from the <code>ImageFile</code> instance.</p>
<h3 id="transferring-an-image">Transferring an image</h3>
<p>As well as using <code>ShowAcquireImage</code> to obtain an image via a
dialogue, you can also directly obtain an image. This is useful
when you have configured the properties and don't want the user
to be able to change them, for example when repeating a scan
with the same dimensions as the previous scan.</p>
<p>For this, you can use the <code>ShowTransfer</code> method. This will still
display a dialogue, but one that only shows the progress of the
transfer and that allows it to be cancelled. As with
<code>ShowAcquireImage</code>, this method will return an <code>ImageFile</code>
containing the results of the operation, and you can also
attempt to specify the image format you wish returned.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
ImageFile image<span class="symbol">;</span>

image <span class="symbol">=</span> dialog<span class="symbol">.</span>ShowTransfer<span class="symbol">(</span>device<span class="symbol">.</span>Items<span class="symbol">[</span><span class="number">1</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">if</span><span class="symbol">(</span>image <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// do something with the image</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="printing-images">Printing images</h3>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/wia-crash-course-print-photos-wizard.png" class="gallery" title="Print Pictures dialogue" ><img src="https://images.cyotek.com/image/thumbnail/devblog/wia-crash-course-print-photos-wizard.png" alt="Print Pictures dialogue" decoding="async" loading="lazy" /></a><figcaption>Print Pictures dialogue</figcaption></figure>
<p>By using the <code>ShowPhotoPrintingWizard</code> you can print one or more
image files, which is a handy way of adding print functionality
to your application with only a few lines of code.</p>
<p>Although you aren't able to control these programmatically, the
Wizard allows the user to choose how images are laid out on the
page and configure assorted options.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Vector files<span class="symbol">;</span>

files <span class="symbol">=</span> <span class="keyword">new</span> Vector<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

files<span class="symbol">.</span>Add<span class="symbol">(</span><span class="string">&quot;Z:\samples\20190811 0349 7.png&quot;</span><span class="symbol">)</span>
files<span class="symbol">.</span>Add<span class="symbol">(</span><span class="string">&quot;Z:\samples\\tower-of-london.jpg&quot;</span><span class="symbol">)</span>

dialog<span class="symbol">.</span>ShowPhotoPrintingWizard<span class="symbol">(</span>files<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>There are a number of caveats with this method that I have
noticed:</p>
<ul>
<li>The Wizard is not modal, meaning a user can click back and
continue working with your application leaving the Wizard open
or open multiple copies of the Wizard</li>
<li>File names must be fully qualified</li>
<li>The Wizard will fail if any of the image files don't have a
recognised image extension. For example, if you created a
<code>.tmp</code> file containing a bitmap and tried to print, it would
fail (and with a <em>very</em> unhelpful &quot;file not found&quot; message)</li>
</ul>
<h2 id="device-and-item-properties">Device and item properties</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/wia-crash-course-select-items-view-properties.png" class="gallery" title="Viewing the properties of an item" ><img src="https://images.cyotek.com/image/thumbnail/devblog/wia-crash-course-select-items-view-properties.png" alt="Viewing the properties of an item" decoding="async" loading="lazy" /></a><figcaption>Viewing the properties of an item</figcaption></figure>
<p>Both the <code>Device</code> and <code>Item</code> interfaces have a <code>Properties</code>
collection that allows you to manipulate the item. Properties
are self-describing, so in addition to staples like ID, name and
value, you can also query the type of the property (both the
value type and also a sub type that describes how the properties
works, for example range or flags). For range based properties,
the minimum and maximum values are accessible, as well as the
step. For properties that are a list of values, you are able to
read these as well. You could use this information to build your
own UI elements rather than using the built in ones, or for
validating user input.</p>
<p>The <code>Properties</code> collection has an indexer that accepts an
index, the ID of a property or the name. However, given that
both the index and ID are integers, if you want to pass an ID
you need to convert it to a string first. To save on confusion,
I ended up adding extension methods as helpers.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> SetPropertyValue<span class="symbol">&lt;</span>T<span class="symbol">&gt;</span><span class="symbol">(</span><span class="keyword">this</span> Properties properties<span class="symbol">,</span> WiaPropertyId id<span class="symbol">,</span> T value<span class="symbol">)</span>
<span class="symbol">{</span>
 Property property<span class="symbol">;</span>

 property <span class="symbol">=</span> properties<span class="symbol">[</span><span class="symbol">(</span><span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>id<span class="symbol">)</span><span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">]</span><span class="symbol">;</span>

 property<span class="symbol">.</span>let_Value<span class="symbol">(</span>value<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">static</span> Property GetProperty<span class="symbol">(</span><span class="keyword">this</span> Properties properties<span class="symbol">,</span> WiaPropertyId id<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> properties<span class="symbol">[</span><span class="symbol">(</span><span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>id<span class="symbol">)</span><span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">]</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Assuming I wanted to configure the DPI and quality of a device
prior to initiating a scan, I could call the extension as
follows</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Properties properties <span class="symbol">=</span> device<span class="symbol">.</span>Properties<span class="symbol">;</span>

properties<span class="symbol">.</span>SetPropertyValue<span class="symbol">(</span>WiaPropertyId<span class="symbol">.</span>WIA_IPS_CUR_INTENT<span class="symbol">,</span> _settings<span class="symbol">.</span>ImageIntent<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// set this first as it resets a bunch of other properties</span>

properties<span class="symbol">.</span>SetPropertyValue<span class="symbol">(</span>WiaPropertyId<span class="symbol">.</span>WIA_IPS_XRES<span class="symbol">,</span> _settings<span class="symbol">.</span>ScanDpi<span class="symbol">)</span><span class="symbol">;</span>
properties<span class="symbol">.</span>SetPropertyValue<span class="symbol">(</span>WiaPropertyId<span class="symbol">.</span>WIA_IPS_YRES<span class="symbol">,</span> _settings<span class="symbol">.</span>ScanDpi<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="converting-wia.imagefile-into-a-bitmap">Converting WIA.ImageFile into a Bitmap</h2>
<p>The <code>ImageFile</code> interface has an <code>ARGBData</code> property that
returns a <code>Vector</code> containing color data for an image. I tried
using this first to set the pixels of a new bitmap, but even
when manipulating the bitmap pixels directly it was a little
slow.</p>
<p>An alternative is to use the <code>FileData</code> property. This returns a
series of bytes that represent the image in the output format
returned by the device. This means we can dump this into a
<code>MemoryStream</code> and call <code>Image.FromFile</code>. However, as .NET likes
to <a href="https://stackoverflow.com/a/3845491/148962" rel="external nofollow noopener">keep the stream open</a> that could lead to complications
and so I usually clone the resulting image. The following
extension method will take a given <code>ImageFile</code> and return a
standalone <code>Bitmap</code> from it.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> Bitmap ToBitmap<span class="symbol">(</span><span class="keyword">this</span> ImageFile image<span class="symbol">)</span>
<span class="symbol">{</span>
 Bitmap result<span class="symbol">;</span>
 <span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">]</span> data<span class="symbol">;</span>

 data <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">]</span><span class="symbol">)</span>image<span class="symbol">.</span>FileData<span class="symbol">.</span>get_BinaryData<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>MemoryStream stream <span class="symbol">=</span> <span class="keyword">new</span> MemoryStream<span class="symbol">(</span>data<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>Image scannedImage <span class="symbol">=</span> Image<span class="symbol">.</span>FromStream<span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="keyword">new</span> Bitmap<span class="symbol">(</span>image<span class="symbol">.</span>Width<span class="symbol">,</span> image<span class="symbol">.</span>Height<span class="symbol">,</span> PixelFormat<span class="symbol">.</span>Format<span class="number">32</span>bppArgb<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Graphics g <span class="symbol">=</span> Graphics<span class="symbol">.</span>FromImage<span class="symbol">(</span>result<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 g<span class="symbol">.</span>Clear<span class="symbol">(</span>Color<span class="symbol">.</span>Transparent<span class="symbol">)</span><span class="symbol">;</span>
 g<span class="symbol">.</span>PageUnit <span class="symbol">=</span> GraphicsUnit<span class="symbol">.</span>Pixel<span class="symbol">;</span>
 g<span class="symbol">.</span>DrawImage<span class="symbol">(</span>scannedImage<span class="symbol">,</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span><span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> image<span class="symbol">.</span>Width<span class="symbol">,</span> image<span class="symbol">.</span>Height<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Note that this extension method isn't suitable for images with
multiple frames (e.g. a multi-page TIFF file) as it will only
keep the first frame. I don't usually work with multi-page
images so I don't have a concrete recommendation for this
use-case. I think I'd lean towards saving the result to a
temporary file and then using <code>Image.FromFile</code>.</p>
<h2 id="closing-thoughts">Closing thoughts</h2>
<p>WIA is very useful for adding basic image printing support to
your application, and perfect if you want to add the ability for
your applications to capture images from various devices. It is
capable of more than I've demonstrated here, but this article
covers the basics and some common uses.</p>
<p>The source code for the demonstration as it was at the time of
publication can be downloaded from the links on this page, any
updates can be found on the <a href="https://github.com/cyotek/ScannerTest" rel="external nofollow noopener">GitHub repository</a>.</p>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/an-introduction-to-using-windows-image-acquisition-wia-via-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comSimulating Bacterial Chemotaxisurn:uuid:059bee56-5f38-4110-9f8c-831c86cc31602021-09-20T07:26:37Z2020-11-02T12:23:37Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/chemotaxis-animation.gif" class="gallery" title="An animation of an earlier version of the application" ><img src="https://images.cyotek.com/image/devblog/chemotaxis-animation.gif" alt="An animation of an earlier version of the application" decoding="async" loading="lazy" /></a><figcaption>An animation of an earlier version of the application</figcaption></figure>
<p>Some months ago I was <a href="https://8thlight.com/blog/travis-petersen/2020/07/07/anxiety-and-flow-part-2.html" rel="external nofollow noopener">reading an article</a> that described the
behaviour of E. coli bacteria and comparing that to working
patterns. An odd comparison, but what struck me was the fact
that the bacteria behaves in such a simple fashion with basic
rules.</p>
<p>This got me thinking that this could be good behaviour to model
for enemy AI given basic rules can lead to complex looking
behaviour as ably demonstrated decades ago with <a href="https://en.wikipedia.org/wiki/Boulder_Dash" rel="external nofollow noopener">Boulder
Dash</a>, and which I myself <a href="/post/boulderdash-part-1-implementing-sprite-ai">wrote about</a> in 2010.</p>
<p>The article didn't mention it but some searching later I found
the name of the behaviour is <strong>Chemotaxis</strong> and it is
essentially the movement of an organism either toward or away
from chemicals, depending on if the detected chemical is
positive (a food source) or negative (a poison).</p>
<p>Although Wikipedia has a <a href="https://en.wikipedia.org/wiki/Chemotaxis" rel="external nofollow noopener">complicated page</a> devoted to it, I
found <a href="http://chemotaxis.biology.utah.edu/Parkinson_Lab/projects/ecolichemotaxis/ecolichemotaxis.html" rel="external nofollow noopener">a page</a> by the <a href="http://chemotaxis.biology.utah.edu/Parkinson_Lab/index.html" rel="external nofollow noopener">Parkinson Lab</a> at the University of
Utah which describes it in a simpler fashion that I could
understand.</p>
<blockquote>
<p>In isotropic chemical environments, E. coli swims in a random
walk pattern produced by alternating episodes of
counter-clockwise (CCW) and clockwise (CW) flagellar rotation
(Fig. 3, left panel). In an attractant or repellent gradient,
the cells monitor chemoeffector concentration changes as they
move about and use that information to modulate the
probability of the next tumbling event (Fig. 3, right panel.
These locomotor responses extend runs that take the cells in
favorable directions (toward attractants and away from
repellents), resulting in net movement toward preferred
environments. Brownian motion and spontaneous tumbling
episodes frequently knock the cells off course, so they must
constantly assess their direction of travel with respect to
the chemical gradient.<br />
Source: <a href="http://chemotaxis.biology.utah.edu/Parkinson_Lab/projects/ecolichemotaxis/ecolichemotaxis.html" rel="external nofollow noopener">An overview of E. coli chemotaxis, Parkinson Lab,
Department of Biology, University of Utah</a></p>
</blockquote>
<p>I say a simpler fashion, but when I looked up <a href="https://en.wikipedia.org/wiki/Brownian_motion" rel="external nofollow noopener">Brownian
motion</a> my brain promptly had a meltdown and I decided that
as this wasn't a scientific simulation I could skip some steps!</p>
<h2 id="before-we-begin">Before we begin</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/chemotaxis-move-100.png" class="gallery" title="The default simulation after 100 moves" ><img src="https://images.cyotek.com/image/thumbnail/devblog/chemotaxis-move-100.png" alt="The default simulation after 100 moves" decoding="async" loading="lazy" /></a><figcaption>The default simulation after 100 moves</figcaption></figure>
<p>As usual, take this code with a pinch of salt. It's a <em>basic</em>
simulation, not a proper model. If you somehow came to this page
expecting hard science then you should go read an article
written by someone who knows what they are talking about.</p>
<p>I'm also not going to describe the code. This is probably the
most complicated sample project I've written for this blog and
while a lot of it is unrelated directly to the simulation core,
there's still too much to cover in a single post. However, I did
write an article on <a href="/post/adding-scripting-to-net-applications">adding scripting to .NET applications</a>
derived from this example, and another on collision detection
using quadtrees, which, while not used by this project, was
inspired by the terrible performance of the simulation!</p>
<p>Finally, please remember this is a basic simulation. I already
noted I had a brain freeze the moment I saw the equations for
Brownian motion and I've also dumbed down or skipped other
aspects, for example E. coli have multiple flagellar which are
used for momentum and direction; I'm not trying to model this
either.</p>
<p>To simply this simulation even further, all strands move at the
same speed and instead of using vectors everything is based on
simple compass headings.</p>
<h2 id="the-rules">The Rules</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/chemotaxis-move-1000.png" class="gallery" title="The default simulation after 1000 moves" ><img src="https://images.cyotek.com/image/thumbnail/devblog/chemotaxis-move-1000.png" alt="The default simulation after 1000 moves" decoding="async" loading="lazy" /></a><figcaption>The default simulation after 1000 moves</figcaption></figure>
<ul>
<li>Each &quot;turn&quot;, there is a chance a strand will change its
heading clockwise or anti-clockwise, as my nod to Brownian
motion collisions and flagellar propulsion</li>
<li>At the start of the turn, each bacterial strand will sample
its location to see if it is closer to a food source
(attractor) or poison (repellant)</li>
<li>The stand will then move according to its current heading</li>
<li>After moving it will sample the new location to see if
it is now closer or further away than the previous sample.</li>
<li>If the closest chemoeffector is a food source and it is now
further away, or is a poison source to which it is now closer,
it will turn left or right to try and get closer or
further away</li>
<li>Colliding directly with a poison source will kill the strand</li>
<li>Colliding directly with a food source will consume the source</li>
</ul>
<p>Although this doesn't sound like a particular complex rule-set,
seeing it in action is quite something!</p>
<h2 id="scenario-setup">Scenario Setup</h2>
<p>The following options are used to configure a scenario. Changing
these requires the simulation to be restarted.</p>
<table>
<thead>
<tr>
<th>Option</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>Environment Seed</td>
<td>Specifies the seed used for the environment random number generator</td>
</tr>
<tr>
<td>Movement Seed</td>
<td>Specifies the seed used for the movement random number generator</td>
</tr>
<tr>
<td>Size</td>
<td>The size of the playing field</td>
</tr>
<tr>
<td>Strands</td>
<td>Initial number of bacteria strands</td>
</tr>
<tr>
<td>Attractors</td>
<td>Initial number of attractor chemoeffectors</td>
</tr>
<tr>
<td>Attractor Action</td>
<td>The action that occurs when a bacteria strand collides with an attractor</td>
</tr>
<tr>
<td>Repellants</td>
<td>Initial number of repellant chemoeffectors</td>
</tr>
<tr>
<td>Repellant Action</td>
<td>The action that occurs when a bacteria strand collides with a repellant</td>
</tr>
<tr>
<td>Attractor Strength</td>
<td>A range that defines the starting strength of an attractor</td>
</tr>
<tr>
<td>Repellant Strength</td>
<td>A range that defines the starting strength of a repellant</td>
</tr>
</tbody>
</table>
<h2 id="scenario-options">Scenario Options</h2>
<p>The following options are available and can be toggled while the
scenario is running.</p>
<table>
<thead>
<tr>
<th>Option</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>Wrap Boundaries</td>
<td>Allows entities to wrap from one side of the playing field to the other. If disabled, entities will change direction when hitting the boundary</td>
</tr>
<tr>
<td>Respawn Attractors</td>
<td>If set, consumed food sources will respawn at a new location</td>
</tr>
<tr>
<td>Allow Binary Fission</td>
<td>If set, bacteria will occasionally undergo <a href="https://en.wikipedia.org/wiki/Fission_(biology)" rel="external nofollow noopener">binary fission</a> and split into a pair</td>
</tr>
<tr>
<td>Allow Strand Collisions</td>
<td>Specifies if bacteria can collide with each other. Will slow down the simulation significantly</td>
</tr>
<tr>
<td>Allow Attrition</td>
<td>Specifies if bacteria can loose strength. Unless the strand finds a food source, it will die. I just added this out of curiosity, it has no basis that I'm aware</td>
</tr>
<tr>
<td>Mobile Repellents</td>
<td>If set, repellants will move across the playing field via a fixed heading</td>
</tr>
</tbody>
</table>
<h2 id="javascript-support">JavaScript Support</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/chemotaxis-javascript-setup.png" class="gallery" title="A not-very realistic scenario created via JavaScript" ><img src="https://images.cyotek.com/image/thumbnail/devblog/chemotaxis-javascript-setup.png" alt="A not-very realistic scenario created via JavaScript" decoding="async" loading="lazy" /></a><figcaption>A not-very realistic scenario created via JavaScript</figcaption></figure>
<p>A simulation can also be set up via JavaScript, useful when you
want something that the randomised setup can't handle. I
extracted the JavaScript support into a improved demonstration
project and wrote <a href="/post/adding-scripting-to-net-applications">adding scripting to .NET applications</a> to
describe it.</p>
<h2 id="performance-implications">Performance implications</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/chemotaxis-trails.png" class="gallery" title="Viewing the trails where a strand has been" ><img src="https://images.cyotek.com/image/thumbnail/devblog/chemotaxis-trails.png" alt="Viewing the trails where a strand has been" decoding="async" loading="lazy" /></a><figcaption>Viewing the trails where a strand has been</figcaption></figure>
<p>The simulation code is pretty slow as I made no effort to
optimise it, e.g. by using a quadtree to break down collision
detection. Also, Windows Forms does not make for a great
simulation environment - I think next time I might try using
OpenGL to host the simulation instead.</p>
<p>There are a number of UI options for controlling the appearance,
e.g. for using simplified shapes, or disabling gradients - it is
very interesting to note just how exceedingly slow .NET is at
drawing a circle. You can also switch on trails to see the last
128 positions a strand has visited.</p>
<h2 id="closing-thoughts">Closing Thoughts</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/chemotaxis-start.png" class="gallery" title="A screenshot of the default simulation" ><img src="https://images.cyotek.com/image/thumbnail/devblog/chemotaxis-start.png" alt="A screenshot of the default simulation" decoding="async" loading="lazy" /></a><figcaption>A screenshot of the default simulation</figcaption></figure>
<p>This was another fun project. It was both fascinating and yet
quite creepy watching them mill around, then suddenly swarm on a
source, consume it completely and then move on. Will this be
useful in any future game I write? Who knows.</p>
<p>This wasn't the usual type of example program I write for this
blog so comments welcome!</p>
<p>The source, as it was at the time this article was published, is
available from the links on this page. Any updates will be
published to our <a href="https://github.com/cyotek/ChemotaxisSimulation" rel="external nofollow noopener">GitHub repository</a>.</p>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/simulating-bacterial-chemotaxis .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comAdding Scripting to .NET Applicationsurn:uuid:1fee5ffa-4669-4233-89e2-0f82c30e1ecd2020-09-20T09:43:51Z2020-08-31T10:01:11Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/script1-grayscale.png" class="gallery" title="The demonstration application converting an image to greyscale" ><img src="https://images.cyotek.com/image/thumbnail/devblog/script1-grayscale.png" alt="The demonstration application converting an image to greyscale" decoding="async" loading="lazy" /></a><figcaption>The demonstration application converting an image to greyscale</figcaption></figure>
<p>Adding scripting to your application can be a good way of
providing your power users with advanced control or for allowing
it to be extended in ways you didn't anticipate.</p>
<p>Our <a href="https://www.cyotek.com/cyotek-palette-editor">Color Palette Editor</a> software allows the use of dynamic
templates via the use of the <a href="https://shopify.github.io/liquid/" rel="external nofollow noopener">Liquid</a> template language by
Shopify. This is quite powerful in its own right and I will
likely use it again in future for other <em>templating</em> activities.
But as powerful as it is, it cannot replace scripting.</p>
<p>I also experimented with adding macro support to our <a href="https://www.cyotek.com/cyotek-gif-animator">Gif
Animator</a>, but given I was too busy making a mess of the
program I never got around to releasing the macro extension.</p>
<p>Recently I was working on a simulator sample and ended up
integrating part of the aforementioned macro solution so I could
set up the simulation via a script. I thought that was
interesting enough in its own right to deserve a post.</p>
<p>However, I am <em>not</em> interesting in writing a scripting language.
(Actually, that's not entirely true... as soon as a print
version of <a href="https://craftinginterpreters.com/" rel="external nofollow noopener">Crafting Interpreters</a> is available I plan on
trying to implement it in C#.) As most of the world seems to run
on JavaScript these days, it is reasonable to use this as a
base. And fortunately, there is a decent JavaScript interpreter
available for .NET, namely <a href="https://github.com/sebastienros/jint" rel="external nofollow noopener">Jint</a>.</p>
<h2 id="using-jint">Using Jint</h2>
<blockquote>
<p>Note: This article is written on the assumption that you are
using Jint version 3. However, the basic code will also work
with version 2, albeit with a noticeable performance decrease.
Unfortunately, there are breaking changes between version 2
and 3 in how you parse the JavaScript so some of the examples
in this article may not work directly in Jint 2.</p>
<p>The demonstration that accompanies this article has variants
for both Jint versions.</p>
</blockquote>
<p>After installing the <a href="https://www.nuget.org/packages/Jint" rel="external nofollow noopener">Jint NuGet package</a>, you could simply
execute a script like this</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">var</span> engine <span class="symbol">=</span> <span class="keyword">new</span> Engine<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>SetValue<span class="symbol">(</span><span class="string">&quot;log&quot;</span><span class="symbol">,</span> <span class="keyword">new</span> Action<span class="symbol">&lt;</span><span class="keyword">object</span><span class="symbol">&gt;</span><span class="symbol">(</span>Console<span class="symbol">.</span>WriteLine<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">;</span>

 engine<span class="symbol">.</span>Execute<span class="symbol">(</span><span class="string">@&quot;
 function hello() {
 log(&#39;Hello World&#39;);
 };

 hello();
 &quot;</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p><em>(Example code from the Jint project page)</em></p>
<p>This is pretty powerful stuff. The <code>SetValue</code> method can be used
to add .NET object instances to be accessible from JavaScript,
or you could define function methods that can be executed by
JavaScript.</p>
<p>You can selectively include either the full .NET Framework or a
list of &quot;safe&quot; assemblies - this is done via the <code>AllowClr</code>
method when creating the engine.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">var</span> Engine <span class="symbol">=</span> <span class="keyword">new</span> Engine<span class="symbol">(</span>options <span class="symbol">=&gt;</span> options<span class="symbol">.</span>AllowClr<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// full CLR</span>

 <span class="keyword">var</span> engine <span class="symbol">=</span> <span class="keyword">new</span> Engine<span class="symbol">(</span>cfg <span class="symbol">=&gt;</span> cfg
 <span class="symbol">.</span>AllowClr<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>Bar<span class="symbol">)</span><span class="symbol">.</span>Assembly<span class="symbol">)</span>
 <span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// partial clr</span>
</pre>
</figure>
<figure class="lang-javascript highlight"><figcaption><span>javascript</span></figcaption><pre class="code">
 <span class="keyword">var</span> file <span class="symbol">=</span> <span class="keyword">new</span> System<span class="symbol">.</span>IO<span class="symbol">.</span>StreamWriter<span class="symbol">(</span><span class="string">&#39;log.txt&#39;</span><span class="symbol">)</span><span class="symbol">;</span>
 file<span class="symbol">.</span>WriteLine<span class="symbol">(</span><span class="string">&#39;Hello World !&#39;</span><span class="symbol">)</span><span class="symbol">;</span>
 file<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">var</span> Foo <span class="symbol">=</span> importNamespace<span class="symbol">(</span><span class="string">&#39;Foo&#39;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">var</span> bar <span class="symbol">=</span> <span class="keyword">new</span> Foo<span class="symbol">.</span>Bar<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 log<span class="symbol">(</span>bar<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p><em>(Example code from the Jint project page)</em></p>
<p>It is also possible to add specific types to the engine. These
can either then be directly instantiated from a script, or
static values accessed.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">var</span> engine <span class="symbol">=</span> <span class="keyword">new</span> Engine<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// no explicit CLR access</span>

 engine<span class="symbol">.</span>SetValue<span class="symbol">(</span><span class="string">&quot;color&quot;</span><span class="symbol">,</span> TypeReference<span class="symbol">.</span>CreateTypeReference<span class="symbol">(</span>engine<span class="symbol">,</span> <span class="keyword">typeof</span><span class="symbol">(</span>System<span class="symbol">.</span>Drawing<span class="symbol">.</span>Color<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<figure class="lang-javascript highlight"><figcaption><span>javascript</span></figcaption><pre class="code">
 <span class="keyword">var</span> c <span class="symbol">=</span> color<span class="symbol">.</span>DarkGoldenrod<span class="symbol">;</span>
</pre>
</figure>
<p>While the <code>Execute</code> method can be used to load and run
JavaScript, you can also call individual functions via the
<code>Invoke</code> method. This could be handy for allowing extensibility
via scripts.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">var</span> add <span class="symbol">=</span> <span class="keyword">new</span> Engine<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>Execute<span class="symbol">(</span><span class="string">&quot;function add(a, b) { return a + b; }&quot;</span><span class="symbol">)</span>
 <span class="symbol">.</span>GetValue<span class="symbol">(</span><span class="string">&quot;add&quot;</span><span class="symbol">)</span>
 <span class="symbol">;</span>

 add<span class="symbol">.</span>Invoke<span class="symbol">(</span><span class="number">1</span><span class="symbol">,</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// -&gt; 3</span>
</pre>
</figure>
<p><em>(Example code from the Jint project page)</em></p>
<h2 id="safe-and-secure">Safe and secure</h2>
<p>By default, Jint doesn't provide access to the full .NET API,
and so scripts should be incapable of performing malicious
actions. As noted in the previous section you can easily add CLR
assemblies or custom objects which could then allow for
malicious actions. You should consider how much functionality
you wish to expose via scripts and risk assess your
application's needs.</p>
<p>When creating an <code>Engine</code> instance, Jint also allows you to
specify constraints such as how much memory can be used, or
program size, as well as allowing script execution to be
cancelled.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">var</span> engine <span class="symbol">=</span> <span class="keyword">new</span> Engine<span class="symbol">(</span>options <span class="symbol">=&gt;</span> <span class="symbol">{</span>

 <span class="comment">// Limit memory allocations to MB</span>
 options<span class="symbol">.</span>LimitMemory<span class="symbol">(</span><span class="number">4</span>_<span class="number">000</span>_<span class="number">000</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// Set a timeout to 4 seconds.</span>
 options<span class="symbol">.</span>TimeoutInterval<span class="symbol">(</span>TimeSpan<span class="symbol">.</span>FromSeconds<span class="symbol">(</span><span class="number">4</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// Set limit of 1000 executed statements.</span>
 options<span class="symbol">.</span>MaxStatements<span class="symbol">(</span><span class="number">1000</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// Use a cancellation token.</span>
 options<span class="symbol">.</span>CancellationToken<span class="symbol">(</span>cancellationToken<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p><em>(Example code from the Jint project page)</em></p>
<p>It is also possible to define your own constraints but this is
something I haven't looked into yet.</p>
<h2 id="creating-a-base">Creating a base</h2>
<p>As I don't really want my applications to have to know about
Jint or how it works, I'm going to wrap it around a helper
class. This class will take care of managing the scripting
engine and providing some common functionality.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> ScriptEnvironment
<span class="symbol">{</span>
 <span class="keyword">bool</span> Interactive <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">bool</span> SuppressErrors <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">void</span> AddFunction<span class="symbol">(</span><span class="keyword">string</span> name<span class="symbol">,</span> Delegate value<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">void</span> AddType<span class="symbol">(</span><span class="keyword">string</span> name<span class="symbol">,</span> Type type<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">void</span> AddValue<span class="symbol">(</span><span class="keyword">string</span> name<span class="symbol">,</span> <span class="keyword">object</span> value<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">object</span> Evaluate<span class="symbol">(</span><span class="keyword">string</span> script<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">object</span> Execute<span class="symbol">(</span><span class="keyword">string</span> script<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">object</span> Invoke<span class="symbol">(</span><span class="keyword">string</span> name<span class="symbol">,</span> <span class="keyword">params</span> <span class="keyword">object</span><span class="symbol">[</span><span class="symbol">]</span> arguments<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">void</span> Load<span class="symbol">(</span><span class="keyword">string</span> script<span class="symbol">)</span><span class="symbol">;</span>
 
 <span class="keyword">abstract</span> <span class="keyword">void</span> ClearScreen<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">abstract</span> <span class="keyword">void</span> ShowAlert<span class="symbol">(</span><span class="keyword">string</span> message<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">abstract</span> <span class="keyword">bool</span> ShowConfirm<span class="symbol">(</span><span class="keyword">string</span> message<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">abstract</span> <span class="keyword">string</span> ShowPrompt<span class="symbol">(</span><span class="keyword">string</span> message<span class="symbol">,</span> <span class="keyword">string</span> defaultValue<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">abstract</span> <span class="keyword">void</span> WriteLine<span class="symbol">(</span><span class="keyword">string</span> value<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>I'm making it abstract as the interactivity features will need
to be implemented separately for each application type - e.g.
once for console applications and once for applications with a
GUI.</p>
<h2 id="initialising-the-engine">Initialising the engine</h2>
<p>I don't want the JavaScript engine to be created until it is
required, so any method that needs the engine to be present will
first call <code>InitializeEngine</code> to ensure the instance is created.</p>
<p>I also have a virtual <code>InitializeEnvironment</code> method that is
called once after the engine is initialised, allowing subclasses
to configure the engine appropriately, for example by making
certain types available, or loading objects for scripting use.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">private</span> <span class="keyword">void</span> InitializeEngine<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_engine <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _engine <span class="symbol">=</span> <span class="keyword">new</span> Engine<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>InitializeEnvironment<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> InitializeEnvironment<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>AddFunction<span class="symbol">(</span><span class="string">&quot;print&quot;</span><span class="symbol">,</span> <span class="keyword">new</span> Action<span class="symbol">&lt;</span><span class="keyword">object</span><span class="symbol">&gt;</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>WriteLine<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>AddFunction<span class="symbol">(</span><span class="string">&quot;log&quot;</span><span class="symbol">,</span> <span class="keyword">new</span> Action<span class="symbol">&lt;</span><span class="keyword">object</span><span class="symbol">&gt;</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>WriteLine<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>AddFunction<span class="symbol">(</span><span class="string">&quot;cls&quot;</span><span class="symbol">,</span> <span class="keyword">new</span> Action<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ClearScreen<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// interactive functions</span>
 <span class="keyword">this</span><span class="symbol">.</span>AddFunction<span class="symbol">(</span><span class="string">&quot;alert&quot;</span><span class="symbol">,</span> <span class="keyword">new</span> Action<span class="symbol">&lt;</span><span class="keyword">object</span><span class="symbol">&gt;</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ShowAlert<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>AddFunction<span class="symbol">(</span><span class="string">&quot;confirm&quot;</span><span class="symbol">,</span> <span class="keyword">new</span> Func<span class="symbol">&lt;</span><span class="keyword">object</span><span class="symbol">,</span> <span class="keyword">bool</span><span class="symbol">&gt;</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ShowConfirm<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>AddFunction<span class="symbol">(</span><span class="string">&quot;prompt&quot;</span><span class="symbol">,</span> <span class="keyword">new</span> Func<span class="symbol">&lt;</span><span class="keyword">object</span><span class="symbol">,</span> <span class="keyword">object</span><span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">&gt;</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ShowPrompt<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>It will always define basic output methods <code>log</code> and <code>print</code>
(both will perform the same action), and also a <code>cls</code> method. I
also define three functions to mirror JavaScripts interactive
aspects, naming <code>alert</code>, <code>confirm</code> and <code>prompt</code>.</p>
<blockquote>
<p>Note: Although the wrapper class has an <code>Interactive</code> property
to enable the use of interactive functions, they will still
always be defined in the engine so scripts don't crash if
interactions are disabled.</p>
</blockquote>
<h2 id="loading-a-script">Loading a script</h2>
<blockquote>
<p>Note: This code in this section applies to Jint 3 only. While
you can perform the same actions using Jint 2, the API is
different.</p>
</blockquote>
<p>The easiest way of getting a script into Jint is to call its
<code>Execute</code> method and pass in a string containing your
JavaScript. However, if you want to perform any sort of
examination of the source, for example to check if a given
function exists, then you need to parse the code.</p>
<p>Fortunately, this isn't something you need to do manually as
Jint 3 uses the <a href="https://github.com/sebastienros/esprima-dotnet" rel="external nofollow noopener">Esprima .NET</a> library for this, and we can
too.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 JavaScriptParser parser<span class="symbol">;</span>
 Script program<span class="symbol">;</span>

 parser <span class="symbol">=</span> <span class="keyword">new</span> JavaScriptParser<span class="symbol">(</span>script<span class="symbol">)</span><span class="symbol">;</span>
 program <span class="symbol">=</span> parser<span class="symbol">.</span>ParseScript<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>The <code>Script</code> object has a <code>ChildNodes</code> property which provides a
full Abstract Syntax Tree (AST) for your script, allowing you to
query the entire program.</p>
<p>For example, consider the following script. Short and simple?</p>
<figure class="lang-javascript highlight"><figcaption><span>javascript</span></figcaption><pre class="code">
<span class="keyword">function</span> main<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">for</span><span class="symbol">(</span><span class="keyword">var</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> picture<span class="symbol">.</span>Length<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">let</span> current <span class="symbol">=</span> picture<span class="symbol">.</span>getPixel<span class="symbol">(</span>i<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">let</span> grayscale <span class="symbol">=</span> toGrayScale<span class="symbol">(</span>current<span class="symbol">)</span><span class="symbol">;</span>

 picture<span class="symbol">.</span>setPixel<span class="symbol">(</span>i<span class="symbol">,</span> grayscale<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">function</span> toGrayScale<span class="symbol">(</span>c<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">let</span> red <span class="symbol">=</span> c<span class="symbol">.</span>R<span class="symbol">;</span>
 <span class="keyword">let</span> green <span class="symbol">=</span> c<span class="symbol">.</span>G<span class="symbol">;</span>
 <span class="keyword">let</span> blue <span class="symbol">=</span> c<span class="symbol">.</span>B<span class="symbol">;</span>
 <span class="keyword">let</span> gray <span class="symbol">=</span> red <span class="symbol">*</span> <span class="number">0.3</span> <span class="symbol">+</span> green <span class="symbol">*</span> <span class="number">0.59</span> <span class="symbol">+</span> blue <span class="symbol">*</span> <span class="number">0.11</span><span class="symbol">;</span>

 <span class="keyword">return</span> color<span class="symbol">.</span>FromArgb<span class="symbol">(</span>gray<span class="symbol">,</span> gray<span class="symbol">,</span> gray<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>This is condensed example of the AST generated for the above script</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
FunctionDeclaration [main()]
 BlockStatement
 ForStatement
 VariableDeclaration [Var]
 VariableDeclarator [i]
 Literal [0]
 BinaryExpression [Less]
 Identifier [i]
 MemberExpression [picture.Length]
 UpdateExpression
 Identifier [i]
 BlockStatement
 VariableDeclaration [Let]
 VariableDeclarator [current]
 CallExpression
 MemberExpression [picture.getPixel]
 Identifier [i]
 VariableDeclaration [Let]
 VariableDeclarator [grayscale]
 CallExpression
 Identifier [toGrayScale]
 Identifier [current]
 ExpressionStatement
 CallExpression
 MemberExpression [picture.setPixel]
 Identifier [i]
 Identifier [grayscale]
FunctionDeclaration [toGrayScale(c)]
 BlockStatement
 VariableDeclaration [Let]
 VariableDeclarator [red]
 MemberExpression [c.R]
 VariableDeclaration [Let]
 VariableDeclarator [green]
 MemberExpression [c.G]
 VariableDeclaration [Let]
 VariableDeclarator [blue]
 MemberExpression [c.B]
 VariableDeclaration [Let]
 VariableDeclarator [gray]
 BinaryExpression [Plus]
 BinaryExpression [Plus]
 BinaryExpression [Times]
 Identifier [red]
 Literal [0.3]
 BinaryExpression [Times]
 Identifier [green]
 Literal [0.59]
 BinaryExpression [Times]
 Identifier [blue]
 Literal [0.11]
 ReturnStatement
 CallExpression
 MemberExpression [color.FromArgb]
 Identifier [gray]
 Identifier [gray]
 Identifier [gray]
</pre>
</figure>
<p>Using the AST, you could examine the script before you allowed
it to be ran. For example, you could check for the presence of a
function named <code>main</code>, and if found, invoke that directly. I
suppose you could also use it to try and ensure a script is
safe, but given the script could be obfuscated I'm not sure how
effective that would be.</p>
<p>Once you have a <code>Program</code> object, you can load this into your
engine instance by calling its <code>Execute</code> method the same as you
would with a string.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">public</span> <span class="keyword">void</span> Load<span class="symbol">(</span><span class="keyword">string</span> script<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Load<span class="symbol">(</span>script<span class="symbol">,</span> <span class="keyword">out</span> Script _<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">void</span> Load<span class="symbol">(</span><span class="keyword">string</span> script<span class="symbol">,</span> <span class="keyword">out</span> Script program<span class="symbol">)</span>
 <span class="symbol">{</span>
 program <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 program <span class="symbol">=</span> <span class="keyword">new</span> JavaScriptParser<span class="symbol">(</span>script<span class="symbol">)</span><span class="symbol">.</span>ParseScript<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// TODO: Validate the program, e.g. check for eval, etc</span>

 <span class="keyword">this</span><span class="symbol">.</span>InitializeEngine<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 _engine<span class="symbol">.</span>Execute<span class="symbol">(</span>program<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>Exception ex<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>HandleException<span class="symbol">(</span>ex<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>_suppressErrors<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>Ideally, after parsing the script I would check it to ensure
that only functions are present and no global statements that
will be executed. After all, it is a little odd that in order to
load a script you actually have to execute it.</p>
<p>The <code>try</code> blocks are a bit off putting too, especially as I will
be doing the same &quot;pattern&quot; elsewhere in the class. Sometimes
when confronted with this I tend to have a method that accepts
an <code>Action</code> and therefore only have the boilerplate in one
place, however to keep this example simple (if slightly more
verbose), I have choose to keep it as is.</p>
<h2 id="script-execution">Script execution</h2>
<p>Our scripting object will expose three different execution
functions - <code>Execute</code>, <code>Evaluate</code> and <code>Invoke</code>.</p>
<p>The first method, <code>Execute,</code> will execute-load a script and then
search for a method named <code>main</code>. If one is found, it will
invoke this. My intended use case for this particular method is
for application plug-ins.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">public</span> <span class="keyword">object</span> Execute<span class="symbol">(</span><span class="keyword">string</span> script<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">object</span> result<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Load<span class="symbol">(</span>script<span class="symbol">,</span> <span class="keyword">out</span> Script program<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>ScriptEnvironment<span class="symbol">.</span>HasMainFunction<span class="symbol">(</span>program<span class="symbol">)</span> <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span>ScriptEnvironment<span class="symbol">.</span>HasMainCaller<span class="symbol">(</span>program<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Invoke<span class="symbol">(</span>MainFunctionName<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> _engine<span class="symbol">.</span>GetCompletionValue<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">.</span>ToObject<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>After the script has executed, I get any completion value,
convert it to a .NET object and then return it.</p>
<p>The second method, <code>Evaluate</code> is intended for read, execute,
print, loop (REPL) scenarios... for example an Immediate style
window, or a scripting command interface. It simply
execute-loads the specified script and then returns any result.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">public</span> <span class="keyword">object</span> Evaluate<span class="symbol">(</span><span class="keyword">string</span> script<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Load<span class="symbol">(</span>script<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> _engine<span class="symbol">.</span>GetCompletionValue<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">.</span>ToObject<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>The final method, <code>Invoke</code> is unique in that it assumes that
<code>Load</code>, <code>Execute</code> or <code>Evaluate</code> have been previously called to
load script into the engine. It will then attempt to execute a
named function.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">public</span> <span class="keyword">object</span> Invoke<span class="symbol">(</span><span class="keyword">string</span> name<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>Invoke<span class="symbol">(</span>name<span class="symbol">,</span> _defaultArguments<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">object</span> Invoke<span class="symbol">(</span><span class="keyword">string</span> name<span class="symbol">,</span> <span class="keyword">params</span> <span class="keyword">object</span><span class="symbol">[</span><span class="symbol">]</span> arguments<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">object</span> result<span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>InitializeEngine<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 result <span class="symbol">=</span> _engine<span class="symbol">.</span>Invoke<span class="symbol">(</span>name<span class="symbol">,</span> arguments<span class="symbol">)</span><span class="symbol">.</span>ToObject<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>Exception ex<span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>HandleException<span class="symbol">(</span>ex<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>_suppressErrors<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
 <span class="symbol">}</span>
</pre>
</figure>
<h2 id="displaying-output">Displaying output</h2>
<p>In many cases, it would be beneficial for scripts to be able to
output content, for whatever reason. While I'm not going to try
and reproduce JavaScript's <code>console</code> object, having a <code>log</code>
function is of great help. In the initialisation section above,
I define both <code>print</code> and <code>log</code> as aliases for outputting content.</p>
<p>Most of the functions that must be overridden to provide
functionality, be it logging or interactivity, require a .NET
string. Therefore we need some code to transform script engine
values into a .NET string, including allowing literal strings
for null or undefined values.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">private</span> <span class="keyword">string</span> GetValueString<span class="symbol">(</span><span class="keyword">object</span> value<span class="symbol">,</span> <span class="keyword">bool</span> useLiterals<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>value <span class="keyword">is</span> JsValue jsValue<span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> ScriptEnvironment<span class="symbol">.</span>GetValueString<span class="symbol">(</span>jsValue<span class="symbol">,</span> useLiterals<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>value <span class="keyword">is</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> useLiterals <span class="symbol">?</span> <span class="string">&quot;null&quot;</span> <span class="symbol">:</span> <span class="keyword">null</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> value<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">string</span> GetValueString<span class="symbol">(</span>JsValue jsValue<span class="symbol">,</span> <span class="keyword">bool</span> useLiterals<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>

 <span class="keyword">switch</span> <span class="symbol">(</span>jsValue<span class="symbol">.</span>Type<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> Types<span class="symbol">.</span>String<span class="symbol">:</span>
 result <span class="symbol">=</span> jsValue<span class="symbol">.</span>AsString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>

 <span class="keyword">case</span> Types<span class="symbol">.</span>Undefined<span class="symbol">:</span>
 result <span class="symbol">=</span> useLiterals <span class="symbol">?</span> <span class="string">&quot;undefined&quot;</span> <span class="symbol">:</span> <span class="keyword">null</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>

 <span class="keyword">case</span> Types<span class="symbol">.</span>Null<span class="symbol">:</span>
 result <span class="symbol">=</span> useLiterals <span class="symbol">?</span> <span class="string">&quot;null&quot;</span> <span class="symbol">:</span> <span class="keyword">null</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>

 <span class="keyword">case</span> Types<span class="symbol">.</span>Boolean<span class="symbol">:</span>
 result <span class="symbol">=</span> jsValue<span class="symbol">.</span>AsBoolean<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>

 <span class="keyword">case</span> Types<span class="symbol">.</span>Number<span class="symbol">:</span>
 result <span class="symbol">=</span> jsValue<span class="symbol">.</span>AsNumber<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>

 <span class="keyword">case</span> Types<span class="symbol">.</span>Object<span class="symbol">:</span>
 result <span class="symbol">=</span> jsValue<span class="symbol">.</span>ToObject<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>

 <span class="keyword">case</span> Types<span class="symbol">.</span>None<span class="symbol">:</span>
 result <span class="symbol">=</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>

 <span class="keyword">default</span><span class="symbol">:</span>
 result <span class="symbol">=</span> jsValue<span class="symbol">.</span>AsString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>With these helpers in place, we can define <code>object</code>-based
methods that are bound to the Jint <code>Engine</code> instance, and then
translate these into .NET strings before calling the
implementation specific overrides.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">private</span> <span class="keyword">void</span> WriteLine<span class="symbol">(</span><span class="keyword">object</span> value<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteLine<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>GetValueString<span class="symbol">(</span>value<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
</pre>
</figure>
<h2 id="interactivity">Interactivity</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/script1-prompt.png" class="gallery" title="A example script demonstrating the basic interactive features" ><img src="https://images.cyotek.com/image/thumbnail/devblog/script1-prompt.png" alt="A example script demonstrating the basic interactive features" decoding="async" loading="lazy" /></a><figcaption>A example script demonstrating the basic interactive features</figcaption></figure>
<p>Although I'm not going to go out of my way to add a lot of
interactivity to the script engine, mirroring JavaScript's
<code>alert</code>, <code>confirm</code> and <code>prompt</code> methods would be of great use
for some types of scripts.</p>
<p>However, as not all hosts might not support interactivity, or
you may wish to disable it on an ad-hoc basic, I have added an
<code>Interactive</code> property to the base class. When set to <code>true</code>,
the interactive methods will work as expected. When <code>false</code>, no
user interface elements will be displayed, and, in the case of
functions, default values returned.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">private</span> <span class="keyword">void</span> ShowAlert<span class="symbol">(</span><span class="keyword">object</span> message<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_interactive<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ShowAlert<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>GetValueString<span class="symbol">(</span>message<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">bool</span> ShowConfirm<span class="symbol">(</span><span class="keyword">object</span> message<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">return</span> _interactive <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>ShowConfirm<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>GetValueString<span class="symbol">(</span>message<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">string</span> ShowPrompt<span class="symbol">(</span><span class="keyword">object</span> message<span class="symbol">,</span> <span class="keyword">object</span> defaultValue<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">return</span> _interactive
 <span class="symbol">?</span> <span class="keyword">this</span><span class="symbol">.</span>ShowPrompt<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>GetValueString<span class="symbol">(</span>message<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>GetValueString<span class="symbol">(</span>defaultValue<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">:</span> <span class="keyword">null</span><span class="symbol">;</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>For a WinForms GUI application, the following overrides can be
used to used to present the UI.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> ShowAlert<span class="symbol">(</span><span class="keyword">string</span> message<span class="symbol">)</span>
 <span class="symbol">{</span>
 MessageBox<span class="symbol">.</span>Show<span class="symbol">(</span>message<span class="symbol">,</span> Application<span class="symbol">.</span>ProductName<span class="symbol">,</span> MessageBoxButtons<span class="symbol">.</span>OK<span class="symbol">,</span> MessageBoxIcon<span class="symbol">.</span>Exclamation<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">bool</span> ShowConfirm<span class="symbol">(</span><span class="keyword">string</span> message<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">return</span> MessageBox<span class="symbol">.</span>Show<span class="symbol">(</span>message<span class="symbol">,</span> Application<span class="symbol">.</span>ProductName<span class="symbol">,</span> MessageBoxButtons<span class="symbol">.</span>YesNo<span class="symbol">,</span> MessageBoxIcon<span class="symbol">.</span>Question<span class="symbol">)</span> <span class="symbol">==</span> DialogResult<span class="symbol">.</span>Yes<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">string</span> ShowPrompt<span class="symbol">(</span><span class="keyword">string</span> message<span class="symbol">,</span> <span class="keyword">string</span> defaultValue<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">return</span> InputDialog<span class="symbol">.</span>ShowInputDialog<span class="symbol">(</span>_logControl<span class="symbol">.</span>FindForm<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">,</span> message<span class="symbol">,</span> Application<span class="symbol">.</span>ProductName<span class="symbol">,</span> defaultValue<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
</pre>
</figure>
<h2 id="thread-safety">Thread safety</h2>
<p>When I first added scripting to an application, it ran in the UI
thread. For this demonstration, I decided to run it on a
separate thread using a <code>BackgroundWorker</code>. I don't really know
if Jint is inherently thread safe or not, but so far I haven't
had any problems with this approach.</p>
<p>Except of course, for when I want to update a WinForms control
as this isn't possible to do on anything but the UI thread. In
addition, the <code>ShowPrompt</code> example above, if called from another
thread, will neither be modal nor positioned correctly.</p>
<h3 id="fire-and-forget">Fire and forget</h3>
<p>In the simplest of cases, you can check if the call is occurring
on a different thread by using the <code>Control.InvokeRequired</code>
property. If this is <code>false</code>, the code is executing on the UI
thread and it is safe to continue. If <code>true</code>, it is on a
different thread and so you need to use the <code>Control.Invoke</code>
method to execute on the UI thread instead.</p>
<p>In the following example, the <code>WriteLine</code> method will either
update a text box if it is safe to do so, or will invoke itself
on the UI thread if not.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> WriteLine<span class="symbol">(</span><span class="keyword">string</span> value<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_logControl<span class="symbol">.</span>InvokeRequired<span class="symbol">)</span>
 <span class="symbol">{</span>
 _logControl<span class="symbol">.</span>Invoke<span class="symbol">(</span><span class="keyword">new</span> Action<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">&gt;</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>WriteLine<span class="symbol">)</span><span class="symbol">,</span> value<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 _logControl<span class="symbol">.</span>AppendText<span class="symbol">(</span>value <span class="symbol">+</span> Environment<span class="symbol">.</span>NewLine<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
</pre>
</figure>
<h3 id="returning-a-result">Returning a result</h3>
<p>When you need to return a result, it is a little more
complicated, but not overly so. As before, I check
<code>InvokeRequired</code> to see if the code is on the UI thread and if
not I set up an asynchronous operation using
<code>Control.BeginInvoke</code>, using an <code>IAsyncResult</code> instance to wait
for completion and access the result via <code>Control.EndInvoke</code>.</p>
<blockquote>
<p>This API is <em>old</em>, introduced long before .NET's <code>Task</code> class.
Unfortunately as WinForms has been stagnating since 2005 or
so, the chances of Microsoft modernising the API seem slim.</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">string</span> ShowPrompt<span class="symbol">(</span><span class="keyword">string</span> message<span class="symbol">,</span> <span class="keyword">string</span> defaultValue<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>
 Form owner<span class="symbol">;</span>

 owner <span class="symbol">=</span> _logControl<span class="symbol">.</span>FindForm<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>owner<span class="symbol">.</span>InvokeRequired<span class="symbol">)</span>
 <span class="symbol">{</span>
 Func<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">&gt;</span> caller<span class="symbol">;</span>
 IAsyncResult asyncResult<span class="symbol">;</span>

 caller <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ShowPromptDialog<span class="symbol">;</span>

 asyncResult <span class="symbol">=</span> owner<span class="symbol">.</span>BeginInvoke<span class="symbol">(</span>caller<span class="symbol">,</span> message<span class="symbol">,</span> defaultValue<span class="symbol">)</span><span class="symbol">;</span>
 asyncResult<span class="symbol">.</span>AsyncWaitHandle<span class="symbol">.</span>WaitOne<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 result <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">string</span><span class="symbol">)</span>owner<span class="symbol">.</span>EndInvoke<span class="symbol">(</span>asyncResult<span class="symbol">)</span><span class="symbol">;</span>

 asyncResult<span class="symbol">.</span>AsyncWaitHandle<span class="symbol">.</span>Close<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> InputDialog<span class="symbol">.</span>ShowInputDialog<span class="symbol">(</span>_logControl<span class="symbol">.</span>FindForm<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">,</span> message<span class="symbol">,</span> Application<span class="symbol">.</span>ProductName<span class="symbol">,</span> defaultValue<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">string</span> ShowPromptDialog<span class="symbol">(</span><span class="keyword">string</span> message<span class="symbol">,</span> <span class="keyword">string</span> defaultValue<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">return</span> InputDialog<span class="symbol">.</span>ShowInputDialog<span class="symbol">(</span>_logControl<span class="symbol">.</span>FindForm<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">,</span> message<span class="symbol">,</span> defaultValue<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>Stepping through the code when an invoke is required - first, I
get a delegate representing the function call I need to make.</p>
<p>Next, I begin an asynchronous operation by calling
<code>Control.BeginInvoke</code>, passing in the delete to execute and the
parameters it requires. This returns an <code>IAsyncResult</code> instance
which I capture.</p>
<p>This result provides access to a <code>WaitHandle</code> which in turn
provides a <code>WaitOne</code> method. By calling this, our non-UI thread
will pause until it receives a signal indicating that the async
operation has completed.</p>
<p>Once the signal has been received and the code continues
execution, we call <code>Control.EndInvoke</code>, passing in the async
result we captured earlier. The <code>EndInvoke</code> method will return
the result of the function call which I then cast appropriately.</p>
<p>Finally, I close the <code>WaitHandle</code> to cause its resources to be
released.</p>
<p>There's quite a lot of boiler-plate code involved, I should
probably create extension methods to simplify this for future
work.</p>
<h3 id="to-multi-thread-or-not-to-multi-thread">To multi-thread or not to multi-thread</h3>
<p>Depending on what functionality you expose to your scripts,
running on multiple threads could be a significant issue as if
the UI itself isn't thread aware, then any calls which interact
with the UI will either have unexpected results or throw the
dreaded <strong>Cross-thread operation not valid</strong> exception.</p>
<h2 id="other-languages">Other languages</h2>
<p>Although I initially created my scripting experiment using
JavaScript, other languages are available. Previously I've used
<a href="https://www.lua.org/" rel="external nofollow noopener">Lua</a> via the <a href="https://www.nuget.org/packages/VikingErik.LuaInterface" rel="external nofollow noopener">VikingErik.LuaInterface</a> package (which I
only remember because of the name!). I'd probably use <a href="http://nlua.org/" rel="external nofollow noopener">NLua</a>
for new work.</p>
<p>Then there is Python. This seems to be becoming more popular (or
maybe always was and I was unaware!). <a href="https://ironpython.net/" rel="external nofollow noopener">IronPython</a> is a .NET
implementation of Python and I will probably look into this in
future. Last time I took note of this library, it had just been
dropped by Microsoft (along with <a href="http://ironruby.net/" rel="external nofollow noopener">IronRuby</a>), although unlike
the latter it appears to have landed on its feet.</p>
<h2 id="the-sample-application">The sample application</h2>
<p>The demonstration program included with this article is a
pretend drawing application. I say pretend as I haven't included
anything other than a script interface, but you can &quot;draw&quot; with
this, and it was a quick and easy way of showing the
functionality.</p>
<p>The application defines a <code>PixelPicture</code> class, an instance of
which is added to the scripting engine via the <code>picture</code>
variable. This exposes a number of methods such as <code>plot</code>,
<code>drawLine</code>, <code>drawCircle</code> etc to perform drawing methods.</p>
<p>It also defines an <code>application</code> variable which can be used to
manipulate the host application, for example to change the
window caption. Nothing exotic, but simple ideas on how you
could add scripting to your own applications.</p>
<p>The code for plotting the pixels for lines, circles, ellipses
and curves uses <a href="https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm" rel="external nofollow noopener">Bresenham's line algorithm</a>, with the C#
implementation coming from easy.Filter's <a href="http://members.chello.at/easyfilter/bresenham.html" rel="external nofollow noopener">The Beauty of
Bresenham's Algorithm</a> page.</p>
<p>The flood fill implementation was taken from <a href="https://rosettacode.org/wiki/Bitmap/Flood_fill#C.23" rel="external nofollow noopener">RosettaCode</a>.</p>
<p>While I have provided versions of the sample application using
both Jint 2 and Jint 3, I upgraded to Jint 3 after starting to
write this article, therefore it is missing refactoring and
enhancements made during the course of writing this piece.</p>
<p>You can download the sample projects from the links on this
article, or visit the <a href="https://github.com/cyotek/ScriptingHostDemo" rel="external nofollow noopener">GitHub repository</a> for the latest
version.</p>
<p>For example, the following script will draw a smiley.</p>
<figure class="lang-javascript highlight"><figcaption><span>javascript</span></figcaption><pre class="code">
<span class="keyword">var</span> size <span class="symbol">=</span> <span class="number">64</span>
<span class="keyword">var</span> half <span class="symbol">=</span> size <span class="symbol">/</span> <span class="number">2</span><span class="symbol">;</span>
<span class="keyword">var</span> quarter <span class="symbol">=</span> size <span class="symbol">/</span> <span class="number">4</span><span class="symbol">;</span>
<span class="keyword">var</span> eighth <span class="symbol">=</span> size <span class="symbol">/</span> <span class="number">8</span><span class="symbol">;</span>
<span class="keyword">var</span> sixteenth <span class="symbol">=</span> size <span class="symbol">/</span> <span class="number">16</span><span class="symbol">;</span>

picture<span class="symbol">.</span>Width <span class="symbol">=</span> size<span class="symbol">;</span>
picture<span class="symbol">.</span>Height <span class="symbol">=</span> size<span class="symbol">;</span>

picture<span class="symbol">.</span>clear<span class="symbol">(</span>color<span class="symbol">.</span>White<span class="symbol">)</span><span class="symbol">;</span>

picture<span class="symbol">.</span>drawCircle<span class="symbol">(</span>half<span class="symbol">,</span> half<span class="symbol">,</span> half <span class="symbol">-</span> <span class="number">1</span><span class="symbol">,</span> color<span class="symbol">.</span>Goldenrod<span class="symbol">)</span><span class="symbol">;</span>
picture<span class="symbol">.</span>floodFill<span class="symbol">(</span>half<span class="symbol">,</span> half<span class="symbol">,</span> color<span class="symbol">.</span>White<span class="symbol">,</span> color<span class="symbol">.</span>Yellow<span class="symbol">)</span><span class="symbol">;</span>

picture<span class="symbol">.</span>drawCircle<span class="symbol">(</span>quarter <span class="symbol">*</span> <span class="number">1.5</span><span class="symbol">,</span> quarter <span class="symbol">*</span> <span class="number">1.25</span><span class="symbol">,</span> eighth<span class="symbol">,</span> color<span class="symbol">.</span>Black<span class="symbol">)</span><span class="symbol">;</span>
picture<span class="symbol">.</span>floodFill<span class="symbol">(</span>quarter <span class="symbol">*</span> <span class="number">1.5</span><span class="symbol">,</span> quarter <span class="symbol">*</span> <span class="number">1.25</span><span class="symbol">,</span> color<span class="symbol">.</span>Yellow<span class="symbol">,</span> color<span class="symbol">.</span>White<span class="symbol">)</span><span class="symbol">;</span>
picture<span class="symbol">.</span>drawCircle<span class="symbol">(</span>quarter <span class="symbol">*</span> <span class="number">1.5</span><span class="symbol">,</span> quarter <span class="symbol">*</span> <span class="number">1.5</span><span class="symbol">,</span> sixteenth<span class="symbol">,</span> color<span class="symbol">.</span>Black<span class="symbol">)</span><span class="symbol">;</span>
picture<span class="symbol">.</span>floodFill<span class="symbol">(</span>quarter <span class="symbol">*</span> <span class="number">1.5</span><span class="symbol">,</span> quarter <span class="symbol">*</span> <span class="number">1.5</span><span class="symbol">,</span> color<span class="symbol">.</span>White<span class="symbol">,</span> color<span class="symbol">.</span>Black<span class="symbol">)</span><span class="symbol">;</span>

picture<span class="symbol">.</span>drawCircle<span class="symbol">(</span>quarter <span class="symbol">*</span> <span class="number">2.5</span><span class="symbol">,</span> quarter <span class="symbol">*</span> <span class="number">1.25</span><span class="symbol">,</span> eighth<span class="symbol">,</span> color<span class="symbol">.</span>Black<span class="symbol">)</span><span class="symbol">;</span>
picture<span class="symbol">.</span>floodFill<span class="symbol">(</span>quarter <span class="symbol">*</span> <span class="number">2.5</span><span class="symbol">,</span> quarter <span class="symbol">*</span> <span class="number">1.25</span><span class="symbol">,</span> color<span class="symbol">.</span>Yellow<span class="symbol">,</span> color<span class="symbol">.</span>White<span class="symbol">)</span><span class="symbol">;</span>
picture<span class="symbol">.</span>drawCircle<span class="symbol">(</span>quarter <span class="symbol">*</span> <span class="number">2.5</span><span class="symbol">,</span> quarter <span class="symbol">*</span> <span class="number">1.5</span><span class="symbol">,</span> sixteenth<span class="symbol">,</span> color<span class="symbol">.</span>Black<span class="symbol">)</span><span class="symbol">;</span>
picture<span class="symbol">.</span>floodFill<span class="symbol">(</span>quarter <span class="symbol">*</span> <span class="number">2.5</span><span class="symbol">,</span> quarter <span class="symbol">*</span> <span class="number">1.5</span><span class="symbol">,</span> color<span class="symbol">.</span>White<span class="symbol">,</span> color<span class="symbol">.</span>Black<span class="symbol">)</span><span class="symbol">;</span>

picture<span class="symbol">.</span>drawEllipse<span class="symbol">(</span>quarter<span class="symbol">,</span> quarter <span class="symbol">*</span> <span class="number">2.25</span><span class="symbol">,</span> half<span class="symbol">,</span> quarter<span class="symbol">,</span> color<span class="symbol">.</span>Black<span class="symbol">)</span><span class="symbol">;</span>
picture<span class="symbol">.</span>floodFill<span class="symbol">(</span>half<span class="symbol">,</span> quarter <span class="symbol">*</span> <span class="number">3</span><span class="symbol">,</span> color<span class="symbol">.</span>Yellow<span class="symbol">,</span> color<span class="symbol">.</span>Black<span class="symbol">)</span><span class="symbol">;</span>

picture<span class="symbol">.</span>drawEllipse<span class="symbol">(</span>quarter<span class="symbol">,</span> quarter <span class="symbol">*</span> <span class="number">1.75</span><span class="symbol">,</span> half<span class="symbol">,</span> quarter<span class="symbol">,</span> color<span class="symbol">.</span>Yellow<span class="symbol">)</span><span class="symbol">;</span>
picture<span class="symbol">.</span>floodFill<span class="symbol">(</span>half<span class="symbol">,</span> half <span class="symbol">*</span> <span class="number">1.25</span><span class="symbol">,</span> color<span class="symbol">.</span>Black<span class="symbol">,</span> color<span class="symbol">.</span>Yellow<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/script1-smiley.png" class="gallery" title="A example of my terrible drawing skills making a smiley" ><img src="https://images.cyotek.com/image/thumbnail/devblog/script1-smiley.png" alt="A example of my terrible drawing skills making a smiley" decoding="async" loading="lazy" /></a><figcaption>A example of my terrible drawing skills making a smiley</figcaption></figure>
<p>Or, for something more dynamic, the following script will plot
the results of sine and cosine.</p>
<figure class="lang-javascript highlight"><figcaption><span>javascript</span></figcaption><pre class="code">
<span class="keyword">var</span> sy<span class="number">1</span><span class="symbol">;</span>
<span class="keyword">var</span> sy<span class="number">2</span><span class="symbol">;</span>
<span class="keyword">var</span> cy<span class="number">1</span><span class="symbol">;</span>
<span class="keyword">var</span> cy<span class="number">2</span><span class="symbol">;</span>

<span class="keyword">var</span> width <span class="symbol">=</span> <span class="number">128</span>
<span class="keyword">var</span> height <span class="symbol">=</span> <span class="number">64</span><span class="symbol">;</span>
<span class="keyword">var</span> third <span class="symbol">=</span> height <span class="symbol">/</span> <span class="number">3</span><span class="symbol">;</span>
<span class="keyword">var</span> lineColor <span class="symbol">=</span> color<span class="symbol">.</span>FromArgb<span class="symbol">(</span><span class="number">102</span><span class="symbol">,</span> <span class="number">51</span><span class="symbol">,</span> <span class="number">153</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="keyword">var</span> lineColor<span class="number">2</span> <span class="symbol">=</span> color<span class="symbol">.</span>FromArgb<span class="symbol">(</span><span class="number">153</span><span class="symbol">,</span> <span class="number">51</span><span class="symbol">,</span> <span class="number">102</span><span class="symbol">)</span><span class="symbol">;</span>

picture<span class="symbol">.</span>Width <span class="symbol">=</span> width<span class="symbol">;</span>
picture<span class="symbol">.</span>Height <span class="symbol">=</span> height<span class="symbol">;</span>
picture<span class="symbol">.</span>clear<span class="symbol">(</span>color<span class="symbol">.</span>White<span class="symbol">)</span><span class="symbol">;</span>

sy<span class="number">2</span> <span class="symbol">=</span> third
cy<span class="number">2</span> <span class="symbol">=</span> third <span class="symbol">*</span> <span class="number">2</span><span class="symbol">;</span>

<span class="keyword">for</span><span class="symbol">(</span><span class="keyword">var</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> width<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
<span class="symbol">{</span>
 sy<span class="number">1</span> <span class="symbol">=</span> third <span class="symbol">+</span> Math<span class="symbol">.</span>sin<span class="symbol">(</span>i<span class="symbol">)</span> <span class="symbol">*</span> <span class="number">10</span><span class="symbol">;</span>
 cy<span class="number">1</span> <span class="symbol">=</span> <span class="symbol">(</span>third <span class="symbol">*</span> <span class="number">2</span><span class="symbol">)</span> <span class="symbol">+</span> Math<span class="symbol">.</span>cos<span class="symbol">(</span>i<span class="symbol">)</span> <span class="symbol">*</span> <span class="number">10</span><span class="symbol">;</span>

 picture<span class="symbol">.</span>drawLine<span class="symbol">(</span>i<span class="symbol">,</span> sy<span class="number">2</span><span class="symbol">,</span> i <span class="symbol">+</span> <span class="number">1</span><span class="symbol">,</span> sy<span class="number">1</span><span class="symbol">,</span> lineColor<span class="symbol">)</span><span class="symbol">;</span>
 picture<span class="symbol">.</span>drawLine<span class="symbol">(</span>i<span class="symbol">,</span> cy<span class="number">2</span><span class="symbol">,</span> i <span class="symbol">+</span> <span class="number">1</span><span class="symbol">,</span> cy<span class="number">1</span><span class="symbol">,</span> lineColor<span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>

 sy<span class="number">2</span> <span class="symbol">=</span> sy<span class="number">1</span><span class="symbol">;</span>
 cy<span class="number">2</span> <span class="symbol">=</span> cy<span class="number">1</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/script1-sine.png" class="gallery" title="Plotting a wave using sine and cosine" ><img src="https://images.cyotek.com/image/thumbnail/devblog/script1-sine.png" alt="Plotting a wave using sine and cosine" decoding="async" loading="lazy" /></a><figcaption>Plotting a wave using sine and cosine</figcaption></figure>
<p>Although this demonstration project is a little contrived, if
you add scripting support to your application you may find it be
a very valuable feature.</p>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/adding-scripting-to-net-applications .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a custom type converter part 3: Types to stringurn:uuid:ef08698e-c016-40d7-b85d-181b3bccea4b2019-07-21T11:35:23Z2019-07-21T11:35:23Z<p>I have discussed creating type converters a few times on this
blog, and recently came across another use for them. I have a
.NET Standard library that uses a variety of <code>struct</code> instances
that are sometimes properties of concrete classes. I also have a
.NET Framework 4.7.2 demonstration application to work with the
library that uses the <code>PropertyGrid</code> control to provide easy
editing facilities.</p>
<blockquote>
<p>Note: While in this article I'm talking about using type
converters purely in relation for use with the <code>PropertyGrid</code>
control, this isn't their only purpose (just as well otherwise
they wouldn't be a part of .NET Standard I assume). You can
learn more about the <code>TypeConverter</code> class on <a href="https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.typeconverter" rel="external nofollow noopener">MSDN</a>.</p>
</blockquote>
<h2 id="why-not-expandableobjectconverter">Why not ExpandableObjectConverter</h2>
<p>In a previous article, I noted you could use the
<a href="/post/creating-a-custom-typeconverter-part-1#the-expandableobjectconverter">ExpandableObjectConverter</a> type to provide a reasonably
decent editing experience for basic objects out the box.
However, this doesn't work for structs, usually for one of two
reasons</p>
<ol>
<li>A well designed <code>struct</code> is generally immutable</li>
<li>A <code>struct</code> is a value type, meaning that the <code>PropertyGrid</code>
is actually editing a copy of the value, not the original</li>
</ol>
<p>There is also a related issue - the <code>PropertyGrid</code> will default
to calling <code>ToString</code> on the source type. If you haven't
overrode <code>ToString</code>, then this will default to the name of the
type but even if you have it is possible that you followed the
conventions of similar types and return a list of names and
values, great for debugging purposes but less so from the end
user in a consumer application.</p>
<p>In short, there isn't an &quot;easy fix&quot; to allow easy editing of
value types. But the solution isn't onerous or convoluted.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/customtypeconverter3-noedit.gif" class="gallery" title="Updating the individual properties does not update the underlying value" ><img src="https://images.cyotek.com/image/devblog/customtypeconverter3-noedit.gif" alt="Updating the individual properties does not update the underlying value" decoding="async" loading="lazy" /></a><figcaption>Updating the individual properties does not update the underlying value</figcaption></figure><h2 id="creating-a-custom-type-converter">Creating a custom Type Converter</h2>
<p>For the purposes of this article, I'm going to be working with a
read-only struct named <code>UserTuple</code>, using the following
definition</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>TypeConverter<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>UserTupleConverter<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">struct</span> UserTuple
<span class="symbol">{</span>
 UserTuple<span class="symbol">(</span><span class="keyword">double</span> x<span class="symbol">,</span> <span class="keyword">double</span> y<span class="symbol">,</span> <span class="keyword">double</span> z<span class="symbol">,</span> <span class="keyword">double</span> w<span class="symbol">)</span>

 <span class="keyword">double</span> X <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">double</span> Y <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">double</span> Z <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">double</span> W <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">static</span> UserTuple Parse<span class="symbol">(</span><span class="keyword">string</span> value<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">bool</span> TryParse<span class="symbol">(</span><span class="keyword">string</span> value<span class="symbol">,</span> <span class="keyword">out</span> UserTuple result<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">string</span> ToString<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">=&gt;</span> <span class="string">&quot;{X=&quot;</span> <span class="symbol">+</span> _x <span class="symbol">+</span> <span class="string">&quot;,Y=&quot;</span> <span class="symbol">+</span> _y <span class="symbol">+</span> <span class="string">&quot;,Z=&quot;</span> <span class="symbol">+</span> _z <span class="symbol">+</span> <span class="string">&quot;,W=&quot;</span> <span class="symbol">+</span> _w <span class="symbol">+</span> <span class="string">&quot;}&quot;</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>This in turn is used by the fictitious <code>UserClass</code> type.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">class</span> UserClass <span class="symbol">:</span> INotifyPropertyChanged
<span class="symbol">{</span>
 <span class="keyword">event</span> PropertyChangedEventHandler PropertyChanged<span class="symbol">;</span>
 
 UserTuple TupleA <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
 UserTuple TupleB <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">void</span> OnPropertyChanged<span class="symbol">(</span><span class="keyword">string</span> propertyName<span class="symbol">)</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>I have also created a <code>UserTupleConverter</code> class that inherits
from <code>TypeConverter</code> and is bound to the <code>UserTuple</code> type via
the <code>TypeConverter</code> attribute.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">class</span> UserTupleConverter <span class="symbol">:</span> TypeConverter
<span class="symbol">{</span>

<span class="symbol">}</span>
</pre>
</figure>
<p>The full types are in the available demonstration download.</p>
<p>I originally covered the basics of creating a type converter in
parts <a href="/post/creating-a-custom-typeconverter-part-1">1</a> and <a href="/post/creating-a-custom-typeconverter-part-2">2</a> of this series. However, to make this
article standalone I'll re-cover the basics.</p>
<p>At the most basic level, we have a class that inherits from
<code>TypeConverter</code> and we override either the <code>CanConvertFrom</code> and
<code>ConvertFrom</code> method pair, or the <code>CanConvertTo</code> and <code>ConvertTo</code>
pair, or both.</p>
<p>The <code>ConvertFrom</code> method is used to create an instance of our
type from another value - often (as in this example), a
<code>string</code>.</p>
<p>The <code>ConvertTo</code> method is used to take an instance of our type
and convert it
to another value. Again, this example is going to use a <code>string</code> but you can do
many things - including design time code generation.</p>
<p>Although I won't cover it here, there are some more advanced
features you can do, for example making your type &quot;expandable&quot;
so that you can edit individual properties, or providing
pre-defined standard values.</p>
<h2 id="converting-a-string-into-a-value">Converting a String into a Value</h2>
<p>To provide our <code>string</code> to <code>UserTuple</code> functionality, we first
override <code>CanConvertFrom</code> and make sure we return <code>true</code> if the
<code>sourceType</code> is <code>string</code>.</p>
<p>For performing the actual conversion, we override <code>ConvertFrom</code>,
see if the passed <code>value</code> is a string and if so call our <code>Parse</code>
function to perform the conversion.</p>
<blockquote>
<p>Note that I am calling <code>Parse</code> and not <code>TryParse</code>. This is so
if the user enters an invalid string, they'll get a
(hopefully!) tailored exception message - otherwise the
exception will be worded similar to &quot;<em>UserTupleConverter
cannot convert from System.String.</em>&quot;, which is clearly
incorrect.</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">bool</span> CanConvertFrom<span class="symbol">(</span>ITypeDescriptorContext context<span class="symbol">,</span> Type sourceType<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> sourceType <span class="symbol">==</span> <span class="keyword">typeof</span><span class="symbol">(</span><span class="keyword">string</span><span class="symbol">)</span> <span class="symbol">||</span> <span class="keyword">base</span><span class="symbol">.</span>CanConvertFrom<span class="symbol">(</span>context<span class="symbol">,</span> sourceType<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">object</span> ConvertFrom<span class="symbol">(</span>ITypeDescriptorContext context<span class="symbol">,</span> CultureInfo culture<span class="symbol">,</span> <span class="keyword">object</span> value<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">object</span> result<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>value <span class="keyword">is</span> <span class="keyword">string</span> s <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>s<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> UserTuple<span class="symbol">.</span>Parse<span class="symbol">(</span>s<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result <span class="symbol">??</span> <span class="keyword">base</span><span class="symbol">.</span>ConvertFrom<span class="symbol">(</span>context<span class="symbol">,</span> culture<span class="symbol">,</span> value<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>That provides the first part of the functionality we need - I
can now type in a separated list of values and have a brand new
value created and assigned, thus neatly bypassing both of the
problems outlined in the list at the start of the article.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/customtypeconverter3-fromstring.gif" class="gallery" title="Setting a value is fine, although the display leaves much to be desired" ><img src="https://images.cyotek.com/image/devblog/customtypeconverter3-fromstring.gif" alt="Setting a value is fine, although the display leaves much to be desired" decoding="async" loading="lazy" /></a><figcaption>Setting a value is fine, although the display leaves much to be desired</figcaption></figure>
<p>However, as you can see in the animation it is hardly a neat
process due to the inclusion of all the additional data from
<code>ToString</code>.</p>
<h2 id="converting-a-value-into-a-string">Converting a Value into a String</h2>
<p>To clean up the raw text displayed in the <code>PropertyGrid</code>, we can
make use of the <code>CanConvertTo</code> and <code>CanConvertTo</code> methods. I
touched on these in a previous post regarding <a href="https://www.cyotek.com/blog/creating-a-custom-typeconverter-part-2#extending-convertto">generating design
time code</a> but glossed over the string aspects.</p>
<blockquote>
<p>It seems you don't need to override <code>CanConvertTo</code> for the
<code>string</code> type as the base <code>TypeConvertor</code> class has a default
<code>ToString</code> implementation, but I choose to include it below
for completeness.</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">bool</span> CanConvertTo<span class="symbol">(</span>ITypeDescriptorContext context<span class="symbol">,</span> Type destinationType<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> destinationType <span class="symbol">==</span> <span class="keyword">typeof</span><span class="symbol">(</span><span class="keyword">string</span><span class="symbol">)</span> <span class="symbol">||</span> <span class="keyword">base</span><span class="symbol">.</span>CanConvertTo<span class="symbol">(</span>context<span class="symbol">,</span> destinationType<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">object</span> ConvertTo<span class="symbol">(</span>ITypeDescriptorContext context<span class="symbol">,</span> CultureInfo culture<span class="symbol">,</span> <span class="keyword">object</span> value<span class="symbol">,</span> Type destinationType<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">object</span> result<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>value <span class="keyword">is</span> UserTuple tuple <span class="symbol">&amp;&amp;</span> destinationType <span class="symbol">==</span> <span class="keyword">typeof</span><span class="symbol">(</span><span class="keyword">string</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="keyword">string</span><span class="symbol">.</span>Format<span class="symbol">(</span><span class="string">&quot;{0}, {1}, {2}, {3}&quot;</span><span class="symbol">,</span> tuple<span class="symbol">.</span>X<span class="symbol">,</span> tuple<span class="symbol">.</span>Y<span class="symbol">,</span> tuple<span class="symbol">.</span>Z<span class="symbol">,</span> tuple<span class="symbol">.</span>W<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="keyword">base</span><span class="symbol">.</span>ConvertTo<span class="symbol">(</span>context<span class="symbol">,</span> culture<span class="symbol">,</span> value<span class="symbol">,</span> destinationType<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>First I test to see if the <code>value</code> is a <code>UserTuple</code> and if it is
I return a manually constructed comma separated list of the four
properties.</p>
<blockquote>
<p>I'm not actually sure what best practice is in this situation.
Certainly, I wouldn't want the parsing functionality part of
this class as it has definite use outside of it, hence having
<code>Parse</code> and <code>TryParse</code> methods on the core type. But for this
&quot;simpler&quot; string representation I have no idea and have chosen
to keep it a private part of the converter class.</p>
</blockquote>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/customtypeconverter3-tostring.gif" class="gallery" title="Clean and tidy reading and writing" ><img src="https://images.cyotek.com/image/devblog/customtypeconverter3-tostring.gif" alt="Clean and tidy reading and writing" decoding="async" loading="lazy" /></a><figcaption>Clean and tidy reading and writing</figcaption></figure>
<p>And that's it - with this simple converter in place, my UI
behaves exactly as I wanted.</p>
<h2 id="downloading-the-demonstration">Downloading the demonstration</h2>
<p>A demonstration application is available from the link below.</p>
<p>Note that as always, this is demonstration code and therefore
may elide extended details or contain less than stellar
functionality (for example the parsing of the <code>UserTuple</code> value
is neither efficient or locale aware).</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2019-07-21 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-a-custom-type-converter-part-3-types-to-string .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comSetting tab stops in a Windows Forms TextBox controlurn:uuid:6e9805c3-c921-40d9-bd90-b4d7267222f82019-05-25T08:14:37Z2019-05-25T00:44:48Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/textbox-tabstops-1b.png" class="gallery" title="An example of a TextBox control using custom tab stops" ><img src="https://images.cyotek.com/image/thumbnail/devblog/textbox-tabstops-1b.png" alt="An example of a TextBox control using custom tab stops" decoding="async" loading="lazy" /></a><figcaption>An example of a TextBox control using custom tab stops</figcaption></figure>
<p>I was adding a Wizard to one of my applications, and the final
screen of this Wizard was a summary of the user's choices. I
wanted the user to be able to copy this to the Clipboard if
required, and so I'd used a <code>TextBox</code> rather than the <code>ListView</code>
I might have otherwise used. However, this presented a minor
issue as I'd chosen to use tabs to delimit the information, and
the varying length of text meant that this wasn't aligned as
expected.</p>
<p>Previously I have &quot;dealt&quot; with this issue by cheating - I'd just
add extra tabs to force everything to line up. However, I do
plan on fully localising this application at some point, not to
mention that even using a different font could potentially
trigger the text to misalign once more. And so I decided to do
it properly this time.</p>
<p>I knew that Win32 <code>Edit</code> controls (of which the Windows Forms
<code>TextBox</code> wraps) supported customising tab stops, but this was
functionality the Framework developers did not expose.
Similarly, the <code>RichTextBox</code> offers the ability to customise tab
stops, but at a selection level and I really couldn't be fussed
with that approach. So I decided to directly invoke the Win32
API to set the tab stops in a <code>TextBox</code> control. How hard could
it be?</p>
<h2 id="introducing-em_settabstops">Introducing EM_SETTABSTOPS</h2>
<p>I already knew in principle how to do this, by using the
<code>SendMessage</code> API call and the <code>EM_SETTABSTOPS</code> message,
although I didn't know the specifics. On checking the
<a href="https://docs.microsoft.com/en-us/windows/desktop/controls/em-settabstops" rel="external nofollow noopener">documentation</a> for the message, it stated that when calling
<code>SendMessage</code> with this message, <code>wParam</code> is used to define how
the tab stops are set, whilst <code>lParam</code> has the tab stop values.
The following operations are available</p>
<table>
<thead>
<tr>
<th>wParam Value</th>
<th>lParam Value</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>0</td>
<td>Resets tab stops to default, which is every 32 dialog units</td>
</tr>
<tr>
<td>1</td>
<td><em>integer</em></td>
<td>Sets all tab stops to be every <em>integer</em> dialog units</td>
</tr>
<tr>
<td>2 or more</td>
<td><em>integer[]</em></td>
<td>Sets custom tab stops (in dialog units) using each value in <em>integer[]</em>. Although not explicitly documented, the last value in the array is used for any additional tabs the user enters into the control</td>
</tr>
</tbody>
</table>
<blockquote>
<p>Incidentally, there's an odd omission. Almost invariably with
the Win32 API, if there's a SET message, there's usually a
corresponding GET as well, e.g. <code>WM_SETTEXT</code> and <code>WM_GETTEXT</code>.
For some reason though, there is no <code>EM_GETTABSTOPS</code> - as far
as I can tell, there isn't a way of getting tab stop
information.</p>
</blockquote>
<h2 id="getting-started">Getting Started</h2>
<p>As the <code>EM_SETTABSTOPS</code> can be called several ways, I'm going to
define 3 different overloads of <code>SendMessage</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">static</span> <span class="keyword">class</span> NativeMethods
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">const</span> <span class="keyword">int</span> EM_SETTABSTOPS <span class="symbol">=</span> <span class="number">0x00CB</span><span class="symbol">;</span>

 <span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32&quot;</span><span class="symbol">,</span> CharSet <span class="symbol">=</span> CharSet<span class="symbol">.</span>Auto<span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">int</span> SendMessage<span class="symbol">(</span>IntPtr hWnd<span class="symbol">,</span> <span class="keyword">int</span> msg<span class="symbol">,</span> <span class="keyword">int</span> wParam<span class="symbol">,</span> <span class="keyword">int</span> lParam<span class="symbol">)</span><span class="symbol">;</span>

 <span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32&quot;</span><span class="symbol">,</span> CharSet <span class="symbol">=</span> CharSet<span class="symbol">.</span>Auto<span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">int</span> SendMessage<span class="symbol">(</span>IntPtr hWnd<span class="symbol">,</span> <span class="keyword">int</span> msg<span class="symbol">,</span> <span class="keyword">int</span> wParam<span class="symbol">,</span> <span class="keyword">int</span><span class="symbol">[</span><span class="symbol">]</span> lParam<span class="symbol">)</span><span class="symbol">;</span>

 <span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32&quot;</span><span class="symbol">,</span> CharSet <span class="symbol">=</span> CharSet<span class="symbol">.</span>Auto<span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">int</span> SendMessage<span class="symbol">(</span>IntPtr hWnd<span class="symbol">,</span> <span class="keyword">int</span> msg<span class="symbol">,</span> <span class="keyword">int</span> wParam<span class="symbol">,</span> <span class="keyword">ref</span> <span class="keyword">int</span> lParam<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>The documentation for the message states that the
<code>EM_SETTABSTOPS</code> message doesn't cause the <code>Edit</code> control to
be refreshed, although in my testing this didn't seem to be
the case - the control was always repainted. My assumption is
the <code>TextBox</code> control is refreshing itself when receiving
certain messages, however I have chosen to also explicitly
request a repaint by calling <code>Invalidate</code>.</p>
</blockquote>
<h2 id="setting-all-tab-stops-to-be-the-same-fixed-value">Setting all tab stops to be the same fixed value</h2>
<p>If you want all tab stops to be the same fixed value with no
variations, you send <code>EM_SETTABSTOPS</code> with <code>wParam</code> set to <code>1</code>
and <code>lParam</code> to the new tab size.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
NativeMethods<span class="symbol">.</span>SendMessage<span class="symbol">(</span>textBox<span class="symbol">.</span>Handle<span class="symbol">,</span> NativeMethods<span class="symbol">.</span>EM_SETTABSTOPS<span class="symbol">,</span> <span class="number">1</span><span class="symbol">,</span> tabSize<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="setting-custom-tab-stops">Setting custom tab stops</h2>
<p>To specify tab stops of different sizes, you send
<code>EM_SETTABSTOPS</code> with <code>wParam</code> with a value greater than one,
and <code>lParam</code> with an array of tab stop positions.</p>
<blockquote>
<p>Although the documentation states that this should be greater
than one, I observed (on Windows 10 1809) that unless <code>wParam</code>
was equal to the length of the array I was passing in, the
control didn't behave exactly as expected.</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
NativeMethods<span class="symbol">.</span>SendMessage<span class="symbol">(</span>textBox<span class="symbol">.</span>Handle<span class="symbol">,</span> NativeMethods<span class="symbol">.</span>EM_SETTABSTOPS<span class="symbol">,</span> tabStops<span class="symbol">.</span>Length<span class="symbol">,</span> tabStops<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<blockquote>
<p>Important! The array of tab stops is specified as absolute
positions, not the size of each stop, e.g each item in the
array should be the sum of all previous entries plus the size
of the tab stop. So for example, if you wanted tab sizes of
<code>100</code>, <code>60</code> and <code>60</code> dialog units, the values to send would be
<code>100</code>, <code>160</code> and <code>220</code>.</p>
</blockquote>
<h2 id="resetting-tab-stops">Resetting tab stops</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/textbox-tabstops-1a.png" class="gallery" title="An example of a TextBox control using default tab stops" ><img src="https://images.cyotek.com/image/thumbnail/devblog/textbox-tabstops-1a.png" alt="An example of a TextBox control using default tab stops" decoding="async" loading="lazy" /></a><figcaption>An example of a TextBox control using default tab stops</figcaption></figure>
<p>To reset tab stops, we send the <code>EM_SETTABSTOPS</code> message to our
<code>TextBox</code> with a <code>wParam</code> value of <code>0</code>. <code>lParam</code> is unused in
this case so I send <code>0</code> as well.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
NativeMethods<span class="symbol">.</span>SendMessage<span class="symbol">(</span>textBox<span class="symbol">.</span>Handle<span class="symbol">,</span> NativeMethods<span class="symbol">.</span>EM_SETTABSTOPS<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="introducing-dialog-units">Introducing Dialog Units</h2>
<p>I mentioned above that tab stops are expressed in terms of
<em>dialog units.</em> But what are these? I certainly wasn't familiar
with them, pretty much all API calls I've ever used express
these types of values in plain pixels.</p>
<p>I can't actually find a dedicated topic in Microsoft's
documentation, but essentially a dialog unit is a device
independent way of specifying position and size information for
dialog controls. I believe they are mostly used in resource
templates, but as these are generally the province of C++
applications they aren't actually something I've used before.</p>
<p>As to the actual values of a dialog unit, they are equal to the
average width, in pixels, of the characters in the font used by
the dialog; the vertical base unit is equal to the height, in
pixels, of the font.</p>
<p>There are also a set of base units, which are the same
calculations but for the system font. I wonder how many modern
Windows developers have seen the system font, given it hasn't
been in widespread used since Windows 3.0!</p>
<h2 id="converting-from-dialog-unit-to-pixels">Converting from Dialog Unit to Pixels</h2>
<p>In order to convert from dialog units to pixels, you are
supposed to be able to use the <a href="https://docs.microsoft.com/en-gb/windows/desktop/api/winuser/nf-winuser-mapdialogrect" rel="external nofollow noopener"><code>MapDialogRect</code></a> function, by
passing in the dialog handle and a <code>RECT</code> structure populated
with the values to convert. The function will modify the <code>RECT</code>
with the converted values, at least in theory. Unfortunately
this is where things get complicated as the documentation states
<em>this function accepts only handles returned by one of the
dialog box creation functions; handles for other windows are not
valid.</em> However, as Windows Forms doesn't use the dialog
functions for creating windows, I was not able to get this API
call to perform a conversion.</p>
<p>The <a href="https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getdialogbaseunits" rel="external nofollow noopener">documentation</a> for <code>GetDialogBaseUnits</code> notes that you
can use <code>GetTextMetrics</code> to calculate the values for a given
font. I'd already <a href="/post/retrieving-font-and-text-metrics-using-csharp">written</a> about this some time ago and so I
did test this but the results were inconclusive, so I wonder if
<code>MapDialogRect</code> is doing additional actions rather than just
returning the average.</p>
<p>(In a move that doesn't surprise me in the slightest given my
experiences with this functionality so far, there isn't a
function to take pixel values and convert them into dialog
units.)</p>
<p>Fortunately, it doesn't really matter<sup>1</sup> as I suppose
you don't really want pixel perfect tab stops. I certainly
didn't, I just wanted rough values so &quot;columns&quot; would line up,
and we can do a naive characters to dialog units conversion by
multiplying the number of characters by 4. Not perfect, but good
enough.</p>
<h2 id="auto-detecting-tab-stops">Auto detecting tab stops</h2>
<figure class="screenshot" ><a href="https://www.cyotek.com/files/articleimages/textbox-tabstops-1c.png" class="gallery" title="An example of a TextBox control where tab stops have been auto-detected based on the contents of the control" ><img src="https://www.cyotek.com/files/articleimages/textbox-tabstops-1c-thumbnail.png" alt="An example of a TextBox control where tab stops have been auto-detected based on the contents of the control" decoding="async" loading="lazy" /></a><figcaption>An example of a TextBox control where tab stops have been auto-detected based on the contents of the control</figcaption></figure>
<p>I mentioned in the article preamble that I wanted to reformat
summary text so that columns would align. Below is a function
that will calculate rough tab stop positions based on a text
string.</p>
<blockquote>
<p>Note: This is rough and ready code and is doing a lot of
string manipulation via <code>string.Split</code> so isn't very efficient
at all. Originally I was going to count characters from tabs
to avoid any type of string processing at all, but I've more
than ran out of the time I'd allocated for this article.</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span><span class="symbol">[</span><span class="symbol">]</span> AutoDetectTabStops<span class="symbol">(</span><span class="keyword">string</span> text<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span><span class="symbol">[</span><span class="symbol">]</span> result<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>text<span class="symbol">)</span> <span class="symbol">&amp;&amp;</span> text<span class="symbol">.</span>IndexOf<span class="symbol">(</span><span class="string">&#39;\t&#39;</span><span class="symbol">)</span> <span class="symbol">!=</span> <span class="symbol">-</span><span class="number">1</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 List<span class="symbol">&lt;</span><span class="keyword">int</span><span class="symbol">&gt;</span> tabStops<span class="symbol">;</span>
 <span class="keyword">string</span><span class="symbol">[</span><span class="symbol">]</span> lines<span class="symbol">;</span>

 tabStops <span class="symbol">=</span> <span class="keyword">new</span> List<span class="symbol">&lt;</span><span class="keyword">int</span><span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 lines <span class="symbol">=</span> text<span class="symbol">.</span>Split<span class="symbol">(</span>_lineSeparators<span class="symbol">,</span> StringSplitOptions<span class="symbol">.</span>RemoveEmptyEntries<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> lines<span class="symbol">.</span>Length<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span><span class="symbol">[</span><span class="symbol">]</span> cells<span class="symbol">;</span>

 cells <span class="symbol">=</span> lines<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">.</span>Split<span class="symbol">(</span>_cellSeparators<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> j <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> j <span class="symbol">&lt;</span> cells<span class="symbol">.</span>Length<span class="symbol">;</span> j<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> estimatedDialogUnits<span class="symbol">;</span>

 estimatedDialogUnits <span class="symbol">=</span> cells<span class="symbol">[</span>j<span class="symbol">]</span><span class="symbol">.</span>Length <span class="symbol">*</span> <span class="number">4</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>tabStops<span class="symbol">.</span>Count <span class="symbol">&lt;=</span> j<span class="symbol">)</span>
 <span class="symbol">{</span>
 tabStops<span class="symbol">.</span>Add<span class="symbol">(</span>estimatedDialogUnits<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>estimatedDialogUnits <span class="symbol">&gt;</span> tabStops<span class="symbol">[</span>j<span class="symbol">]</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 tabStops<span class="symbol">[</span>j<span class="symbol">]</span> <span class="symbol">=</span> estimatedDialogUnits<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">1</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> tabStops<span class="symbol">.</span>Count<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 tabStops<span class="symbol">[</span>i<span class="symbol">]</span> <span class="symbol">+=</span> tabStops<span class="symbol">[</span>i <span class="symbol">-</span> <span class="number">1</span><span class="symbol">]</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 result <span class="symbol">=</span> tabStops<span class="symbol">.</span>ToArray<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="keyword">new</span><span class="symbol">[</span><span class="symbol">]</span> <span class="symbol">{</span> <span class="number">32</span> <span class="symbol">}</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">bool</span> AutoDetectTabStops<span class="symbol">(</span><span class="keyword">this</span> TextBoxBase textBox<span class="symbol">,</span> <span class="keyword">string</span> text<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>textBox <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException<span class="symbol">(</span>nameof<span class="symbol">(</span>textBox<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> textBox<span class="symbol">.</span>SetTabStops<span class="symbol">(</span>AutoDetectTabStops<span class="symbol">(</span>text<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">bool</span> AutoDetectTabStops<span class="symbol">(</span><span class="keyword">this</span> TextBoxBase textBox<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> textBox<span class="symbol">.</span>AutoDetectTabStops<span class="symbol">(</span>textBox<span class="symbol">.</span>Text<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="demonstration-project">Demonstration project</h2>
<p>As usual, a demonstration program is available for the link
below, including a helper class for adding some useful tab stop
related extension methods to the <code>TextBox</code> and <code>RichTextBox</code>
controls.</p>
<hr />
<p><sup>1.</sup> <small>Also, I lied. I suppose if you were
displaying a ruler for a word processor then you'd very much
want pixel perfect tab stops. I did try doing a basic ruler for
this demonstration, but ended up having to cheat the
calculations as I could get <code>MapDialogRect</code> to work, I didn't
have time to do things like add scrolling support and at the end
of the day it didn't really add much to the demo. </small></p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2019-05-25 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/setting-tab-stops-in-a-windows-forms-textbox-control .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comMigrating from Azure translation API version 2 to 3urn:uuid:78c834ff-f60c-4299-99ca-bd299f9af9622019-04-11T18:31:15Z2019-04-11T18:31:15Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/translatetext-2a.png" class="gallery" title="Updated sample project working with the version 3 API" ><img src="https://images.cyotek.com/image/thumbnail/devblog/translatetext-2a.png" alt="Updated sample project working with the version 3 API" decoding="async" loading="lazy" /></a><figcaption>Updated sample project working with the version 3 API</figcaption></figure>
<p>Almost two years ago I wrote a post describing how to <a href="/post/translating-text-with-azure-cognitive-services">translate
text using Azure cognitive services</a>, however the API it uses
is to be switched off and so I needed to migrate from the
version 2 API to version 3.</p>
<p>Whilst most of the code I post on this blog is used in one form
or another, I've been using the <code>TranslationClient</code> client
presented in that article as-is for the past two years. <em>OK, I
changed the namespace. But otherwise it's identical.</em></p>
<blockquote>
<p>Although I have finally stopped using older classes such as
<code>HttpWebRequest</code> in favour of <code>HttpClient</code> and
<code>async</code>/<code>await</code>, so far I haven't updated existing code to
make use of them. As I noted above, I'm still using the
<code>TranslationClient</code> class introduced in my previous blog post
and at this time I simply want to retrofit the class to use
the V3 API.</p>
<p>This also means I'm still not using any of the extra features
offered by the API even though it probably makes more sense to
combine some of the functionality now (as Microsoft have done
with the API's themselves), however as I want the new class to
be a drop in replacement for the old I have left this as an
exercise for a future blog post</p>
</blockquote>
<p>The official migration documentation can be found on
<a href="https://docs.microsoft.com/en-us/azure/cognitive-services/translator/migrate-to-v3" rel="external nofollow noopener">Microsoft's site</a>.</p>
<h2 id="dependencies">Dependencies</h2>
<p>At first glance, the biggest change between v2 and v3 is the
output format. Previously it was XML, now JSON. This is a bit
of a double edged sword as while JSON is the standard these
days, XML parsing is built into the .NET framework and JSON is
not (yet).</p>
<p><a href="https://www.newtonsoft.com/json" rel="external nofollow noopener">JSON.net</a> is a fine library for working with JSON, but
thanks to the way NuGet works it quickly spread like a plague
though my application libraries, and so I ended up blanket
purging it. Instead, for some time I've been using a modified
version of the fantastic <a href="https://github.com/toptensoftware/PetaJson" rel="external nofollow noopener">PetaJson</a> which is a single <code>.cs</code>
file I embed in any projects that require JSON support.</p>
<p>The switch from XML to JSON does mean that a reference to
<code>System.Runtime.Serialization</code> is no longer required which is a
plus.</p>
<h2 id="new-end-points">New end points</h2>
<p>I'm already only using a limited subset of functionality via
three separate version 2 API's. In version 3, two of these have
been consolidated into one. The following table outlines the
different endpoints</p>
<table>
<thead>
<tr>
<th>v2 Method</th>
<th>v3 Method</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Translate</code></td>
<td><code>translate</code></td>
</tr>
<tr>
<td><code>GetLanguageNames</code></td>
<td><code>languages</code></td>
</tr>
<tr>
<td><code>GetLanguagesForTranslate</code></td>
<td><code>languages</code></td>
</tr>
</tbody>
</table>
<p>In addition, the base URI has changed from
<code>https://api.microsofttranslator.com/v2/http.svc/</code> to
<code>https://api.cognitive.microsofttranslator.com/</code>.</p>
<h3 id="regional-end-points">Regional end points</h3>
<p>Although you can simply use the default base URI above and have
Azure choose an appropriate data centre, you can optionally
specify a specific region as follows.</p>
<table>
<thead>
<tr>
<th>Region</th>
<th>Base URL</th>
</tr>
</thead>
<tbody>
<tr>
<td>North America</td>
<td><code>api-nam.cognitive.microsofttranslator.com</code></td>
</tr>
<tr>
<td>Europe</td>
<td><code>api-eur.cognitive.microsofttranslator.com</code></td>
</tr>
<tr>
<td>Asia Pacific</td>
<td><code>api-apc.cognitive.microsofttranslator.com</code></td>
</tr>
</tbody>
</table>
<h2 id="specifying-an-api-version">Specifying an API version</h2>
<p>All requests to the API (apart from the initial authentication)
need to include the <code>api-version</code> query parameter, although
currently the only supported value is <code>3.0</code>. Failure to include
this will result in a <code>400</code> status code along with a body
similar to the following</p>
<figure class="lang-json highlight"><figcaption><span>json</span></figcaption><pre class="code">
<span class="symbol">{</span><span class="string">&quot;error&quot;</span><span class="symbol">:</span><span class="symbol">{</span><span class="string">&quot;code&quot;</span><span class="symbol">:</span><span class="number">400021</span><span class="symbol">,</span><span class="string">&quot;message&quot;</span><span class="symbol">:</span><span class="string">&quot;The API version parameter is not valid.&quot;</span><span class="symbol">}</span><span class="symbol">}</span>
</pre>
</figure>
<h2 id="authentication">Authentication</h2>
<p>I'm using the same code to obtain an authentication token as I
was for the version 2 API, as far as I know this isn't going to
be removed - please see the <a href="/post/translating-text-with-azure-cognitive-services#creating-a-login-token">original article</a> for details.</p>
<blockquote>
<p>According to the <a href="https://docs.microsoft.com/en-us/azure/cognitive-services/translator/reference/v3-0-reference" rel="external nofollow noopener">reference</a> instead of generating an
access token from your API key, you can pass the key directly
via the <code>Ocp-Apim-Subscription-Key</code>. Given that this was also
supported in the v2 API I'm not sure why I choose the more
convoluted method of generating an access token, something
else to potentially refactor away in a future update,
especially given the fact that exact code has had a bug in it
for over two years now.</p>
</blockquote>
<h2 id="two-year-old-bugs-and-why-you-shouldnt-blindly-ignore-resharper">Two-year old bugs and why you shouldn't blindly ignore ReSharper</h2>
<p>Imagine my surprise when the first thing that happened after
changing URI constants was the program crashed in a place I
wasn't expecting! As it turns out, there was a bug in the
original code and which just happened to have worked up until
now.</p>
<p>When requesting an API token, the token is the body of the
response. The class has a private <code>GetResponseString</code> string
method for pulling this out (and incidentally is also useful for
debugging purposes). This method checks to see if a character
set is defined on the <code>HttpWebResponse</code> (via the <code>CharacterSet</code>
property) and if so uses that to read text appropriately,
otherwise falls back to UTF-8.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="comment">// WARNING! Broken code below</span>

<span class="keyword">private</span> <span class="keyword">string</span> GetResponseString<span class="symbol">(</span>HttpWebResponse response<span class="symbol">)</span>
<span class="symbol">{</span>
 Encoding encoding<span class="symbol">;</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>

 <span class="comment">// ReSharper disable once AssignNullToNotNullAttribute</span>
 encoding <span class="symbol">=</span> <span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>response<span class="symbol">.</span>CharacterSet<span class="symbol">)</span> <span class="symbol">?</span> Encoding<span class="symbol">.</span>UTF<span class="number">8</span> <span class="symbol">:</span> Encoding<span class="symbol">.</span>GetEncoding<span class="symbol">(</span>response<span class="symbol">.</span>CharacterSet<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Stream stream <span class="symbol">=</span> response<span class="symbol">.</span>GetResponseStream<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>StreamReader reader <span class="symbol">=</span> <span class="keyword">new</span> StreamReader<span class="symbol">(</span>stream<span class="symbol">,</span> encoding<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> reader<span class="symbol">.</span>ReadToEnd<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>At least, that was the theory. In reality, if a character set is
present UFT-8 is always used, and if not present it tries to use
the null object and crashes. ReSharper very helpfully warns you
of this very thing with its &quot;<em>Possible 'null' assignment to
entity marked with 'NotNull' attribute</em>&quot; warning, and I
completely ignored as I'm so used to seeing it with various file
API's that evidently I treat it as noise without paying
attention.</p>
<p><em>Oops.</em> Well, it's fixed now!</p>
<h2 id="getting-the-list-of-languages">Getting the list of languages</h2>
<p>The <code>GetLanguagesForTranslate</code> API has been replaced with
<code>languages</code> and rather than returning a simple list of language
codes, it now returns a little bit more - at the most basic
level it includes the name (native and localised) and the
language direction.</p>
<figure class="lang-json highlight"><figcaption><span>json</span></figcaption><pre class="code">
<span class="symbol">{</span>
 <span class="string">&quot;translation&quot;</span><span class="symbol">:</span> <span class="symbol">{</span>
 <span class="string">&quot;af&quot;</span><span class="symbol">:</span> <span class="symbol">{</span>
 <span class="string">&quot;name&quot;</span><span class="symbol">:</span> <span class="string">&quot;Afrikaans&quot;</span><span class="symbol">,</span>
 <span class="string">&quot;nativeName&quot;</span><span class="symbol">:</span> <span class="string">&quot;Afrikaans&quot;</span><span class="symbol">,</span>
 <span class="string">&quot;dir&quot;</span><span class="symbol">:</span> <span class="string">&quot;ltr&quot;</span>
 <span class="symbol">}</span><span class="symbol">,</span>
 <span class="string">&quot;ar&quot;</span><span class="symbol">:</span> <span class="symbol">{</span>
 <span class="string">&quot;name&quot;</span><span class="symbol">:</span> <span class="string">&quot;Arabic&quot;</span><span class="symbol">,</span>
 <span class="string">&quot;nativeName&quot;</span><span class="symbol">:</span> <span class="string">&quot;العربية&quot;</span><span class="symbol">,</span>
 <span class="string">&quot;dir&quot;</span><span class="symbol">:</span> <span class="string">&quot;rtl&quot;</span>
 <span class="symbol">}</span><span class="symbol">,</span>
 <span class="string">&quot;bg&quot;</span><span class="symbol">:</span> <span class="symbol">{</span>
 <span class="string">&quot;name&quot;</span><span class="symbol">:</span> <span class="string">&quot;Bulgarian&quot;</span><span class="symbol">,</span>
 <span class="string">&quot;nativeName&quot;</span><span class="symbol">:</span> <span class="string">&quot;Български&quot;</span><span class="symbol">,</span>
 <span class="string">&quot;dir&quot;</span><span class="symbol">:</span> <span class="string">&quot;ltr&quot;</span>
 <span class="symbol">}</span><span class="symbol">,</span>

 <span class="symbol">!</span> SNIP <span class="symbol">!</span>

 <span class="string">&quot;yue&quot;</span><span class="symbol">:</span> <span class="symbol">{</span>
 <span class="string">&quot;name&quot;</span><span class="symbol">:</span> <span class="string">&quot;Cantonese (Traditional)&quot;</span><span class="symbol">,</span>
 <span class="string">&quot;nativeName&quot;</span><span class="symbol">:</span> <span class="string">&quot;粵語 (繁體中文)&quot;</span><span class="symbol">,</span>
 <span class="string">&quot;dir&quot;</span><span class="symbol">:</span> <span class="string">&quot;ltr&quot;</span>
 <span class="symbol">}</span><span class="symbol">,</span>
 <span class="string">&quot;zh-Hans&quot;</span><span class="symbol">:</span> <span class="symbol">{</span>
 <span class="string">&quot;name&quot;</span><span class="symbol">:</span> <span class="string">&quot;Chinese Simplified&quot;</span><span class="symbol">,</span>
 <span class="string">&quot;nativeName&quot;</span><span class="symbol">:</span> <span class="string">&quot;简体中文&quot;</span><span class="symbol">,</span>
 <span class="string">&quot;dir&quot;</span><span class="symbol">:</span> <span class="string">&quot;ltr&quot;</span>
 <span class="symbol">}</span><span class="symbol">,</span>
 <span class="string">&quot;zh-Hant&quot;</span><span class="symbol">:</span> <span class="symbol">{</span>
 <span class="string">&quot;name&quot;</span><span class="symbol">:</span> <span class="string">&quot;Chinese Traditional&quot;</span><span class="symbol">,</span>
 <span class="string">&quot;nativeName&quot;</span><span class="symbol">:</span> <span class="string">&quot;繁體中文&quot;</span><span class="symbol">,</span>
 <span class="string">&quot;dir&quot;</span><span class="symbol">:</span> <span class="string">&quot;ltr&quot;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Using the <code>scope</code> query parameter, you specify a comma separated
list of group information to return. The available group names
are <code>translation</code>, <code>transliteration</code> and <code>dictionary</code>. As I'm
only interested in translations, that is the only scope I'll
provide. As an aside, if you omit this parameter it will act as
if you had specified all scopes.</p>
<p>Our original <code>GetLanguages</code> function changes to this</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">string</span><span class="symbol">[</span><span class="symbol">]</span> GetLanguages<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">string</span><span class="symbol">[</span><span class="symbol">]</span> results<span class="symbol">;</span>
 HttpWebRequest request<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CheckToken<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 request <span class="symbol">=</span> WebRequest<span class="symbol">.</span>CreateHttp<span class="symbol">(</span><span class="string">&quot;https://api.cognitive.microsofttranslator.com/languages?api-version=3.0&amp;scope=translation&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 request<span class="symbol">.</span>Headers<span class="symbol">.</span>Add<span class="symbol">(</span><span class="string">&quot;Authorization&quot;</span><span class="symbol">,</span> <span class="string">&quot;Bearer &quot;</span> <span class="symbol">+</span> _authorizationToken<span class="symbol">)</span><span class="symbol">;</span>
 request<span class="symbol">.</span>Accept <span class="symbol">=</span> <span class="string">&quot;application/json&quot;</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>HttpWebResponse response <span class="symbol">=</span> <span class="symbol">(</span>HttpWebResponse<span class="symbol">)</span>request<span class="symbol">.</span>GetResponse<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>Stream stream <span class="symbol">=</span> response<span class="symbol">.</span>GetResponseStream<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>StreamReader reader <span class="symbol">=</span> <span class="keyword">new</span> StreamReader<span class="symbol">(</span>stream<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>GetResponseEncoding<span class="symbol">(</span>response<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">&gt;&gt;&gt;</span> jsonEntities<span class="symbol">;</span>
 Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">&gt;&gt;</span> languages<span class="symbol">;</span>

 jsonEntities <span class="symbol">=</span> <span class="keyword">new</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">&gt;&gt;&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 Json<span class="symbol">.</span>ParseInto<span class="symbol">(</span>reader<span class="symbol">,</span> jsonEntities<span class="symbol">)</span><span class="symbol">;</span>

 results <span class="symbol">=</span> jsonEntities<span class="symbol">.</span>TryGetValue<span class="symbol">(</span><span class="string">&quot;translation&quot;</span><span class="symbol">,</span> <span class="keyword">out</span> languages<span class="symbol">)</span> <span class="symbol">?</span> languages<span class="symbol">.</span>Keys<span class="symbol">.</span>ToArray<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">:</span> <span class="keyword">new</span> <span class="keyword">string</span><span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> results<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>I have to admit, I'm not a fan of this awful &quot;dictionary of
dictionary of dictionaries&quot; nonsense. But at the <code>translations</code>
element is an object with language codes as property names
rather than an array, offhand I'm not sure how I'd get that
converted into a strongly typed keyed collection, regardless of
if using PetaJson or JSON.net - I will be revisiting this in a
future post.</p>
<p>I'm also not a fan of having to load the entire JSON string into
parsed objects and then discard most of it. PetaJSON has a
<code>Reader</code> class which behaves very much like <code>XmlReader</code> and
ideally I should have used that to walk the JSON.</p>
<blockquote>
<p>In the above code, I've left in place the obtaining and
setting an authentication token. However, unlike the v2 API,
authentication is <em>not</em> required for using the <code>/languages</code>
API. It is still required for actions that requiring billing,
such as the <code>/translate</code> API itself.</p>
</blockquote>
<h2 id="getting-language-names">Getting language names</h2>
<p>As I've laboriously noted above, in the v3 API, Microsoft
combined the original <code>GetLanguagesForTranslate</code> and
<code>GetLanguageNames</code> into a single API call and so getting the
actual names for each language is a simple case of taking the
above code and pulling out a little more information from the
nest of <del>vipers</del> dictionaries.</p>
<figure class="lang-json highlight"><figcaption><span>json</span></figcaption><pre class="code">
<span class="symbol">{</span>
 <span class="string">&quot;translation&quot;</span><span class="symbol">:</span> <span class="symbol">{</span>
 <span class="string">&quot;ar&quot;</span><span class="symbol">:</span> <span class="symbol">{</span>
 <span class="string">&quot;name&quot;</span><span class="symbol">:</span> <span class="string">&quot;Arabic&quot;</span><span class="symbol">,</span>
 <span class="string">&quot;nativeName&quot;</span><span class="symbol">:</span> <span class="string">&quot;العربية&quot;</span><span class="symbol">,</span>
 <span class="string">&quot;dir&quot;</span><span class="symbol">:</span> <span class="string">&quot;rtl&quot;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Remembering that the JSON output includes <code>name</code>, <code>nativeName</code>
and <code>dir</code> attributes; this time around, we're interested in
pulling out the <code>name</code> field. This is the display name in the
requested locale (<code>nativeName</code> is the display name in the locale
of the language itself). But how do you specify the requested
locale? In v2, you used the <code>locale</code> query parameter but for v3
it is done by setting the <code>Accept-Language</code> header.</p>
<p>There's also another important difference - with the v2 API, you
made a <code>POST</code> and the body had a list of the languages for which
you wanted localised names for. However, for v3 there is no such
filtering available, it will return localised names for all
supported languages.</p>
<p>As I'm trying to keep the same behaviour that means I'm going to
need to add this filtering myself (although by the time I'd
finished this article I was questioning my reasoning for not
just rewriting the class from scratch in a modern fashion and
forcing our internal application deal with it).</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">string</span><span class="symbol">[</span><span class="symbol">]</span> GetLocalizedLanguageNames<span class="symbol">(</span><span class="keyword">string</span> locale<span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">[</span><span class="symbol">]</span> languages<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">string</span><span class="symbol">[</span><span class="symbol">]</span> results<span class="symbol">;</span>
 HttpWebRequest request<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CheckToken<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 request <span class="symbol">=</span> WebRequest<span class="symbol">.</span>CreateHttp<span class="symbol">(</span><span class="string">&quot;https://api.cognitive.microsofttranslator.com/languages?api-version=3.0&amp;scope=translation&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 request<span class="symbol">.</span>Headers<span class="symbol">.</span>Add<span class="symbol">(</span><span class="string">&quot;Authorization&quot;</span><span class="symbol">,</span> <span class="string">&quot;Bearer &quot;</span> <span class="symbol">+</span> _authorizationToken<span class="symbol">)</span><span class="symbol">;</span>
 request<span class="symbol">.</span>Headers<span class="symbol">.</span>Add<span class="symbol">(</span><span class="string">&quot;Accept-Language&quot;</span><span class="symbol">,</span> locale<span class="symbol">)</span><span class="symbol">;</span>
 request<span class="symbol">.</span>Accept <span class="symbol">=</span> <span class="string">&quot;application/json&quot;</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>HttpWebResponse response <span class="symbol">=</span> <span class="symbol">(</span>HttpWebResponse<span class="symbol">)</span>request<span class="symbol">.</span>GetResponse<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>Stream stream <span class="symbol">=</span> response<span class="symbol">.</span>GetResponseStream<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>StreamReader reader <span class="symbol">=</span> <span class="keyword">new</span> StreamReader<span class="symbol">(</span>stream<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>GetResponseEncoding<span class="symbol">(</span>response<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">&gt;&gt;&gt;</span> jsonEntities<span class="symbol">;</span>
 Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">&gt;&gt;</span> responseLanguages<span class="symbol">;</span>

 jsonEntities <span class="symbol">=</span> <span class="keyword">new</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">&gt;&gt;&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 Json<span class="symbol">.</span>ParseInto<span class="symbol">(</span>reader<span class="symbol">,</span> jsonEntities<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>jsonEntities<span class="symbol">.</span>TryGetValue<span class="symbol">(</span><span class="string">&quot;translation&quot;</span><span class="symbol">,</span> <span class="keyword">out</span> responseLanguages<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 results <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">string</span><span class="symbol">[</span>languages<span class="symbol">.</span>Length<span class="symbol">]</span><span class="symbol">;</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> languages<span class="symbol">.</span>Length<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">&gt;</span> languageData<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>responseLanguages<span class="symbol">.</span>TryGetValue<span class="symbol">(</span>languages<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">,</span> <span class="keyword">out</span> languageData<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 results<span class="symbol">[</span>i<span class="symbol">]</span> <span class="symbol">=</span> languageData<span class="symbol">[</span><span class="string">&quot;name&quot;</span><span class="symbol">]</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 results <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">string</span><span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> results<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>I <em>really</em> don't like this code. Too late for second guessing
now though!</p>
<h2 id="translating-text">Translating text</h2>
<p>The final part of this migration exercise is the actual text
translation. Again, there's some small differences from v2 but
nothing too troublesome.</p>
<p>Firstly, the text to translate is no longer a query parameter,
but part of the body text as a JSON object. This makes sense in
a way as for v3, Microsoft merged the <code>Translate</code> and
<code>TranslateArray</code> API's into one. But it still means it's slightly
more awkward to use.</p>
<p>The body JSON is simple enough and looks like this</p>
<figure class="lang-json highlight"><figcaption><span>json</span></figcaption><pre class="code">
<span class="symbol">[</span>
 <span class="symbol">{</span><span class="string">&quot;Text&quot;</span><span class="symbol">:</span> <span class="string">&quot;Hello World&quot;</span><span class="symbol">}</span>
<span class="symbol">]</span>
</pre>
</figure>
<blockquote>
<p>Note that for some reason the <code>Text</code> attribute is in title
case rather than lower case in all the other examples</p>
</blockquote>
<p>The language to convert from and to are still specified via the
<code>from</code> and <code>to</code> query parameters as with v2.</p>
<p>The response is a JSON array, similar to the following.</p>
<figure class="lang-json highlight"><figcaption><span>json</span></figcaption><pre class="code">
<span class="symbol">[</span>
 <span class="symbol">{</span>
 <span class="string">&quot;translations&quot;</span><span class="symbol">:</span> <span class="symbol">[</span>
 <span class="symbol">{</span>
 <span class="string">&quot;text&quot;</span><span class="symbol">:</span> <span class="string">&quot;Hallo Welt&quot;</span><span class="symbol">,</span>
 <span class="string">&quot;to&quot;</span><span class="symbol">:</span> <span class="string">&quot;de&quot;</span>
 <span class="symbol">}</span>
 <span class="symbol">]</span>
 <span class="symbol">}</span>
<span class="symbol">]</span>
</pre>
</figure>
<p>However, it can include a <em>great</em> deal more information
depending on if you use auto detection, transliteration and
more. I'm not covering any of that here in my 1:1 conversion.</p>
<p>As I don't really want to manually write JSON and deal with
having to escape text, I'll create an interim object and use
PetaJson to write it out. I've made it private for now as it is
only used inside of this method. It was also at this point I
threw up my hands in disgust at more dictionary of dictionaries
and wrote a few limited POCO's for the response output that I'm
interested in.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">partial</span> <span class="keyword">class</span> TranslationClient
<span class="symbol">{</span>
 <span class="keyword">private</span> <span class="keyword">class</span> TextInput
 <span class="symbol">{</span>
 <span class="keyword">private</span> <span class="keyword">string</span> _text<span class="symbol">;</span>

 <span class="keyword">public</span> TextInput<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span> <span class="symbol">}</span>

 <span class="keyword">public</span> TextInput<span class="symbol">(</span><span class="keyword">string</span> text<span class="symbol">)</span>
 <span class="symbol">{</span>
 _text <span class="symbol">=</span> text<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="symbol">[</span>Json<span class="symbol">(</span><span class="string">&quot;Text&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">string</span> Text
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _text<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> _text <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 
 <span class="keyword">private</span> <span class="keyword">class</span> TranslationResult
 <span class="symbol">{</span>
 <span class="keyword">private</span> <span class="keyword">string</span> _targetLanguage<span class="symbol">;</span>
 <span class="keyword">private</span> <span class="keyword">string</span> _text<span class="symbol">;</span>

 <span class="symbol">[</span>Json<span class="symbol">(</span><span class="string">&quot;to&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">string</span> TargetLanguage
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _targetLanguage<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> _targetLanguage <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="symbol">[</span>Json<span class="symbol">(</span><span class="string">&quot;text&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">string</span> Text
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _text<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> _text <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">class</span> TranslateResponse
 <span class="symbol">{</span>
 <span class="keyword">private</span> TranslationResult<span class="symbol">[</span><span class="symbol">]</span> _translations<span class="symbol">;</span>

 <span class="keyword">public</span> TranslationResult<span class="symbol">[</span><span class="symbol">]</span> Translations
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _translations<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> _translations <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>With the helpers in place, I can now expand the <code>Translate</code>
method to work with the v3 API</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">string</span> Translate<span class="symbol">(</span><span class="keyword">string</span> text<span class="symbol">,</span> <span class="keyword">string</span> <span class="keyword">from</span><span class="symbol">,</span> <span class="keyword">string</span> to<span class="symbol">)</span>
<span class="symbol">{</span>
 HttpWebRequest request<span class="symbol">;</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>
 <span class="keyword">string</span> queryString<span class="symbol">;</span>
 TranslateResponse<span class="symbol">[</span><span class="symbol">]</span> responses<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CheckToken<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 queryString <span class="symbol">=</span> <span class="string">&quot;?api-version=3.0&amp;from=&quot;</span> <span class="symbol">+</span> <span class="keyword">from</span> <span class="symbol">+</span> <span class="string">&quot;&amp;to=&quot;</span> <span class="symbol">+</span> to<span class="symbol">;</span>

 request <span class="symbol">=</span> WebRequest<span class="symbol">.</span>CreateHttp<span class="symbol">(</span><span class="string">&quot;https://api.cognitive.microsofttranslator.com/translate&quot;</span> <span class="symbol">+</span> queryString<span class="symbol">)</span><span class="symbol">;</span>
 request<span class="symbol">.</span>Headers<span class="symbol">.</span>Add<span class="symbol">(</span><span class="string">&quot;Authorization&quot;</span><span class="symbol">,</span> <span class="string">&quot;Bearer &quot;</span> <span class="symbol">+</span> _authorizationToken<span class="symbol">)</span><span class="symbol">;</span>
 request<span class="symbol">.</span>ContentType <span class="symbol">=</span> <span class="string">&quot;application/json&quot;</span><span class="symbol">;</span>
 request<span class="symbol">.</span>Accept <span class="symbol">=</span> <span class="string">&quot;application/json&quot;</span><span class="symbol">;</span>
 request<span class="symbol">.</span>Method <span class="symbol">=</span> WebRequestMethods<span class="symbol">.</span>Http<span class="symbol">.</span>Post<span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Stream stream <span class="symbol">=</span> request<span class="symbol">.</span>GetRequestStream<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>TextWriter writer <span class="symbol">=</span> <span class="keyword">new</span> StreamWriter<span class="symbol">(</span>stream<span class="symbol">,</span> Encoding<span class="symbol">.</span>UTF<span class="number">8</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Json<span class="symbol">.</span>Write<span class="symbol">(</span>writer<span class="symbol">,</span> <span class="keyword">new</span><span class="symbol">[</span><span class="symbol">]</span> <span class="symbol">{</span> <span class="keyword">new</span> TextInput<span class="symbol">(</span>text<span class="symbol">)</span> <span class="symbol">}</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">using</span> <span class="symbol">(</span>HttpWebResponse response <span class="symbol">=</span> <span class="symbol">(</span>HttpWebResponse<span class="symbol">)</span>request<span class="symbol">.</span>GetResponse<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>Stream stream <span class="symbol">=</span> response<span class="symbol">.</span>GetResponseStream<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>StreamReader reader <span class="symbol">=</span> <span class="keyword">new</span> StreamReader<span class="symbol">(</span>stream<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>GetResponseEncoding<span class="symbol">(</span>response<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 responses <span class="symbol">=</span> Json<span class="symbol">.</span>Parse<span class="symbol">&lt;</span>TranslateResponse<span class="symbol">[</span><span class="symbol">]</span><span class="symbol">&gt;</span><span class="symbol">(</span>reader<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 result <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>responses <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> responses<span class="symbol">.</span>Length <span class="symbol">==</span> <span class="number">1</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 TranslateResponse translation<span class="symbol">;</span>

 translation <span class="symbol">=</span> responses<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>translation<span class="symbol">.</span>Translations <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> translation<span class="symbol">.</span>Translations<span class="symbol">.</span>Length <span class="symbol">==</span> <span class="number">1</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> translation<span class="symbol">.</span>Translations<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span><span class="symbol">.</span>Text<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Much more complicated than the previous version! Still it works.
Doesn't it?</p>
<h2 id="wait-the-output-is-different">Wait, the output is different?</h2>
<p>After I had the conversion complete, I noticed that one of the
variations of Klingon wasn't listed in the language list any
more. Curious, I ran the original application and back it
popped. At first I thought they might have been combined with
the new script support but this doesn't seem to be the case.
Fortunately, no user has asked for our software to be in
Klingon, so I can ignore this omission!</p>
<p>I also noted the codes for Chinese have changed - in v2 they are
<code>zh-CHS</code> (Simplified) and <code>zh-CHT</code> (Traditional), but in v3 they
are now <code>zh-Hans</code> and <code>zh-Hant</code>. Apparently the latter is the
proper way of doing things now, but this a breaking change for
me as various shell scripts and data files refer to the old
style and will need changing.</p>
<p>Even more oddly however, the first part of the &quot;Major-General's
Song&quot; that defaults in the demonstration program now translates
differently in the two versions</p>
<p>English Text:</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
I am the very model of a modern Major-General,
I&#39;ve information vegetable, animal, and mineral,
I know the kings of England, and I quote the fights historical
From Marathon to Waterloo, in order categorical;a
I&#39;m very well acquainted, too, with matters mathematical,
I understand equations, both the simple and quadratical,
About binomial theorem I&#39;m teeming with a lot o&#39; news,
With many cheerful facts about the square of the hypotenuse.
</pre>
</figure>
<p>German Translation (version 2 API):</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
Ich bin sehr Modell modern Major-General,
Ich habe Informationen Gem&#252;se, Tiere und Mineralien,
Ich kenne die K&#246;nige von England, und ich zitiere die historischen K&#228;mpfe
Vom Marathon zu Waterloo in Reihenfolge kategorische; ein
Ich bin sehr gut, auch mit mathematischen Fragen kennen,
Ich verstehe Gleichungen, einfache und quadratischem,
&#220;ber binomiale Theorem bin ich mit viel o-Nachrichten nur so wimmelt,
Mit vielen fr&#246;hlichen Fakten &#252;ber das Quadrat der Hypotenuse.
</pre>
</figure>
<p>German Translation (version 3 API):</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
Ich bin das Vorbild eines modernen Generalstabs,
Ich habe Informationen pflanzliche, tierische und mineralische,
Ich kenne die K&#246;nige von England, und ich zitiere die K&#228;mpfe historisch
Von Marathon bis Waterloo, in der Reihenfolge kategorisch; ein
Ich bin sehr gut mit den Fragen mathematisch,
Ich verstehe Gleichungen, sowohl die einfachen als auch die quadratischen,
&#220;ber binomiale Theorem Ich bin voller viel o &#39; News,
Mit vielen fr&#246;hlichen Fakten &#252;ber das Quadrat der Hypotenuse.
</pre>
</figure>
<p>I have no idea as to why this is, I assume it's because
according to the documentation it uses &quot;neural machine
translation by default&quot;, although it doesn't seem to state how
to disable it.</p>
<p>In the end, I updated the demonstration program to include both
the v2 and v3 classes so you I could toggle between them to
easily see the differences.</p>
<h2 id="to-be-continued">To be continued</h2>
<p>Attached to this post is an upgraded demonstration project which
is a little more robust than the methods above, it is also
available on our GitHub page. Note that you will need to use
your own API key, the one in the demonstration program has been
invalidated.</p>
<p>I'm really not a fan of the new code and have made a note on my
blog Todo list to revisit this topic in the future and rewrite
it properly using modern techniques, and also to investigate
some of the additional functionality the translation API offers.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2019-04-11 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/migrating-from-azure-translation-api-version-2-to-3 .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comHandling the orientation EXIF tag in images using C#urn:uuid:2462f9f8-d0d8-4bef-ae9c-dd83814f82c52019-03-09T11:48:18Z2019-03-09T11:12:10Z<p>Two weeks ago I received an interesting support ticket from a
user of our <a href="/tag/imagebox">ImageBox</a> control, stating that when being used
for <code>.png</code> files, the control was absolutely file but when used
with <code>.jpg</code> files that had been rotated using Windows Explorer
shell context menus, they were displayed in the original
orientation.</p>
<p>As soon as I read the ticket I had a hunch what was going to be
the ultimate cause, and was able to quickly reproduce the
problem and then confirm my hunch.</p>
<h2 id="introducing-exchangeable-image-file-format">Introducing exchangeable image file format</h2>
<p>JEPG images support extension attributes via the <strong>Exchangeable
image file format</strong> (<strong>Exif</strong>). These attributes can be used to
provide additional information about an image, such as how and
where it was captured. It can also store information such as the
software used to manipulate the image, artist and copyright
information, or camera model details. In other words, it's
similar to how you can add tags to an MP3 file with album
information, although usually this doesn't cause your MP3 file
to play backwards!</p>
<p>The Exif specification is available from the <a href="http://www.cipa.jp/index_e.html" rel="external nofollow noopener">CIPA</a> website,
although at 191
pages long it is a hefty read.</p>
<h2 id="introducing-the-orientation-attribute">Introducing the orientation attribute</h2>
<p>I mentioned that MP3 tags don't cause your MP3 files to play
backwards (at least I'm not aware of any that do!), but Exif
supports an orientation attribute that means the source image
has one orientation, but should be displayed in another in order
to appear correctly.</p>
<p>The tag supports one of the following 8 values</p>
<table>
<thead>
<tr>
<th>EXIF Orientation Value</th>
<th>Row #0 is:</th>
<th>Column #0 is:</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Top</td>
<td>Left side</td>
</tr>
<tr>
<td>2*</td>
<td>Top</td>
<td>Right side</td>
</tr>
<tr>
<td>3</td>
<td>Bottom</td>
<td>Right side</td>
</tr>
<tr>
<td>4*</td>
<td>Bottom</td>
<td>Left side</td>
</tr>
<tr>
<td>5*</td>
<td>Left side</td>
<td>Top</td>
</tr>
<tr>
<td>6</td>
<td>Right side</td>
<td>Top</td>
</tr>
<tr>
<td>7*</td>
<td>Right side</td>
<td>Bottom</td>
</tr>
<tr>
<td>8</td>
<td>Left side</td>
<td>Bottom</td>
</tr>
</tbody>
</table>
<p>The starred values also flip the image in addition to rotating
it.</p>
<p>Source: <a href="https://www.impulseadventure.com/photo/exif-orientation.html" rel="external nofollow noopener">ImpulseAdventure - JPEG / Exif Orientation and
Rotation</a></p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/exif-1a.png" class="gallery" title="An example of an uncommon orientation value" ><img src="https://images.cyotek.com/image/thumbnail/devblog/exif-1a.png" alt="An example of an uncommon orientation value" decoding="async" loading="lazy" /></a><figcaption>An example of an uncommon orientation value</figcaption></figure><h2 id="enter-windows-explorer">Enter Windows Explorer</h2>
<p>Windows Explorer has knowledge of the Exif orientation tag, and
automatically rotates thumbnail previews so they appear correct
- very helpful!</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/exif-1c.png" class="gallery" title="Windows Explorer showing correctly orientated images" ><img src="https://images.cyotek.com/image/thumbnail/devblog/exif-1c.png" alt="Windows Explorer showing correctly orientated images" decoding="async" loading="lazy" /></a><figcaption>Windows Explorer showing correctly orientated images</figcaption></figure>
<p>However, if you context click an image and make use of the
<strong>Rotate Left</strong> and <strong>Rotate Right</strong> commands, Explorer doesn't
truly rotate the images. Instead, for JEPG images, an
appropriate orientation attribute is added (plus a large block
of XML, it wouldn't be Microsoft if they weren't adding extra
unwanted data in files). This is quite clever in a way, as it
means it doesn't touch the original image data at all, but does
mean that any application that doesn't understand Exif is going
to display the image wrong.</p>
<p>Also somewhat annoyingly, when you view the properties of the
image, the <strong>Details</strong> tab includes lots of information gleaned
from Exif - but neglects to mention that a custom orientation is
in use.</p>
<h2 id="enter.net">Enter .NET</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/exif-1d.png" class="gallery" title="The top image is using the Image object directly after loading, the bottom image is the result of manually processing the orientation attribute" ><img src="https://images.cyotek.com/image/thumbnail/devblog/exif-1d.png" alt="The top image is using the Image object directly after loading, the bottom image is the result of manually processing the orientation attribute" decoding="async" loading="lazy" /></a><figcaption>The top image is using the Image object directly after loading, the bottom image is the result of manually processing the orientation attribute</figcaption></figure>
<p>When it comes to loading images in .NET, I usually use
<code>Image.FromFile</code> or <code>Image.FromStream</code> if I'm just loading
common formats. Unfortunately, these return the images &quot;as-is&quot;,
without doing any post processing. I said unfortunately, but
actually this is probably a good thing - most likely you don't
want .NET manipulating images for you.</p>
<h2 id="fixing-the-problem">Fixing the problem</h2>
<p>The <code>Image</code> class does provide access to Exif properties, albeit
in a very obsure way. <code>Image.PropertyIdList</code> returns an <code>int</code>
array of defined extension values, and <code>GetPropertyItem</code> will
return a <code>PropertyItem</code> instance describing an existing
property. You can then use the <code>Value</code> property to get the
attribute value, although this may require further conversion to
use (e.g. convert raw bytes into a string).</p>
<blockquote>
<p>Note: Calling <code>GetPropertyItem</code> will throw an
<code>ArgumentException</code> if you request a property that doesn't
exist. You should use <code>PropertyIdList</code> to check that the
property exists first.</p>
</blockquote>
<p>The following extension method (body code from <a href="https://stackoverflow.com/a/23400751/148962" rel="external nofollow noopener">this Stack
Overflow answer</a>) will take a source <code>Image</code>, check to see if
the orientation attribute is present and if it is rotate/flip
the image as appropriate and then remove the attribute.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> NormalizeOrientation<span class="symbol">(</span><span class="keyword">this</span> Image image<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>Array<span class="symbol">.</span>IndexOf<span class="symbol">(</span>image<span class="symbol">.</span>PropertyIdList<span class="symbol">,</span> ExifOrientationTagId<span class="symbol">)</span> <span class="symbol">&gt;</span> <span class="symbol">-</span><span class="number">1</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> orientation<span class="symbol">;</span>

 orientation <span class="symbol">=</span> image<span class="symbol">.</span>GetPropertyItem<span class="symbol">(</span>ExifOrientationTagId<span class="symbol">)</span><span class="symbol">.</span>Value<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>orientation <span class="symbol">&gt;=</span> <span class="number">1</span> <span class="symbol">&amp;&amp;</span> orientation <span class="symbol">&lt;=</span> <span class="number">8</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">switch</span> <span class="symbol">(</span>orientation<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> <span class="number">2</span><span class="symbol">:</span>
 image<span class="symbol">.</span>RotateFlip<span class="symbol">(</span>RotateFlipType<span class="symbol">.</span>RotateNoneFlipX<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="number">3</span><span class="symbol">:</span>
 image<span class="symbol">.</span>RotateFlip<span class="symbol">(</span>RotateFlipType<span class="symbol">.</span>Rotate<span class="number">180</span>FlipNone<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="number">4</span><span class="symbol">:</span>
 image<span class="symbol">.</span>RotateFlip<span class="symbol">(</span>RotateFlipType<span class="symbol">.</span>Rotate<span class="number">180</span>FlipX<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="number">5</span><span class="symbol">:</span>
 image<span class="symbol">.</span>RotateFlip<span class="symbol">(</span>RotateFlipType<span class="symbol">.</span>Rotate<span class="number">90</span>FlipX<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="number">6</span><span class="symbol">:</span>
 image<span class="symbol">.</span>RotateFlip<span class="symbol">(</span>RotateFlipType<span class="symbol">.</span>Rotate<span class="number">90</span>FlipNone<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="number">7</span><span class="symbol">:</span>
 image<span class="symbol">.</span>RotateFlip<span class="symbol">(</span>RotateFlipType<span class="symbol">.</span>Rotate<span class="number">270</span>FlipX<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="number">8</span><span class="symbol">:</span>
 image<span class="symbol">.</span>RotateFlip<span class="symbol">(</span>RotateFlipType<span class="symbol">.</span>Rotate<span class="number">270</span>FlipNone<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 image<span class="symbol">.</span>RemovePropertyItem<span class="symbol">(</span>ExifOrientationTagId<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>If your application deals with user submitted images, having
something like this in your arsenal may be useful; I know I have
several applications that now need this &quot;fix&quot;.</p>
<h2 id="demonstration-program">Demonstration program</h2>
<p>The linked source code download includes a demonstration program
that features each of 8 supported orientation values and shows
how they are displayed both before and after processing the
orientation attribute.</p>
<h2 id="fixing-imagebox">Fixing ImageBox</h2>
<p>At this time I'm still mulling over if this is something I
should be handling within ImageBox, or if it is the
responsibility of the program using the control. Given ImageBox
typical use cases, I'm leaning towards the idea of adding a new
option to automatically handle this but am currently undecided.</p>
<h2 id="credits">Credits</h2>
<ul>
<li>Tower of London photograph used for sample images - <a href="https://www.publicdomainpictures.net/en/view-image.php?image=18490" rel="external nofollow noopener">Vera
Kratochvil</a></li>
<li>Information on Exif orientation - <a href="https://www.impulseadventure.com/photo/exif-orientation.html" rel="external nofollow noopener">ImpulseAdventure</a></li>
<li>Code for processing Exif orientation tags in C# - <a href="https://stackoverflow.com/a/23400751/148962" rel="external nofollow noopener">ReenignE</a></li>
</ul>
<h2 id="update-history">Update History</h2>
<ul>
<li>2019-03-09 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/handling-the-orientation-exif-tag-in-images-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comReading cookies from Internet Explorerurn:uuid:8c68829f-767f-485e-84f2-b089ee8d20c12019-01-20T19:17:34Z2019-01-20T19:17:34Z<p>In order to work around cases where it wasn't possible
automatically authenticate with a website, I wanted the ability
to use an embedded Internet Explorer window for manual
authentication and then reuse the cookies. This article
describes how to read cookies indirectly using
<code>InternetGetCookieEx</code> or directly from a <code>WebBrowser</code> control.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/getcookies-1b.png" class="gallery" title="Reading cookies via InternetGetCookieEx" ><img src="https://images.cyotek.com/image/thumbnail/devblog/getcookies-1b.png" alt="Reading cookies via InternetGetCookieEx" decoding="async" loading="lazy" /></a><figcaption>Reading cookies via InternetGetCookieEx</figcaption></figure><h2 id="reading-cookies">Reading cookies</h2>
<p>The <a href="https://docs.microsoft.com/en-us/windows/desktop/api/wininet/nf-wininet-internetgetcookieexw" rel="external nofollow noopener"><code>InternetGetCookieEx</code></a> Win32 API can be used to read
cookie name value pairs for a given URI. You can use it to read
all cookies or a named cookie. You can also specify which type
of cookies to include.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
BOOLAPI InternetGetCookieExW<span class="symbol">(</span>
 LPCWSTR lpszUrl<span class="symbol">,</span>
 LPCWSTR lpszCookieName<span class="symbol">,</span>
 LPWSTR lpszCookieData<span class="symbol">,</span>
 LPDWORD lpdwSize<span class="symbol">,</span>
 DWORD dwFlags<span class="symbol">,</span>
 LPVOID lpReserved
<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>To use this function, we specify the URL we want to query, the
name of the cookie to lookup (or <code>null</code> for all), a buffer to
store the cookie data, the length of the buffer and then flags
to describe what we want returned. The function returns <code>true</code>
if it succeeds, otherwise <code>false</code>. In line with other Win32
calls, you can use <code>GetLastError</code> to get the error code in the
event of a failure.</p>
<p>The last part is quite important as if the buffer provided is
too small, the function will fail and calling <code>GetLastError</code>
will return <code>ERROR_INSUFFICIENT_BUFFER</code>. In this case, the
buffer length parameter will contain the size required to
contain the data so that you can allocate a new buffer and call
the function again.</p>
<p>I saw a number of naïve implementations on the internet that
called <code>InternetGetCookieEx</code> without doing any sort of error
checking; this could lead to subtle bugs in the event that the
cookies are longer than the supplied buffer as no data will be
returned.</p>
<p>The following function can be used to get all cookies for a
given URI. I create a buffer that will hold 1024 characters and
make a call to <code>InternetGetCookieEx</code>. If it fails but the error
code is <code>ERROR_INSUFFICIENT_BUFFER</code> then I increase the buffer
size and try again.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;wininet.dll&quot;</span><span class="symbol">,</span> CharSet <span class="symbol">=</span> CharSet<span class="symbol">.</span>Auto<span class="symbol">,</span> SetLastError <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">bool</span> InternetGetCookieEx<span class="symbol">(</span><span class="keyword">string</span> lpszUrl<span class="symbol">,</span> <span class="keyword">string</span> lpszCookieName<span class="symbol">,</span> StringBuilder lpszCookieData<span class="symbol">,</span> <span class="keyword">ref</span> <span class="keyword">int</span> lpdwSize<span class="symbol">,</span> <span class="keyword">int</span> dwFlags<span class="symbol">,</span> IntPtr lpReserved<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">const</span> <span class="keyword">int</span> ERROR_INSUFFICIENT_BUFFER <span class="symbol">=</span> <span class="number">122</span><span class="symbol">;</span>

<span class="keyword">const</span> <span class="keyword">int</span> INTERNET_COOKIE_HTTPONLY <span class="symbol">=</span> <span class="number">0x00002000</span><span class="symbol">;</span>

<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">string</span> GetCookies<span class="symbol">(</span><span class="keyword">string</span> uri<span class="symbol">)</span>
<span class="symbol">{</span>
 StringBuilder buffer<span class="symbol">;</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>
 <span class="keyword">int</span> bufferLength<span class="symbol">;</span>
 <span class="keyword">int</span> flags<span class="symbol">;</span>

 bufferLength <span class="symbol">=</span> <span class="number">1024</span><span class="symbol">;</span>
 buffer <span class="symbol">=</span> <span class="keyword">new</span> StringBuilder<span class="symbol">(</span>bufferLength<span class="symbol">)</span><span class="symbol">;</span>

 flags <span class="symbol">=</span> INTERNET_COOKIE_HTTPONLY<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>InternetGetCookieEx<span class="symbol">(</span>uri<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">,</span> buffer<span class="symbol">,</span> <span class="keyword">ref</span> bufferLength<span class="symbol">,</span> flags<span class="symbol">,</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> buffer<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>Marshal<span class="symbol">.</span>GetLastWin<span class="number">32</span>Error<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">==</span> ERROR_INSUFFICIENT_BUFFER<span class="symbol">)</span>
 <span class="symbol">{</span>
 buffer<span class="symbol">.</span>Length <span class="symbol">=</span> bufferLength<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>NativeMethods<span class="symbol">.</span>InternetGetCookieEx<span class="symbol">(</span>uri<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">,</span> buffer<span class="symbol">,</span> <span class="keyword">ref</span> bufferLength<span class="symbol">,</span> flags<span class="symbol">,</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> buffer<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>In the example above, I'm requesting to include HTTP only
cookies. The documentation states: &quot;Do not use this flag if
you expose a scriptable interface, because this has security
implications. It is imperative that you use this flag only if
you can guarantee that you will never expose the cookie to
third-party code by way of an extensibility mechanism you
provide.&quot;</p>
</blockquote>
<p>According to the documentation, the <code>lpdwSize</code> parameter will be
updated to specify the number of <em>bytes</em> required to hold the
value, however, I'm creating a buffer that uses that value in
<em>chars</em> to keep it simple.</p>
<h2 id="using-the-webbrowser-control-directly">Using the WebBrowser control directly</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/getcookies-1a.png" class="gallery" title="Reading cookies using both InternetGetCookieEx and an embedded WebBrowser control" ><img src="https://images.cyotek.com/image/thumbnail/devblog/getcookies-1a.png" alt="Reading cookies using both InternetGetCookieEx and an embedded WebBrowser control" decoding="async" loading="lazy" /></a><figcaption>Reading cookies using both InternetGetCookieEx and an embedded WebBrowser control</figcaption></figure>
<p>While using <code>InternetGetCookieEx</code> would be my preferred
approach, if you already have a <code>WebBrowser</code> control you can
read the cookies directly from the control. The <code>Document</code>
property of a <code>WebBrowser</code> control returns a <code>HtmlDocument</code> that
in turn contains the <code>Cookie</code> property. The format of the
returned data matches that of <code>InternetGetCookieEx</code>.</p>
<blockquote>
<p>There is one important difference between using
<code>InternetGetCookieEx</code> and <code>HtmlDocument.Cookie</code> - the later
<strong>does not</strong> include HTTP only cookies. If you require access
to all cookies for a given URI, then use <code>InternetGetCookieEx</code>
instead.</p>
</blockquote>
<h2 id="what-about-the-cookie-properties">What about the cookie properties?</h2>
<p>The methods describe above only seem to return the names and
values of cookies, excluding properties such as paths and expiry
dates. Unfortunately I don't know of a method of accessing these
extended properties in modern versions of Windows.</p>
<h2 id="using-the-cookies">Using the cookies</h2>
<p>As Windows and browsers have evolved, there is probably little
use for this particular API call. Internet Explorer is obsolete
and third party browsers (and Edge) use their own cookie storage
mechanisms that this API cannot access. Speaking for myself, the
only use I have for this technique is to make custom requests to
an internet resource after using an embedded IE window to
authenticate with a website.</p>
<p>The following example demonstrates how you could assign cookies
to a <code>HttpWebRequest</code>. Note that the <code>SetCookies</code> method of the
<code>CookieContainer</code> class requires the cookies to be comma
separated, rather than the semi-colon seperated values returned
by the above function.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
CookieContainer cookies<span class="symbol">;</span>
Uri uri<span class="symbol">;</span>
<span class="keyword">string</span> cookieData<span class="symbol">;</span>

uri <span class="symbol">=</span> <span class="keyword">new</span> Uri<span class="symbol">(</span><span class="string">&quot;https://demo.cyotek.com/features/cookies.php&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

cookieData <span class="symbol">=</span> GetCookies<span class="symbol">(</span>uri<span class="symbol">)</span><span class="symbol">;</span>

cookies <span class="symbol">=</span> <span class="keyword">new</span> CookieContainer<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
cookies<span class="symbol">.</span>SetCookies<span class="symbol">(</span>uri<span class="symbol">,</span> cookieData<span class="symbol">.</span>Replace<span class="symbol">(</span><span class="string">&quot;; &quot;</span><span class="symbol">,</span> <span class="string">&quot;,&quot;</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

HttpWebRequest request<span class="symbol">;</span>

request <span class="symbol">=</span> WebRequest<span class="symbol">.</span>CreateHttp<span class="symbol">(</span>uri<span class="symbol">)</span><span class="symbol">;</span>
request<span class="symbol">.</span>CookieContainer <span class="symbol">=</span> cookies<span class="symbol">;</span>
</pre>
</figure>
<h2 id="demonstration-program">Demonstration program</h2>
<p>The attached demonstration program includes a complete sample
for reading cookies either via <code>InternetGetCookieEx</code> or from a
<code>WebBrowser</code> control, and is also available from our <a href="https://github.com/cyotek/InternetGetCookieExDemo" rel="external nofollow noopener">GitHub
page</a>.</p>
<h2 id="postscript">Postscript</h2>
<p>I originally wrote this demonstration back in October 2018 but I
delayed it while trying to find out a solution to the missing
properties. I was also curious as to why Edge didn't seem to use
the same system and had a vague idea looking into that. Then
came the news that Microsoft have decided to <a href="https://blogs.windows.com/windowsexperience/2018/12/06/microsoft-edge-making-the-web-better-through-more-open-source-collaboration/" rel="external nofollow noopener">drop their custom
Edge engine and use Chromium instead</a>.</p>
<p>This seems astonishingly short sighted to me and is a real
disappointment.</p>
<p>While I confess I don't use Edge as my primary browser (Firefox
is my daily driver due to its extension support and excellent
developer tools), I found it to be a decent browser which didn't
let me down when I did use it. To me it is perplexing that they
are essentially ceding control to Google. It's like Windows
Phone all over again and I <strong>like</strong> my Windows Phone. It is also
frustrating as currently you can get a <em>reasonable</em> browsing
experience by embedding Internet Explorer via the <code>WebBrowser</code>
control into your application (with a <a href="/post/configuring-the-emulation-mode-of-an-internet-explorer-webbrowser-control">little help</a>) without
having to worry about shipping any dependencies and I had hopes
of doing the same with Edge for a truly modern experience.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2019-01-20 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/reading-cookies-from-internet-explorer .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comUsing message filters in Windows Forms applicationsurn:uuid:b0298816-c2f9-463e-8969-3753c00445a02019-01-01T19:36:52Z2019-01-01T19:36:52Z<p>For a utility application, I wanted to add an item in the system
menu. It's been quite a long time since I last did this (and was
in VB6), so I decided to find some ready made source code. <a href="https://github.com/ygoe/FieldLog/blob/master/LogSubmit/Unclassified/UI/SystemMenu.cs" rel="external nofollow noopener">This
class</a> provides a nice little helper for wrapping the system
menu to add new commands to it, but it requires the owner form
to hook into its window procedure and forward messages on which
I felt was an awkward design.</p>
<p>The code snippets below should illustrate my point - first we
initialise the instance of the <code>SystemMenu</code> class, but in order
for custom commands to be processed we have to override to
override the form's <code>WndProc</code> and pass any messages received
into the <code>SystemMenu</code> class.</p>
<p>Form:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> SystemMenu _menu<span class="symbol">;</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnLoad<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnLoad<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 _menu <span class="symbol">=</span> <span class="keyword">new</span> SystemMenu<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">)</span><span class="symbol">;</span>
 _menu<span class="symbol">.</span>AddCommand<span class="symbol">(</span><span class="string">&quot;&amp;Defaults...&quot;</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ShowDefaultsDialog<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>
 _menu<span class="symbol">.</span>AddCommand<span class="symbol">(</span><span class="string">&quot;&amp;Properties...&quot;</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ShowPropertiesDialog<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>
 _menu<span class="symbol">.</span>AddCommand<span class="symbol">(</span><span class="string">&quot;&amp;About...&quot;</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ShowAboutDialog<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> WndProc<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_menu <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _menu<span class="symbol">.</span>HandleMessage<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>WndProc<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>SystemMenu class:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">void</span> HandleMessage<span class="symbol">(</span><span class="keyword">ref</span> Message msg<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>msg<span class="symbol">.</span>Msg <span class="symbol">==</span> WM_SYSCOMMAND<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnSysCommandMessage<span class="symbol">(</span><span class="keyword">ref</span> msg<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> OnSysCommandMessage<span class="symbol">(</span><span class="keyword">ref</span> Message msg<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">long</span><span class="symbol">)</span>msg<span class="symbol">.</span>WParam <span class="symbol">&gt;</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> <span class="symbol">(</span><span class="keyword">long</span><span class="symbol">)</span>msg<span class="symbol">.</span>WParam <span class="symbol">&lt;=</span> lastId<span class="symbol">)</span>
 <span class="symbol">{</span>
 actions<span class="symbol">[</span><span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>msg<span class="symbol">.</span>WParam <span class="symbol">-</span> <span class="number">1</span><span class="symbol">]</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>This definitely isn't an ideal situation! As <code>WndProc</code> is
protected and there is no equivalent event, perhaps the original
author of this code thought this was the only solution.
Fortunately there is a (little used?) feature of Windows Forms
that can inspect and manipulate source messages at an
application level.</p>
<h2 id="introducing-message-filters">Introducing Message Filters</h2>
<p>The static <code>Application</code> class has the <a href="https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.application.addmessagefilter?view=netframework-4.7.2" rel="external nofollow noopener"><code>AddMessageFilter</code></a>
and <a href="https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.application.removemessagefilter?view=netframework-4.7.2" rel="external nofollow noopener"><code>RemoveMessageFilter</code></a> methods, both of which accept a
single parameter - an object implementing the
<a href="https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.imessagefilter?view=netframework-4.7.2" rel="external nofollow noopener"><code>IMessageFilter</code></a> interface. This interface has a single
method, <code>PreFilterMessage</code>, allowing you to inspect a message
and then either allow it to be dispatched or blocked (&quot;eaten&quot;).</p>
<blockquote>
<p>Warning! Caution is advised when dealing with message filters.
The MSDN documentation notes that adding filters can degrade
application performance but you can also adversely effect your
application if you accidentally block messages you shouldn't.</p>
</blockquote>
<h2 id="implementing-a-message-filter">Implementing a message filter</h2>
<p>Lets take the <code>SystemMenu</code> class from the start of the article.
I've stripped out as much as the code as possible to try and
focus only on message filtering.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">sealed</span> <span class="keyword">class</span> SystemMenu
<span class="symbol">{</span>
 <span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">int</span> WM_SYSCOMMAND <span class="symbol">=</span> <span class="number">0x112</span><span class="symbol">;</span>
 <span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">int</span> MF_STRING <span class="symbol">=</span> <span class="number">0x0</span><span class="symbol">;</span>
 <span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">int</span> MF_SEPARATOR <span class="symbol">=</span> <span class="number">0x800</span><span class="symbol">;</span>

 <span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32.dll&quot;</span><span class="symbol">,</span> CharSet <span class="symbol">=</span> CharSet<span class="symbol">.</span>Auto<span class="symbol">,</span> SetLastError <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">extern</span> IntPtr GetSystemMenu<span class="symbol">(</span>IntPtr hWnd<span class="symbol">,</span> <span class="keyword">bool</span> bRevert<span class="symbol">)</span><span class="symbol">;</span>

 <span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32.dll&quot;</span><span class="symbol">,</span> CharSet <span class="symbol">=</span> CharSet<span class="symbol">.</span>Auto<span class="symbol">,</span> SetLastError <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">bool</span> AppendMenu<span class="symbol">(</span>IntPtr hMenu<span class="symbol">,</span> <span class="keyword">int</span> uFlags<span class="symbol">,</span> <span class="keyword">int</span> uIDNewItem<span class="symbol">,</span> <span class="keyword">string</span> lpNewItem<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">private</span> Form _owner<span class="symbol">;</span>
 <span class="keyword">private</span> <span class="keyword">int</span> _lastId <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="keyword">private</span> List<span class="symbol">&lt;</span>Action<span class="symbol">&gt;</span> _actions <span class="symbol">=</span> <span class="keyword">new</span> List<span class="symbol">&lt;</span>Action<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">public</span> SystemMenu<span class="symbol">(</span>Form owner<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>owner <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException<span class="symbol">(</span>nameof<span class="symbol">(</span>owner<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 _owner <span class="symbol">=</span> owner<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">void</span> AddCommand<span class="symbol">(</span><span class="keyword">string</span> text<span class="symbol">,</span> Action action<span class="symbol">,</span> <span class="keyword">bool</span> separatorBeforeCommand<span class="symbol">)</span>
 <span class="symbol">{</span>
 IntPtr systemMenuHandle<span class="symbol">;</span>
 <span class="keyword">int</span> id<span class="symbol">;</span>

 systemMenuHandle <span class="symbol">=</span> GetSystemMenu<span class="symbol">(</span>_owner<span class="symbol">.</span>Handle<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>

 id <span class="symbol">=</span> <span class="symbol">++</span>_lastId<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>separatorBeforeCommand<span class="symbol">)</span>
 <span class="symbol">{</span>
 AppendMenu<span class="symbol">(</span>systemMenuHandle<span class="symbol">,</span> MF_SEPARATOR<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 AppendMenu<span class="symbol">(</span>systemMenuHandle<span class="symbol">,</span> MF_STRING<span class="symbol">,</span> id<span class="symbol">,</span> text<span class="symbol">)</span><span class="symbol">;</span>

 _actions<span class="symbol">.</span>Add<span class="symbol">(</span>action<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">void</span> HandleMessage<span class="symbol">(</span><span class="keyword">ref</span> Message msg<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>msg<span class="symbol">.</span>Msg <span class="symbol">==</span> WM_SYSCOMMAND<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnSysCommandMessage<span class="symbol">(</span><span class="keyword">ref</span> msg<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 
 <span class="keyword">private</span> <span class="keyword">void</span> OnSysCommandMessage<span class="symbol">(</span><span class="keyword">ref</span> Message msg<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">long</span><span class="symbol">)</span>msg<span class="symbol">.</span>WParam <span class="symbol">&gt;</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> <span class="symbol">(</span><span class="keyword">long</span><span class="symbol">)</span>msg<span class="symbol">.</span>WParam <span class="symbol">&lt;=</span> lastId<span class="symbol">)</span>
 <span class="symbol">{</span>
 actions<span class="symbol">[</span><span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>msg<span class="symbol">.</span>WParam <span class="symbol">-</span> <span class="number">1</span><span class="symbol">]</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="implementing-imessagefilter">Implementing IMessageFilter</h3>
<p>First of all, we need to implement the interface. I'm choosing
to explicitly implement it as it doesn't need a public surface.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">sealed</span> <span class="keyword">class</span> SystemMenu <span class="symbol">:</span> IMessageFilter
<span class="symbol">{</span>
 <span class="keyword">bool</span> IMessageFilter<span class="symbol">.</span>PreFilterMessage<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">false</span><span class="symbol">;</span> <span class="comment">// allow the message to be dispatched</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>This empty implementation returns <code>false</code> to ensure we don't eat
any messages. If instead we returned <code>true</code> then our application
would be completely broken - it wouldn't paint and you wouldn't
be able to interact with anything on it.</p>
<p>With that said, a message filter that doesn't do anything is a
bit of a waste, so I'll remove the public <code>HandleMessage</code> method
and wrap its code into <code>PreFilterMessage</code>. I also need to adjust
<code>OnSysCommandMessage</code> to return a result code as well - again,
if you swallow all <code>WM_SYSCOMMAND</code> messages then your custom
actions might work but the default ones like <strong>Close</strong>,
<strong>Maximize</strong>, etc won't - and as this is an <em>application</em> filter
it will break all of your application windows.</p>
<p>Also, I added a check to make sure that the <code>WM_SYSCOMMAND</code>
message is destined for our owner form - there's no point
intercepting it for other forms in our application.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">bool</span> OnSysCommandMessage<span class="symbol">(</span><span class="keyword">ref</span> Message msg<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>
 <span class="keyword">int</span> commandId<span class="symbol">;</span>

 commandId <span class="symbol">=</span> msg<span class="symbol">.</span>WParam<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 result <span class="symbol">=</span> commandId <span class="symbol">&gt;</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> commandId <span class="symbol">&lt;=</span> _lastId<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>result<span class="symbol">)</span>
 <span class="symbol">{</span>
 _actions<span class="symbol">[</span>commandId <span class="symbol">-</span> <span class="number">1</span><span class="symbol">]</span><span class="symbol">.</span>Invoke<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">bool</span> IMessageFilter<span class="symbol">.</span>PreFilterMessage<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>Msg <span class="symbol">==</span> WM_SYSCOMMAND <span class="symbol">&amp;&amp;</span> m<span class="symbol">.</span>HWnd <span class="symbol">==</span> _owner<span class="symbol">.</span>Handle<span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>OnSysCommandMessage<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span> <span class="comment">// allow the message to continue being processed</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The message filter is now complete.</p>
<h2 id="installing-the-filter">Installing the filter</h2>
<p>To install the filter we call <code>Application.AddMessageFilter</code> and
pass in our class instance. I choose to do this from the
constructor.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> SystemMenu<span class="symbol">(</span>Form owner<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>owner <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException<span class="symbol">(</span>nameof<span class="symbol">(</span>owner<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 _owner <span class="symbol">=</span> owner<span class="symbol">;</span>

 Application<span class="symbol">.</span>AddMessageFilter<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>If we now run the application, we'll find that our <code>SystemMenu</code>
class is now self contained and working perfectly.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/messagefilters-1a.png" class="gallery" title="An example of system menu modifications using a message filter to intercept the WM_SYSCOMMAND message" ><img src="https://images.cyotek.com/image/thumbnail/devblog/messagefilters-1a.png" alt="An example of system menu modifications using a message filter to intercept the WM_SYSCOMMAND message" decoding="async" loading="lazy" /></a><figcaption>An example of system menu modifications using a message filter to intercept the WM_SYSCOMMAND message</figcaption></figure><h2 id="removing-the-filter">Removing the filter</h2>
<p>Once we've finished with the filter we should remove it - in
this example, once the form is closed there isn't much point in
waiting for messages that will never arrive. We could make the
class disposable via the <code>IDisposable</code> interface but as the
<code>Form</code> object has a <code>FormClosed</code> event we can use that and free
the caller of having to anything except &quot;fire and forget&quot;.</p>
<p>Once again, I modify the constructor, this time to wire up the
event, and then supply the event handler itself.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> SystemMenu<span class="symbol">(</span>Form owner<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>owner <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException<span class="symbol">(</span>nameof<span class="symbol">(</span>owner<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 _owner <span class="symbol">=</span> owner<span class="symbol">;</span>

 owner<span class="symbol">.</span>FormClosed <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>FormClosedHandler<span class="symbol">;</span>

 Application<span class="symbol">.</span>AddMessageFilter<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> FormClosedHandler<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> FormClosedEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 Application<span class="symbol">.</span>RemoveMessageFilter<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">)</span><span class="symbol">;</span>

 _actions <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>

 _owner<span class="symbol">.</span>FormClosed <span class="symbol">-=</span> <span class="keyword">this</span><span class="symbol">.</span>FormClosedHandler<span class="symbol">;</span>
 _owner <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>As well as removing the handler, I detach the event and free up
objects as well, on the assumption that nothing else is going to
be done with the class.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Although you probably won't have much call to use them, message
filters can be useful when dealing with certain windows
messages. I originally became aware of them when I needed to
have mouse wheel scrolling working on a control without focus
and now again for this generic system menu class.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2019-01-01 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/using-message-filters-in-windows-forms-applications .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comWorking with CorelDRAW Palettes part 2, writing .pal filesurn:uuid:48c14153-7efe-4c22-bd19-4302d4102cc92018-08-05T11:31:22Z2018-08-05T11:31:22Z<p>In my <a href="/post/reading-coreldraw-palettes-part-1-pal-files">previous article</a>, I described how to read an archaic
CorelDRAW! 3.0 palette file. This continuation covers how to
write files in this format.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/coreldraw-2a.png" class="gallery" title="The sample application adapted to support writing CorelDRAW palettes" ><img src="https://images.cyotek.com/image/thumbnail/devblog/coreldraw-2a.png" alt="The sample application adapted to support writing CorelDRAW palettes" decoding="async" loading="lazy" /></a><figcaption>The sample application adapted to support writing CorelDRAW palettes</figcaption></figure><h2 id="writing-the-palette">Writing the palette</h2>
<p>Just like reading the file, writing is also a simple enough
process.</p>
<p>The first task is to work out the longest swatch name, with a
minimum length of 32. This will allow us to write the text
aligned so it's still easily readable or writable with a
standard text editor.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">int</span> GetLongestName<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> result<span class="symbol">;</span>

 result <span class="symbol">=</span> <span class="number">32</span><span class="symbol">;</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> _palette<span class="symbol">.</span>Count<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> name<span class="symbol">;</span>

 name <span class="symbol">=</span> _palette<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">.</span>Name<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>name<span class="symbol">)</span> <span class="symbol">&amp;&amp;</span> name<span class="symbol">.</span>Length <span class="symbol">&gt;</span> result<span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> name<span class="symbol">.</span>Length<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>After that, it is a matter of looping our colours and for each
colour write a line describing it to a <code>TextWriter</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">void</span> Save<span class="symbol">(</span>Stream stream<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> longestSwatchName<span class="symbol">;</span>
 StringBuilder sb<span class="symbol">;</span>

 longestSwatchName <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetLongestName<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 sb <span class="symbol">=</span> <span class="keyword">new</span> StringBuilder<span class="symbol">(</span>longestSwatchName <span class="symbol">+</span> <span class="number">20</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>TextWriter writer <span class="symbol">=</span> <span class="keyword">new</span> StreamWriter<span class="symbol">(</span>stream<span class="symbol">,</span> Encoding<span class="symbol">.</span>ASCII<span class="symbol">,</span> <span class="number">1024</span><span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> _palette<span class="symbol">.</span>Count<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Color color<span class="symbol">;</span>
 <span class="keyword">string</span> name<span class="symbol">;</span>

 color <span class="symbol">=</span> _palette<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">;</span>
 name <span class="symbol">=</span> color<span class="symbol">.</span>Name<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>ConvertRgbToCmyk<span class="symbol">(</span>color<span class="symbol">,</span> <span class="keyword">out</span> <span class="keyword">byte</span> c<span class="symbol">,</span> <span class="keyword">out</span> <span class="keyword">byte</span> m<span class="symbol">,</span> <span class="keyword">out</span> <span class="keyword">byte</span> y<span class="symbol">,</span> <span class="keyword">out</span> <span class="keyword">byte</span> k<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>WriteName<span class="symbol">(</span>sb<span class="symbol">,</span> name<span class="symbol">,</span> longestSwatchName<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteNumber<span class="symbol">(</span>sb<span class="symbol">,</span> c<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteNumber<span class="symbol">(</span>sb<span class="symbol">,</span> m<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteNumber<span class="symbol">(</span>sb<span class="symbol">,</span> y<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteNumber<span class="symbol">(</span>sb<span class="symbol">,</span> k<span class="symbol">)</span><span class="symbol">;</span>

 writer<span class="symbol">.</span>WriteLine<span class="symbol">(</span>sb<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 sb<span class="symbol">.</span>Length <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>After writing each line, I reset the <code>Length</code> property of the
<code>StringBuilder</code> instance to zero so I can reuse the same
instance, avoiding new object allocations.</p>
<blockquote>
<p>Newer versions of .NET include a <code>Clear</code> method, but it does
exactly the same thing under the hood.</p>
</blockquote>
<p>Although I encountered the <code>SUB</code> character in my previous
digging into these palettes, given it was the exception rather
than the norm I've chosen not to write an end of file character
in this sample application.</p>
<h3 id="writing-the-swatch-name">Writing the swatch name</h3>
<p>Remembering that the names need to be quoted, we write <code>&quot;</code>
characters before and after the actual swatch name. Then, to
ensure that subsequent values are aligned, we write some padding
spaces. We could do this by creating a new string with a space
character repeated the requisite count, but as usual I'm going
try and sidestep the allocation.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> WriteName<span class="symbol">(</span>StringBuilder sb<span class="symbol">,</span> <span class="keyword">string</span> name<span class="symbol">,</span> <span class="keyword">int</span> longestSwatchName<span class="symbol">)</span>
<span class="symbol">{</span>
 sb<span class="symbol">.</span>Append<span class="symbol">(</span><span class="string">&#39;&quot;&#39;</span><span class="symbol">)</span><span class="symbol">;</span>
 sb<span class="symbol">.</span>Append<span class="symbol">(</span>name<span class="symbol">)</span><span class="symbol">;</span>
 sb<span class="symbol">.</span>Append<span class="symbol">(</span><span class="string">&#39;&quot;&#39;</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">//sb.Append(new string(&#39; &#39;, longestSwatchName - name.Length));</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> j <span class="symbol">=</span> <span class="symbol">(</span>name <span class="symbol">??</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">.</span>Length<span class="symbol">;</span> j <span class="symbol">&lt;</span> longestSwatchName<span class="symbol">;</span> j<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 sb<span class="symbol">.</span>Append<span class="symbol">(</span><span class="string">&#39; &#39;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Once again, this article is overdue and so I haven't done any
profiling. When I get back on track I will probably revisit
these potentially micro-optimisation routines I keep creating
and see if there's a concrete reason for doing them or if I'm
just writing code for no reason.</p>
<h3 id="writing-colour-values">Writing colour values</h3>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/coreldraw-2c.png" class="gallery" title="An example of the neatly-aligned output produced by this article" ><img src="https://images.cyotek.com/image/thumbnail/devblog/coreldraw-2c.png" alt="An example of the neatly-aligned output produced by this article" decoding="async" loading="lazy" /></a><figcaption>An example of the neatly-aligned output produced by this article</figcaption></figure>
<p>As with writing the swatch name, I don't want to create a string
version of the source value, and then another string for the
padding as these are allocations that aren't required.</p>
<p>Instead, with a range of <code>0-100</code> that means I'm only dealing
values that will be 1, 2 or 3 characters long so I decided to
create a helper to write the padded value without creating
interim strings (the <code>100</code> below doesn't count as it is
automatically interned).</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> WriteNumber<span class="symbol">(</span>StringBuilder sb<span class="symbol">,</span> <span class="keyword">byte</span> value<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">//sb.Append(value.ToString().PadRight(4));</span>

 <span class="keyword">if</span> <span class="symbol">(</span>value <span class="symbol">==</span> <span class="number">100</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 sb<span class="symbol">.</span>Append<span class="symbol">(</span><span class="string">&quot;100 &quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 sb<span class="symbol">.</span>Append<span class="symbol">(</span><span class="string">&#39; &#39;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>value <span class="symbol">&lt;</span> <span class="number">10</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 sb<span class="symbol">.</span>Append<span class="symbol">(</span><span class="string">&#39; &#39;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 sb<span class="symbol">.</span>Append<span class="symbol">(</span>value<span class="symbol">)</span><span class="symbol">;</span>

 sb<span class="symbol">.</span>Append<span class="symbol">(</span><span class="string">&#39; &#39;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>In the first version of this program, I was left aligning the
strings, when they should actually be right aligned. The
alignment doesn't actually seem to matter - CorelDRAW! 3
opened a left aligned palette quite happily, but better to be
consistent.</p>
</blockquote>
<h2 id="converting-rgb-to-cmyk">Converting RGB to CMYK</h2>
<p>Although I covered RGB to CMYK conversion in a <a href="/post/converting-colours-between-rgb-and-cmyk-in-csharp">previous
article</a>, the version used in this sample is slightly
different to account for the fact that CorelDRAW 3 palettes use
the range 0-100.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/coreldraw-2b.png" class="gallery" title="Converting from RGB to CYMK and back to RGB can result slightly mismatched results" ><img src="https://images.cyotek.com/image/thumbnail/devblog/coreldraw-2b.png" alt="Converting from RGB to CYMK and back to RGB can result slightly mismatched results" decoding="async" loading="lazy" /></a><figcaption>Converting from RGB to CYMK and back to RGB can result slightly mismatched results</figcaption></figure>
<blockquote>
<p>As previously noted, converting from RGB to CMYK and then back
to RGB can introduce subtle errors which means the original
RGB colour may not match the converted version.</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> ConvertRgbToCmyk<span class="symbol">(</span>Color color<span class="symbol">,</span> <span class="keyword">out</span> <span class="keyword">byte</span> c<span class="symbol">,</span> <span class="keyword">out</span> <span class="keyword">byte</span> m<span class="symbol">,</span> <span class="keyword">out</span> <span class="keyword">byte</span> y<span class="symbol">,</span> <span class="keyword">out</span> <span class="keyword">byte</span> k<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">float</span> r<span class="symbol">;</span>
 <span class="keyword">float</span> g<span class="symbol">;</span>
 <span class="keyword">float</span> b<span class="symbol">;</span>
 <span class="keyword">float</span> divisor<span class="symbol">;</span>

 r <span class="symbol">=</span> color<span class="symbol">.</span>R <span class="symbol">/</span> <span class="number">255</span>F<span class="symbol">;</span>
 g <span class="symbol">=</span> color<span class="symbol">.</span>G <span class="symbol">/</span> <span class="number">255</span>F<span class="symbol">;</span>
 b <span class="symbol">=</span> color<span class="symbol">.</span>B <span class="symbol">/</span> <span class="number">255</span>F<span class="symbol">;</span>

 divisor <span class="symbol">=</span> <span class="number">1</span> <span class="symbol">-</span> Math<span class="symbol">.</span>Max<span class="symbol">(</span>Math<span class="symbol">.</span>Max<span class="symbol">(</span>r<span class="symbol">,</span> g<span class="symbol">)</span><span class="symbol">,</span> b<span class="symbol">)</span><span class="symbol">;</span>

 c <span class="symbol">=</span> ClampCmyk<span class="symbol">(</span><span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> r <span class="symbol">-</span> divisor<span class="symbol">)</span> <span class="symbol">/</span> <span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> divisor<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 m <span class="symbol">=</span> ClampCmyk<span class="symbol">(</span><span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> g <span class="symbol">-</span> divisor<span class="symbol">)</span> <span class="symbol">/</span> <span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> divisor<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 y <span class="symbol">=</span> ClampCmyk<span class="symbol">(</span><span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> b <span class="symbol">-</span> divisor<span class="symbol">)</span> <span class="symbol">/</span> <span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> divisor<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 k <span class="symbol">=</span> ClampCmyk<span class="symbol">(</span>divisor<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">byte</span> ClampCmyk<span class="symbol">(</span><span class="keyword">float</span> value<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>value <span class="symbol">&lt;</span> <span class="number">0</span> <span class="symbol">||</span> <span class="keyword">float</span><span class="symbol">.</span>IsNaN<span class="symbol">(</span>value<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 value <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> Convert<span class="symbol">.</span>ToByte<span class="symbol">(</span>value <span class="symbol">*</span> <span class="number">100</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="sample-application">Sample application</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/coreldraw-2d.png" class="gallery" title="Opening a palette created by this article in CorelDRAW! 3.0" ><img src="https://images.cyotek.com/image/thumbnail/devblog/coreldraw-2d.png" alt="Opening a palette created by this article in CorelDRAW! 3.0" decoding="async" loading="lazy" /></a><figcaption>Opening a palette created by this article in CorelDRAW! 3.0</figcaption></figure>
<p>The sample application from <a href="/post/reading-coreldraw-palettes-part-1-pal-files">part 1</a> has been updated to
include the ability to write CorelDRAW! 3.0 palettes in addition
to reading them.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2018-08-05 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/working-with-coreldraw-palettes-part-2-writing-pal-files .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comWorking with CorelDRAW Palettes part 1, reading .pal filesurn:uuid:3ac3d2e9-dbda-4a28-9767-6bf147b7a1022018-08-05T11:34:00Z2018-07-21T10:44:50Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/coreldraw-1d.png" class="gallery" title="Demonstration of reading a classic CorelDRAW! 3.0 palette" ><img src="https://images.cyotek.com/image/thumbnail/devblog/coreldraw-1d.png" alt="Demonstration of reading a classic CorelDRAW! 3.0 palette" decoding="async" loading="lazy" /></a><figcaption>Demonstration of reading a classic CorelDRAW! 3.0 palette</figcaption></figure>
<p>I recently picked up a copy of CorelDRAW! 3.0 from eBay which
came on two CD's with a different version on each. That gave me
two different surprises, the first in that 3.0A wasn't an
improved version of 3.0, and secondly instead of the <code>.cpl</code>
format I was expecting to find, there were two different <code>.pal</code>
formats, one text based (for CorelDRAW!) and one binary (for
PHOTO-PAINT! (very shouty this software!)). This first article
covers reading the text based palette format.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/coreldraw-1b.jpg" class="gallery" title="Unlike NCC-1701-A, 3.0A is a beta version of CorelDRAW and also has a nicer disc" ><img src="https://images.cyotek.com/image/thumbnail/devblog/coreldraw-1b.jpg" alt="Unlike NCC-1701-A, 3.0A is a beta version of CorelDRAW and also has a nicer disc" decoding="async" loading="lazy" /></a><figcaption>Unlike NCC-1701-A, 3.0A is a beta version of CorelDRAW and also has a nicer disc</figcaption></figure><h2 id="format">Format</h2>
<p>The palette format itself is simple enough, from the example
colours below we can infer that each colour entry is in CMYK
format with the range <code>0-100</code>. Although it looks as if it is a
fixed width format, when looking at other palettes using this
format this isn't the case and columns can be of differing
widths.</p>
<p>I'm not sure what limits there are on the number of colours, but
one of the palettes I looked at had over 2000 colours so clearly
not as limited as some older formats.</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
&quot;Black&quot; 0 0 0 100
&quot;90% Black&quot; 0 0 0 90
&quot;White&quot; 0 0 0 0
&quot;Blue&quot; 100 100 0 0
&quot;Green&quot; 100 0 100 0
&quot;Red&quot; 0 100 100 0
</pre>
</figure>
<p>In order to have a common base to compare to, I recreated
<a href="http://pixeljoint.com/forum/forum_posts.asp?TID=16247" rel="external nofollow noopener">DawnBringer's 32 colour palette</a> using the CorelDRAW
application.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/coreldraw-1a.png" class="gallery" title="When using this application, I remembered that right click menus and tooltips were amazing inventions" ><img src="https://images.cyotek.com/image/thumbnail/devblog/coreldraw-1a.png" alt="When using this application, I remembered that right click menus and tooltips were amazing inventions" decoding="async" loading="lazy" /></a><figcaption>When using this application, I remembered that right click menus and tooltips were amazing inventions</figcaption></figure>
<p>From experimenting with CorelDRAW, I also found that it won't
allow swatch names to be longer than 29 characters.</p>
<h2 id="reading-the-palette">Reading the palette</h2>
<p>Reading the palette is straight forward enough, although one of
the palettes I tested did have a old school wild card. As this
is a plain text format with each colour on a single line, I'm
just going to use a <code>StreamReader</code> to pull out each line.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">void</span> Load<span class="symbol">(</span>Stream stream<span class="symbol">)</span>
<span class="symbol">{</span>
 _palette<span class="symbol">.</span>Clear<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>TextReader reader <span class="symbol">=</span> <span class="keyword">new</span> StreamReader<span class="symbol">(</span>stream<span class="symbol">,</span> Encoding<span class="symbol">.</span>ASCII<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> line<span class="symbol">;</span>

 <span class="keyword">while</span> <span class="symbol">(</span><span class="symbol">(</span>line <span class="symbol">=</span> reader<span class="symbol">.</span>ReadLine<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span> <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> <span class="symbol">(</span>line<span class="symbol">.</span>Length <span class="symbol">==</span> <span class="number">0</span> <span class="symbol">||</span> line<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span> <span class="symbol">!=</span> <span class="symbol">(</span><span class="keyword">char</span><span class="symbol">)</span><span class="number">26</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>line<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// Parse the color</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="end-of-file">End of file</h3>
<p>In the above while loop, instead of just checking that the line
is not <code>null</code> as I usually would, I also have an extra check to
see if the first character is <code>(char)26</code> and if so, to cancel
loading. But what is this character?</p>
<p>This is the <code>SUB</code> or <a href="https://en.wikipedia.org/wiki/Substitute_character" rel="external nofollow noopener">substitute character</a> and was
originally designed to be used in place of a character that is
invalid. Historical DOS systems and its predecessors used this
as an end of file character, which is how it has been used in
one of the palettes that shipped with this version of CorelDRAW.</p>
<p>An interesting facet of computing history I wasn't expecting to find!</p>
<h3 id="reading-the-swatch-name">Reading the swatch name</h3>
<p>As swatch names start with a quote and end with a quote, and are
the only quoted string in the line we can find the end of the
name by using <code>LastIndexOf</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">string</span> name<span class="symbol">;</span>

lastQuote <span class="symbol">=</span> line<span class="symbol">.</span>LastIndexOf<span class="symbol">(</span><span class="string">&#39;&quot;&#39;</span><span class="symbol">)</span><span class="symbol">;</span>

name <span class="symbol">=</span> line<span class="symbol">.</span>Substring<span class="symbol">(</span><span class="number">1</span><span class="symbol">,</span> lastQuote <span class="symbol">-</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>As the .NET <code>Color</code> structure doesn't allow the <code>Name</code> property
to be set it isn't used by the demonstration, but is still
preserved for use with custom structures.</p>
<p>While testing I discovered that although CorelDRAW lets you
enter quotes into the palette editor, it silently discarded any
swatches with quotes in their name.</p>
<h3 id="reading-the-colour-values">Reading the colour values</h3>
<p>Although I don't go to <a href="https://ayende.com/blog/175714/timing-the-time-it-takes-to-parse-time-part-ii" rel="external nofollow noopener">crazy lengths</a>, string parsing is
typically quite expensive in terms of memory and/or processing
power and so I often try and optimise this as I go.</p>
<p>In this case, while I could do <code>string.Split</code>, I'd rather avoid
the array allocation so I'm manually looking for the number
sequences. As we already have the end of the swatch name from
the previous section, we can walk the rest of the string,
discarding the white-space, finding the longest sequence of
digits and then parsing out the result.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">byte</span> NextNumber<span class="symbol">(</span><span class="keyword">string</span> line<span class="symbol">,</span> <span class="keyword">ref</span> <span class="keyword">int</span> start<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> length<span class="symbol">;</span>
 <span class="keyword">int</span> valueLength<span class="symbol">;</span>
 <span class="keyword">int</span> maxLength<span class="symbol">;</span>
 <span class="keyword">byte</span> result<span class="symbol">;</span>

 <span class="comment">// skip any leading spaces</span>
 <span class="keyword">while</span> <span class="symbol">(</span><span class="keyword">char</span><span class="symbol">.</span>IsWhiteSpace<span class="symbol">(</span>line<span class="symbol">[</span>start<span class="symbol">]</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 start<span class="symbol">++</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 length <span class="symbol">=</span> line<span class="symbol">.</span>Length<span class="symbol">;</span>
 maxLength <span class="symbol">=</span> Math<span class="symbol">.</span>Min<span class="symbol">(</span><span class="number">3</span><span class="symbol">,</span> length <span class="symbol">-</span> start<span class="symbol">)</span><span class="symbol">;</span>
 valueLength <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> maxLength<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">char</span><span class="symbol">.</span>IsDigit<span class="symbol">(</span>line<span class="symbol">[</span>start <span class="symbol">+</span> i<span class="symbol">]</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 valueLength<span class="symbol">++</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 result <span class="symbol">=</span> <span class="keyword">byte</span><span class="symbol">.</span>Parse<span class="symbol">(</span>line<span class="symbol">.</span>Substring<span class="symbol">(</span>start<span class="symbol">,</span> valueLength<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 start <span class="symbol">+=</span> valueLength<span class="symbol">;</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>I start by iterating the string from an given start position and
keep moving forward as long as the current character is a space.</p>
<p>Once I've ran out of spaces, I check how much of the string is
left to determine the maximum number of characters to read -
normally I want to read 3 as this is the maximum number of
digits which can be present, however if it is at the end of the
line and the final result isn't a 3 digit number there won't be
enough data to read.</p>
<p>With the maximum number of digits established, I then check
through these characters - if I find a digit, I increment a
counter telling me how long the final number is. Anything else
and I break out.</p>
<p>With both the start of the numeric field and its length I can
then use <code>byte.Parse</code> on the substring.</p>
<blockquote>
<p>It would be interesting to know if I could use the new
<code>Span&lt;T&gt;</code> functionality in .NET Core to skip any string
processing at all. An experiment for a future day!</p>
</blockquote>
<p>The final action I perform is to record the new start position,
so the next call to the method will continue from this point.</p>
<p>With this method in place, I can quite easily read out the four
colour components without doing any array allocation.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">int</span> numberPosition<span class="symbol">;</span>
<span class="keyword">byte</span> cyan<span class="symbol">;</span>
<span class="keyword">byte</span> magenta<span class="symbol">;</span>
<span class="keyword">byte</span> yellow<span class="symbol">;</span>
<span class="keyword">byte</span> black<span class="symbol">;</span>

numberPosition <span class="symbol">=</span> lastQuote <span class="symbol">+</span> <span class="number">1</span><span class="symbol">;</span>
cyan <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>NextNumber<span class="symbol">(</span>line<span class="symbol">,</span> <span class="keyword">ref</span> numberPosition<span class="symbol">)</span><span class="symbol">;</span>
magenta <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>NextNumber<span class="symbol">(</span>line<span class="symbol">,</span> <span class="keyword">ref</span> numberPosition<span class="symbol">)</span><span class="symbol">;</span>
yellow <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>NextNumber<span class="symbol">(</span>line<span class="symbol">,</span> <span class="keyword">ref</span> numberPosition<span class="symbol">)</span><span class="symbol">;</span>
black <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>NextNumber<span class="symbol">(</span>line<span class="symbol">,</span> <span class="keyword">ref</span> numberPosition<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h3 id="does-it-really-matter">Does it really matter?</h3>
<p>Reading palettes is hardly going to be a performance critical
operation, so maybe you don't need to write any exotic code. If
you don't care about the allocations, you could write the value
extraction a lot simpler by using <code>string.Split</code>. Note that even
here, I still want to avoid &quot;silent&quot; allocations and use a
prepared array with the split characters.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">readonly</span> <span class="keyword">char</span><span class="symbol">[</span><span class="symbol">]</span> _whitespace <span class="symbol">=</span> <span class="symbol">{</span> <span class="string">&#39; &#39;</span> <span class="symbol">}</span><span class="symbol">;</span>

<span class="keyword">string</span><span class="symbol">[</span><span class="symbol">]</span> parts<span class="symbol">;</span>

parts <span class="symbol">=</span> line<span class="symbol">.</span>Substring<span class="symbol">(</span>lastQuote <span class="symbol">+</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">.</span>Split<span class="symbol">(</span>_whitespace<span class="symbol">,</span> StringSplitOptions<span class="symbol">.</span>RemoveEmptyEntries<span class="symbol">)</span><span class="symbol">;</span>
cyan <span class="symbol">=</span> <span class="keyword">byte</span><span class="symbol">.</span>Parse<span class="symbol">(</span>parts<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span>
magenta <span class="symbol">=</span> <span class="keyword">byte</span><span class="symbol">.</span>Parse<span class="symbol">(</span>parts<span class="symbol">[</span><span class="number">1</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span>
yellow <span class="symbol">=</span> <span class="keyword">byte</span><span class="symbol">.</span>Parse<span class="symbol">(</span>parts<span class="symbol">[</span><span class="number">2</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span>
black <span class="symbol">=</span> <span class="keyword">byte</span><span class="symbol">.</span>Parse<span class="symbol">(</span>parts<span class="symbol">[</span><span class="number">3</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>Personally, I prefer the former approach however I have to admit
I didn't profile either approach as the article is overdue.</p>
<h2 id="converting-cmyk-to-rgb">Converting CMYK to RGB</h2>
<p>In absolutely no co-incidence at all, my previous article
described how to <a href="/post/converting-colours-between-rgb-and-cmyk-in-csharp">convert between CMYK and RGB</a> so I don't
need to have a digression in this article.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> Color ConvertCmykToRgb<span class="symbol">(</span><span class="keyword">int</span> c<span class="symbol">,</span> <span class="keyword">int</span> m<span class="symbol">,</span> <span class="keyword">int</span> y<span class="symbol">,</span> <span class="keyword">int</span> k<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> r<span class="symbol">;</span>
 <span class="keyword">int</span> g<span class="symbol">;</span>
 <span class="keyword">int</span> b<span class="symbol">;</span>
 <span class="keyword">float</span> multiplier<span class="symbol">;</span>

 multiplier <span class="symbol">=</span> <span class="number">1</span> <span class="symbol">-</span> k <span class="symbol">/</span> <span class="number">100</span>F<span class="symbol">;</span>

 r <span class="symbol">=</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span><span class="number">255</span> <span class="symbol">*</span> <span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> c <span class="symbol">/</span> <span class="number">100</span>F<span class="symbol">)</span> <span class="symbol">*</span> multiplier<span class="symbol">)</span><span class="symbol">;</span>
 g <span class="symbol">=</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span><span class="number">255</span> <span class="symbol">*</span> <span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> m <span class="symbol">/</span> <span class="number">100</span>F<span class="symbol">)</span> <span class="symbol">*</span> multiplier<span class="symbol">)</span><span class="symbol">;</span>
 b <span class="symbol">=</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span><span class="number">255</span> <span class="symbol">*</span> <span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> y <span class="symbol">/</span> <span class="number">100</span>F<span class="symbol">)</span> <span class="symbol">*</span> multiplier<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> Color<span class="symbol">.</span>FromArgb<span class="symbol">(</span>r<span class="symbol">,</span> g<span class="symbol">,</span> b<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>With this helper in place and regardless of which method you
used to get the individual C, M, Y and K components, we can now
fully read CorelDRAW 3.0 text based palettes into .NET.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
_palette<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ConvertCmykToRgb<span class="symbol">(</span>cyan<span class="symbol">,</span> magenta<span class="symbol">,</span> yellow<span class="symbol">,</span> black<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="sample-application">Sample application</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/coreldraw-1c.png" class="gallery" title="A familiar looking application demonstrating how to read CorelDRAW! 3.0 palettes" ><img src="https://images.cyotek.com/image/thumbnail/devblog/coreldraw-1c.png" alt="A familiar looking application demonstrating how to read CorelDRAW! 3.0 palettes" decoding="async" loading="lazy" /></a><figcaption>A familiar looking application demonstrating how to read CorelDRAW! 3.0 palettes</figcaption></figure>
<p>As usual, an example project demonstrating how to read CorelDRAW
<code>.pal</code> files is available from the link below.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2018-07-21 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/reading-coreldraw-palettes-part-1-pal-files .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comConverting colours between RGB and CMYK in C#urn:uuid:3688a8c4-646e-4731-8a8b-4b91bfbcbcad2018-07-15T10:19:51Z2018-07-15T09:10:01Z<p>In my previous articles on reading and writing colours from
various palette/swatch formats, I left CMYK conversion as an
exercise for the reader and only demonstrated RGB aspects. This
article demonstrates how to convert colours in CMYK format to
RGB and vice versa.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/cmyk-rgb-conv-1a.png" class="gallery" title="Two way colour conversions between CMYK and RGB" ><img src="https://images.cyotek.com/image/thumbnail/devblog/cmyk-rgb-conv-1a.png" alt="Two way colour conversions between CMYK and RGB" decoding="async" loading="lazy" /></a><figcaption>Two way colour conversions between CMYK and RGB</figcaption></figure><h2 id="about-cmyk">About CMYK</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/CMYK_subtractive_color_mixing.svg" class="gallery" title="" ><img src="https://images.cyotek.com/image/devblog/CMYK_subtractive_color_mixing.svg" alt="" decoding="async" loading="lazy" /></a><figcaption></figcaption></figure>
<p>(Image credit: <a href="https://commons.wikimedia.org/wiki/File:CMYK_subtractive_color_mixing.svg" rel="external nofollow noopener">SharkD [CC0]</a>, from Wikimedia Commons)</p>
<p>CMYK is traditionally used in the printing industry. It is a
subtractive colour model and uses four colours (cyan, magenta,
yellow and black). Unlike additive colour models (e.g. the RGB
model normally discussed on this blog), combining all primary
colours in subtractive results in black, whereas combining all
primary colours in additive results in white.</p>
<p>Unlike RGB which generally uses the range <code>0-255</code>, most examples
of CMYK I've seen use percentages instead, e.g. 37% cyan, 18%
magenta, no yellow and 31% black. In this article I'm using the
range <code>0-1</code> to describe the colours. (Just to be awkward, the
demonstration front end uses <code>0-100</code> to make it more user
friendly!)</p>
<h2 id="a-caveat-on-conversion">A caveat on conversion</h2>
<p>As RGB uses completely different colour channels as well as
different value ranges, conversions may not be directly
compatible. For example, converting from RGB to CMYK and then
back to RGB there is a chance to have a final RGB value that is
just slightly off from the source. And that's before we even get
into gamut and colour profiles, neither of which I'll be
covering in this article.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/cmyk-rgb-conv-1b.png" class="gallery" title="An example of an imprecise two way conversion" ><img src="https://images.cyotek.com/image/thumbnail/devblog/cmyk-rgb-conv-1b.png" alt="An example of an imprecise two way conversion" decoding="async" loading="lazy" /></a><figcaption>An example of an imprecise two way conversion</figcaption></figure><h2 id="converting-cmyk-to-rgb">Converting CMYK to RGB</h2>
<p>Red is calculated from the cyan and black colours</p>
<blockquote>
<p>R = 255 × (1-C) × (1-K)</p>
</blockquote>
<p>Green is calculated from the magenta and black colours</p>
<blockquote>
<p>G = 255 × (1-M) × (1-K)</p>
</blockquote>
<p>Blue is calculated from the yellow and black colours</p>
<blockquote>
<p>B = 255 × (1-Y) × (1-K)</p>
</blockquote>
<p>Formula credit: <a href="https://www.rapidtables.com/convert/color/cmyk-to-rgb.html" rel="external nofollow noopener">RapidTables</a></p>
<p>The following function converts CMYK into RGB</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> Color ConvertCmykToRgb<span class="symbol">(</span><span class="keyword">float</span> c<span class="symbol">,</span> <span class="keyword">float</span> m<span class="symbol">,</span> <span class="keyword">float</span> y<span class="symbol">,</span> <span class="keyword">float</span> k<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> r<span class="symbol">;</span>
 <span class="keyword">int</span> g<span class="symbol">;</span>
 <span class="keyword">int</span> b<span class="symbol">;</span>

 r <span class="symbol">=</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span><span class="number">255</span> <span class="symbol">*</span> <span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> c<span class="symbol">)</span> <span class="symbol">*</span> <span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> k<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 g <span class="symbol">=</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span><span class="number">255</span> <span class="symbol">*</span> <span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> m<span class="symbol">)</span> <span class="symbol">*</span> <span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> k<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 b <span class="symbol">=</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span><span class="number">255</span> <span class="symbol">*</span> <span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> y<span class="symbol">)</span> <span class="symbol">*</span> <span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> k<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> Color<span class="symbol">.</span>FromArgb<span class="symbol">(</span>r<span class="symbol">,</span> g<span class="symbol">,</span> b<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="converting-rgb-to-cmyk">Converting RGB to CMYK</h2>
<p>The R,G,B values are divided by 255 to change the range from <code>0-255</code> to <code>0-1</code>:</p>
<blockquote>
<p>R = R/255<br />
G = G/255<br />
B = B/255</p>
</blockquote>
<p>The black key is calculated from the maximum red, green or blue colour</p>
<blockquote>
<p>K = 1-max(R, G, B)</p>
</blockquote>
<p>Cyan is calculated from red and black</p>
<blockquote>
<p>C = (1-R-K) / (1-K)</p>
</blockquote>
<p>Magenta is calculated from green and black</p>
<blockquote>
<p>M = (1-G-K) / (1-K)</p>
</blockquote>
<p>Yellow is calculated from blue and black</p>
<blockquote>
<p>Y = (1-B-K) / (1-K)</p>
</blockquote>
<p>Formula credit: <a href="https://www.rapidtables.com/convert/color/rgb-to-cmyk.html" rel="external nofollow noopener">RapidTables</a></p>
<p>The following functions convert RGB to CYMK</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> CmykColor ConvertRgbToCmyk<span class="symbol">(</span><span class="keyword">int</span> r<span class="symbol">,</span> <span class="keyword">int</span> g<span class="symbol">,</span> <span class="keyword">int</span> b<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">float</span> c<span class="symbol">;</span>
 <span class="keyword">float</span> m<span class="symbol">;</span>
 <span class="keyword">float</span> y<span class="symbol">;</span>
 <span class="keyword">float</span> k<span class="symbol">;</span>
 <span class="keyword">float</span> rf<span class="symbol">;</span>
 <span class="keyword">float</span> gf<span class="symbol">;</span>
 <span class="keyword">float</span> bf<span class="symbol">;</span>

 rf <span class="symbol">=</span> r <span class="symbol">/</span> <span class="number">255</span>F<span class="symbol">;</span>
 gf <span class="symbol">=</span> g <span class="symbol">/</span> <span class="number">255</span>F<span class="symbol">;</span>
 bf <span class="symbol">=</span> b <span class="symbol">/</span> <span class="number">255</span>F<span class="symbol">;</span>

 k <span class="symbol">=</span> ClampCmyk<span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> Math<span class="symbol">.</span>Max<span class="symbol">(</span>Math<span class="symbol">.</span>Max<span class="symbol">(</span>rf<span class="symbol">,</span> gf<span class="symbol">)</span><span class="symbol">,</span> bf<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 c <span class="symbol">=</span> ClampCmyk<span class="symbol">(</span><span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> rf <span class="symbol">-</span> k<span class="symbol">)</span> <span class="symbol">/</span> <span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> k<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 m <span class="symbol">=</span> ClampCmyk<span class="symbol">(</span><span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> gf <span class="symbol">-</span> k<span class="symbol">)</span> <span class="symbol">/</span> <span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> k<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 y <span class="symbol">=</span> ClampCmyk<span class="symbol">(</span><span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> bf <span class="symbol">-</span> k<span class="symbol">)</span> <span class="symbol">/</span> <span class="symbol">(</span><span class="number">1</span> <span class="symbol">-</span> k<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> <span class="keyword">new</span> CmykColor<span class="symbol">(</span>c<span class="symbol">,</span> m<span class="symbol">,</span> y<span class="symbol">,</span> k<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">float</span> ClampCmyk<span class="symbol">(</span><span class="keyword">float</span> value<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>value <span class="symbol">&lt;</span> <span class="number">0</span> <span class="symbol">||</span> <span class="keyword">float</span><span class="symbol">.</span>IsNaN<span class="symbol">(</span>value<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 value <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> value<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>You might notice that it is possible for <code>(1 - k)</code> to return 0
if <code>k</code> is calculated to be solid black (<code>1</code>). As this is using
floating point math, you don't get the normal
<code>DivideByZeroException</code>, but instead of the result of the call
is <code>NaN</code>. The <code>ClampCmyk</code> function make sure that values less
than zero are normalised as zero, and it also does the same for
<code>NaN</code> values.</p>
<p>I did write a version of the function that checked to see if
<code>(1 - k)</code> was <code>0</code> and then return zero for the <code>c</code>, <code>m</code> and <code>y</code>
fields but the above version was easier to read.</p>
<h2 id="demonstration-program">Demonstration program</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/cmyk-rgb-conv-1w.gif" class="gallery" title="The demonstration program in action" ><img src="https://images.cyotek.com/image/devblog/cmyk-rgb-conv-1w.gif" alt="The demonstration program in action" decoding="async" loading="lazy" /></a><figcaption>The demonstration program in action</figcaption></figure>
<p>The demonstration program lets you freely convert CMYK to RGB
and RGB to CMYK uses the above functions, and a lot of sliders
and spin buttons. I also included some predefined RGB colours,
courtesy of <a href="http://androidarts.com/palette/16pal.htm" rel="external nofollow noopener">Arne's 16 colour palette</a>.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2018-07-15 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/converting-colours-between-rgb-and-cmyk-in-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comDisplaying text in an empty ListBoxurn:uuid:93c73c30-05a2-41e9-8411-f5370adb35732018-04-29T21:41:23Z2018-04-28T14:02:05Z<p>While looking at ways of improving the UI of a dialog in an
application, I wanted to display some status text in a <code>ListBox</code>
control that was empty. The default Windows Forms <code>ListBox</code>
(which uses the underlying native Win32 control) doesn't support
this, but with a little effort we can extend the control.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/listbox-emptytext.gif" class="gallery" title="A demonstration of the sample project" ><img src="https://images.cyotek.com/image/devblog/listbox-emptytext.gif" alt="A demonstration of the sample project" decoding="async" loading="lazy" /></a><figcaption>A demonstration of the sample project</figcaption></figure><h2 id="a-brief-primer-on-painting-in-windows-forms">A brief primer on painting in Windows Forms</h2>
<p>When a <code>Control</code> receives either the <code>WM_PAINT</code> or
<code>WM_ERASEBKGND</code> messages, it will check to see if the
<code>ControlStyles.UserPaint</code> style is set. If set then the
<code>WM_PAINT</code> message will cause the <code>Paint</code> event to be raised,
and for <code>WM_ERASEBKGND</code> the <code>PaintBackground</code> event - but only
if the the <code>AllPaintingInWmPaint</code> style is not set.</p>
<p>For both messages, if the <code>UserPaint</code> style is not set, then the
control will call the default window procedure allowing that to
handle the message.</p>
<p>This is important to note, as for certain controls (such as
<code>ListBox</code> which wrap a native window) the <code>UserPaint</code> style is
not set, meaning the paint events are never raised. If you try
and set the flag yourself, then you will find the paint events
work again - but the native control will stop painting correctly
due to the default window procedure not being called.</p>
<p>Unfortunately, while you can manually call the default window
procedure via the <code>DefWndProc</code> method, you won't have access to
the original message data to pass to it.</p>
<h2 id="capturing-wm_paint">Capturing WM_PAINT</h2>
<p>Based on the above primer, we now know that we can't easily use
<code>OnPaint</code> to provide our custom drawing. Instead, we'll
intercept the <code>WM_PAINT</code> message when it arrives for our control
and initiate painting manually.</p>
<blockquote>
<p>Although the <code>Control</code> class offers many events for easily
hooking into various actions, it isn't possible to hook into
window procedures in this manner. The simplest solution is to
create an inherited class and then override the <code>WndProc</code>
method.</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">const</span> <span class="keyword">int</span> WM_PAINT <span class="symbol">=</span> <span class="number">15</span><span class="symbol">;</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> WndProc<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// make sure we call existing procedures!</span>
 <span class="keyword">base</span><span class="symbol">.</span>WndProc<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>Msg <span class="symbol">==</span> WM_PAINT<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// perform some custom painting</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="painting-our-custom-message">Painting our custom message</h2>
<p>Even though we're very slightly going outside the box to
intercept windows messages, we don't need to actually use any
Win32 calls. Instead we call <code>CreateGraphics</code> to get a
<code>Graphics</code> instance for our control and paint away as we
normally would.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> DrawText<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count <span class="symbol">==</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>_emptyText<span class="symbol">)</span> <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>DesignMode<span class="symbol">)</span>
 <span class="symbol">{</span>
 TextFormatFlags flags<span class="symbol">;</span>

 flags <span class="symbol">=</span> TextFormatFlags<span class="symbol">.</span>ExpandTabs <span class="symbol">|</span> TextFormatFlags<span class="symbol">.</span>HorizontalCenter <span class="symbol">|</span> TextFormatFlags<span class="symbol">.</span>NoPrefix <span class="symbol">|</span> TextFormatFlags<span class="symbol">.</span>WordBreak <span class="symbol">|</span> TextFormatFlags<span class="symbol">.</span>WordEllipsis <span class="symbol">|</span> TextFormatFlags<span class="symbol">.</span>VerticalCenter<span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Graphics g <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>CreateGraphics<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 TextRenderer<span class="symbol">.</span>DrawText<span class="symbol">(</span>g<span class="symbol">,</span> _emptyText<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Font<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ClientRectangle<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ForeColor<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>BackColor<span class="symbol">,</span> flags<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/listbox-emptytext-1a.png" class="gallery" title="Displaying a message in an empty ListBox" ><img src="https://images.cyotek.com/image/thumbnail/devblog/listbox-emptytext-1a.png" alt="Displaying a message in an empty ListBox" decoding="async" loading="lazy" /></a><figcaption>Displaying a message in an empty ListBox</figcaption></figure>
<p>In this example it will print the message centred in the middle
of the list with word wrapping enabled.</p>
<h2 id="clearing-up-after-messy-resizing">Clearing up after messy resizing</h2>
<p>There's just one flaw with the above code - as soon as you
resize the control, it will paint the text again without
clearing the existing content, which can result in a bit of a
mess. As I discussed above, Windows uses the <code>WM_ERASEBKGND</code> to
notify a window that it should erase its background and so if we
adjust our <code>WndProc</code> to intercept this message we can clean up
after ourselves.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/listbox-emptytext-1b.png" class="gallery" title="Messy output after resizing the window" ><img src="https://images.cyotek.com/image/thumbnail/devblog/listbox-emptytext-1b.png" alt="Messy output after resizing the window" decoding="async" loading="lazy" /></a><figcaption>Messy output after resizing the window</figcaption></figure><figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">const</span> <span class="keyword">int</span> WM_ERASEBKGND <span class="symbol">=</span> <span class="number">20</span><span class="symbol">;</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> WndProc<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>WndProc<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>Msg <span class="symbol">==</span> WM_PAINT<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnWmPaint<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>Msg <span class="symbol">==</span> WM_ERASEBKGND <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>ShouldDrawEmptyText<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ClearBackground<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> ClearBackground<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>Graphics g <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>CreateGraphics<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 g<span class="symbol">.</span>Clear<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>BackColor<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p><del>This time I'm simply instructing the control to draw
itself, which will cause the underlying native window to repaint
its background ready for our re-positioned text to be
drawn.</del></p>
<p>In the original posting of this article, I'd accidentally
defined <code>WM_ERASEBKGND</code> as <code>14</code> which is actually
<code>WM_GETTEXTLENGTH</code>. So the example managed to work only by
chance. Calling <code>Invalidate</code> from <code>WM_ERASEBKGND</code> is the wrong
approach as it leads to mass flicker. In the revised version, I
just manually erase the background.</p>
<p>And that is pretty much it, short and sweet - the associated
download includes an updated fully functional demonstration
project.</p>
<h2 id="adding-empty-text-support-to-other-controls">Adding empty text support to other controls</h2>
<p>While this article describes extending the <code>ListBox</code> control, it
should be possible to use in other controls too. For example, I
use the exact same technique to add empty text support to the
<code>ListView</code> control.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2018-04-28 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/displaying-text-in-an-empty-listbox .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comUpdating AssemblyInfo.cs version information via batch fileurn:uuid:a4c9c694-22d3-4baf-b133-35f10ff97f8a2018-03-25T16:13:42Z2018-03-25T16:13:42Z<p>Over a year ago I wrote how to <a href="/post/using-a-jenkins-pipeline-to-build-and-publish-nuget-packages">build and publish NuGet packages
via Jenkins</a> in which I stated I would follow up with another
article on modifying <code>AssemblyInfo.cs</code> via a batch file. Of
course, I forgot to write that post. Recently I was adding a
NuGet publish job to a TeamCity server which reminded me and
therefore finally here is the article.</p>
<h2 id="dont-jenkins-and-teamcity-already-do-this">Don't Jenkins and TeamCity already do this?</h2>
<p>While both Jenkins and TeamCity include or have available
plugins for updating <code>AssemblyInfo.cs</code>, they both suffer from
the problem in that they can <em>write</em> a version into the file but
they can't <em>read</em> from it first to derive a new value. However,
if you simply want to set a full version from within either CI
tool you can without having to bother with anything in this
post. As I wish to combine part of the existing version with a
CI supplied value, I need to look at alternatives.</p>
<h2 id="reading-text-from-a-file-via-a-batch-script">Reading text from a file via a batch script</h2>
<p>The &quot;simplest&quot; way of reading text from a file in a batch script
is to use a Unix utility named sed (stream editor). Why did I
quote &quot;simplest&quot;? You'll see!</p>
<p>You can download a version compiled for Windows from
<a href="http://gnuwin32.sourceforge.net/packages/sed.htm" rel="external nofollow noopener">SourceForge</a>. If you choose to download the portable
Binaries distribution make sure you also pick up the
Dependencies package as well as this contains required DLL's.</p>
<h2 id="using-sed">Using sed</h2>
<p>Although sed can operate using pipes in the familiar manner for
DOS/batch commands, it also has an inline editing mode which is
more convenient for our purposes.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
sed -i <span class="string">&quot;expression&quot;</span> filename
</pre>
</figure>
<blockquote>
<p>When using the <code>/i</code> option, sed will leave a temporary file
behind. This normally shouldn't be a concern if you're using a
CI tool and have performed a fresh checkout or clean-up before
building, but can become <em>really</em> annoying if you run it in
your source tree. You can always including a command to delete
the temporary files (for example <code>DEL sed*.</code>), assuming you
don't have real files with a similar pattern.</p>
</blockquote>
<h2 id="defining-an-expression-to-update-the-revision">Defining an expression to update the revision</h2>
<p>I may have lied when I said sed was simple! To modify our file,
we want to substitute part of the existing <code>AssemblyFileVersion</code>
or <code>AssemblyInformationalVersion</code> values. To do this we'll use
sed's <em>substitution</em> command with a source pattern (a regular
expression) and then another pattern for the replacement. Due to
the way sed works, all 3 of these values need to be a single
string parameter. (You can use external files, but that is
beyond the scope of this example)</p>
<blockquote>
<p>Getting the expressions working in sed can take a lot of trial
and error. To easily test your expressions omit the <code>-i</code>
switch from the command line - sed will then output the file
to the console, allowing you to see the results of your
expression without modifying the original file.</p>
</blockquote>
<p>As a simple example, assume I wanted to replace the word
<em>Assembly</em> with <em>Library</em>. The expression <code>s/Assembly/Library/</code>
would handle this - <code>s</code> is the command to use.</p>
<blockquote>
<p>In the above example I'm using forward slash <code>/</code> to separate
the arguments - the final separator is also required. As well
as a slash you can also use the <code>^</code> character, which may be
easier to read.</p>
</blockquote>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
&gt; sed <span class="string">&quot;s/Assembly/Library/&quot;</span> Properties\AssemblyInfo.cs
[assembly: LibraryVersion(&quot;1.0.0.0&quot;)]
[assembly: LibraryFileVersion(&quot;1.4.3.1&quot;)]
[assembly: LibraryInformationalVersion(&quot;1.4.0.1&quot;)]
</pre>
</figure>
<p>Admittedly that's not a very useful example. So we'll now change
it to match the attribute instead.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
&gt; sed <span class="string">&quot;s/Assembly\(Informational\|File\)Version/Library/&quot;</span> Properties\AssemblyInfo.cs
[assembly: AssemblyVersion(&quot;1.0.0.0&quot;)]
[assembly: Library(&quot;1.4.3.1&quot;)]
[assembly: Library(&quot;1.4.0.1&quot;)]
</pre>
</figure>
<blockquote>
<p>I'm deliberately <strong>not</strong> changing the assembly version given I
strong name all assemblies and definitely do not want bindings
to break from build number changes.</p>
</blockquote>
<p>Notice how the regex capture group and logical or characters are
escaped? If they aren't they won't function as a regular
expression and no matches will be made.</p>
<p>Now we'll extend the pattern further to include the version
information</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
&gt; sed <span class="string">&quot;s/\(Assembly\(Informational\|File\)Version(\d34[0-9]\+\.[0-9]\+\.[0-9]\+\.\)[0-9]/Library/&quot;</span> Properties\AssemblyInfo.cs
[assembly: AssemblyVersion(&quot;1.0.0.0&quot;)]
[assembly: Library&quot;)]
[assembly: Library&quot;)]
</pre>
</figure>
<p>The lovely looking expression now captures the version as well.
Note that I couldn't get sed to accept a quote character in the
expression regardless of if I tried escaping it, but fortunately
it provides the <code>\d</code> sequence for special characters - <code>\d34</code> is
the quote. Although it's awkward to read, we capture
<code>AttributeName(&quot;nnn.nnn.nnn.</code> in a separate capture group so we
can use it in our replacement expression.</p>
<p>Finally, lets actually replace the value with something useful.
Jenkins and TeamCity both set an environment variable named
<code>BUILD_NUMBER</code> so you can simply combine that with the group
captured by the expression.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
&gt; <span class="keyword">set</span> BUILD_NUMBER=0325

&gt; sed <span class="string">&quot;s/\(Assembly\(Informational\|File\)Version(\d34[0-9]\+\.[0-9]\+\.[0-9]\+\.\)[0-9]\+/\1%BUILD_NUMBER%/&quot;</span> Properties\AssemblyInfo.cs
[assembly: AssemblyVersion(&quot;1.0.0.0&quot;)]
[assembly: AssemblyFileVersion(&quot;1.4.3.0325&quot;)]
[assembly: AssemblyInformationalVersion(&quot;1.4.0.0325&quot;)]
</pre>
</figure>
<p>The <code>\1</code> component of the replacement pattern states which
zero-based capture group to use, so in this example the second
group which excludes the final version part.</p>
<p>And there we have it, a sed expression to update our assembly
information.</p>
<h2 id="updating-the-build-number-instead">Updating the build number instead</h2>
<p>The above expression works very well with versions that use four
components, e.g. <code>4.0.0.0</code>. However, if you follow <a href="https://semver.org/" rel="external nofollow noopener">Semantic
Versioning</a> then you probably only use versions containing 3
components, in which case you'll want to update the 3rd part
(build) instead of the 4th (revision).</p>
<p>Although I still use 4 part versions for product versions and
for assemblies that aren't currently packaged, those that are
try to follow SemVer. For these assemblies, I set
<code>AssemblyInformationalVersion</code> to be a 3 part version, with the
last part always zero. I leave <code>AssemblyFileVersion</code> at 4 parts
with the third and fourth parts always zero.</p>
<p>The following expression can be used to update the third part of
a version - it's identical to the 4 part version, except for
dropping one set of captured digits.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
s/\(Assembly\(Informational\|File\)Version(\d34[0-9]\+\.[0-9]\+\.\)[0-9]\+/\1<span class="string">%BUILD_NUMBER%</span>/
</pre>
</figure>
<h2 id="putting-it-all-together">Putting it all together</h2>
<p>Below is an example batch script that I've been using for just
over two years at time of writing to handle updating the version
of my components. I have two versions of this file, one for when
I want to update the third part of a version, and another for
updating the fourth. I also prefer using <code>^</code> as the sed
separator rather than <code>/</code>.</p>
<p>The calls to <code>cecho</code> can be replaced with just <code>echo</code> (and also
remove the <code>{x}</code> sequences); this is a utility for printing to
the <a href="/post/colorecho-adding-colour-to-echoed-batch-text">console in colour</a>.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
@ECHO <span class="keyword">OFF</span>

<span class="keyword">SET</span> SRC=<span class="string">%1

IF &quot;%</span>~1<span class="string">&quot;==&quot;</span><span class="string">&quot; GOTO :error
IF &quot;</span><span class="string">%BUILD_NUMBER%</span><span class="string">&quot;==&quot;</span><span class="string">&quot; GOTO :notset
IF NOT EXIST &quot;</span><span class="string">%SRC%</span><span class="string">&quot; GOTO :notfound

SED -i &quot;</span>s^\(Assembly\(Informational\|File\)Version(\d34[0-9]\+\.[0-9]\+\.[0-9]\+\.\)[0-9]\+^\1<span class="string">%BUILD_NUMBER%</span>^<span class="string">&quot; &quot;</span><span class="string">%SRC%</span>&quot;

<span class="keyword">IF</span> <span class="string">%ERRORLEVEL%</span> NEQ 0 <span class="keyword">GOTO</span> :failed

<span class="keyword">GOTO</span> :eof

:notset
CECHO {0e}WARNING: BUILD_NUMBER environment variable <span class="keyword">not</span> <span class="keyword">set</span>, performing no action{#}{\n}
<span class="keyword">EXIT</span> /b 0

:failed
CECHO {0c}ERROR : Failed to process command{#}{\n}
<span class="keyword">EXIT</span> /b 1

:notfound
CECHO {0c}ERROR : Source <span class="string">&#39;%1&#39;</span> <span class="keyword">not</span> found{#}{\n}
<span class="keyword">EXIT</span> /b 1

:error
CECHO {0c}ERROR : Source <span class="keyword">not</span> specified{#}{\n}
<span class="keyword">EXIT</span> /b 1
</pre>
</figure>
<h2 id="updating-all-assemblyinfo.cs-files">Updating all AssemblyInfo.cs files</h2>
<p>Although the above batch scripts are quite handy when updating a
single file, what happens if you have multiple files to update?
I recently started applying build numbers to the versions of our
product suites and I had no intention of manually keeping track
of which files to update.</p>
<p>Modern version of Windows include the <code>forfiles.exe</code> utility
which &quot;Selects a file (or set of files) and executes a command
on that file. This is helpful for batch jobs.&quot;. And helpful it
is for numerous scenarios - as well as file based matching it
can also search by date and so another task I use it for is
clearing old temporary files.</p>
<p>In the below example, I use the <code>/S</code> flag to search
sub-directories, and the <code>/M</code> parameter to specify I want to
match <code>AssemblyInfo.cs</code>. And finally, I set the command to run
our updater batch file. This lets me update everything in a
single directory tree.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
@FORFILES /S /M AssemblyInfo.cs /C <span class="string">&quot;CMD /C CALL updateversioninfo.cmd @path&quot;</span>
</pre>
</figure>
<blockquote>
<p>In some of my products, I have a shared <code>AssemblyInfo.cs</code>,
imaginatively named <code>SharedAssemblyInfo.cs</code>. To have this
picked up by the above command, change the mask argument to be
<code>*AssemblyInfo.cs</code>.</p>
</blockquote>
<h2 id="what-about-powershell">What about PowerShell?</h2>
<p>If you wanted a vanilla option which didn't require a third
party program you could use PowerShell. As I'm slightly old
school in regards to how I set up my build files, I still mainly
use batch and so I haven't explored this option.</p>
<h2 id="what-about-visual-basic">What about Visual Basic?</h2>
<p>While I haven't programmed in Visual Basic .NET for over a
decade, everything in this article can be used just as easily
with VB projects - you'd just need to adjust the expression to
cover how you define attributes in VB.net, and of course change
<code>.cs</code> to <code>.vb</code></p>
<h2 id="what-about-visual-studio-2017-projects">What about Visual Studio 2017 projects?</h2>
<p>Some projects created with Visual Studio 2017 store the assembly
information directly in the XML project file. You could use the
above technique with these projects too, but it's not something
I've looked at as I still haven't fully switched to Visual
Studio 2017 yet, and the projects that I do use it with, I
deliberately choose to continue to have the meta data stored in
<code>AssemblyInfo.cs</code> files.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2018-03-25 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/updating-assemblyinfo-cs-version-information-via-batch-file .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comReading and writing 18-bit RGB VGA Palette (pal) files with C#urn:uuid:e4d099f7-b7c6-40cf-92c6-57a78288b1ff2017-12-27T11:07:26Z2017-12-26T11:00:43Z<p>18-bit RGB palettes are an old format used by VGA displays of
yesteryear (although interestingly <a href="https://en.wikipedia.org/wiki/List_of_monochrome_and_RGB_palettes#18-bit_RGB" rel="external nofollow noopener">Wikipedia</a> states they
are still used by many LCD monitors). These palettes use 6-bits
for each of the red, green and blue channels and usually allowed
a maximum of 256 colours from the 262,144 unique colours
available.</p>
<p>Files using this format are usually quite recognisable, having
the extension <code>pal</code> and a size of 768 bytes.</p>
<p>You can find examples of these palettes in many old games -
files I have tested during the writing of this article came from
Command and Conquer, Powermonger, Ultima 4, Stonekeep and
Hardcore 4x4. Just to mix things up though, some palettes used
24-bit colour - examples I have tested include StarTopia and (I
think) Daggerfall.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/westwood-1a.png" class="gallery" title="An example of loading a 18-bit palette from OpenRA" ><img src="https://images.cyotek.com/image/thumbnail/devblog/westwood-1a.png" alt="An example of loading a 18-bit palette from OpenRA" decoding="async" loading="lazy" /></a><figcaption>An example of loading a 18-bit palette from OpenRA</figcaption></figure>
<p>This article will describe how to read and write 18-bit palette
files.</p>
<h2 id="source-files">Source Files</h2>
<p>As this minor odyssey originally started with a user request to
add support for &quot;Westwood&quot; palettes to our software, the example
project was created using Command and Conquer palettes.</p>
<p>Three of the sample files in this project (<code>ccursor.pal</code>,
<code>jungle.pal</code> and <code>snow.pal</code>) were download from <a href="https://github.com/OpenRA/" rel="external nofollow noopener">OpenRA</a>.
<code>desert.pal</code> came from <a href="https://forums.cncnet.org/topic/7026-improved-desertmix-pal-for-ra" rel="external nofollow noopener">CnCNet</a>.</p>
<p>As I also own a couple of Red Alert games, I tested this project
on palettes extracted from game files using <a href="http://xhp.xwis.net/" rel="external nofollow noopener">XCC
Mixer</a>.</p>
<p>Finally, I also tested on palette files found in various games I
have installed as mentioned in the introduction above.</p>
<h2 id="reading-18-bit-palettes">Reading 18-bit palettes</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/westwood-1e.png" class="gallery" title="An example of loading an 18-bit palette from Command and Conquer Red Alert II" ><img src="https://images.cyotek.com/image/thumbnail/devblog/westwood-1e.png" alt="An example of loading an 18-bit palette from Command and Conquer Red Alert II" decoding="async" loading="lazy" /></a><figcaption>An example of loading an 18-bit palette from Command and Conquer Red Alert II</figcaption></figure>
<blockquote>
<p>The code I present in this article is example code and can be
optimised in various ways (for example not reading and writing
a single byte at a time), however I choose to have the sample
code fairly basic to avoid complicating the article. A more
optimised version can be found on our <a href="https://github.com/cyotek/Cyotek.Drawing.PaletteFormat.RgbTriplets18/" rel="external nofollow noopener">GitHub</a> page.</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> Color<span class="symbol">[</span><span class="symbol">]</span> _palette<span class="symbol">;</span>

<span class="keyword">public</span> <span class="keyword">void</span> Load<span class="symbol">(</span>Stream stream<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> length<span class="symbol">;</span>

 length <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span>stream<span class="symbol">.</span>Length <span class="symbol">/</span> <span class="number">3</span><span class="symbol">)</span><span class="symbol">;</span>

 _palette <span class="symbol">=</span> <span class="keyword">new</span> Color<span class="symbol">[</span>length<span class="symbol">]</span><span class="symbol">;</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> length<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> r<span class="symbol">;</span>
 <span class="keyword">int</span> g<span class="symbol">;</span>
 <span class="keyword">int</span> b<span class="symbol">;</span>

 r <span class="symbol">=</span> stream<span class="symbol">.</span>ReadByte<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">&lt;&lt;</span> <span class="number">2</span><span class="symbol">;</span>
 g <span class="symbol">=</span> stream<span class="symbol">.</span>ReadByte<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">&lt;&lt;</span> <span class="number">2</span><span class="symbol">;</span>
 b <span class="symbol">=</span> stream<span class="symbol">.</span>ReadByte<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">&lt;&lt;</span> <span class="number">2</span><span class="symbol">;</span>

 _palette<span class="symbol">[</span>i<span class="symbol">]</span> <span class="symbol">=</span> Color<span class="symbol">.</span>FromArgb<span class="symbol">(</span><span class="symbol">(</span><span class="number">255</span> <span class="symbol">&lt;&lt;</span> <span class="number">24</span><span class="symbol">)</span> <span class="symbol">|</span> <span class="symbol">(</span>r <span class="symbol">&lt;&lt;</span> <span class="number">16</span><span class="symbol">)</span> <span class="symbol">|</span> <span class="symbol">(</span>g <span class="symbol">&lt;&lt;</span> <span class="number">8</span><span class="symbol">)</span> <span class="symbol">|</span> b<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Reading the palettes is straight forward - the number of colours
present is the size of the file divided by 3. This is normally
768 bytes for a total of 256 colours.</p>
<p>Each colour is then represented by 3 bytes for the red, green
and blue channels. As each value is a single byte, there are no
endian issues to worry about.</p>
<p>For each byte read, I use bit shifting to move the bits two
positions to the left, causing the first two bits to be
discarded and the last two set to zero. This converts the value
from the 0-63 range to 0-255, which is a lot easier to work with
in most editing software. We can then combine the three channels
together to get our RGB colour.</p>
<blockquote>
<p>In the above code, I'm using the bitwise OR operator along
with shifting to combine the three channels values into a
single integer value. You could use <code>Color.FromArgb(r, g, b)</code>
but then you'd need to manually make sure the <code>r</code>, <code>g</code>, and
<code>b</code> values are between <code>0</code> and <code>255</code>. If you open a 24-bit
palette using the above code, the shifted values will be too
large and will cause <code>Color.FromArgb</code> to throw an exception.</p>
</blockquote>
<p>We can now read 18-bit palette files. (I did say it was simple!)</p>
<blockquote>
<p>To make this code load 24-bit palettes instead of 18-bit, just
remove the bit-shift (<code>&lt;&lt; 2</code>).</p>
</blockquote>
<h2 id="writing-18-bit-palettes">Writing 18-bit Palettes</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/westwood-1b.png" class="gallery" title="n example of writing an OpenRA palette, showing no differences between the two" ><img src="https://images.cyotek.com/image/thumbnail/devblog/westwood-1b.png" alt="n example of writing an OpenRA palette, showing no differences between the two" decoding="async" loading="lazy" /></a><figcaption>n example of writing an OpenRA palette, showing no differences between the two</figcaption></figure>
<p>Writing an 18-bit palette is the exact reverse of reading. We
simply loop through our colours, and write a single byte for
each of the 3 supported channels. However, remembering that the
18-bit format uses 6-bits per channel we need to convert our
0-255 range down to 0-63. This is easy enough by shifting the
bits right instead of left.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">void</span> Save<span class="symbol">(</span>Stream stream<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> _palette<span class="symbol">.</span>Length<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Color color<span class="symbol">;</span>
 <span class="keyword">byte</span> r<span class="symbol">;</span>
 <span class="keyword">byte</span> g<span class="symbol">;</span>
 <span class="keyword">byte</span> b<span class="symbol">;</span>

 color <span class="symbol">=</span> _palette<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">;</span>

 r <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span>color<span class="symbol">.</span>R <span class="symbol">&gt;&gt;</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>
 g <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span>color<span class="symbol">.</span>G <span class="symbol">&gt;&gt;</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>
 b <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span>color<span class="symbol">.</span>B <span class="symbol">&gt;&gt;</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>

 stream<span class="symbol">.</span>WriteByte<span class="symbol">(</span>r<span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteByte<span class="symbol">(</span>g<span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteByte<span class="symbol">(</span>b<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>To make this code save 24-bit palettes instead of 18-bit, just
remove the bit-shift (<code>&gt;&gt; 2</code>).</p>
</blockquote>
<p>This will perfectly save existing 18-bit palettes loaded into
the program via the code in in the previous section. But what
happens if you try and save colours that cover ranges beyond
what 18-bit supports? Given there's 16,777,216 unique colours in
a 24-bit palette, there's a lot of &quot;missing&quot; colours.
Fortunately, the values will be automatically converted to the
nearest equivalent and are so close it's quite likely you
wouldn't notice any difference.</p>
<p>The screenshots below show examples of converting 24-bit colour
palettes into 18-bit - you can see the converted results are
<em>very</em> similar. Swatches outlined in black are direct matches,
those outlined in red are the ones that don't quite fit - the
RGB values displayed on the right hand side of each mismatch
show that the difference is +/-3 at worst. It does mean that
editing the palettes with software such as our own <a href="https://www.cyotek.com/cyotek-palette-editor">Palette
Editor</a> (which will get 18-bit support
in the next version) could mean subtle shifting when converting
24-bit palettes to 18-bit but the output looks almost identical.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/westwood-1c.png" class="gallery" title="An example of converting a 24-bit palette to 18-bit and how the colours differ slightly" ><img src="https://images.cyotek.com/image/thumbnail/devblog/westwood-1c.png" alt="An example of converting a 24-bit palette to 18-bit and how the colours differ slightly" decoding="async" loading="lazy" /></a><figcaption>An example of converting a 24-bit palette to 18-bit and how the colours differ slightly</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/westwood-1d.png" class="gallery" title="Another example of converting a 24-bit palette to 18-bit" ><img src="https://images.cyotek.com/image/thumbnail/devblog/westwood-1d.png" alt="Another example of converting a 24-bit palette to 18-bit" decoding="async" loading="lazy" /></a><figcaption>Another example of converting a 24-bit palette to 18-bit</figcaption></figure><h2 id="final-thoughts">Final Thoughts</h2>
<p>This is such simple code I was hesitant about writing an article
regarding it, however in the end I decided it was worth it as I
always assumed these RGB triplet palettes were 24-bit and I have
been puzzled in the past opening what I now know to be an 18-bit
palette and wondering why it was so dark. Hopefully therefore
someone else will find this information useful too.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/westwood-1f.png" class="gallery" title="Loading an 18-bit palette from Powermonger" ><img src="https://images.cyotek.com/image/thumbnail/devblog/westwood-1f.png" alt="Loading an 18-bit palette from Powermonger" decoding="async" loading="lazy" /></a><figcaption>Loading an 18-bit palette from Powermonger</figcaption></figure>
<p>The usual sample project is available from the links below, and
a reusable library can be found on our <a href="https://github.com/cyotek/Cyotek.Drawing.PaletteFormat.RgbTriplets18/" rel="external nofollow noopener">GitHub</a> page.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-12-26 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/reading-and-writing-18-bit-rgb-vga-palette-pal-files-with-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comUpload data to blob storage with Azure Functionsurn:uuid:709864d7-109e-4500-b1cd-109b32a48fe82017-11-11T20:41:32Z2017-11-11T20:41:32Z<p>Some time ago I used a third party product which accepted data
from client applications via a HTTP WCF service and saved this
data as files on the local disk. A Windows service would then
periodically poll for new files and load the data into a SQL
Server database. This worked, as long as both the HTTP server
and the loader service were on the same computer/network. As
this wasn't suitable for my needs, the software vendor provided
me with the source code for the WCF service and I modified this
to store the data in Azure blob storage. Those blobs were then
periodically downloaded by our unassuming <a href="/post/downloading-new-and-changed-azure-storage-blobs-at-scheduled-intervals">Azure Container
Echo</a> program from where the loader service would pick it up.</p>
<p>Although this sounds somewhat convoluted, it does mean all the
parts were happily independent and could be located anywhere,
with the availability of one part having no affect on the other.
I decided that was a good pattern to use for the other ad-hoc
information I want to collect, except this time the final
destination is going to be <a href="https://ravendb.net/" rel="external nofollow noopener">RavenDB</a>.</p>
<p>However, I didn't want yet another website to maintain, nor do I
want to bolt anything else onto a creaky cyotek.com. So I
settled on using Azure Functions, where the only thing I need to
worry about the code required to do the data processing, and
excluding initial setup (custom domains, SSL, etc) everything
else is handled without me having to lift a finger.</p>
<h2 id="about-azure-functions">About Azure Functions</h2>
<blockquote>
<p>Azure Functions is a serverless compute service that enables
you to run code on-demand without having to explicitly
provision or manage infrastructure. Use Azure Functions to run
a script or piece of code in response to a variety of events.
(source <a href="https://docs.microsoft.com/en-us/azure/azure-functions/" rel="external nofollow noopener">Microsoft</a>)</p>
</blockquote>
<p>I started writing an overview of functions and how to create
them but then the post was in danger of turning what was
supposed to be focused into a sprawling mass. I'm therefore
going to assume the reader has familiarity with Azure functions,
their creation and basic use.</p>
<h2 id="getting-started">Getting Started</h2>
<p>To get started, the article is making the assumption that you
have created a <strong>HTTP Trigger</strong> function using the <strong>C#</strong>
language. I've replaced the default code with the following
placeholder.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> System<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Net<span class="symbol">;</span>

<span class="keyword">public</span> <span class="keyword">static</span> async Task<span class="symbol">&lt;</span>HttpResponseMessage<span class="symbol">&gt;</span> Run<span class="symbol">(</span>HttpRequestMessage req<span class="symbol">,</span> TraceWriter log<span class="symbol">)</span>
<span class="symbol">{</span>
 HttpStatusCode result<span class="symbol">;</span>

 result <span class="symbol">=</span> HttpStatusCode<span class="symbol">.</span>BadRequest<span class="symbol">;</span>

 <span class="keyword">return</span> req<span class="symbol">.</span>CreateResponse<span class="symbol">(</span>result<span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>This will reject all requests with a 400 status code.</p>
<h2 id="checking-the-content-type">Checking the content type</h2>
<p>As the only data type I'm going to work with is JSON, it makes a
little bit of sense to check the content type and reject the
request if it doesn't match.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">string</span> contentType<span class="symbol">;</span>

contentType <span class="symbol">=</span> req<span class="symbol">.</span>Content<span class="symbol">.</span>Headers<span class="symbol">?.</span>ContentType<span class="symbol">?.</span>MediaType<span class="symbol">;</span>

<span class="keyword">if</span><span class="symbol">(</span>contentType <span class="symbol">==</span> <span class="string">&quot;application/json&quot;</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// we can continue</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>The Test tool that is part of the function editor seems to
automatically include a JSON content type header if one hasn't
been explicitly defined</p>
</blockquote>
<h2 id="getting-the-body">Getting the body</h2>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">string</span> body<span class="symbol">;</span>

body <span class="symbol">=</span> await req<span class="symbol">.</span>Content<span class="symbol">.</span>ReadAsStringAsync<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">if</span><span class="symbol">(</span><span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>body<span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// we can continue</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="additional-considerations">Additional considerations</h2>
<p>Although I'm not demonstrating it in this example, there are
other checks you may wish to perform. For example, in my
versions of this function I check for the presence of a
non-standard version header. If it's not set, or isn't a value
I'm expecting, I perform no further work on the request. This
should allow me to use the same URI for different versions of
the data if I later choose to expand them.</p>
<p>You could also try validating that the body is actually a block
of valid JSON in the format you're expecting in case a badly
behaved application is sending corrupt data (or someone randomly
hits the endpoints if they are open for anonymous access).</p>
<p>Although I suspect it was more to do with the fact that HTTPS
wasn't a given rather than trying to filter out bad data, the
third party software I mentioned at the start of the article
encrypted all the information before sending it to the WCF
service, which then had to be decrypted before putting it into
blob storage.</p>
<h2 id="referencing-the-azure-libraries">Referencing the Azure libraries</h2>
<p>The default function only has access to standard framework
assemblies. However, the C# code you write is not entirely C# -
it's a scripting variant. And one of the features this variant
supports is the <code>#r</code> directive for referencing external
assemblies.</p>
<p>Adding the following line to the top of the script will add a
reference to the library we need for working with blob storage.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
#r <span class="string">&quot;Microsoft.WindowsAzure.Storage&quot;</span>
</pre>
</figure>
<h2 id="connecting-to-our-storage-account">Connecting to our storage account</h2>
<p>To connect to Azure storage we need the connection string of our
storage account. We could hard code the entire string, or just
bits of it. (put the pitchforks down, there's a follow up!)</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> Microsoft<span class="symbol">.</span>WindowsAzure<span class="symbol">.</span>Storage<span class="symbol">;</span>

<span class="keyword">string</span> accessKey<span class="symbol">;</span>
<span class="keyword">string</span> accountName<span class="symbol">;</span>
<span class="keyword">string</span> connectionString<span class="symbol">;</span>
CloudStorageAccount storageAccount<span class="symbol">;</span>

accessKey <span class="symbol">=</span> <span class="string">&quot;&lt;ACCESSKEY&gt;&quot;</span><span class="symbol">;</span>
accountName <span class="symbol">=</span> <span class="string">&quot;&lt;ACCOUNTNAME&gt;&quot;</span><span class="symbol">;</span>
connectionString <span class="symbol">=</span> <span class="string">&quot;DefaultEndpointsProtocol=https;AccountName=&quot;</span> <span class="symbol">+</span> accountName <span class="symbol">+</span> <span class="string">&quot;;AccountKey=&quot;</span> <span class="symbol">+</span> accessKey <span class="symbol">+</span> <span class="string">&quot;;EndpointSuffix=core.windows.net&quot;</span><span class="symbol">;</span>
storageAccount <span class="symbol">=</span> CloudStorageAccount<span class="symbol">.</span>Parse<span class="symbol">(</span>connectionString<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>Simple enough, except for hard coding the account details as
this means you need to edit the function if they change, or
worse edit it in multiple places if you have similar functions.</p>
<h2 id="connecting-to-our-storage-account-redux">Connecting to our storage account, redux</h2>
<p>The Function App that you have created actually seems to be a
disguised ASP.NET website and provides access to many of the
things you would define in <code>web.config</code>, including application
settings. You can access these by clicking <strong>Application
settings</strong> from the overview page of the function app.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/azure-function-1a.png" class="gallery" title="Application settings page for a Function App" ><img src="https://images.cyotek.com/image/thumbnail/devblog/azure-function-1a.png" alt="Application settings page for a Function App" decoding="async" loading="lazy" /></a><figcaption>Application settings page for a Function App</figcaption></figure>
<p>In the <strong>Application settings</strong> group, click <strong>Add new setting</strong>
then fill in the row. I'll use this feature to define the access
key and storage account name. As the Azure platform injects its
own settings in here as well, I opted to prefix mine to avoid
any potential clashes.</p>
<p>Remember to hit the Save button at the top of the page!</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/azure-function-1b.png" class="gallery" title="Configuring additional app settings" ><img src="https://images.cyotek.com/image/thumbnail/devblog/azure-function-1b.png" alt="Configuring additional app settings" decoding="async" loading="lazy" /></a><figcaption>Configuring additional app settings</figcaption></figure>
<p>Now we can go back and change our function to use the new
settings instead</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> System<span class="symbol">.</span>Configuration<span class="symbol">;</span>
<span class="keyword">using</span> Microsoft<span class="symbol">.</span>WindowsAzure<span class="symbol">.</span>Storage<span class="symbol">;</span>

<span class="keyword">string</span> accessKey<span class="symbol">;</span>
<span class="keyword">string</span> accountName<span class="symbol">;</span>
<span class="keyword">string</span> connectionString<span class="symbol">;</span>
CloudStorageAccount storageAccount<span class="symbol">;</span>

accessKey <span class="symbol">=</span> ConfigurationManager<span class="symbol">.</span>AppSettings<span class="symbol">[</span><span class="string">&quot;CyotekStorageAccessKey&quot;</span><span class="symbol">]</span><span class="symbol">;</span>
accountName <span class="symbol">=</span> ConfigurationManager<span class="symbol">.</span>AppSettings<span class="symbol">[</span><span class="string">&quot;CyotekStorageAccountName&quot;</span><span class="symbol">]</span><span class="symbol">;</span>
connectionString <span class="symbol">=</span> <span class="string">&quot;DefaultEndpointsProtocol=https;AccountName=&quot;</span> <span class="symbol">+</span> accountName <span class="symbol">+</span> <span class="string">&quot;;AccountKey=&quot;</span> <span class="symbol">+</span> accessKey <span class="symbol">+</span> <span class="string">&quot;;EndpointSuffix=core.windows.net&quot;</span><span class="symbol">;</span>
storageAccount <span class="symbol">=</span> CloudStorageAccount<span class="symbol">.</span>Parse<span class="symbol">(</span>connectionString<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="uploading-data-into-blob-storage">Uploading data into blob storage</h2>
<p>With account information in hand, it's time to work with the
blob storage. First we need to get the container that we want to
put our blob into - you can think of this as an equivalent of a
directory on your local file system, with the blob a file.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> Microsoft<span class="symbol">.</span>Azure<span class="symbol">;</span>
<span class="keyword">using</span> Microsoft<span class="symbol">.</span>WindowsAzure<span class="symbol">.</span>Storage<span class="symbol">;</span>
<span class="keyword">using</span> Microsoft<span class="symbol">.</span>WindowsAzure<span class="symbol">.</span>Storage<span class="symbol">.</span>Blob<span class="symbol">;</span>

CloudBlobClient client<span class="symbol">;</span>
CloudBlobContainer container<span class="symbol">;</span>

client <span class="symbol">=</span> storageAccount<span class="symbol">.</span>CreateCloudBlobClient<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

container <span class="symbol">=</span> client<span class="symbol">.</span>GetContainerReference<span class="symbol">(</span><span class="string">&quot;&lt;CONTAINERNAME&gt;&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>Unfortunately it doesn't seem to be possible to store
application settings / secrets on a per function basis. However,
as it's unlikely I'd have multiple functions writing to the same
container, I'm happy enough to hard code the container name.</p>
<p>Next, try and create the container if it doesn't exist.
Alternatively, you could create the container up front and not
bother with this code at all.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
await container<span class="symbol">.</span>CreateIfNotExistsAsync<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>With container housekeeping performed, we can now create our
blob (or file) in the container.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
CloudBlockBlob blob<span class="symbol">;</span>
<span class="keyword">string</span> name<span class="symbol">;</span>

name <span class="symbol">=</span> Guid<span class="symbol">.</span>NewGuid<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">.</span>ToString<span class="symbol">(</span><span class="string">&quot;n&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

blob <span class="symbol">=</span> container<span class="symbol">.</span>GetBlockBlobReference<span class="symbol">(</span>name<span class="symbol">)</span><span class="symbol">;</span>
blob<span class="symbol">.</span>Properties<span class="symbol">.</span>ContentType <span class="symbol">=</span> <span class="string">&quot;application/json&quot;</span><span class="symbol">;</span>
</pre>
</figure>
<p>I auto generate a filename using a GUID as I don't want it to be
possible for the request to allow a filename specified, and just
like with a normal file system, blob names need to be unique.</p>
<p>As well as basic properties such as <code>ContentType</code>,
<code>ContentEncoding</code>, <code>ContentLanguage</code> that you can set yourself,
you can also define and create your own meta data key value
pairs. In this case I'm only setting the content type.</p>
<p>With our blob reference ready, we can now upload our data.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> <span class="symbol">(</span>Stream stream <span class="symbol">=</span> <span class="keyword">new</span> MemoryStream<span class="symbol">(</span>Encoding<span class="symbol">.</span>UTF<span class="number">8</span><span class="symbol">.</span>GetBytes<span class="symbol">(</span>body<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 await blob<span class="symbol">.</span>UploadFromStreamAsync<span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>It would have been slightly better to just use the request's
input stream directly, instead of converting the body content
I'd previously read back into a stream, but then I would lose
the ability to perform any further validation.</p>
<p>With the above code I now have a fully functional function that
I can post data to and have it placed into blob storage.
Happily, the function editor even includes a small testing tool
so you can test it directly from your browser window.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/azure-function-1d.png" class="gallery" title="Using the testing tool and output log to test functions" ><img src="https://images.cyotek.com/image/thumbnail/devblog/azure-function-1d.png" alt="Using the testing tool and output log to test functions" decoding="async" loading="lazy" /></a><figcaption>Using the testing tool and output log to test functions</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/azure-function-1e.png" class="gallery" title="Viewing the contents of blob storage and verifying the data was uploaded" ><img src="https://images.cyotek.com/image/thumbnail/devblog/azure-function-1e.png" alt="Viewing the contents of blob storage and verifying the data was uploaded" decoding="async" loading="lazy" /></a><figcaption>Viewing the contents of blob storage and verifying the data was uploaded</figcaption></figure><h2 id="closing-thoughts">Closing thoughts</h2>
<p>By default, a function will respond to all standard HTTP verbs.
If your function is used to upload data only, then you'll
probably want to disable all verbs bar <code>POST</code>. You can do this
by selecting the <strong>Integrate</strong> option listed below the function
name in the sidebar.</p>
<p>You may wish to change the authorisation level from the default
Function (which requires an access key) to Anonymous.</p>
<p>Finally, if you bind your own custom domain to the function then
you may also wish to change the default route to a custom one -
that way you may find it easier to migrate from functions to a
self hosted solution in future, or make it easier to manage
multiple functions in the same Function App.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/azure-function-1c.png" class="gallery" title="Reconfiguring the verbs, route and authorisation for a function" ><img src="https://images.cyotek.com/image/thumbnail/devblog/azure-function-1c.png" alt="Reconfiguring the verbs, route and authorisation for a function" decoding="async" loading="lazy" /></a><figcaption>Reconfiguring the verbs, route and authorisation for a function</figcaption></figure><h2 id="full-function">Full function</h2>
<p>This is the complete final version of the function. I hope you
find this useful as a starting point for your own adventures in
Azure!</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
#r <span class="string">&quot;Microsoft.WindowsAzure.Storage&quot;</span>

<span class="keyword">using</span> System<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Configuration<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Net<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Text<span class="symbol">;</span>
<span class="keyword">using</span> Microsoft<span class="symbol">.</span>Azure<span class="symbol">;</span>
<span class="keyword">using</span> Microsoft<span class="symbol">.</span>WindowsAzure<span class="symbol">.</span>Storage<span class="symbol">;</span>
<span class="keyword">using</span> Microsoft<span class="symbol">.</span>WindowsAzure<span class="symbol">.</span>Storage<span class="symbol">.</span>Blob<span class="symbol">;</span>

<span class="keyword">public</span> <span class="keyword">static</span> async Task<span class="symbol">&lt;</span>HttpResponseMessage<span class="symbol">&gt;</span> Run<span class="symbol">(</span>HttpRequestMessage req<span class="symbol">,</span> TraceWriter log<span class="symbol">)</span>
<span class="symbol">{</span>
 HttpStatusCode result<span class="symbol">;</span>
 <span class="keyword">string</span> contentType<span class="symbol">;</span>

 result <span class="symbol">=</span> HttpStatusCode<span class="symbol">.</span>BadRequest<span class="symbol">;</span>

 contentType <span class="symbol">=</span> req<span class="symbol">.</span>Content<span class="symbol">.</span>Headers<span class="symbol">?.</span>ContentType<span class="symbol">?.</span>MediaType<span class="symbol">;</span>

 <span class="keyword">if</span><span class="symbol">(</span>contentType <span class="symbol">==</span> <span class="string">&quot;application/json&quot;</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> body<span class="symbol">;</span>

 body <span class="symbol">=</span> await req<span class="symbol">.</span>Content<span class="symbol">.</span>ReadAsStringAsync<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span><span class="symbol">(</span><span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>body<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> name<span class="symbol">;</span>

 name <span class="symbol">=</span> Guid<span class="symbol">.</span>NewGuid<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">.</span>ToString<span class="symbol">(</span><span class="string">&quot;n&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 await CreateBlob<span class="symbol">(</span>name <span class="symbol">+</span> <span class="string">&quot;.json&quot;</span><span class="symbol">,</span> body<span class="symbol">,</span> log<span class="symbol">)</span><span class="symbol">;</span>

 result <span class="symbol">=</span> HttpStatusCode<span class="symbol">.</span>OK<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> req<span class="symbol">.</span>CreateResponse<span class="symbol">(</span>result<span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> async <span class="keyword">static</span> Task CreateBlob<span class="symbol">(</span><span class="keyword">string</span> name<span class="symbol">,</span> <span class="keyword">string</span> data<span class="symbol">,</span> TraceWriter log<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">string</span> accessKey<span class="symbol">;</span>
 <span class="keyword">string</span> accountName<span class="symbol">;</span>
 <span class="keyword">string</span> connectionString<span class="symbol">;</span>
 CloudStorageAccount storageAccount<span class="symbol">;</span>
 CloudBlobClient client<span class="symbol">;</span>
 CloudBlobContainer container<span class="symbol">;</span>
 CloudBlockBlob blob<span class="symbol">;</span>

 accessKey <span class="symbol">=</span> ConfigurationManager<span class="symbol">.</span>AppSettings<span class="symbol">[</span><span class="string">&quot;CyotekStorageAccessKey&quot;</span><span class="symbol">]</span><span class="symbol">;</span>
 accountName <span class="symbol">=</span> ConfigurationManager<span class="symbol">.</span>AppSettings<span class="symbol">[</span><span class="string">&quot;CyotekStorageAccountName&quot;</span><span class="symbol">]</span><span class="symbol">;</span>
 connectionString <span class="symbol">=</span> <span class="string">&quot;DefaultEndpointsProtocol=https;AccountName=&quot;</span> <span class="symbol">+</span> accountName <span class="symbol">+</span> <span class="string">&quot;;AccountKey=&quot;</span> <span class="symbol">+</span> accessKey <span class="symbol">+</span> <span class="string">&quot;;EndpointSuffix=core.windows.net&quot;</span><span class="symbol">;</span>
 storageAccount <span class="symbol">=</span> CloudStorageAccount<span class="symbol">.</span>Parse<span class="symbol">(</span>connectionString<span class="symbol">)</span><span class="symbol">;</span>

 client <span class="symbol">=</span> storageAccount<span class="symbol">.</span>CreateCloudBlobClient<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 container <span class="symbol">=</span> client<span class="symbol">.</span>GetContainerReference<span class="symbol">(</span><span class="string">&quot;testing123&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 await container<span class="symbol">.</span>CreateIfNotExistsAsync<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 blob <span class="symbol">=</span> container<span class="symbol">.</span>GetBlockBlobReference<span class="symbol">(</span>name<span class="symbol">)</span><span class="symbol">;</span>
 blob<span class="symbol">.</span>Properties<span class="symbol">.</span>ContentType <span class="symbol">=</span> <span class="string">&quot;application/json&quot;</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Stream stream <span class="symbol">=</span> <span class="keyword">new</span> MemoryStream<span class="symbol">(</span>Encoding<span class="symbol">.</span>UTF<span class="number">8</span><span class="symbol">.</span>GetBytes<span class="symbol">(</span>data<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 await blob<span class="symbol">.</span>UploadFromStreamAsync<span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-11-11 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/upload-data-to-blob-storage-with-azure-functions .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comArranging items radially around a central point using C#urn:uuid:86f1fb70-2193-4df7-8ff4-79ee17d814c62020-04-06T17:00:09Z2017-11-05T10:33:39Z<p>Recently I was looking for a way of display hierarchical
information in a more compact form than the previous
horizontal/vertical trees I was using. One of the concepts I
looked into during this was the idea of arranging children
radially around a central node.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/radial-tree-1a.png" class="gallery" title="The demonstration program with a few child nodes" ><img src="https://images.cyotek.com/image/thumbnail/devblog/radial-tree-1a.png" alt="The demonstration program with a few child nodes" decoding="async" loading="lazy" /></a><figcaption>The demonstration program with a few child nodes</figcaption></figure>
<p>This post discusses the sample project I created to explore the
first part of this concept.</p>
<blockquote>
<p>Caveat emptor. This post is out of my usual comfort zone as
its dealing with trigonometry. Errors and misunderstandings
may abound.</p>
</blockquote>
<h2 id="calculating-the-angle">Calculating the angle</h2>
<p>The first thing we need to do is calculate the angle between one
node and the next. The following formula will calculate the
number of <del>degrees</del> radians in the angle.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="comment">// count is the number of children we&#39;ll be placing around the center</span>
<span class="keyword">double</span> angle <span class="symbol">=</span> <span class="number">360.0</span> <span class="symbol">/</span> count <span class="symbol">*</span> Math<span class="symbol">.</span>PI <span class="symbol">/</span> <span class="number">180.0</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="calculating-a-default-distance">Calculating a default distance</h2>
<p>All the nodes in this diagram are going to be rendered as
circles. This makes some of the layout work easier due to there
being no corners, as the shapes can be closer to together
without overlapping. To start with, I'll simply try and place
the new nodes right next to the centre node.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="comment">// technically as I&#39;m using circles the Width and Height should always be the same, but this may change in future</span>
distance <span class="symbol">=</span> Math<span class="symbol">.</span>Max<span class="symbol">(</span>node<span class="symbol">.</span>Width<span class="symbol">,</span> node<span class="symbol">.</span>Height<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="positioning-each-node">Positioning each node</h2>
<p>Once we have the angle, we loop through each child node and
using the angle and distance values we can calculate the centre
point of the child using the sine and cosine mathematical
functions. Once we've got the centre, we offset it by half the
nodes width and height to get the upper left corner.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> nodes<span class="symbol">.</span>Count<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
<span class="symbol">{</span>
 DiagramNode child<span class="symbol">;</span>
 <span class="keyword">int</span> x<span class="symbol">;</span>
 <span class="keyword">int</span> y<span class="symbol">;</span>

 child <span class="symbol">=</span> nodes<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">;</span>

 <span class="comment">// calculate the center of the child node offset from</span>
 <span class="comment">// the central node</span>
 x <span class="symbol">=</span> cx <span class="symbol">+</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span>Math<span class="symbol">.</span>Cos<span class="symbol">(</span>angle <span class="symbol">*</span> i<span class="symbol">)</span> <span class="symbol">*</span> distance<span class="symbol">)</span><span class="symbol">;</span>
 y <span class="symbol">=</span> cy <span class="symbol">+</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span>Math<span class="symbol">.</span>Sin<span class="symbol">(</span>angle <span class="symbol">*</span> i<span class="symbol">)</span> <span class="symbol">*</span> distance<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// adjust the final location to be the top left instead of the center</span>
 child<span class="symbol">.</span>Location <span class="symbol">=</span> <span class="keyword">new</span> Point<span class="symbol">(</span>x <span class="symbol">-</span> <span class="symbol">(</span>child<span class="symbol">.</span>Width <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">,</span> y <span class="symbol">-</span> <span class="symbol">(</span>child<span class="symbol">.</span>Height <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/radial-tree-1b.png" class="gallery" title="Overlapping nodes make for pretty patterns but otherwise aren't much use" ><img src="https://images.cyotek.com/image/thumbnail/devblog/radial-tree-1b.png" alt="Overlapping nodes make for pretty patterns but otherwise aren't much use" decoding="async" loading="lazy" /></a><figcaption>Overlapping nodes make for pretty patterns but otherwise aren't much use</figcaption></figure>
<p>With this code in place, our diagram is taking shape - at least
until we add enough nodes that they start to overlap with each
other. If this happens, we need to detect if the nodes overlap
using Euclidean geometry.</p>
<h2 id="testing-if-two-circles-overlap">Testing if two circles overlap</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/radial-tree-1d.png" class="gallery" title="Without corners, nodes can overlap each other without having an impact" ><img src="https://images.cyotek.com/image/thumbnail/devblog/radial-tree-1d.png" alt="Without corners, nodes can overlap each other without having an impact" decoding="async" loading="lazy" /></a><figcaption>Without corners, nodes can overlap each other without having an impact</figcaption></figure>
<p>To test if circles overlap, we calculate the distance between
the centre of the first circle and the second. If the distance
is less than the radius of the two circles, then they intersect.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">bool</span> DoCirclesInsersect<span class="symbol">(</span><span class="keyword">int</span> cx<span class="number">1</span><span class="symbol">,</span> <span class="keyword">int</span> cy<span class="number">1</span><span class="symbol">,</span> <span class="keyword">int</span> r<span class="number">1</span><span class="symbol">,</span> <span class="keyword">int</span> cx<span class="number">2</span><span class="symbol">,</span> <span class="keyword">int</span> cy<span class="number">2</span><span class="symbol">,</span> <span class="keyword">int</span> r<span class="number">2</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> dx<span class="symbol">;</span>
 <span class="keyword">int</span> dy<span class="symbol">;</span>
 <span class="keyword">double</span> distance<span class="symbol">;</span>

 <span class="comment">// Find the distance between the centers</span>
 dx <span class="symbol">=</span> cx<span class="number">1</span> <span class="symbol">-</span> cx<span class="number">2</span><span class="symbol">;</span>
 dy <span class="symbol">=</span> cy<span class="number">1</span> <span class="symbol">-</span> cy<span class="number">2</span><span class="symbol">;</span>
 distance <span class="symbol">=</span> Math<span class="symbol">.</span>Sqrt<span class="symbol">(</span>dx <span class="symbol">*</span> dx <span class="symbol">+</span> dy <span class="symbol">*</span> dy<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> distance <span class="symbol">&lt;</span> r<span class="number">1</span> <span class="symbol">+</span> r<span class="number">2</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>This is almost exactly the same method used in my previous
post of <a href="/post/finding-nearest-colors-using-euclidean-distance">finding nearest colours</a>.</p>
</blockquote>
<p>For a much more detailed version of this function which can also
determine at which points on the edges of the circles intersect
see this <a href="http://csharphelper.com/blog/2014/09/determine-where-two-circles-intersect-in-c/" rel="external nofollow noopener">C# Helper post</a>. It also describes the math,
something I'm not even going to try and do.</p>
<h2 id="brute-forcing-the-intersection">Brute forcing the intersection</h2>
<figure class="screenshot" ><a href="https://www.cyotek.com/files/articleimages/radial-tree-1c.png" class="gallery" title="Increasing the distance between the centre node and its children mean they can fit without overlapping" ><img src="https://www.cyotek.com/files/articleimages/radial-tree-1c-thumbnail.png" alt="Increasing the distance between the centre node and its children mean they can fit without overlapping" decoding="async" loading="lazy" /></a><figcaption>Increasing the distance between the centre node and its children mean they can fit without overlapping</figcaption></figure>
<p>I'm sure there's probably a much better way of doing this, but
as I don't know of one I resorted to brute forcing - after
positioning the children, I check them to see if there's any
overlap. If there are intersections, I increment the distance,
re-position the nodes and test again. To avoid nodes being
placed excessively far from the centre node, once the distance
is above a defined maximum I abort the testing and use the
maximum value, regardless of overlap.</p>
<blockquote>
<p>The below code only considers the intersection of one child
node with an adjacent child node. However, if the central node
is smaller than the children, then it is possible for the
child nodes to overlap the parent. The full demonstration
program tests for intersection with both the parent node and
the next child node to avoid this.</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">bool</span> TestNodes<span class="symbol">(</span>List<span class="symbol">&lt;</span>DiagramNode<span class="symbol">&gt;</span> nodes<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>
 <span class="keyword">int</span> count<span class="symbol">;</span>

 result <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 count <span class="symbol">=</span> nodes<span class="symbol">.</span>Count<span class="symbol">;</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> count<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> cx<span class="number">1</span><span class="symbol">;</span>
 <span class="keyword">int</span> cy<span class="number">1</span><span class="symbol">;</span>
 <span class="keyword">int</span> cx<span class="number">2</span><span class="symbol">;</span>
 <span class="keyword">int</span> cy<span class="number">2</span><span class="symbol">;</span>
 <span class="keyword">int</span> r<span class="number">1</span><span class="symbol">;</span>
 <span class="keyword">int</span> r<span class="number">2</span><span class="symbol">;</span>
 <span class="keyword">int</span> next<span class="symbol">;</span>
 Point c<span class="number">1</span><span class="symbol">;</span>
 Point c<span class="number">2</span><span class="symbol">;</span>

 next <span class="symbol">=</span> i <span class="symbol">&lt;</span> count <span class="symbol">-</span> <span class="number">1</span> <span class="symbol">?</span> i <span class="symbol">+</span> <span class="number">1</span> <span class="symbol">:</span> <span class="number">0</span><span class="symbol">;</span>

 c<span class="number">1</span> <span class="symbol">=</span> nodes<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">.</span>Center<span class="symbol">;</span>
 c<span class="number">2</span> <span class="symbol">=</span> nodes<span class="symbol">[</span>next<span class="symbol">]</span><span class="symbol">.</span>Center<span class="symbol">;</span>

 cx<span class="number">1</span> <span class="symbol">=</span> c<span class="number">1</span><span class="symbol">.</span>X<span class="symbol">;</span>
 cy<span class="number">1</span> <span class="symbol">=</span> c<span class="number">1</span><span class="symbol">.</span>Y<span class="symbol">;</span>
 r<span class="number">1</span> <span class="symbol">=</span> nodes<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">.</span>Width <span class="symbol">/</span> <span class="number">2</span><span class="symbol">;</span>

 cx<span class="number">2</span> <span class="symbol">=</span> c<span class="number">2</span><span class="symbol">.</span>X<span class="symbol">;</span>
 cy<span class="number">2</span> <span class="symbol">=</span> c<span class="number">2</span><span class="symbol">.</span>Y<span class="symbol">;</span>
 r<span class="number">2</span> <span class="symbol">=</span> nodes<span class="symbol">[</span>next<span class="symbol">]</span><span class="symbol">.</span>Width <span class="symbol">/</span> <span class="number">2</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>DoCirclesInsersect<span class="symbol">(</span>cx<span class="number">1</span><span class="symbol">,</span> cy<span class="number">1</span><span class="symbol">,</span> r<span class="number">1</span><span class="symbol">,</span> cx<span class="number">2</span><span class="symbol">,</span> cy<span class="number">2</span><span class="symbol">,</span> r<span class="number">2</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> PositionDiagram<span class="symbol">(</span>DiagramNode node<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> count<span class="symbol">;</span>
 <span class="keyword">double</span> angle<span class="symbol">;</span>
 <span class="keyword">int</span> distance<span class="symbol">;</span>
 <span class="keyword">int</span> cx<span class="symbol">;</span>
 <span class="keyword">int</span> cy<span class="symbol">;</span>
 List<span class="symbol">&lt;</span>DiagramNode<span class="symbol">&gt;</span> childNodes<span class="symbol">;</span>
 Point center<span class="symbol">;</span>

 childNodes <span class="symbol">=</span> node<span class="symbol">.</span>ChildNodes<span class="symbol">;</span>

 count <span class="symbol">=</span> childNodes<span class="symbol">.</span>Count<span class="symbol">;</span>

 angle <span class="symbol">=</span> <span class="number">360.0</span> <span class="symbol">/</span> count <span class="symbol">*</span> Math<span class="symbol">.</span>PI <span class="symbol">/</span> <span class="number">180.0</span><span class="symbol">;</span>

 <span class="comment">// if we were using squares we&#39;d need some extra padding</span>
 <span class="comment">// but as I&#39;m using ellipsis we can use use the largest axis</span>
 distance <span class="symbol">=</span> Math<span class="symbol">.</span>Max<span class="symbol">(</span>node<span class="symbol">.</span>Width<span class="symbol">,</span> node<span class="symbol">.</span>Height<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// need to use the centerpoint of our node</span>
 <span class="comment">// to ensure all other nodes are an equal distance away</span>
 center <span class="symbol">=</span> node<span class="symbol">.</span>Center<span class="symbol">;</span>
 cx <span class="symbol">=</span> center<span class="symbol">.</span>X<span class="symbol">;</span>
 cy <span class="symbol">=</span> center<span class="symbol">.</span>Y<span class="symbol">;</span>

 <span class="comment">// position the children</span>
 <span class="keyword">this</span><span class="symbol">.</span>ArrangeNodes<span class="symbol">(</span>childNodes<span class="symbol">,</span> cx<span class="symbol">,</span> cy<span class="symbol">,</span> angle<span class="symbol">,</span> distance<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// if there is more than one child node, check to see if any intersect with each </span>
 <span class="comment">// other. if they do, and the distance is within a given maximum, increase the distance</span>
 <span class="comment">// and try again. I&#39;ve no doubt there&#39;s a much better way of doing this</span>
 <span class="comment">// than brute forcing!</span>
 <span class="keyword">if</span> <span class="symbol">(</span>count <span class="symbol">&gt;</span> <span class="number">1</span> <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>TestNodes<span class="symbol">(</span>childNodes<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>BruteForceNodeLayout<span class="symbol">(</span>childNodes<span class="symbol">,</span> angle<span class="symbol">,</span> cx<span class="symbol">,</span> cy<span class="symbol">,</span> distance<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> BruteForceNodeLayout<span class="symbol">(</span>List<span class="symbol">&lt;</span>DiagramNode<span class="symbol">&gt;</span> childNodes<span class="symbol">,</span> <span class="keyword">double</span> angle<span class="symbol">,</span> <span class="keyword">int</span> cx<span class="symbol">,</span> <span class="keyword">int</span> cy<span class="symbol">,</span> <span class="keyword">int</span> distance<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">bool</span> success<span class="symbol">;</span>

 <span class="keyword">do</span>
 <span class="symbol">{</span>
 <span class="comment">// increment the distance</span>
 distance <span class="symbol">+=</span> childNodes<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span><span class="symbol">.</span>Width <span class="symbol">/</span> <span class="number">4</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>distance <span class="symbol">&gt;</span> _maximumDistance<span class="symbol">)</span>
 <span class="symbol">{</span>
 distance <span class="symbol">=</span> _maximumDistance<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="comment">// first arrange all the nodes around the central node with a minimum distance</span>
 <span class="keyword">this</span><span class="symbol">.</span>ArrangeNodes<span class="symbol">(</span>childNodes<span class="symbol">,</span> cx<span class="symbol">,</span> cy<span class="symbol">,</span> angle<span class="symbol">,</span> distance<span class="symbol">)</span><span class="symbol">;</span>

 success <span class="symbol">=</span> distance <span class="symbol">&gt;=</span> _maximumDistance <span class="symbol">||</span> <span class="keyword">this</span><span class="symbol">.</span>TestNodes<span class="symbol">(</span>childNodes<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span> <span class="keyword">while</span> <span class="symbol">(</span><span class="symbol">!</span>success<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>Once the child nodes have moved sufficiently far enough away
from the centre you could try staggering the nodes to make
better use of the free space; this may allow for a closer
grouping.</p>
</blockquote>
<p>Although the demonstration program doesn't show this, this code
works perfectly well if the child nodes are of varying sizes -
it will try and position according to the largest child it
finds.</p>
<h2 id="initial-starting-position">Initial starting position</h2>
<p>If you consider a clock face, the painting of the first node in
this example always occurs at 3 o'clock. This is actually
perfect for my needs, but if you wanted it to start from
somewhere else (for example 12 o'clock), you'd need to adapt the
code in <code>ArrangeNodes</code>.</p>
<h2 id="final-thoughts">Final thoughts</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/radial-tree-1.gif" class="gallery" title="An animated example of the demonstration program" ><img src="https://images.cyotek.com/image/thumbnail/devblog/radial-tree-1.gif" alt="An animated example of the demonstration program" decoding="async" loading="lazy" /></a><figcaption>An animated example of the demonstration program</figcaption></figure>
<p>The technique in this article can be useful in other
circumstances, for example I first used code similar to this to
create a 12 node colour wheel as part of another concept
program. But the general principle could be used for other
things, such as dials, gauges and clock faces.</p>
<p>Due to the brute forcing for positioning, this code is nowhere
near as optimal as I'd otherwise like it - if anyone has ideas
for solving this I'd love to <a href="https://cyotek.com/contact">hear them</a>!</p>
<p>I cut out a lot of the code from this article and just focused
on the core functionality, a fully functional sample can be
downloaded from the link below.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-11-05 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/arranging-items-radially-around-a-central-point-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comPainting animated images using C#urn:uuid:208900b0-fcd0-4f0d-81ba-927e384cecdf2017-10-31T06:29:29Z2017-10-28T12:10:49Z<p>While reviewing <a href="/post/book-review-the-csharp-helper-top-100">The C# Top 100</a> it occurred to me that in my
own code base I have many bits of code which may make useful
blog posts, and that shouldn't take as long to write as the ones
I usually create. Plus, I've a fair amount of source code for
extending built in controls in various ways, creating new
controls from scratch and other useful library code - I need to
explore ways of decoupling some of that and releasing it for
anyone to use.</p>
<p>To get started with this idea is a simple article on painting
animated images using C#. If you assign an animated GIF file to
the <code>Image</code> property of a <code>Control</code>, the .NET Framework will
take care of animating the image for you. However, it only
provides this automatically for the <code>Image</code> property and not for
other properties such as <code>BackgroundImage</code>, or any custom image
properties you add to your own components.</p>
<p>Fortunately the framework doesn't secret the animation
functionality away and provides the static <code>ImageAnimator</code> class
(located in <code>System.Drawing</code>) to handle the bulk of the work.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/draw-animated-image-1c.gif" class="gallery" title="The demonstration program with an animated image" ><img src="https://images.cyotek.com/image/thumbnail/devblog/draw-animated-image-1c.gif" alt="The demonstration program with an animated image" decoding="async" loading="lazy" /></a><figcaption>The demonstration program with an animated image</figcaption></figure>
<p><em>(Image Credit: <a href="https://en.wikipedia.org/wiki/GIF#/media/File:Newtons_cradle_animation_book_2.gif" rel="external nofollow noopener">Dominique Toussaint</a>)</em></p>
<h2 id="checking-if-an-image-can-be-animated">Checking if an image can be animated</h2>
<p>The first thing to do is check if an animate can be animated.
While calling the various <code>ImageAnimator</code> methods with static
images (or even <code>null</code> references) won't crash, if your image is
static then there's no need to call them in the first place.</p>
<p>The <code>ImageAnimator.CanAnimate</code> method takes a source <code>Image</code> and
returns if it supports animation or not. Passing <code>null</code> to this
method will also return <code>false</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> Image _image<span class="symbol">;</span>
<span class="keyword">private</span> <span class="keyword">bool</span> _isAnimating<span class="symbol">;</span>

_image <span class="symbol">=</span> LoadImageFromSomewhere<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
_isAnimating <span class="symbol">=</span> ImageAnimator<span class="symbol">.</span>CanAnimate<span class="symbol">(</span>_image<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/draw-animated-image-1b.png" class="gallery" title="The demonstration program with a static image" ><img src="https://images.cyotek.com/image/thumbnail/devblog/draw-animated-image-1b.png" alt="The demonstration program with a static image" decoding="async" loading="lazy" /></a><figcaption>The demonstration program with a static image</figcaption></figure>
<p><em>(Image Credit: <a href="http://www.publicdomainpictures.net/view-image.php?image=18490" rel="external nofollow noopener">Vera Kratochvil</a>)</em></p>
<h2 id="preparing-for-animation">Preparing for animation</h2>
<p>If your image supports animation, the next step is to call
<code>ImageAnimator.Animate</code>, passing in both the source image and an
<code>EventHandler</code> to receive change notifications.</p>
<p>This method will create a background thread that will
periodically check watched images and advance frames as
required. When it detects a new frame should be painted, it will
call the event handler registered for the image, allowing you to
handle the update, e.g. repaint your control.</p>
<blockquote>
<p>Only one thread is created no matter how many images are being
animated</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> SetupAnimation<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 _isAnimating <span class="symbol">=</span> ImageAnimator<span class="symbol">.</span>CanAnimate<span class="symbol">(</span>_image<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_isAnimating<span class="symbol">)</span>
 <span class="symbol">{</span>
 ImageAnimator<span class="symbol">.</span>Animate<span class="symbol">(</span>_image<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>OnFrameChangedHandler<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> OnFrameChangedHandler<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> EventArgs eventArgs<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// trigger a repaint of our image</span>
 customPaintPanel<span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>One mistake I sometimes see developers do is calling the
<code>Refresh</code> method of a custom Windows Forms control. Calling
<code>Refresh</code> will force the control and its children to be
repainted immediately. An alternative way is to call
<code>Invalidate</code> (without any arguments) which will mark the
window to be repainted without forcing the paint or repainting
child windows - generally this is more suitable and reduces
the number of unneeded repaints.</p>
</blockquote>
<h2 id="halting-animation">Halting animation</h2>
<p>When you are finished with the source image, you should call
<code>ImageAnimator.StopAnimate</code> to remove the image and callback
from the list of watched images.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> CleanUp<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_image <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// disable animations</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_isAnimating<span class="symbol">)</span>
 <span class="symbol">{</span>
 ImageAnimator<span class="symbol">.</span>StopAnimate<span class="symbol">(</span>_image<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>OnFrameChangedHandler<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 _isAnimating <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>

 _image<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 _image <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="painting-the-image">Painting the image</h2>
<p>The current frame is part of the <code>Image</code> instance's metadata, so
you don't need to do anything specific to paint animated images
vs static. With that said, the <code>ImageAnimator</code> class tracks the
current frame separately and doesn't update the source <code>Image</code>
until requested, which you do by calling
<code>ImageAnimator.UpdateFrames</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> customPaintPanel_Paint<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> PaintEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_isAnimating<span class="symbol">)</span>
 <span class="symbol">{</span>
 ImageAnimator<span class="symbol">.</span>UpdateFrames<span class="symbol">(</span>_image<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>DrawImage<span class="symbol">(</span>_image<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="wrapping-up">Wrapping up</h2>
<p>As you can probably see, this is quite a simple process and
makes it easy to support animated graphics in applications that
reference <code>System.Drawing</code>.</p>
<p>A complete example demonstrating how to use the <code>ImageAnimator</code>
class is available from the link below.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-10-28 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/painting-animated-images-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comBook Review: The C# Helper Top 100urn:uuid:1d38c682-9597-4194-8ed7-dd3481b43c082017-10-21T20:59:59Z2017-10-21T20:59:59Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/bookshelf1.jpg" class="gallery" title="A not-great photo of some of my technical books" ><img src="https://images.cyotek.com/image/thumbnail/devblog/bookshelf1.jpg" alt="A not-great photo of some of my technical books" decoding="async" loading="lazy" /></a><figcaption>A not-great photo of some of my technical books</figcaption></figure>
<p>Almost 20 years ago after finishing a training scheme I was
hired for my first permanent role. After getting paid for the
first time I immediately rushed off to the bookshop to buy
myself a programming book. The selection of books in my chosen
language of the time (Visual Basic (not .NET!)) wasn't large,
but in the end I bought Rod Stephens <strong>Custom Controls
Library</strong>, which still graces my technical bookshelves even now.
Since then I've bought many computing books, but I couldn't tell
you the circumstances under which I bought most of them. This
one however was the first and so I still remember it to this day
- and this is also why if I'm asked for a technical book author
Rod's name will be the first name that comes to mind. (For the
curious, Dan Appleman would be the second, aside from the fact
his Win32 book was awesome I'm pretty sure he takes the award
for the longest titles in my collection).</p>
<p>Recently Rod announced he'd released a new self-published book,
titled <a href="http://csharphelper.com/top100.htm" rel="external nofollow noopener">The C# Helper Top 100</a>. Unsurprisingly given the
title, this is a collection of the most popular posts on the <a href="http://csharphelper.com/" rel="external nofollow noopener">C#
Helper</a> website. Although I've mostly given up on buying
technical books that I barely read (plus I was fairly certain
this book wasn't going to be my cup of tea), I decided I would
buy it and review it as I have found some of Rod's posts to be
helpful to me in the past. As I don't really enjoy reading these
type of books electronically, I bought a paper copy from Amazon.
(And also printed by Amazon according to the final page which I
found interesting).</p>
<blockquote>
<p>Full Disclosure. Unlike my <a href="/post/essential-algorithms-a-book-review">earlier review</a> of one of Rod's
books I bought this out of my own pocket on a whim. Everything
in this review are my own opinions and interpretations and my
own words.</p>
</blockquote>
<h2 id="trotting-out-old-adages">Trotting out old adages</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/csht100-cover.jpg" class="gallery" title="The cover of The C# Helper Top 100" ><img src="https://images.cyotek.com/image/thumbnail/devblog/csht100-cover.jpg" alt="The cover of The C# Helper Top 100" decoding="async" loading="lazy" /></a><figcaption>The cover of The C# Helper Top 100</figcaption></figure>
<p>First impressions are generally important and I have to say I
really dislike the cover - to me it feels very rough and ill
fitting (and what's with all the different shades of green). But
it should be all about the content, not the cover and I often
have the same thoughts about a great many indie or self
published fiction novels I buy and am (usually) nicely surprised
by the stories within them.</p>
<h2 id="book-layout-and-typesetting">Book layout and typesetting</h2>
<p>Despite being a self published book, Rod has obviously taken
some care to lay the book out properly and it compares well
against other traditionally published books in my library. The
only layout issues I spotted during my read were the odd jarring
block of white space when a small paragraph is situated next to
a tall image.</p>
<h2 id="who-is-the-book-aimed-for">Who is the book aimed for</h2>
<p>Rod is quite a prolific writer and he has penned <em>many</em> posts
over the years, so I was curious as to what the selection of the
top 100 would be. While they cover a surprising range of ground,
I think most of them fall into the beginner to intermediate
category - although there are some much more advanced examples
dealing with mathematics and 3D rendering. On the whole though,
I think this book would be more suited to beginner or junior
developers. I found the book was approachable and easy to read.</p>
<h2 id="book-outline">Book Outline</h2>
<p>The book is divided into a number of categories labelled as
parts. Although the list might be daunting, each part is quite
small - usually between 10 and 20 pages.</p>
<ul>
<li>Serialization</li>
<li>Graphing</li>
<li>Text Processing</li>
<li>DataGridView</li>
<li>Microsoft Office Integration</li>
<li>WPF</li>
<li>Graphics</li>
<li>Image Processing</li>
<li>Cryptography</li>
<li>Dialogs</li>
<li>Internet</li>
<li>Miscellaneous Controls</li>
<li>Geometry</li>
<li>Algorithms</li>
<li>Three-Dimensional Programs</li>
<li>ListView and TreeView</li>
<li>System Interactions</li>
<li>Mathematics</li>
<li>Multimedia</li>
<li>Interoperability</li>
<li>Printing</li>
<li>Miscellany</li>
</ul>
<h2 id="chapter-breakdown">Chapter Breakdown</h2>
<p>This section is a bit long, so if you don't particularly care to
read a description of all the contents, you can <a
href="#getting-the-source-code">skip this section</a>.</p>
<p>The first part, <strong>Serialization</strong>, is comprised of two chapters,
one detailing a basic approach to loading data from CSV and the
second converting objects to and from JSON using the
<code>DataContractJsonSerializer</code>.</p>
<p>Next up is <strong>Graphing</strong>. This part includes several chapters on
drawing line graphs in WPF, and drawing a histogram in Windows
Forms.</p>
<p>Part 3 is <strong>Text Processing</strong>. The chapters in this section
including transforms such as Pascal, Camel and Title case, along
with a pair of useful chapters on converting file sizes into
more friendly representations. I found some of the chapters in
this part to be quite infuriating as they involve techniques
such as the use of string concatenation in loops and regular
expressions rather than something more efficient. However,
eventually I conceded that over-complicating examples was a bad
idea and so it was probably better to keep them simple. However,
it's fair to say that most of the examples in this section
ruffled my feathers.</p>
<p>Following is part 4, a very short part on the <strong>DataGridView</strong>
control. Although the chapters in this section are quite brief,
they do give a nice introduction to a control which can be
difficult to use. I rarely use this control myself as generally
I find it to be clunky.</p>
<p>Then we have part 5 dealing with <strong>Microsoft Office
Integration</strong>. The chapters here describe reading and writing
data from Excel workbooks, and writing data to Word documents. I
read these chapters out of curiosity, but these days I like to
avoid COM as much as possible and when I need to work with
<code>xlsx</code> or <code>docx</code> files, I work with the file data directly and
don't touch Office at all.</p>
<p>Part 6 concerns <strong>WPF</strong>. Given that many of the examples in the
book deal with either WPF or Windows Forms I'm not too certain
why this has it's own section but it's another short one. WPF is
a technology I've never really used although I have seem some
quite clean and nice looking applications written using WPF, as
well as some dire ones. One of the chapters dealt with making a
label blink and I'm astounded is a popular post - I seriously
hope I don't come across an application where the developer is
making labels blink.</p>
<p><strong>Graphics</strong> is the subject matter for part 7. This part is
comprised of multiple chapters for basic painting, print text
(including aligned and justified text), including getting font
metrics, but by using the <code>Font</code> object unlike <a href="/post/retrieving-font-and-text-metrics-using-csharp">my approach</a>
which used direct P/Invoke. It has another example on drawing
rotated text which uses transforms on a <code>Graphics</code> object to do
the work and seems an awful lot easier than the way I used to do
it. Most of these examples using <code>System.Drawing</code>, but there is
also a brief topic on custom rendering with WPF too.</p>
<p>The next part, <strong>Image Processing</strong>, is another reasonably
chunky chapter. Part 8 starts off by describing how to save JPG
files with custom compression values to compare the output. This
example also details a method of cloning an image without
keeping the source stream around, something that <a href="https://stackoverflow.com/a/3845491/148962" rel="external nofollow noopener">bit
me</a> quite a few
years ago and is possibly worth an article of its own. This is
followed by an example showing one way of making an image grey
scale by manipulating bitmap bits directly, which is much better
than using <code>Bitmap.GetPixel</code> and <code>Bitmap.SetPixel</code>. Other
examples include cropping images, scaling images, selecting
parts of images, comparing images and generating a Mandelbrot
set. It then wraps up with a couple of examples on working with
bitmaps in WPF. I was very surprised when I read these examples
as it mentioned a <strong>Microsoft Windows Media Photo</strong> file format,
a format I hadn't heard of before.</p>
<p>Part 9 is <strong>Cryptography</strong>. This part is quite small with only a
pair of chapters. The first of these details how to encrypt or
decrypt a file using AES encryption. The second involves
generating cryptographically random numbers. After many of the
other more basic examples so far, I was quite pleased to see
something like this being highly ranked posts as these are quite
advanced topics and rolling your own encryption is generally not
a good idea.</p>
<p><strong>Dialogs</strong> are the subject matter of part 10. There are two
chapters to this part, the first dealing with various ways of
allowing a user to select a folder, and the second for building
a password dialog. The folder selection chapter starts
reasonably enough, describing how .NET only provides access to
the old browse for folder dialog instead of the newer one that
was added to later versions of Windows. However, its
recommendation to eschew using the WindowsAPICodePack NuGet
package and instead use Excel via COM interop is a really
curious recommendation and not one I agree with in the
slightest. I don't see why you'd want to avoid using a third
party package which you can distribute with your application and
instead rely on a huge, bloated, and slow application which you
definitely cannot distribute. This seems to be another quite
popular post but I would hope people aren't writing applications
that go an ask Excel to display a folder selection dialog. The
second chapter is for building a password dialog and is an
extremely basic example.</p>
<p>Part 11 is titled <strong>Internet</strong>. This part includes chapters on
getting stock quotes, weather forecasts, and getting file
attributes from an FTP server. There's also an example on using
the <code>WebBrowser</code> control with custom HTML.</p>
<blockquote>
<p>For some reason there isn't a part 12</p>
</blockquote>
<p>Part 13 is named <strong>Miscellaneous Controls</strong>. This section
includes a few short examples on working with Windows Forms
controls, such as making a checkable group box (in a slightly
odd fashion), finding controls by name, finding <code>ListBox</code> items
using partial matching and wrapping up with making a
<code>RichTextBox</code> control fit its contents.</p>
<p>Moving on we have part 14, <strong>Geometry</strong>. From a personal
standpoint I found this to be one of the most useful part of the
book as it dealt with topics I'm not hugely competent with and
could be useful in some future projects of mine. The chapters
include detecting where if two lines are touching, or where they
would eventually intersect, finding the distance between lines
and points, circle intersection, and a number of chapters on
polygons, such as calculating areas, detecting if points are
within a polygon and a few more.</p>
<p>As to be expected from Rod, we have part 15 on <strong>Algorithms</strong>
although it is another quite short section. First up is
generating all permutations of a set of values, a round robin
tournament generator, and finally drawing a family tree. This
last example could be reused for any hierarchy diagram really
and could be a useful starting point for someone. In fact, while
I didn't use this specific example, Rod has a couple of other
examples for calculating tree layouts on C# Helper and I did use
one of those a few years back as the basis for the sitemap
diagrams in <a href="https://cyotek.com/cyotek-webcopy">WebCopy</a>.</p>
<p>Part 16 deals with <strong>Three-Dimensional Programs</strong>. This is a
reasonably sized part that has a number of chapters dealing with
rotation, applying textures, drawing smooth surfaces and drawing
wireframes. All of these examples use WPF. While I scanned
through the examples, it wasn't a thorough reading - I'm not
really interested in trying to write a 3D game or have need for
3D in business applications.</p>
<p>After the trickiness of 3D, we go back to something simpler with
part 17. The <strong>ListView and TreeView</strong> part has a number of
introductory topics for the <code>ListView</code> control, detailing how to
use icons, groups and perform custom sorting. This is followed
by slightly more complicated article on printing a <code>ListView</code>
which again I feel could be a very nice starting point for a
reusable component for someones code base. The final chapter
describes how to populate a <code>TreeView</code> with the contents of an
XML document.</p>
<p>For part 18 Rod shares a number of chapters regarding <strong>System
Interactions</strong>. Firstly it starts of by describing how to get
detailed printer information using Windows Management
Instrumentation (WMI). I have briefly encountered this in the
past but never really used it so it was an interesting read,
even if I do tend to do that sort of thing the &quot;long way&quot; with
P/Invoke. Next it describes how to combine or resolve relative
paths by using <code>Path.GetFullPath</code>, followed by an example on
playing system sounds. It wraps up an interesting mix of
chapters by describing how to get the serial number of a hard
drive.</p>
<p>Part 19 is about <strong>Mathematics</strong>. I only scanned through this
section for the usual reason. First up is a complicated looking
example on solving a system of equations. This is followed by a
pair of chapters on linear/polynomial least squares fit. Next is
the Sieve of Eratosthenes and numbering factoring. It finishes
of with a nice simple example of converting numbers between
bases.</p>
<p>Part 20 deals with <strong>Multimedia</strong>. Briefly anyway, it's a very
short part. The first example shows how to use animated GIFs by
loading them into other controls like any other image. This
example might have been more interesting if it used the
<code>ImageAnimator</code> class instead to show how to manually animate an
image (for example if you're doing custom painting in a
ownerdraw control). It is then followed by an interesting
article showing how to play videos using WPF.</p>
<p>Almost at the end is part 21 <strong>Interoperability</strong>. This is
another interesting part which deals with working with other
applications. It starts with a chapter on creating a COM DLL for
use with Excel, then details how to use drag and drop. The part
concludes with a pair of chapters dealing with the Clipboard.
Aside from the COM DLL, the knowledge in these chapters should
be part of every programmers toolkit as they are very useful
techniques to add to applications and it is encouraging to see
they are popular.</p>
<p>The penultimate part is 22, <strong>Printing</strong>. The first chapter
details how to create a tabular report, in a very similar manner
to the <code>ListView</code> printing example featured earlier in the book.
This is then followed by a chapter demonstrating how to print
multiple pages. Although short, printing support is another
feature which I think can be quite important to have and this
too is good to see as a popular topic.</p>
<p>And finally we have part 23, <strong>Miscellany</strong>. This starts by
describing how to create a <code>TypeConverter</code> for use with a
<code>PropertyGrid</code> control. Although these can be useful things to
create (and I have <a href="/tag/typeconverter">wrote about them</a>
in the past), I am surprised they are that popular. Next up is a
description of using Unicode symbols in your application (which
sadly also applies to emoji). There is a useful chapter on using
the <code>BackgroundWorker</code> component, which can be an excellent way
of making your application do background tasks without getting
into the complexity of threads or the TPL. The final chapter of
the book is an article on using the <code>Stopwatch</code> class.</p>
<p>As you'd often expect with a technical book, the final section
is an index, nothing much to add to that!</p>
<h2 id="getting-the-source-code">Getting the source code</h2>
<p>The book doesn't come with a CD, but you can download the source
directly from the book's <a href="http://csharphelper.com/top100.htm" rel="external nofollow noopener">web page</a> (click <strong>Source Code</strong>
in the page footer, this is a direct download).</p>
<p>I think it's a shame that there isn't a shortcut to the online
versions of the articles, as this would make downloading
individual samples easier, as well as being able to view
comments or participate in them - sometimes I have found more
value in the comments for an article than in the article itself
(this is a general comment, not specific to the C# Helper blog),
and quite often users post questions to Rod and he always seems
to take the time to answer them no matter how inane they are.</p>
<h2 id="errata">Errata</h2>
<p>Aside from the missing part 12, I didn't notice any other
obvious errors when reading the book until I got to the end. I
suspect the final section &quot;<strong>Aferward</strong>&quot; was probably supposed
to be &quot;<strong>Afterw<em>o</em>rd</strong>&quot;, and for some reason all the <strong>Index</strong>
pages are also titled as <strong>Afterward</strong>.</p>
<h2 id="closing-words">Closing Words</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/bookshelf2.jpg" class="gallery" title="Another not-great photo of some more of my technical books" ><img src="https://images.cyotek.com/image/thumbnail/devblog/bookshelf2.jpg" alt="Another not-great photo of some more of my technical books" decoding="async" loading="lazy" /></a><figcaption>Another not-great photo of some more of my technical books</figcaption></figure>
<p>I found it quite a struggle to see what class of developer I
would recommend read a book like this given the diverse subject
matter. The random selection of articles within some sections
mean I think it's very hit and miss on what people might be
interested in. On the other hand, it's not a book focused on any
one technology so this is perhaps to be expected.</p>
<p>Any intermediate or above developer is probably going to know
everything that is relevant to their field without needing the
book, and wouldn't be concerned with the chapters that weren't
relevant.</p>
<p>If these really are the top 100 posts then there's some really
odd examples. However, the <a href="http://csharphelper.com/blog/" rel="external nofollow noopener">C# Helper Blog</a> has hundreds more
posts, and is well worth a look if you're a budding C#
developer. In fact, it's worth subscribing to the RSS feed
anyway as while I'm not personally interested in many of the
articles, something does occasionally pop up now and then to
catch my eye. There's very few websites that I've been following
for years; many of them disappeared long ago - this is one that
has stayed the course.</p>
<blockquote>
<p>This is only the second technical book review that I've
written and I'm still trying to find my &quot;voice&quot; with them.
Comments, feedback and constructive criticism welcome!</p>
</blockquote>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-10-21 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/book-review-the-csharp-helper-top-100 .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comGetting a window rectangle without the drop shadowurn:uuid:2ba09a3e-cc60-49b3-ab7e-6d6992aeb9412017-08-27T14:41:05Z2017-08-27T13:47:45Z<p>In my <a href="/post/capturing-screenshots-using-csharp-and-p-invoke">last article</a>, I describe how to use the Win32
API to capture screenshots of the desktop. There was one
frustrating problem with this however - when capturing an image
based on the value of the <code>Bounds</code> property of a <code>Form</code>
unexpected values were returned for the left position, width and
height of the window, causing my screenshots to be too big.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/window-boundaries-1a.png" class="gallery" title="An example of unexpected values when asking for window boundaries" ><img src="https://images.cyotek.com/image/thumbnail/devblog/window-boundaries-1a.png" alt="An example of unexpected values when asking for window boundaries" decoding="async" loading="lazy" /></a><figcaption>An example of unexpected values when asking for window boundaries</figcaption></figure>
<p>I thought that was odd but as I wanted to be able to capture
unmanaged windows in future then using <code>Form.Bounds</code> wasn't
going to be possible anyway and I would have to use
<code>GetWindowRect</code>. I'm sure that deep down in the Windows Forms
code base it uses the same API so I was expecting to get the
same &quot;wrong&quot; results, and I wasn't disappointed.</p>
<p>Although I'm calling these values &quot;wrong&quot;, technically they are
correct - here's another example this time using a plain white
background.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/window-boundaries-1b.png" class="gallery" title="Drop shadows appear around windows in Windows 10" ><img src="https://images.cyotek.com/image/thumbnail/devblog/window-boundaries-1b.png" alt="Drop shadows appear around windows in Windows 10" decoding="async" loading="lazy" /></a><figcaption>Drop shadows appear around windows in Windows 10</figcaption></figure>
<p>As you can see, Windows 10 has a subtle drop shadow affect
around three edges of a window, and it seems that is classed as
being part of the window. This was surprising to me as I would
assumed that it wouldn't be included being part of the OS theme
rather than the developers deliberate choice.</p>
<p>Windows has the very handy hotkey <kbd>Alt+Print Screen</kbd>
which will capture a screenshot of the active window and place
it on the Clipboard. I've used this hotkey for untold years and
it never includes a drop shadow, so clearly there's a way of
excluding it. Some quick searching later reveals an answer - the
<a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa969515%28v=vs.85%29.aspx" rel="external nofollow noopener"><code>DwmGetWindowAttribute</code></a> function. This
was introduced in Windows Vista and allows you to retrieve
various extended aspects of a window, similar I think to
<code>GetWindowLong</code>.</p>
<blockquote>
<p>DWM stands for Desktop Window Manager and is the way that
windows have been rendered since Vista, replacing the old GDI
system.</p>
</blockquote>
<p>There's a <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa969530(v=vs.85).aspx" rel="external nofollow noopener"><code>DWMWINDOWATTRIBUTE</code></a> enumeration
which lists the various supported attributes, but the one we
need is <code>DWMWA_EXTENDED_FRAME_BOUNDS</code>. Using this attribute will
return what I consider the window boundaries without the shadow.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">const</span> <span class="keyword">int</span> DWMWA_EXTENDED_FRAME_BOUNDS <span class="symbol">=</span> <span class="number">9</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;dwmapi.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">int</span> DwmGetWindowAttribute<span class="symbol">(</span>IntPtr hwnd<span class="symbol">,</span> <span class="keyword">int</span> dwAttribute<span class="symbol">,</span> <span class="keyword">out</span> RECT pvAttribute<span class="symbol">,</span> <span class="keyword">int</span> cbAttribute<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>Calling it is a little bit more complicated that some other
API's. The <code>pvAttribute</code> argument is a pointer to a value - and
it can be of a number of different types. For this reason, the
<code>cbAttribute</code> value must be filled in with the size of the value
in bytes. This is a fairly common technique in Win32, although
I'm more used to seeing <code>cbSize</code> as a member of a <code>struct</code>, not
as a parameter on the call itself. Fortunately, we don't have to
work this out manually as the <code>Marshal</code> class provides a
<code>SizeOf</code> method we can use.</p>
<p>For sanities sake, I will also check the result code, and if
it's not <code>0</code> (<code>S_OK</code>) then I'll fall back to <code>GetWindowRect</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">if</span> <span class="symbol">(</span>DwmGetWindowAttribute<span class="symbol">(</span>hWnd<span class="symbol">,</span> DWMWA_EXTENDED_FRAME_BOUNDS<span class="symbol">,</span> <span class="keyword">out</span> region<span class="symbol">,</span> Marshal<span class="symbol">.</span>SizeOf<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>RECT<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span> <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">)</span>
<span class="symbol">{</span>
 NativeMethods<span class="symbol">.</span>GetWindowRect<span class="symbol">(</span>hWnd<span class="symbol">,</span> <span class="keyword">out</span> region<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Now I have a <code>RECT</code> structure that describes what <em>I</em> consider
to be the window boundaries.</p>
<h2 id="a-note-on-windows-versions">A note on Windows versions</h2>
<p>As the <code>DwmGetWindowAttribute</code> API was introduced in Windows
Vista, if you want this code to work in Windows XP you'll need
to check the current version of Windows. The easiest way is
using <code>Environment.OsVersion</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> Bitmap CaptureWindow<span class="symbol">(</span>IntPtr hWnd<span class="symbol">)</span>
<span class="symbol">{</span>
 RECT region<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>Environment<span class="symbol">.</span>OSVersion<span class="symbol">.</span>Version<span class="symbol">.</span>Major <span class="symbol">&lt;</span> <span class="number">6</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 GetWindowRect<span class="symbol">(</span>hWnd<span class="symbol">,</span> <span class="keyword">out</span> region<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>DwmGetWindowAttribute<span class="symbol">(</span>hWnd<span class="symbol">,</span> DWMWA_EXTENDED_FRAME_BOUNDS<span class="symbol">,</span> <span class="keyword">out</span> region<span class="symbol">,</span> Marshal<span class="symbol">.</span>SizeOf<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>RECT<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span> <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 GetWindowRect<span class="symbol">(</span>hWnd<span class="symbol">,</span> <span class="keyword">out</span> region<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>CaptureRegion<span class="symbol">(</span>Rectangle<span class="symbol">.</span>FromLTRB<span class="symbol">(</span>region<span class="symbol">.</span>teft<span class="symbol">,</span> region<span class="symbol">.</span>top<span class="symbol">,</span> region<span class="symbol">.</span>bight<span class="symbol">,</span> region<span class="symbol">.</span>bottom<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Although it should have no impact in this example, newer
versions of Windows will lie to you about the version unless
your application explicitly states that it is supported by the
current Windows version, via an <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd371711(v=vs.85).aspx" rel="external nofollow noopener">application
manifest</a>. This is another topic out of the scope of
this particular article, but they are useful for a number of
different cases.</p>
<h2 id="sample-code">Sample code</h2>
<p>There's no explicit download to go with this article as it is
all part of the <strong>Simple Screenshot Capture</strong> source code in the
<a href="/post/capturing-screenshots-using-csharp-and-p-invoke">previous article</a>.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-08-27 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/getting-a-window-rectangle-without-the-drop-shadow .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCapturing screenshots using C# and p/invokeurn:uuid:758976f2-dbe8-449a-80ef-1fc869e8d5fb2019-02-25T16:31:04Z2017-08-27T13:40:29Z<p>I was recently updating some documentation and wanted to
programmatically capture some screenshots of the application in
different states. This article describes how you can easily
capture screenshots in your own applications.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/capture-screenshot-1c.png" class="gallery" title="Capturing a screenshot of the desktop" ><img src="https://images.cyotek.com/image/thumbnail/devblog/capture-screenshot-1c.png" alt="Capturing a screenshot of the desktop" decoding="async" loading="lazy" /></a><figcaption>Capturing a screenshot of the desktop</figcaption></figure><h2 id="using-the-win32-api">Using the Win32 API</h2>
<p>This article makes use of a number of Win32 API methods.
Although you may not have much call to use them directly in day
to day .NET (not to mention Microsoft wanting everyone to use
universal &quot;apps&quot; these days), they are still extraordinarily
useful and powerful.</p>
<p>This article does assume you know the basics of platform invoke
so I won't cover it here. In regards to the actual API's I'm
using, you can find lots of information about them either on
<a href="https://msdn.microsoft.com/en-us/library/ee663300(v=vs.85).aspx" rel="external nofollow noopener">MSDN</a>, or <a href="http://www.pinvoke.net/" rel="external nofollow noopener">PInvoke.net</a>.</p>
<blockquote>
<p>A number of the API's used in this article are GDI calls.
Generally, when you're using the Win32 GDI API, you need to do
things in pairs. If something is created (pens, brushes,
bitmaps, icons etc.), then it usually needs to be explicitly
destroyed when finished with (there are some exceptions just
to keep you on your toes). Although there haven't been GDI
limits in Windows for some time now (as far as I know!), it's
still good not to introduce memory leaks. In addition, device
contexts always have a number of objects associated with them.
If you assign a new object to a context, you must restore the
original object when you're done. I'm a little rusty with this
so hopefully I'm not missing anything out.</p>
</blockquote>
<h2 id="setting-up-a-device-context-for-use-with-bitblt">Setting up a device context for use with BitBlt</h2>
<p>To capture a screenshot, I'm going to be using the <code>BitBlt</code> API.
This copies information from one device context to another,
meaning I'm going to need a source and destination context to
process.</p>
<p>The source is going to be the desktop, so first I'll use the
<code>GetDesktopWindow</code> and <code>GetWindowDC</code> calls to obtain this. As
calling <code>GetWindowDC</code> essentially places a lock on it, I also
need to release it when I'm finished with it.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
IntPtr desktophWnd <span class="symbol">=</span> GetDesktopWindow<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
IntPtr desktopDc <span class="symbol">=</span> GetWindowDC<span class="symbol">(</span>desktophWnd<span class="symbol">)</span><span class="symbol">;</span>

<span class="comment">// TODO</span>

ReleaseDC<span class="symbol">(</span>desktophWnd<span class="symbol">,</span> desktopDc<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>Now for the destination - for this, I'm going to create a memory
context using <code>CreateCompatibleDC</code>. When you call this API, you
pass in an existing DC and the new one will be created based on
that.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
IntPtr memoryDc <span class="symbol">=</span> CreateCompatibleDC<span class="symbol">(</span>desktopDc<span class="symbol">)</span><span class="symbol">;</span>

<span class="comment">// TODO</span>

DeleteDC<span class="symbol">(</span>memoryDc<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>There's still one last step to perform - by itself, that memory
DC isn't hugely useful. We need to create and assign a GDI
bitmap to it. To do this, first create a bitmap using
<code>CreateCompatibleBitmap</code> and then attach it to the DC using
<code>SelectObject</code>. <code>SelectObject</code> will also return the relevant old
object which we need to restore (again using <code>SelectObject</code>)
when we're done. We also use <code>DeleteObject</code> to clean up the
bitmap.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
IntPtr bitmap <span class="symbol">=</span> CreateCompatibleBitmap<span class="symbol">(</span>desktopDc<span class="symbol">,</span> width<span class="symbol">,</span> height<span class="symbol">)</span><span class="symbol">;</span>
IntPtr oldBitmap <span class="symbol">=</span> SelectObject<span class="symbol">(</span>memoryDc<span class="symbol">,</span> bitmap<span class="symbol">)</span><span class="symbol">;</span>

<span class="comment">// TODO</span>

SelectObject<span class="symbol">(</span>memoryDc<span class="symbol">,</span> oldBitmap<span class="symbol">)</span><span class="symbol">;</span>
DeleteObject<span class="symbol">(</span>bitmap<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>Although this might seem like a lot of effort, it's not all that
different from using objects implementing <code>IDisposable</code> in C#,
just C# makes it a little easier with things like the <code>using</code>
statement.</p>
<h2 id="calling-bitblt-to-capture-a-screenshot">Calling BitBlt to capture a screenshot</h2>
<p>With the above setup out the way, we have a device context which
provides access to a bitmap of the desktop, and we have a new
device context ready to transfer data to. All that's left to do
is make the <code>BitBlt</code> call.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">const</span> <span class="keyword">int</span> SRCCOPY <span class="symbol">=</span> <span class="number">0x00CC0020</span><span class="symbol">;</span>
<span class="keyword">const</span> <span class="keyword">int</span> CAPTUREBLT <span class="symbol">=</span> <span class="number">0x40000000</span><span class="symbol">;</span>

<span class="keyword">bool</span> success <span class="symbol">=</span> BitBlt<span class="symbol">(</span>memoryDc<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> width<span class="symbol">,</span> height<span class="symbol">,</span> desktopDc<span class="symbol">,</span> left<span class="symbol">,</span> top<span class="symbol">,</span> SRCCOPY <span class="symbol">|</span> CAPTUREBLT<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>success<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> Win<span class="number">32</span>Exception<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>If you've ever used the <code>DrawImage</code> method of a <code>Graphics</code>
object before, this call should be fairly familiar - we pass in
the DC to write too, along with the upper left corner where data
will be copied (<code>0, 0</code> in this example), followed by the <code>width</code>
and <code>height</code> of the rectangle - this applies to both the source
and destination. Finally, we pass in the source device context,
and the upper left corner where data will be copied from, along
with flags that detail how the data will be copied.</p>
<p>In my old VB6 days, I would just use <code>SRCCOPY</code> (direct copy),
but in those days windows were simpler things. The <code>CAPTUREBLT</code>
flag ensures the call works properly with layered windows.</p>
<p>If the call fails, I throw a new <code>Win32Exception</code> object without
any parameters - this will take care of looking up the result
code for the <code>BitBlt</code> failure and filling in an appropriate
message.</p>
<p>Now that our destination bitmap has been happily &quot;painted&quot; with
the specified region from the desktop we need to get it into
.NET-land. We can do this via the <code>FromHbitmap</code> static method of
the <code>Image</code> class - this method accepts a GDI bitmap handle and
return a fully fledged .NET <code>Bitmap</code> object from it.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Bitmap result <span class="symbol">=</span> Image<span class="symbol">.</span>FromHbitmap<span class="symbol">(</span>bitmap<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="putting-it-all-together">Putting it all together</h2>
<p>As the above code is piecemeal, the following helper method will
accept a <code>Rectangle</code> which describes which part of the desktop
you want to capture and will then return a <code>Bitmap</code> object
containing the captured information.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;gdi32.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">bool</span> BitBlt<span class="symbol">(</span>IntPtr hdcDest<span class="symbol">,</span> <span class="keyword">int</span> nxDest<span class="symbol">,</span> <span class="keyword">int</span> nyDest<span class="symbol">,</span> <span class="keyword">int</span> nWidth<span class="symbol">,</span> <span class="keyword">int</span> nHeight<span class="symbol">,</span> IntPtr hdcSrc<span class="symbol">,</span> <span class="keyword">int</span> nXSrc<span class="symbol">,</span> <span class="keyword">int</span> nYSrc<span class="symbol">,</span> <span class="keyword">int</span> dwRop<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;gdi32.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> IntPtr CreateCompatibleBitmap<span class="symbol">(</span>IntPtr hdc<span class="symbol">,</span> <span class="keyword">int</span> width<span class="symbol">,</span> <span class="keyword">int</span> nHeight<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;gdi32.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> IntPtr CreateCompatibleDC<span class="symbol">(</span>IntPtr hdc<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;gdi32.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> IntPtr DeleteDC<span class="symbol">(</span>IntPtr hdc<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;gdi32.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> IntPtr DeleteObject<span class="symbol">(</span>IntPtr hObject<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> IntPtr GetDesktopWindow<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> IntPtr GetWindowDC<span class="symbol">(</span>IntPtr hWnd<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">bool</span> ReleaseDC<span class="symbol">(</span>IntPtr hWnd<span class="symbol">,</span> IntPtr hDc<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;gdi32.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> IntPtr SelectObject<span class="symbol">(</span>IntPtr hdc<span class="symbol">,</span> IntPtr hObject<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">const</span> <span class="keyword">int</span> SRCCOPY <span class="symbol">=</span> <span class="number">0x00CC0020</span><span class="symbol">;</span>

<span class="keyword">const</span> <span class="keyword">int</span> CAPTUREBLT <span class="symbol">=</span> <span class="number">0x40000000</span><span class="symbol">;</span>

<span class="keyword">public</span> Bitmap CaptureRegion<span class="symbol">(</span>Rectangle region<span class="symbol">)</span>
<span class="symbol">{</span>
 IntPtr desktophWnd<span class="symbol">;</span>
 IntPtr desktopDc<span class="symbol">;</span>
 IntPtr memoryDc<span class="symbol">;</span>
 IntPtr bitmap<span class="symbol">;</span>
 IntPtr oldBitmap<span class="symbol">;</span>
 <span class="keyword">bool</span> success<span class="symbol">;</span>
 Bitmap result<span class="symbol">;</span>

 desktophWnd <span class="symbol">=</span> GetDesktopWindow<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 desktopDc <span class="symbol">=</span> GetWindowDC<span class="symbol">(</span>desktophWnd<span class="symbol">)</span><span class="symbol">;</span>
 memoryDc <span class="symbol">=</span> CreateCompatibleDC<span class="symbol">(</span>desktopDc<span class="symbol">)</span><span class="symbol">;</span>
 bitmap <span class="symbol">=</span> CreateCompatibleBitmap<span class="symbol">(</span>desktopDc<span class="symbol">,</span> region<span class="symbol">.</span>Width<span class="symbol">,</span> region<span class="symbol">.</span>Height<span class="symbol">)</span><span class="symbol">;</span>
 oldBitmap <span class="symbol">=</span> SelectObject<span class="symbol">(</span>memoryDc<span class="symbol">,</span> bitmap<span class="symbol">)</span><span class="symbol">;</span>

 success <span class="symbol">=</span> BitBlt<span class="symbol">(</span>memoryDc<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> region<span class="symbol">.</span>Width<span class="symbol">,</span> region<span class="symbol">.</span>Height<span class="symbol">,</span> desktopDc<span class="symbol">,</span> region<span class="symbol">.</span>Left<span class="symbol">,</span> region<span class="symbol">.</span>Top<span class="symbol">,</span> SRCCOPY <span class="symbol">|</span> CAPTUREBLT<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>success<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> Win<span class="number">32</span>Exception<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 result <span class="symbol">=</span> Image<span class="symbol">.</span>FromHbitmap<span class="symbol">(</span>bitmap<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">finally</span>
 <span class="symbol">{</span>
 SelectObject<span class="symbol">(</span>memoryDc<span class="symbol">,</span> oldBitmap<span class="symbol">)</span><span class="symbol">;</span>
 DeleteObject<span class="symbol">(</span>bitmap<span class="symbol">)</span><span class="symbol">;</span>
 DeleteDC<span class="symbol">(</span>memoryDc<span class="symbol">)</span><span class="symbol">;</span>
 ReleaseDC<span class="symbol">(</span>desktophWnd<span class="symbol">,</span> desktopDc<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>Note the <code>try ... finally</code> block used to try and free GDI
resources if the <code>BitBlt</code> or <code>FromHbitmap</code> calls fail. Also
note how the clean-up is the exact reverse of
creation/selection.</p>
</blockquote>
<p>Now that we have this method, we can use it in various ways as
demonstrated below.</p>
<h2 id="capturing-a-single-window">Capturing a single window</h2>
<p>If you want to capture a window in your application, you could
call <code>Capture</code> with the value of the <code>Bounds</code> property of your
<code>Form</code>. But if you want to capture an external window then
you're going to need to go back to the Win32 API. The
<code>GetWindowRect</code> function will return any window's boundaries.</p>
<p>Win32 has its own version of .NET's <code>Rectangle</code> structure, named
<code>RECT</code>. This differs slightly from the .NET version in that it
has <code>right</code> and <code>bottom</code> properties, not <code>width</code> and <code>height</code>.
The <code>Rectangle</code> class has a helper method, <code>FromLTRB</code> which
constructs a <code>Rectangle</code> from left, top, right and bottom
properties which means you don't need to perform the subtraction
yourself.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/capture-screenshot-1a.png" class="gallery" title="Capturing a screenshot of a single window" ><img src="https://images.cyotek.com/image/thumbnail/devblog/capture-screenshot-1a.png" alt="Capturing a screenshot of a single window" decoding="async" loading="lazy" /></a><figcaption>Capturing a screenshot of a single window</figcaption></figure><figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32.dll&quot;</span><span class="symbol">,</span> SetLastError <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">bool</span> GetWindowRect<span class="symbol">(</span>IntPtr hwnd<span class="symbol">,</span> <span class="keyword">out</span> RECT lpRect<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>StructLayout<span class="symbol">(</span>LayoutKind<span class="symbol">.</span>Sequential<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">struct</span> RECT
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">int</span> left<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> top<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> right<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> bottom<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> Bitmap CaptureWindow<span class="symbol">(</span>IntPtr hWnd<span class="symbol">)</span>
<span class="symbol">{</span>
 RECT region<span class="symbol">;</span>

 GetWindowRect<span class="symbol">(</span>hWnd<span class="symbol">,</span> <span class="keyword">out</span> region<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>CaptureRegion<span class="symbol">(</span>Rectangle<span class="symbol">.</span>FromLTRB<span class="symbol">(</span>region<span class="symbol">.</span>Left<span class="symbol">,</span> region<span class="symbol">.</span>Top<span class="symbol">,</span> region<span class="symbol">.</span>Right<span class="symbol">,</span> region<span class="symbol">.</span>Bottom<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> Bitmap CaptureWindow<span class="symbol">(</span>Form form<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>CaptureWindow<span class="symbol">(</span>form<span class="symbol">.</span>Handle<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>Depending on the version of Windows you're using, you may find
that you get slightly unexpected results when calling
<code>Form.Bounds</code> or <code>GetWindowRect</code>. As I don't want to digress
to much, I'll follow up why and how to resolve in another post
(the attached sample application includes the complete code
for both articles).</p>
</blockquote>
<h2 id="capturing-the-active-window">Capturing the active window</h2>
<p>As a slight variation on the previous section, you can use the
<code>GetForegroundWindow</code> API call to get the handle of the active
window.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> IntPtr GetForegroundWindow<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">public</span> Bitmap CaptureActiveWindow<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>CaptureWindow<span class="symbol">(</span>GetForegroundWindow<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="capturing-a-single-monitor">Capturing a single monitor</h2>
<p>.NET offers the <code>Screen</code> static class which provides access to
all monitors on your system via the <code>AllScreens</code> property. You
can use the <code>FromControl</code> method to find out which monitor a
form is hosted on, and get the region that represents the
monitor - with or without areas covered by the task bar and
other app bars. This means it trivial to capture the contents of
a given monitor.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/capture-screenshot-1b.png" class="gallery" title="Capturing a screenshot of a specific monitor" ><img src="https://images.cyotek.com/image/thumbnail/devblog/capture-screenshot-1b.png" alt="Capturing a screenshot of a specific monitor" decoding="async" loading="lazy" /></a><figcaption>Capturing a screenshot of a specific monitor</figcaption></figure><figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> Bitmap CaptureMonitor<span class="symbol">(</span>Screen monitor<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>CaptureMonitor<span class="symbol">(</span>monitor<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> Bitmap CaptureMonitor<span class="symbol">(</span>Screen monitor<span class="symbol">,</span> <span class="keyword">bool</span> workingAreaOnly<span class="symbol">)</span>
<span class="symbol">{</span>
 Rectangle region<span class="symbol">;</span>

 region <span class="symbol">=</span> workingAreaOnly <span class="symbol">?</span> monitor<span class="symbol">.</span>WorkingArea <span class="symbol">:</span> monitor<span class="symbol">.</span>Bounds<span class="symbol">;</span>

 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>CaptureRegion<span class="symbol">(</span>region<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> Bitmap CaptureMonitor<span class="symbol">(</span><span class="keyword">int</span> index<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>CaptureMonitor<span class="symbol">(</span>index<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> Bitmap CaptureMonitor<span class="symbol">(</span><span class="keyword">int</span> index<span class="symbol">,</span> <span class="keyword">bool</span> workingAreaOnly<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>CaptureMonitor<span class="symbol">(</span>Screen<span class="symbol">.</span>AllScreens<span class="symbol">[</span>index<span class="symbol">]</span><span class="symbol">,</span> workingAreaOnly<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="capturing-the-entire-desktop">Capturing the entire desktop</h2>
<p>It is also quite simple to capture the entire desktop without
having to know all the details of monitor arrangements. We just
need to enumerate the available monitors and use
<code>Rectangle.Union</code> to merge two rectangles together. When this is
complete, you'll have one rectangle which describes all
available monitors.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/capture-screenshot-1d.png" class="gallery" title="Capturing a screenshot of the entire desktop" ><img src="https://images.cyotek.com/image/thumbnail/devblog/capture-screenshot-1d.png" alt="Capturing a screenshot of the entire desktop" decoding="async" loading="lazy" /></a><figcaption>Capturing a screenshot of the entire desktop</figcaption></figure><figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> Bitmap CaptureDesktop<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>CaptureDesktop<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> Bitmap CaptureDesktop<span class="symbol">(</span><span class="keyword">bool</span> workingAreaOnly<span class="symbol">)</span>
<span class="symbol">{</span>
 Rectangle desktop<span class="symbol">;</span>
 Screen<span class="symbol">[</span><span class="symbol">]</span> screens<span class="symbol">;</span>

 desktop <span class="symbol">=</span> Rectangle<span class="symbol">.</span>Empty<span class="symbol">;</span>
 screens <span class="symbol">=</span> Screen<span class="symbol">.</span>AllScreens<span class="symbol">;</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> screens<span class="symbol">.</span>Length<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Screen screen<span class="symbol">;</span>

 screen <span class="symbol">=</span> screens<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">;</span>

 desktop <span class="symbol">=</span> Rectangle<span class="symbol">.</span>Union<span class="symbol">(</span>desktop<span class="symbol">,</span> workingAreaOnly <span class="symbol">?</span> screen<span class="symbol">.</span>WorkingArea <span class="symbol">:</span> screen<span class="symbol">.</span>Bounds<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>CaptureRegion<span class="symbol">(</span>desktop<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>There is one slight problem with this approach - if the
resolutions of your monitors are different sizes, or are
misaligned from each other, the gaps will be filled in solid
black. It would be nicer to make these areas transparent,
however at this point in time I don't need to capture the
whole desktop so I'll leave this either as an exercise for the
reader, or a subsequent update.</p>
</blockquote>
<h2 id="capturing-an-arbitrary-region">Capturing an arbitrary region</h2>
<p>Of course, you could just call <code>CaptureRegion</code> with a custom
rectangle to pick up some arbitrary part of the desktop. The
above helpers are just that, helpers!</p>
<h2 id="a-note-on-display-scaling-and-high-dpi-monitors">A note on display scaling and high DPI monitors</h2>
<p>Although I don't have a high DPI monitor, I did temporarily
scale the display to 125% to test that the correct regions were
still captured. I tested with a manifest stating that the
application supported high DPI and again without, in both cases
the correct sized images were captured.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/capture-screenshot-1e.png" class="gallery" title="Capturing a scaled window that supports high DPI" ><img src="https://images.cyotek.com/image/thumbnail/devblog/capture-screenshot-1e.png" alt="Capturing a scaled window that supports high DPI" decoding="async" loading="lazy" /></a><figcaption>Capturing a scaled window that supports high DPI</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/capture-screenshot-1f.png" class="gallery" title="Capturing a a scaled window that doesn't support high DPI" ><img src="https://images.cyotek.com/image/thumbnail/devblog/capture-screenshot-1f.png" alt="Capturing a a scaled window that doesn't support high DPI" decoding="async" loading="lazy" /></a><figcaption>Capturing a a scaled window that doesn't support high DPI</figcaption></figure><h2 id="the-demo-program">The demo program</h2>
<p>A demonstration program for the techniques in this article is
available from the links below. It's also available on
<a href="https://github.com/cyotek/SimpleScreenshotCapture" rel="external nofollow noopener">GitHub</a>.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-08-27 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/capturing-screenshots-using-csharp-and-p-invoke .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comWriting custom Markdig extensionsurn:uuid:6879fba4-b9bf-4434-a1a1-0f6f6ab3e9dc2017-08-06T09:16:44Z2017-08-05T22:53:59Z<p><a href="https://github.com/lunet-io/markdig" rel="external nofollow noopener">Markdig</a>, according to its description, &quot;is a fast,
powerful, <a href="http://commonmark.org/" rel="external nofollow noopener">CommonMark</a> compliant, extensible Markdown
processor for .NET&quot;. While most of our older projects use
<a href="http://www.toptensoftware.com/markdowndeep/" rel="external nofollow noopener">MarkdownDeep</a> (including an increasingly creaky cyotek.com),
current projects use Markdig and thus far it has proven to be an
excellent library.</p>
<p>One of the many overly complicated aspects of cyotek.com is that
in addition to the markdown processing, every single block of
content is also ran through a byzantine number of regular
expressions for custom transforms. When cyotek.com is updated to
use Markdig, I definitely don't want these expressions to hang
around. Enter, Markdig extensions.</p>
<p>Markdig extensions allow you extend Markdig to include
additional transforms, things that might not conform to the
CommonMark specification such as YAML blocks or pipe tables.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
MarkdownPipeline pipline<span class="symbol">;</span>
<span class="keyword">string</span> html<span class="symbol">;</span>
<span class="keyword">string</span> markdown<span class="symbol">;</span>

markdown <span class="symbol">=</span> <span class="string">&quot;# Header 1&quot;</span><span class="symbol">;</span>

pipline <span class="symbol">=</span> <span class="keyword">new</span> MarkdownPipelineBuilder<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>Build<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

html <span class="symbol">=</span> Markdown<span class="symbol">.</span>ToHtml<span class="symbol">(</span>markdown<span class="symbol">,</span> pipline<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// &lt;h1&gt;Header 1&lt;/h1&gt;</span>

pipline <span class="symbol">=</span> <span class="keyword">new</span> MarkdownPipelineBuilder<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>UseAutoIdentifiers<span class="symbol">(</span><span class="symbol">)</span> <span class="comment">// enable the Auto Identifiers extension</span>
 <span class="symbol">.</span>Build<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

html <span class="symbol">=</span> Markdown<span class="symbol">.</span>ToHtml<span class="symbol">(</span>markdown<span class="symbol">,</span> pipline<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// &lt;h1 id=&quot;header-1&quot;&gt;Header 1&lt;/h1&gt;</span>
</pre>
</figure>
<p><em>Example of using an extension to automatically generate <code>id</code>
attributes for heading elements.</em></p>
<p>I recently updated our internal crash aggregation system to be
able to create MantisBT issues via our <a href="https://github.com/cyotek/MantisSharp" rel="external nofollow noopener">MantisSharp</a> library.
In these issues, stack traces include the line number or IL
offset in the format <code>#&lt;number&gt;</code>. To my vague annoyance, Mantis
Bug Tracker treats these as hyperlinks to other issues in the
system in a similar fashion to how GitHub automatically links to
issues or pull requires. It did however give me an idea to
create a Markdig extension that performs the same functionality.</p>
<h2 id="deciding-on-the-pattern">Deciding on the pattern</h2>
<p>The first thing you need to do is decide the markdown pattern to
trigger the extension. Our example is perhaps a bit <em>too</em> basic
as it is a simple <code>#&lt;number&gt;</code>, whereas if you think of other
issue systems such as JIRA, it would be <code>&lt;string&gt;-&lt;number&gt;</code>. As
well as the &quot;body&quot; of the pattern you also need to consider the
characters which surround it. For example, you might only allow
white space, or perhaps brackets or braces - whenever I
reference a JIRA issue I tend to surround them in square braces,
e.g. <code>[PRJ-1234]</code>.</p>
<p>The other thing to consider is the criteria of the core pattern.
Using our example above, should we have a minimum number of
digits before triggering, or a maximum? <code>#999999999</code> is probably
not a valid issue number!</p>
<h2 id="extension-components">Extension components</h2>
<p>A Markdig extension is comprised of a few moving parts.
Depending on how complicated your extension is, you may not need
all parts, or could perhaps reuse existing parts.</p>
<ul>
<li>The extension itself (always required)</li>
<li>A parser</li>
<li>A renderer</li>
<li>A object used to represent data in the <a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree" rel="external nofollow noopener">abstract syntax
tree</a> (AST)</li>
<li>A object used to configure the extension functionality</li>
</ul>
<p>In this plugin, I'll be demonstrating all of these parts.</p>
<blockquote>
<p>Happily enough, there's actually already an extension built
into Markdig for rendering JIRA links which was great as a
getting started point, including the original
<a href="https://github.com/clarkd/MarkdigJiraLinker" rel="external nofollow noopener">MarkdigJiraLinker</a> extension by <a href="https://daveclarke.me" rel="external nofollow noopener">Dave Clarke</a>. As I
mentioned at the start, Markdig has a <em>lot</em> of extensions,
some simple, some complex - there's going to be a fair chunk
of useful code in there to help you with your own.</p>
</blockquote>
<h2 id="supporting-classes">Supporting classes</h2>
<p>I'm actually going to create the components in a backwards order
from the list above, as each step depends on the one before it,
so it would make for awkward reading if I was referencing things
that don't yet exist.</p>
<p>To get started with some actual code, I'm going to need a couple
of supporting classes - an options object for configuring the
extension (at the bare minimum we need to supply the base URI of
a MantisBT installation), and also class to present a link in
the AST.</p>
<p>First the options class. As well as that base URI, I'll also add
an option to determine if the links generated by the application
should open in a new window or not via the <code>target</code> attribute.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">class</span> MantisLinkOptions
<span class="symbol">{</span>
 <span class="keyword">public</span> MantisLinkOptions<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>OpenInNewWindow <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> MantisLinkOptions<span class="symbol">(</span><span class="keyword">string</span> url<span class="symbol">)</span>
 <span class="symbol">:</span> <span class="keyword">this</span><span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Url <span class="symbol">=</span> url<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> MantisLinkOptions<span class="symbol">(</span>Uri uri<span class="symbol">)</span>
 <span class="symbol">:</span> <span class="keyword">this</span><span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Url <span class="symbol">=</span> uri<span class="symbol">.</span>OriginalString<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">bool</span> OpenInNewWindow <span class="symbol">{</span><span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">string</span> Url <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
</pre>
</figure>
<p>Next up is the object which will present our link in the syntax
tree. Markdig nodes are very similar to HTML, coming in two
flavours - block and inline. In this article I'm only covering
simple inline nodes.</p>
<p>I'm going to inherit from <code>LeafInline</code> and add a single property
to hold the Mantis issue number.</p>
<blockquote>
<p>There is actually a more specific <code>LinkInline</code> element which
is probably a much better choice to use (as it also means you
shouldn't need a custom renderer). However, I'm doing this
example the &quot;long way&quot; so that when I move onto the more
complex use cases I have for Markdig, I have a better
understanding of the API.</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>DebuggerDisplay<span class="symbol">(</span><span class="string">&quot;#{&quot;</span> <span class="symbol">+</span> nameof<span class="symbol">(</span>IssueNumber<span class="symbol">)</span> <span class="symbol">+</span> <span class="string">&quot;}&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">class</span> MantisLink <span class="symbol">:</span> LeafInline
<span class="symbol">{</span>
 <span class="keyword">public</span> StringSlice IssueNumber <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="string-vs-stringslice">String vs StringSlice</h3>
<p>In the above class, I'm using the <code>StringSlice</code> struct offered
by Markdig. You can use a normal <code>string</code> if you wish (or any
other type for that matter), but <code>StringSlice</code> was specifically
designed for Markdig to improve performance and reduce
allocations. In fact, that's how I heard of Markdig to start
with, when I read Alexandre's comprehensive <a href="http://xoofx.com/blog/2016/06/13/implementing-a-markdown-processor-for-dotnet/" rel="external nofollow noopener">blog post</a> on
the subject last year.</p>
<h2 id="creating-the-renderer">Creating the renderer</h2>
<p>With the two supporting classes out the way, I can now create
the rendering component. Markdig renderer's take an element from
the AST and spit out some content. Easy enough - we create a
class, inherit <code>HtmlObjectRenderer&lt;T&gt;</code> (where <code>T</code> is the name of
your AST class, e.g. <code>MantisLink</code>) and override the <code>Write</code>
method. If you are using a configuration class, then creating a
constructor to assign that is also a good idea.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">class</span> MantisLinkRenderer <span class="symbol">:</span> HtmlObjectRenderer<span class="symbol">&lt;</span>MantisLink<span class="symbol">&gt;</span>
<span class="symbol">{</span>
 <span class="keyword">private</span> MantisLinkOptions _options<span class="symbol">;</span>

 <span class="keyword">public</span> MantisLinkRenderer<span class="symbol">(</span>MantisLinkOptions options<span class="symbol">)</span>
 <span class="symbol">{</span>
 _options <span class="symbol">=</span> options<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> Write<span class="symbol">(</span>HtmlRenderer renderer<span class="symbol">,</span> MantisLink obj<span class="symbol">)</span>
 <span class="symbol">{</span>
 StringSlice issueNumber<span class="symbol">;</span>

 issueNumber <span class="symbol">=</span> obj<span class="symbol">.</span>IssueNumber<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>renderer<span class="symbol">.</span>EnableHtmlForInline<span class="symbol">)</span>
 <span class="symbol">{</span>
 renderer<span class="symbol">.</span>Write<span class="symbol">(</span><span class="string">&quot;&lt;a href=\&quot;&quot;</span><span class="symbol">)</span><span class="symbol">.</span>Write<span class="symbol">(</span>_options<span class="symbol">.</span>Url<span class="symbol">)</span><span class="symbol">.</span>Write<span class="symbol">(</span><span class="string">&quot;view.php?id=&quot;</span><span class="symbol">)</span><span class="symbol">.</span>Write<span class="symbol">(</span>issueNumber<span class="symbol">)</span><span class="symbol">.</span>Write<span class="symbol">(</span><span class="string">&#39;&quot;&#39;</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_options<span class="symbol">.</span>OpenInNewWindow<span class="symbol">)</span>
 <span class="symbol">{</span>
 renderer<span class="symbol">.</span>Write<span class="symbol">(</span><span class="string">&quot; target=\&quot;blank\&quot; rel=\&quot;noopener noreferrer\&quot;&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 renderer<span class="symbol">.</span>Write<span class="symbol">(</span><span class="string">&#39;&gt;&#39;</span><span class="symbol">)</span><span class="symbol">.</span>Write<span class="symbol">(</span><span class="string">&#39;#&#39;</span><span class="symbol">)</span><span class="symbol">.</span>Write<span class="symbol">(</span>issueNumber<span class="symbol">)</span><span class="symbol">.</span>Write<span class="symbol">(</span><span class="string">&quot;&lt;/a&gt;&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 renderer<span class="symbol">.</span>Write<span class="symbol">(</span><span class="string">&#39;#&#39;</span><span class="symbol">)</span><span class="symbol">.</span>Write<span class="symbol">(</span>obj<span class="symbol">.</span>IssueNumber<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>So how does this work? The <code>Write</code> method we're overriding
supplies the <code>HtmlRenderer</code> to write to, and the <code>MantisLink</code>
object to render.</p>
<p>First we need to check if we should be rendering HTML by
checking the <code>EnableHtmlForInline</code> property. If this is <code>false</code>,
then we output the plain text, e.g. just the issue number and
the <code>#</code> prefix.</p>
<p>If we are writing full HTML, then it's a matter of building a
HTML <code>a</code> tag with the fully qualified URI generated from the
base URI in the options object, and the AST node's issue number.
We also add a <code>target</code> attribute if the options state that links
should be in a new window. If we <em>do</em> add a <code>target</code> attribute
I'm also adding a <code>rel</code> attribute as per MDN <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-target" rel="external nofollow noopener">guidelines</a>.</p>
<p>Notice how the <code>HtmlRenderer</code> objects <code>Write</code> method happily
accepts <code>string</code>, <code>char</code> or <code>StringSlice</code> arguments, meaning we
can mix and match to suit our purposes.</p>
<h2 id="creating-the-parser">Creating the parser</h2>
<p>With rendering out of the way, it's time for the most complex
part of creating an extension - parsing it from a source
document. For that, we need to inherit from <code>InlineParser</code> and
overwrite the <code>Match</code> method, as well as setting up the
characters that would trigger the parse routine - that single
<code>#</code> character in our example.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">class</span> MantisLinkInlineParser <span class="symbol">:</span> InlineParser
<span class="symbol">{</span>
 <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> <span class="keyword">char</span><span class="symbol">[</span><span class="symbol">]</span> _openingCharacters <span class="symbol">=</span>
 <span class="symbol">{</span>
 <span class="string">&#39;#&#39;</span>
 <span class="symbol">}</span><span class="symbol">;</span>

 <span class="keyword">public</span> MantisLinkInlineParser<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>OpeningCharacters <span class="symbol">=</span> _openingCharacters<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">bool</span> Match<span class="symbol">(</span>InlineProcessor processor<span class="symbol">,</span> <span class="keyword">ref</span> StringSlice slice<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">bool</span> matchFound<span class="symbol">;</span>
 <span class="keyword">char</span> previous<span class="symbol">;</span>

 matchFound <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>

 previous <span class="symbol">=</span> slice<span class="symbol">.</span>PeekCharExtra<span class="symbol">(</span><span class="symbol">-</span><span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>previous<span class="symbol">.</span>IsWhiteSpaceOrZero<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">||</span> previous <span class="symbol">==</span> <span class="string">&#39;(&#39;</span> <span class="symbol">||</span> previous <span class="symbol">==</span> <span class="string">&#39;[&#39;</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">char</span> current<span class="symbol">;</span>
 <span class="keyword">int</span> start<span class="symbol">;</span>
 <span class="keyword">int</span> end<span class="symbol">;</span>

 slice<span class="symbol">.</span>NextChar<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 current <span class="symbol">=</span> slice<span class="symbol">.</span>CurrentChar<span class="symbol">;</span>
 start <span class="symbol">=</span> slice<span class="symbol">.</span>Start<span class="symbol">;</span>
 end <span class="symbol">=</span> start<span class="symbol">;</span>

 <span class="keyword">while</span> <span class="symbol">(</span>current<span class="symbol">.</span>IsDigit<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 end <span class="symbol">=</span> slice<span class="symbol">.</span>Start<span class="symbol">;</span>
 current <span class="symbol">=</span> slice<span class="symbol">.</span>NextChar<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>current<span class="symbol">.</span>IsWhiteSpaceOrZero<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">||</span> current <span class="symbol">==</span> <span class="string">&#39;)&#39;</span> <span class="symbol">||</span> current <span class="symbol">==</span> <span class="string">&#39;]&#39;</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> inlineStart<span class="symbol">;</span>

 inlineStart <span class="symbol">=</span> processor<span class="symbol">.</span>GetSourcePosition<span class="symbol">(</span>slice<span class="symbol">.</span>Start<span class="symbol">,</span> <span class="keyword">out</span> <span class="keyword">int</span> line<span class="symbol">,</span> <span class="keyword">out</span> <span class="keyword">int</span> column<span class="symbol">)</span><span class="symbol">;</span>

 processor<span class="symbol">.</span>Inline <span class="symbol">=</span> <span class="keyword">new</span> MantisLink
 <span class="symbol">{</span>
 Span <span class="symbol">=</span>
 <span class="symbol">{</span>
 Start <span class="symbol">=</span> inlineStart<span class="symbol">,</span>
 End <span class="symbol">=</span> inlineStart <span class="symbol">+</span> <span class="symbol">(</span>end <span class="symbol">-</span> start<span class="symbol">)</span> <span class="symbol">+</span> <span class="number">1</span>
 <span class="symbol">}</span><span class="symbol">,</span>
 Line <span class="symbol">=</span> line<span class="symbol">,</span>
 Column <span class="symbol">=</span> column<span class="symbol">,</span>
 IssueNumber <span class="symbol">=</span> <span class="keyword">new</span> StringSlice<span class="symbol">(</span>slice<span class="symbol">.</span>Text<span class="symbol">,</span> start<span class="symbol">,</span> end<span class="symbol">)</span>
 <span class="symbol">}</span><span class="symbol">;</span>

 matchFound <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> matchFound<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>In the constructor, we set the <code>OpeningCharacters</code> property to a
character array. When Markdig is parsing content, if it comes
across any of the characters in this array it will automatically
call your extension.</p>
<p>This neatly leads us onto the meat of this class - overriding
the <code>Match</code> method. Here, we scan the source document and try to
build up our node. If we're successful, we update the processor
and let Markdig handle the rest.</p>
<p>We know the current character is going to be <code>#</code> as this is our
only supported opener. However, we need to check the previous
character to make sure that we try and process an distinct
entity, and not a <code>#</code> character that happens to be in the middle
of another string.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
previous <span class="symbol">=</span> slice<span class="symbol">.</span>PeekCharExtra<span class="symbol">(</span><span class="symbol">-</span><span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">if</span> <span class="symbol">(</span>previous<span class="symbol">.</span>IsWhiteSpaceOrZero<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">||</span> previous <span class="symbol">==</span> <span class="string">&#39;(&#39;</span> <span class="symbol">||</span> previous <span class="symbol">==</span> <span class="string">&#39;[&#39;</span><span class="symbol">)</span>
</pre>
</figure>
<p>Here I use an extension method exposed by Markdig to check if
the previous character was either whitespace, or nothing at all,
i.e. the start of the document. I'm also checking for <code>(</code> or <code>[</code>
characters in case the issue number has been wrapped in brackets
or square braces.</p>
<p>If we pass this check, then it's time to parse the issue number.
First we advance the character stream (to discard the <code>#</code>
opener) and also initalize the values for creating a final
<code>StringSlice</code> if we're successful.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
slice<span class="symbol">.</span>NextChar<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

current <span class="symbol">=</span> slice<span class="symbol">.</span>CurrentChar<span class="symbol">;</span>
start <span class="symbol">=</span> slice<span class="symbol">.</span>Start<span class="symbol">;</span>
end <span class="symbol">=</span> start<span class="symbol">;</span>
</pre>
</figure>
<p>As our GitHub/MantisBT issue numbers are just that, plain
numbers, we simply keep advancing the stream until we run out of
digits.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">while</span> <span class="symbol">(</span>current<span class="symbol">.</span>IsDigit<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 end <span class="symbol">=</span> slice<span class="symbol">.</span>Start<span class="symbol">;</span>
 current <span class="symbol">=</span> slice<span class="symbol">.</span>NextChar<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>As I'm going to work exclusively with the <code>StringSlice</code>
struct, I'm only recording where the new slice will end. Even
if you wanted to use a more traditional string, it probably
makes sense to keep the above construct and then build your
string at the end.</p>
</blockquote>
<p>Once we've ran out of digits, we now essentially do a reverse of
the check we made at the start - now we want to see if the next
character is white space, the end of the stream, or a closing
bracket/brace.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">if</span> <span class="symbol">(</span>current<span class="symbol">.</span>IsWhiteSpaceOrZero<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">||</span> current <span class="symbol">==</span> <span class="string">&#39;)&#39;</span> <span class="symbol">||</span> current <span class="symbol">==</span> <span class="string">&#39;]&#39;</span><span class="symbol">)</span>
</pre>
</figure>
<blockquote>
<p>I didn't add a check for this, but potentially you should also
look for matching pair - so if a bracket was used at the
start, a closing bracket should therefore be present at the
end.</p>
</blockquote>
<p>Assuming this final check passes, that means we have a valid
<code>#&lt;number&gt;</code> sequence, and so we create a new <code>MantisLink</code>
object with the <code>IssueNumber</code> property populated with a brand
new string slice. We then assign this new object to the <code>Inline</code>
property of the processor.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
inlineStart <span class="symbol">=</span> processor<span class="symbol">.</span>GetSourcePosition<span class="symbol">(</span>slice<span class="symbol">.</span>Start<span class="symbol">,</span> <span class="keyword">out</span> <span class="keyword">int</span> line<span class="symbol">,</span> <span class="keyword">out</span> <span class="keyword">int</span> column<span class="symbol">)</span><span class="symbol">;</span>

processor<span class="symbol">.</span>Inline <span class="symbol">=</span> <span class="keyword">new</span> MantisLink
 <span class="symbol">{</span>
 Span <span class="symbol">=</span>
 <span class="symbol">{</span>
 Start <span class="symbol">=</span> inlineStart<span class="symbol">,</span>
 End <span class="symbol">=</span> inlineStart <span class="symbol">+</span> <span class="symbol">(</span>end <span class="symbol">-</span> start<span class="symbol">)</span>
 <span class="symbol">}</span><span class="symbol">,</span>
 Line <span class="symbol">=</span> line<span class="symbol">,</span>
 Column <span class="symbol">=</span> column<span class="symbol">,</span>
 IssueNumber <span class="symbol">=</span> <span class="keyword">new</span> StringSlice<span class="symbol">(</span>slice<span class="symbol">.</span>Text<span class="symbol">,</span> start<span class="symbol">,</span> end<span class="symbol">)</span>
 <span class="symbol">}</span><span class="symbol">;</span>
</pre>
</figure>
<blockquote>
<p>I'm not sure if the <code>Line</code> and <code>Column</code> properties are used
directly by Markdig, or if they are only for debugging or
advanced AST scenarios. I'm also uncertain what the purpose of
setting the <code>Span</code> property is - even though I based this code
on the code from the Markdig repository, it doesn't seem to
quite match up should I print out its contents. This leaves me
wondering if I'm setting the wrong values. So far I haven't
noticed any adverse effects though.</p>
</blockquote>
<h2 id="creating-the-extension">Creating the extension</h2>
<p>The first thing to set up is the core extension. Markdig
extensions implement the <code>IMarkdownExtension</code> interface. This
simple interface exposes two overloads of a <code>Setup</code> method for
configuring the parsing and rendering aspect of the extension.</p>
<p>One of these overloads is for customising the pipeline - we'll
add our parser here. The second overload is for setting up the
renderer. Depending on the nature of your extension you may only
need one or the other.</p>
<p>As this class is responsible for creating any renders or parsers
your extension needs, that also means it needs to have access to
any required configuration classes to pass down.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">class</span> MantisLinkerExtension <span class="symbol">:</span> IMarkdownExtension
<span class="symbol">{</span>
 <span class="keyword">private</span> <span class="keyword">readonly</span> MantisLinkOptions _options<span class="symbol">;</span>

 <span class="keyword">public</span> MantisLinkerExtension<span class="symbol">(</span>MantisLinkOptions options<span class="symbol">)</span>
 <span class="symbol">{</span>
 _options <span class="symbol">=</span> options<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">void</span> Setup<span class="symbol">(</span>MarkdownPipelineBuilder pipeline<span class="symbol">)</span>
 <span class="symbol">{</span>
 OrderedList<span class="symbol">&lt;</span>InlineParser<span class="symbol">&gt;</span> parsers<span class="symbol">;</span>

 parsers <span class="symbol">=</span> pipeline<span class="symbol">.</span>InlineParsers<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>parsers<span class="symbol">.</span>Contains<span class="symbol">&lt;</span>MantisLinkInlineParser<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 parsers<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> MantisLinkInlineParser<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">void</span> Setup<span class="symbol">(</span>MarkdownPipeline pipeline<span class="symbol">,</span> IMarkdownRenderer renderer<span class="symbol">)</span>
 <span class="symbol">{</span>
 HtmlRenderer htmlRenderer<span class="symbol">;</span>
 ObjectRendererCollection renderers<span class="symbol">;</span>

 htmlRenderer <span class="symbol">=</span> renderer <span class="keyword">as</span> HtmlRenderer<span class="symbol">;</span>
 renderers <span class="symbol">=</span> htmlRenderer<span class="symbol">?.</span>ObjectRenderers<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>renderers <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span>renderers<span class="symbol">.</span>Contains<span class="symbol">&lt;</span>MantisLinkRenderer<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 renderers<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> MantisLinkRenderer<span class="symbol">(</span>_options<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Firstly, I make sure the constructor accepts an argument of the
<code>MantisLinkOptions</code> class to pass to the renderer.</p>
<p>In the <code>Setup</code> overload that configures the pipeline, I first
check to make sure the <code>MantisLinkInlineParser</code> parser isn't
already present; if not I add it.</p>
<p>In a very similar fashion, in the <code>Setup</code> overload that
configures the renderer, I first check to see if a
<code>HtmlRenderer</code> renderer was provided - after all, you could be
using a custom renderer which wasn't HTML based. If I have got a
<code>HtmlRenderer</code> renderer then I do a similar check to make sure a
<code>MantisLinkRenderer</code> instance isn't present, and if not I create
on using the provided options class and add it.</p>
<h2 id="adding-an-initialisation-extension-method">Adding an initialisation extension method</h2>
<p>Although you could register extensions by directly manipulating
the <code>Extensions</code> property of a <code>MarkdownPipelineBuilder</code>,
generally Markdig extensions include an extension method which
performs the boilerplate code of checking and adding the
extension. The extension below checks to see if the
<code>MantisLinkerExtension</code> has been registered with a given
pipeline, and if not adds it with the specified options.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> MarkdownPipelineBuilder UseMantisLinks<span class="symbol">(</span><span class="keyword">this</span> MarkdownPipelineBuilder pipeline<span class="symbol">,</span> MantisLinkOptions options<span class="symbol">)</span>
<span class="symbol">{</span>
 OrderedList<span class="symbol">&lt;</span>IMarkdownExtension<span class="symbol">&gt;</span> extensions<span class="symbol">;</span>

 extensions <span class="symbol">=</span> pipeline<span class="symbol">.</span>Extensions<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>extensions<span class="symbol">.</span>Contains<span class="symbol">&lt;</span>MantisLinkerExtension<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 extensions<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> MantisLinkerExtension<span class="symbol">(</span>options<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> pipeline<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="using-the-extension">Using the extension</h2>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
MarkdownPipeline pipline<span class="symbol">;</span>
<span class="keyword">string</span> html<span class="symbol">;</span>
<span class="keyword">string</span> markdown<span class="symbol">;</span>

markdown <span class="symbol">=</span> <span class="string">&quot;See issue #1&quot;</span><span class="symbol">;</span>

pipline <span class="symbol">=</span> <span class="keyword">new</span> MarkdownPipelineBuilder<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>Build<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

html <span class="symbol">=</span> Markdown<span class="symbol">.</span>ToHtml<span class="symbol">(</span>markdown<span class="symbol">,</span> pipline<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// &lt;p&gt;See issue #1&lt;/p&gt;</span>

pipline <span class="symbol">=</span> <span class="keyword">new</span> MarkdownPipelineBuilder<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>UseMantisLinks<span class="symbol">(</span><span class="keyword">new</span> MantisLinkOptions<span class="symbol">(</span><span class="string">&quot;https://issues.cyotek.com/&quot;</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">.</span>Build<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

html <span class="symbol">=</span> Markdown<span class="symbol">.</span>ToHtml<span class="symbol">(</span>markdown<span class="symbol">,</span> pipline<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// &lt;p&gt;See issue &lt;a href=&quot;https://issues.cyotek.com/view.php?id=1&quot; target=&quot;blank&quot; rel=&quot;noopener noreferrer&quot;&gt;#1&lt;/a&gt;&lt;/p&gt;</span>
</pre>
</figure>
<p><em>Example of using an extension to automatically generate links
for MantisBT issue numbers.</em></p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>In this article I showed how to introduce new inline elements
parsed from markdown. This example at least was straightforward,
however there is more that can be done. More advanced extensions
such as pipeline tables have much more complex parsers that
generate a complete AST of their own.</p>
<p>Markdig supports other ways to extend itself too. For example,
the Auto Identifiers shown at the start of the article doesn't
parse markdown but instead manipulates the AST even as it is
being generated. The Emphasis Extra extension injects itself
into another extension to add more functionality to that. There
appears to be quite a few ways you can hook into the library in
order to add your own custom functionality!</p>
<p>A complete sample project can be downloaded from the URL below
or from the <a href="https://github.com/cyotek/MarkdigMantisLink" rel="external nofollow noopener">GitHub page</a> for the project.</p>
<p>Although I wrote this example with Mantis Bug Tracker in mind,
it wouldn't take very much effort at all to make it cover
innumerable other websites.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-08-05 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/writing-custom-markdig-extensions .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comAnnouncing MantisSharp, a .NET client for using the MantisBT REST APIurn:uuid:333839c8-ea35-4ed7-b124-2eac848bb2d22017-07-10T18:28:52Z2017-07-10T18:28:52Z<p>I've released a new open source project named MantisSharp, a
simple .NET client for working with the recently introduced REST
API for <a href="https://mantisbt.org/" rel="external nofollow noopener">Mantis Bug Tracker</a>.</p>
<p>The library is just getting started and is missing various
functions (hello documentation!) but it seems to be usable - as
well as the WinForms sample browser that I was using for
development testing, I also tested it in an ASP.NET MVC
application, both locally and then remotely using the
development version of cyotek.com.</p>
<p>It's probably not ready for prime time, I need to add docs,
samples and finally get serious about using await/async, plus
get a .NET Standard build done. But I think it's getting off to
a good start.</p>
<p>The GitHub repository can be found at
<a href="https://github.com/cyotek/MantisSharp">https://github.com/cyotek/MantisSharp</a> - the <a href="https://github.com/cyotek/MantisSharp/blob/master/README.md" rel="external nofollow noopener">readme</a> has
lots of extra details so I'm not going to repeat it here.</p>
<h2 id="why-create-this-library">Why create this library?</h2>
<p>Originally I wanted to use the MantisBT REST API to
automatically generate the product roadmaps on cyotek.com -
currently these are manual, and looking at the last modification
dates on the content entries shows the latest update was in
2015. Ouch. As I've been properly planning releases in our
MantisBT instance, it made sense to use that data. However, I
don't want to open access (anonymous or otherwise) to the
MantisBT instance itself, hence deciding to use the new API they
added recently.</p>
<p>I wasn't planning create a full blown library, I thought I'd
just load the JSON into a <code>dynamic</code> and grab what I needed that
way. But that untyped code offended me so much (and oddly enough
there didn't seem to be another client out there from a <em>very</em>
brief check of NuGet) that in the end it was inevitable.</p>
<p>Assuming more than just me uses this library I'd love to hear
your feedback.</p>
<h2 id="getting-started">Getting Started</h2>
<p>As well as the source, you can grab precompiled binaries via a
NuGet package</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
Install-Package MantisSharp -Pre
</pre>
</figure>
<p>The package includes builds for .NET 3.5, 4.0, 4.5 and 4.6. 4.7
will follow when I pave my machine and get the Creators Update,
.NET Standard will follow as soon as I actually add it as a
target and resolve any API issues.</p>
<p>Then just create an instance of the <code>MantisClient</code>, passing the
base URI where your MantisBT installation is hosted, along with
an API key. Also note that by default the REST API is disabled
and needs to be explicitly switched on for external access.
(There's a <a href="https://github.com/cyotek/MantisSharp/wiki" rel="external nofollow noopener">wiki page</a> which tells you how).</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
MantisClient client <span class="symbol">=</span> <span class="keyword">new</span> MantisClient<span class="symbol">(</span><span class="string">&quot;YOUR_MANTIS_URI&quot;</span><span class="symbol">,</span> <span class="string">&quot;YOUR_API_KEY&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

<span class="comment">// list all projects</span>
<span class="keyword">foreach</span> <span class="symbol">(</span>Project project <span class="keyword">in</span> client<span class="symbol">.</span>GetProjects<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span>project<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="comment">// list all issues</span>
<span class="keyword">foreach</span> <span class="symbol">(</span>Issue issue <span class="keyword">in</span> client<span class="symbol">.</span>GetIssues<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span>issue<span class="symbol">.</span>Summary<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="comment">// list issues for a single project</span>
<span class="keyword">var</span> issues <span class="symbol">=</span> client<span class="symbol">.</span>GetIssues<span class="symbol">(</span><span class="number">4</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// or pass in a Project reference</span>

<span class="comment">// get a single issue</span>
Issue issue <span class="symbol">=</span> client<span class="symbol">.</span>GetIssue<span class="symbol">(</span><span class="number">52</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="known-issues">Known Issues</h2>
<p>There's still outstanding work to do, some of which is detailed
in the readme. I also haven't done much testing yet, and our
MantisBT database is currently quite small, so I don't know how
the library will perform under bigger databases.</p>
<h2 id="examples">Examples</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/mantissharp-1b.png" class="gallery" title="An example of the WinForms demonstration application" ><img src="https://images.cyotek.com/image/thumbnail/devblog/mantissharp-1b.png" alt="An example of the WinForms demonstration application" decoding="async" loading="lazy" /></a><figcaption>An example of the WinForms demonstration application</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/mantissharp-1a.png" class="gallery" title="An example of creating a roadmap type page using the REST API" ><img src="https://images.cyotek.com/image/thumbnail/devblog/mantissharp-1a.png" alt="An example of creating a roadmap type page using the REST API" decoding="async" loading="lazy" /></a><figcaption>An example of creating a roadmap type page using the REST API</figcaption></figure><h2 id="update-history">Update History</h2>
<ul>
<li>2017-07-10 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/announcing-mantissharp-a-net-client-for-using-the-mantisbt-rest-api .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comTranslating text with Azure cognitive servicesurn:uuid:3f0d87b9-4550-4739-a22b-99341a20d7912021-02-11T17:15:09Z2017-05-05T18:41:10Z<p>Some time ago, I used the Bing Translator API to help create
localization for some of our products. As Microsoft recently
retired the Data Market used to provide this service it was high
time to migrate to the replacement Cognitive Services API hosted
on Azure. This article covers using the basics of Azure
cognitive services to translate text using simple HTTP requests.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/translatetext-1c.png" class="gallery" title="Sample project demonstrating the use of the cognitive services API" ><img src="https://images.cyotek.com/image/thumbnail/devblog/translatetext-1c.png" alt="Sample project demonstrating the use of the cognitive services API" decoding="async" loading="lazy" /></a><figcaption>Sample project demonstrating the use of the cognitive services API</figcaption></figure><h2 id="getting-started">Getting started</h2>
<p>I'm going to assume you've already signed up for the Text
Translation Cognitive Services API. If you haven't, you can find
a step by step guide on the <a href="https://docs.microsofttranslator.com/text-translate.html#getting-started" rel="external nofollow noopener">API documentation</a> site. Just as
with the original version, there's a free tier where you can
translate 2 million characters per month.</p>
<p>Once you have created your API service, display the <strong>Keys</strong>
page and copy one of the keys for use in your application (it
doesn't matter which one you choose).</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/translatetext-1b.png" class="gallery" title="Manage keys page in the Azure Portal" ><img src="https://images.cyotek.com/image/thumbnail/devblog/translatetext-1b.png" alt="Manage keys page in the Azure Portal" decoding="async" loading="lazy" /></a><figcaption>Manage keys page in the Azure Portal</figcaption></figure>
<blockquote>
<p>Remember that these keys should be kept secret. Don't paste
them in screenshots as I have above (unless you regenerated
the key after taking the screenshot!), don't commit them to
public code repositories - treat them as any other password.
<em>&quot;Keep it secret, keep it safe&quot;</em>.</p>
</blockquote>
<h2 id="creating-a-login-token">Creating a login token</h2>
<p>The first thing we need to do generate an authentication token.
We do this by sending a <code>POST</code> request to Microsoft's
authentication API along with a custom
<code>Ocp-Apim-Subscription-Key</code> header that contains the API key we
copied earlier.</p>
<blockquote>
<p>Note: When using the <code>HttpWebRequest</code> object, you <strong>must</strong> set
the <code>ContentLength</code> to be zero even though we're not actually
setting any body content. If the header isn't present the
authentication server will throw a <code>411</code> (Length Required)
HTTP exception.</p>
</blockquote>
<p>Assuming we have passed a valid API key, the response body will
contain a token we can use with subsequent requests.</p>
<p>Tokens are only valid for 10 minutes and it is recommended you
renew these after 8 or so minutes. For this reason, I store the
current time so that future requests can compare the stored time
against the current and automatically renew the token if
required.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">string</span> _authorizationKey<span class="symbol">;</span>
<span class="keyword">private</span> <span class="keyword">string</span> _authorizationToken<span class="symbol">;</span>
<span class="keyword">private</span> DateTime _timestampWhenTokenExpires<span class="symbol">;</span>

<span class="keyword">private</span> <span class="keyword">void</span> RefreshToken<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 HttpWebRequest request<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>_authorizationKey<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidOperationException<span class="symbol">(</span><span class="string">&quot;Authorization key not set.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 request <span class="symbol">=</span> WebRequest<span class="symbol">.</span>CreateHttp<span class="symbol">(</span><span class="string">&quot;https://api.cognitive.microsoft.com/sts/v1.0/issueToken&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 request<span class="symbol">.</span>Method <span class="symbol">=</span> WebRequestMethods<span class="symbol">.</span>Http<span class="symbol">.</span>Post<span class="symbol">;</span>
 request<span class="symbol">.</span>Headers<span class="symbol">.</span>Add<span class="symbol">(</span><span class="string">&quot;Ocp-Apim-Subscription-Key&quot;</span><span class="symbol">,</span> _authorizationKey<span class="symbol">)</span><span class="symbol">;</span>
 request<span class="symbol">.</span>ContentLength <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> <span class="comment">// Must be set to avoid 411 response</span>

 <span class="keyword">using</span> <span class="symbol">(</span>HttpWebResponse response <span class="symbol">=</span> <span class="symbol">(</span>HttpWebResponse<span class="symbol">)</span>request<span class="symbol">.</span>GetResponse<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _authorizationToken <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetResponseString<span class="symbol">(</span>response<span class="symbol">)</span><span class="symbol">;</span>

 _timestampWhenTokenExpires <span class="symbol">=</span> DateTime<span class="symbol">.</span>UtcNow<span class="symbol">.</span>AddMinutes<span class="symbol">(</span><span class="number">8</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="using-the-token">Using the token</h2>
<p>For all subsequent requests in this article, we'll be sending
the token with the request. This is done via the <code>Authorization</code>
header which needs to be set with the string <code>Bearer &lt;TOKEN&gt;</code>.</p>
<h2 id="getting-available-languages">Getting available languages</h2>
<p>The translation API can translate a reasonable range of
languages (including for some reason Klingon), but it can't
translate all languages. Therefore, if you're building a
solution that uses the translation API it's probably a good idea
to find out what languages are available. This can be done by
calling the <code>GetLanguagesForTranslate</code> service method.</p>
<p>Rather annoyingly the translation API doesn't use
straightforward JSON objects but instead the ancient XML
serialization dialect (it appears to be a WCF service rather
than newer WebAPI) which seems an odd choice in this day and age
of easily consumed JSON services. Still, at least it means I can
create a self contained example project without needing external
packages.</p>
<p>First we create the <code>HttpWebRequest</code> object and assign our
<code>Authorization</code> header. Next, we set the value of the <code>Accept</code>
header to be <code>application/xml</code>. The API call actually seems to
ignore this header and always return XML regardless, but at
least if it changes in future to support multiple outputs our
existing code is explicit in what it wants.</p>
<p>The body of the response contains a XML document similar to the
following</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">ArrayOfstring</span> <span class="name">xmlns</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://schemas.microsoft.com/2003/10/Serialization/Arrays</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>af<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>ar<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>bn<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="comment">&lt;!-- SNIP --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>ur<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>vi<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>cy<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">ArrayOfstring</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>You could parse it yourself, but I usually don't like the
overhead of having to work with name-spaced XML documents.
Fortunately, I can just use the <code>DataContractSerializer</code> to
parse it for me.</p>
<blockquote>
<p>In order to use the <code>DataContractSerializer</code> class you need to
have a reference to <code>System.Runtime.Serialization</code> in your
project.</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">string</span><span class="symbol">[</span><span class="symbol">]</span> GetLanguages<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 HttpWebRequest request<span class="symbol">;</span>
 <span class="keyword">string</span><span class="symbol">[</span><span class="symbol">]</span> results<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CheckToken<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 request <span class="symbol">=</span> WebRequest<span class="symbol">.</span>CreateHttp<span class="symbol">(</span><span class="string">&quot;https://api.microsofttranslator.com/v2/http.svc/GetLanguagesForTranslate&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 request<span class="symbol">.</span>Headers<span class="symbol">.</span>Add<span class="symbol">(</span><span class="string">&quot;Authorization&quot;</span><span class="symbol">,</span> <span class="string">&quot;Bearer &quot;</span> <span class="symbol">+</span> _authorizationToken<span class="symbol">)</span><span class="symbol">;</span>
 request<span class="symbol">.</span>Accept <span class="symbol">=</span> <span class="string">&quot;application/xml&quot;</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>WebResponse response <span class="symbol">=</span> request<span class="symbol">.</span>GetResponse<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>Stream stream <span class="symbol">=</span> response<span class="symbol">.</span>GetResponseStream<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 results <span class="symbol">=</span> <span class="symbol">(</span><span class="symbol">(</span>List<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">&gt;</span><span class="symbol">)</span><span class="keyword">new</span> DataContractSerializer<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>List<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">&gt;</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ReadObject<span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToArray<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> results<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="getting-language-names">Getting language names</h2>
<p>The previous section obtains a list of ISO language codes, but
generally you would probably want to present something more
friendly to end-users. We can obtain localized language names
via the <code>GetLanguageNames</code> method.</p>
<p>This time we need to perform a <code>POST</code>, and include a custom body
containing the language codes we wish to retrieve friendly names
for, along with a query string argument that specifies which
language to use for the friendly names.</p>
<p>The body should be XML similar to the following. This is
identical to the output of the <code>GetLanguagesForTranslate</code> call
above.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">ArrayOfstring</span> <span class="name">xmlns</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://schemas.microsoft.com/2003/10/Serialization/Arrays</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>af<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>ar<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>bn<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="comment">&lt;!-- SNIP --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>ur<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>vi<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>cy<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">ArrayOfstring</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>The response body will be a string array where each element
contains the friendly language name of the matching element from
the request body. The following example is a sample of output
when German (<code>de</code>) friendly names are requested.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">ArrayOfstring</span> <span class="name">xmlns</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://schemas.microsoft.com/2003/10/Serialization/Arrays</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>Afrikaans<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>Arabisch<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>Bangla<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="comment">&lt;!-- SNIP --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>Urdu<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>Vietnamesisch<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>Walisisch<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">ArrayOfstring</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>Previously we used the <code>DataContractSerializer</code> deserialize the
response body and we can use the same class to serialize the
request body too. We also have to specify the <code>Content-Type</code> of
the data we're transmitting. And of course make sure we include
the <code>locale</code> query string argument in the posted URI.</p>
<blockquote>
<p>If you forget to set the <code>Content-Type</code> header then according
to the <a href="https://docs.microsofttranslator.com/text-translate.html#!/default/post_GetLanguageNames" rel="external nofollow noopener">documentation</a> you'd probably expect it to return
400 (Bad Request). Somewhat curiously, it returns 200 (OK)
with a 500-esque HTML error message in the body. So don't
forget to set the content type!</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">string</span><span class="symbol">[</span><span class="symbol">]</span> GetLocalizedLanguageNames<span class="symbol">(</span><span class="keyword">string</span> locale<span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">[</span><span class="symbol">]</span> languages<span class="symbol">)</span>
<span class="symbol">{</span>
 HttpWebRequest request<span class="symbol">;</span>
 <span class="keyword">string</span><span class="symbol">[</span><span class="symbol">]</span> results<span class="symbol">;</span>
 DataContractSerializer serializer<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CheckToken<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 
 serializer <span class="symbol">=</span> <span class="keyword">new</span> DataContractSerializer<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span><span class="keyword">string</span><span class="symbol">[</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 request <span class="symbol">=</span> WebRequest<span class="symbol">.</span>CreateHttp<span class="symbol">(</span><span class="string">&quot;https://api.microsofttranslator.com/v2/http.svc/GetLanguageNames?locale=&quot;</span> <span class="symbol">+</span> locale<span class="symbol">)</span><span class="symbol">;</span>
 request<span class="symbol">.</span>Headers<span class="symbol">.</span>Add<span class="symbol">(</span><span class="string">&quot;Authorization&quot;</span><span class="symbol">,</span> <span class="string">&quot;Bearer &quot;</span> <span class="symbol">+</span> _authorizationToken<span class="symbol">)</span><span class="symbol">;</span>
 request<span class="symbol">.</span>Accept <span class="symbol">=</span> <span class="string">&quot;application/xml&quot;</span><span class="symbol">;</span>
 request<span class="symbol">.</span>ContentType <span class="symbol">=</span> <span class="string">&quot;application/xml&quot;</span><span class="symbol">;</span> <span class="comment">// must be set to avoid invalid 200 response</span>
 request<span class="symbol">.</span>Method <span class="symbol">=</span> WebRequestMethods<span class="symbol">.</span>Http<span class="symbol">.</span>Post<span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Stream stream <span class="symbol">=</span> request<span class="symbol">.</span>GetRequestStream<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 serializer<span class="symbol">.</span>WriteObject<span class="symbol">(</span>stream<span class="symbol">,</span> languages<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">using</span> <span class="symbol">(</span>WebResponse response <span class="symbol">=</span> request<span class="symbol">.</span>GetResponse<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>Stream stream <span class="symbol">=</span> response<span class="symbol">.</span>GetResponseStream<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 results <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">string</span><span class="symbol">[</span><span class="symbol">]</span><span class="symbol">)</span>serializer<span class="symbol">.</span>ReadObject<span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> results<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="translating-phrases">Translating phrases</h2>
<p>The final piece of the puzzle is to actually translate a string.
We can do this using the <code>Translate</code> service method, which is a
simple enough method to use - you pass the text, source language
and output language as query string parameters, and the
translation will be returned in the response body as an XML
string.</p>
<blockquote>
<p>You can also specify a category for the translation. I believe
this is for use with Microsoft's Translation Hub so as of yet
I haven't tried experimenting with this parameter.</p>
</blockquote>
<p>The following example is a the response returned when requesting
a translation of <code>Hello World!</code> from English (<code>en</code>) to German
(<code>de</code>).</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">string</span> <span class="name">xmlns</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://schemas.microsoft.com/2003/10/Serialization/</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>Hallo Welt!<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>The request is similar to other examples in this article. The
only point to note is that as the <code>text</code> query string argument
will contain user enterable content, I'm encoding it using
<code>Uri.EscapeDataString</code> to account for any special characters.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">string</span> Translate<span class="symbol">(</span><span class="keyword">string</span> text<span class="symbol">,</span> <span class="keyword">string</span> <span class="keyword">from</span><span class="symbol">,</span> <span class="keyword">string</span> to<span class="symbol">)</span>
<span class="symbol">{</span>
 HttpWebRequest request<span class="symbol">;</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>
 <span class="keyword">string</span> queryString<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CheckToken<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 queryString <span class="symbol">=</span> <span class="keyword">string</span><span class="symbol">.</span>Concat<span class="symbol">(</span><span class="string">&quot;text=&quot;</span><span class="symbol">,</span> Uri<span class="symbol">.</span>EscapeDataString<span class="symbol">(</span>text<span class="symbol">)</span><span class="symbol">,</span> <span class="string">&quot;&amp;from=&quot;</span><span class="symbol">,</span> <span class="keyword">from</span><span class="symbol">,</span> <span class="string">&quot;&amp;to=&quot;</span><span class="symbol">,</span> to<span class="symbol">)</span><span class="symbol">;</span>

 request <span class="symbol">=</span> WebRequest<span class="symbol">.</span>CreateHttp<span class="symbol">(</span><span class="string">&quot;https://api.microsofttranslator.com/v2/http.svc/Translate?&quot;</span> <span class="symbol">+</span> queryString<span class="symbol">)</span><span class="symbol">;</span>
 request<span class="symbol">.</span>Headers<span class="symbol">.</span>Add<span class="symbol">(</span><span class="string">&quot;Authorization&quot;</span><span class="symbol">,</span> <span class="string">&quot;Bearer &quot;</span> <span class="symbol">+</span> _authorizationToken<span class="symbol">)</span><span class="symbol">;</span>
 request<span class="symbol">.</span>Accept <span class="symbol">=</span> <span class="string">&quot;application/xml&quot;</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>WebResponse response <span class="symbol">=</span> request<span class="symbol">.</span>GetResponse<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>Stream stream <span class="symbol">=</span> response<span class="symbol">.</span>GetResponseStream<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">string</span><span class="symbol">)</span>_stringDataContractSerializer<span class="symbol">.</span>ReadObject<span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="other-api-methods">Other API methods</h2>
<p>The <code>GetLanguagesForTranslate</code>, <code>GetLanguageNames</code> and
<code>Translate</code> API methods above describe the basics of using the
translation services. The service API does offer additional
functionality, such as the ability to translate multiple strings
at once or to return multiple translations for a single string
or even to try and detect the language of a piece of text. These
are for use in more advanced scenarios that what I'm currently
interested in and so I haven't looked further into these
methods.</p>
<h2 id="sample-application">Sample application</h2>
<p>The code samples in this article are both overly verbose (lots
of duplicate setup and processing code) and functionally lacking
(no checking of status codes or handling of errors). The
download sample accompanying this article includes a more robust
<code>TranslationClient</code> class that can be easily used to add the
basics of the translation APIs to your own applications.</p>
<p>Note that unlike most of my other articles/samples this one
won't run out the box - the keys seen in the application and
screenshots have been revoked, and you'll need to substitute the
ones you get when you created your service using the Azure
Portal.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-05-05 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/translating-text-with-azure-cognitive-services .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comUsing custom type converters with C# and YamlDotNet, part 2urn:uuid:5cb26e3d-52a9-41db-b50d-0392dd965bcd2017-04-24T19:31:29Z2017-04-24T19:31:29Z<p>Recently I discussed using type converters to perform custom
serialization of types in YamlDotNet. In this post I'll
concentrate on expanding the type converter to support
deserialization as well.</p>
<p>I'll be reusing a lot of code and knowledge from the <a href="/post/using-custom-type-converters-with-csharp-and-yamldotnet-part-1">first
part</a> of this mini-series, so if you haven't read that yet it
is a good place to start.</p>
<blockquote>
<p>Even more so that with part 1, in this article I'm completely
winging it. This code works in my demonstration program but
I'm by no means confident it is error free or the best way of
reading YAML objects.</p>
</blockquote>
<p>To deserialize data via a type converter, we need to implement
the <code>ReadYaml</code> method of the <code>IYamlTypeConverter</code> interface.
This method provides an object implementing <code>IParser</code> for
reading the YAML, along with a <code>type</code> parameter describing the
type of object the method should return. This latter parameter
can be ignored unless your converter can handle multiple object
types.</p>
<p>The <code>IParser</code> interface itself is very basic - a <code>MoveNext</code>
method to advance the parser, and a <code>Current</code> property which
returns the current <code>ParsingEvent</code> object (the same types of
object we originally used to write the YAML).</p>
<p>YamlDotNet also adds a few extension methods to this interface
which may be of use. Although in this sample project I'm only
using the base interface, I try to point out where you could use
these extension methods which you may find more readable to use.</p>
<p>A key tip is to always advance the parser by calling <code>MoveNext</code>
- if you don't, then YamlDotNet will call your converter again
and again in an infinite loop. This is the very first issue I
encountered when I wrote some placeholder code as below and then
ran the demo program.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">object</span> ReadYaml<span class="symbol">(</span>IParser parser<span class="symbol">,</span> Type type<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// As we&#39;re not advancing the parser, we&#39;ve just introduced an infinte loop</span>
 <span class="keyword">return</span> <span class="keyword">new</span> ContentCategory<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>You should probably consider having automated tests that run as
you're writing the code using a tool such as NCrunch. Just as
with serializing, I found writing deserialization code using
YamlDotNet to be non-intuitive and debugging counter productive.</p>
<h2 id="reading-property-maps">Reading property maps</h2>
<p>To read a map, we first check to ensure the current element is
<code>MappingStart</code> instance. Then just keep reading and processing
nodes until we get a corresponding <code>MappingEnd</code> object.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> Type _mappingStartType <span class="symbol">=</span> <span class="keyword">typeof</span><span class="symbol">(</span>MappingStart<span class="symbol">)</span><span class="symbol">;</span>
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> Type _mappingEndType <span class="symbol">=</span> <span class="keyword">typeof</span><span class="symbol">(</span>MappingEnd<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">public</span> <span class="keyword">object</span> ReadYaml<span class="symbol">(</span>IParser parser<span class="symbol">,</span> Type type<span class="symbol">)</span>
<span class="symbol">{</span>
 ContentCategory result<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>parser<span class="symbol">.</span>Current<span class="symbol">.</span>GetType<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">!=</span> _mappingStartType<span class="symbol">)</span> <span class="comment">// You could also use parser.Accept&lt;MappingStart&gt;()</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="string">&quot;Invalid YAML content.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 parser<span class="symbol">.</span>MoveNext<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// move on from the map start</span>

 result <span class="symbol">=</span> <span class="keyword">new</span> ContentCategory<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">do</span>
 <span class="symbol">{</span>
 <span class="comment">// do something with the current node</span>

 parser<span class="symbol">.</span>MoveNext<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span> <span class="keyword">while</span> <span class="symbol">(</span>parser<span class="symbol">.</span>Current<span class="symbol">.</span>GetType<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">!=</span> _mappingEndType<span class="symbol">)</span><span class="symbol">;</span>

 parser<span class="symbol">.</span>MoveNext<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// skip the mapping end (or crash)</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>With the basics in place, we can now process the nodes inside
our loop. As it is a mapping, any value should be preceded by a
scalar name and often will be followed by a simple scalar value.
For this reason I added a helper method to check if the current
node is a <code>Scalar</code> and if so return its value (otherwise to
throw an exception).</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">string</span> GetScalarValue<span class="symbol">(</span>IParser parser<span class="symbol">)</span>
<span class="symbol">{</span>
 Scalar scalar<span class="symbol">;</span>

 scalar <span class="symbol">=</span> parser<span class="symbol">.</span>Current <span class="keyword">as</span> Scalar<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>scalar <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="string">&quot;Failed to retrieve scalar value.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 
 <span class="comment">// You could replace the above null check with parser.Expect&lt;Scalar&gt; which will throw its own exception</span>
 
 <span class="keyword">return</span> scalar<span class="symbol">.</span>Value<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Inside the main processing loop, I get the scalar value that
represents the name of the property to process and advance the
reader to get it ready to process the property value. I then
check the property name and act accordingly depending on if it
is a simple or complex type.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">string</span> value<span class="symbol">;</span>

value <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetScalarValue<span class="symbol">(</span>parser<span class="symbol">)</span><span class="symbol">;</span>
parser<span class="symbol">.</span>MoveNext<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// skip the scalar property name</span>

<span class="keyword">switch</span> <span class="symbol">(</span>value<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">case</span> <span class="string">&quot;Name&quot;</span><span class="symbol">:</span>
 result<span class="symbol">.</span>Name <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetScalarValue<span class="symbol">(</span>parser<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="string">&quot;Title&quot;</span><span class="symbol">:</span>
 result<span class="symbol">.</span>Title <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetScalarValue<span class="symbol">(</span>parser<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="string">&quot;Topics&quot;</span><span class="symbol">:</span>
 <span class="keyword">this</span><span class="symbol">.</span>ReadTopics<span class="symbol">(</span>parser<span class="symbol">,</span> result<span class="symbol">.</span>Topics<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="string">&quot;Categories&quot;</span><span class="symbol">:</span>
 <span class="keyword">this</span><span class="symbol">.</span>ReadContentCategories<span class="symbol">(</span>parser<span class="symbol">,</span> result<span class="symbol">.</span>Categories<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">default</span><span class="symbol">:</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="string">&quot;Unexpected scalar value &#39;&quot;</span> <span class="symbol">+</span> value <span class="symbol">+</span> <span class="string">&quot;&#39;.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>For the sample <code>Name</code> and <code>Title</code> properties of my
<code>ContentCategory</code> object, I use the <code>GetScalarValue</code> helper
method above to just return the string value. The <code>Topics</code> and
<code>Categories</code> properties however are collection objects, which
leads us nicely to the next section.</p>
<h2 id="reading-lists">Reading lists</h2>
<p>Reading lists is fairly similar to maps, except this time we
start by looking for <code>SequenceStart</code> and ending with
<code>SequenceEnd</code>. Otherwise the logic is fairly similar. For
example, in the demonstration project, the <code>Topics</code> property is
a list of strings and therefore can be easily read by reading
each scalar entry in the sequence.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> Type _sequenceEndType <span class="symbol">=</span> <span class="keyword">typeof</span><span class="symbol">(</span>SequenceEnd<span class="symbol">)</span><span class="symbol">;</span>
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> Type _sequenceStartType <span class="symbol">=</span> <span class="keyword">typeof</span><span class="symbol">(</span>SequenceStart<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">private</span> <span class="keyword">void</span> ReadTopics<span class="symbol">(</span>IParser parser<span class="symbol">,</span> StringCollection topics<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>parser<span class="symbol">.</span>Current<span class="symbol">.</span>GetType<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">!=</span> _sequenceStartType<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="string">&quot;Invalid YAML content.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 parser<span class="symbol">.</span>MoveNext<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// skip the sequence start</span>

 <span class="keyword">do</span>
 <span class="symbol">{</span>
 topics<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>GetScalarValue<span class="symbol">(</span>parser<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 parser<span class="symbol">.</span>MoveNext<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span> <span class="keyword">while</span> <span class="symbol">(</span>parser<span class="symbol">.</span>Current<span class="symbol">.</span>GetType<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">!=</span> _sequenceEndType<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Sequences don't have to be lists of simple values, they can be
complex objects of their own. As our <code>ContentCategory</code> object
can have children of the same type, another helper method
repeatedly calls the base <code>ReadYaml</code> method to construct child
objects.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> ReadContentCategories<span class="symbol">(</span>IParser parser<span class="symbol">,</span> ContentCategoryCollection categories<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>parser<span class="symbol">.</span>Current<span class="symbol">.</span>GetType<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">!=</span> _sequenceStartType<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="string">&quot;Invalid YAML content.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 parser<span class="symbol">.</span>MoveNext<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// skip the sequence start</span>

 <span class="keyword">do</span>
 <span class="symbol">{</span>
 categories<span class="symbol">.</span>Add<span class="symbol">(</span><span class="symbol">(</span>ContentCategory<span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>ReadYaml<span class="symbol">(</span>parser<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span> <span class="keyword">while</span> <span class="symbol">(</span>parser<span class="symbol">.</span>Current<span class="symbol">.</span>GetType<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">!=</span> _sequenceEndType<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>What I don't know how to do however, is invoke the original
parser logic for handling other types. Nor do I know how our
custom type converters are supposed to make use of
<code>INamingConvention</code> implementations. The demo project is using
capitalisation, but the production code is using pure lowercase
to avoid any ambiguity.</p>
<h2 id="using-the-custom-type-converter">Using the custom type converter</h2>
<p>Just as we did with the <code>SerializerBuilder</code> in part 1, we use
the <code>WithTypeConverter</code> method on a <code>DeserializerBuilder</code>
instance to inform YamlDotNet of the existence of our converter.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Deserializer deserializer<span class="symbol">;</span>

deserializer <span class="symbol">=</span> <span class="keyword">new</span> DeserializerBuilder<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>WithTypeConverter<span class="symbol">(</span><span class="keyword">new</span> ContentCategoryYamlTypeConverter<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">.</span>Build<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>It would be nice if I could decorate my types with a YamlDotNet
version of the standard <code>TypeConverter</code> attribute and so avoid
having to manually use <code>WithTypeConverter</code> but this doesn't seem
to be a supported feature.</p>
<h2 id="closing">Closing</h2>
<p>Custom YAML serialization and deserialization with YamlDotNet
isn't as straightforward as perhaps could be but it isn't
difficult to do. Even better, if you serialize valid YAML then
it's entirely possible (as in my case where I'm attempting to
serialize less default values) that you don't need to write
custom deserialization code at all as YamlDotNet will handle it
for you.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-04-24 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/using-custom-type-converters-with-csharp-and-yamldotnet-part-2 .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comUsing custom type converters with C# and YamlDotNet, part 1urn:uuid:1b8d8ca1-3ac4-4ae4-91ae-a30c9e17a0312017-04-24T18:20:21Z2017-04-01T16:36:51Z<p>One of our internal tools eschews XML or JSON configuration
files in favour of something more human readable - <a href="http://yaml.org/" rel="external nofollow noopener">YAML</a>
using <a href="http://aaubry.net/pages/yamldotnet.html" rel="external nofollow noopener">YamlDotNet</a>. For the most part the serialisation and
deserialisation of YAML documents in .NET objects is as straight
forward as using libraries such as <a href="http://www.newtonsoft.com/json" rel="external nofollow noopener">JSON.net</a> but when I was
working on some basic serialisation there were a few issues.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/yaml-serialisation-1a.png" class="gallery" title="A demonstration program showing the basics of YAML serialisation" ><img src="https://images.cyotek.com/image/thumbnail/devblog/yaml-serialisation-1a.png" alt="A demonstration program showing the basics of YAML serialisation" decoding="async" loading="lazy" /></a><figcaption>A demonstration program showing the basics of YAML serialisation</figcaption></figure><h2 id="setting-the-scene">Setting the scene</h2>
<p>For this demonstration project, I'm going to use a pair of basic
classes.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">sealed</span> <span class="keyword">class</span> ContentCategoryCollection <span class="symbol">:</span> Collection<span class="symbol">&lt;</span>ContentCategory<span class="symbol">&gt;</span>
<span class="symbol">{</span>
 <span class="keyword">private</span> ContentCategory _parent<span class="symbol">;</span>

 <span class="keyword">public</span> ContentCategory Parent
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _parent<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 _parent <span class="symbol">=</span> value<span class="symbol">;</span>

 <span class="keyword">foreach</span> <span class="symbol">(</span>ContentCategory item <span class="keyword">in</span> <span class="keyword">this</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 item<span class="symbol">.</span>Parent <span class="symbol">=</span> value<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> InsertItem<span class="symbol">(</span><span class="keyword">int</span> index<span class="symbol">,</span> ContentCategory item<span class="symbol">)</span>
 <span class="symbol">{</span>
 item<span class="symbol">.</span>Parent <span class="symbol">=</span> _parent<span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>InsertItem<span class="symbol">(</span>index<span class="symbol">,</span> item<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">internal</span> <span class="keyword">sealed</span> <span class="keyword">class</span> ContentCategory
<span class="symbol">{</span>
 <span class="keyword">private</span> ContentCategoryCollection _categories<span class="symbol">;</span>

 <span class="keyword">private</span> StringCollection _topics<span class="symbol">;</span>

 <span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> ContentCategoryCollection Categories
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _categories <span class="symbol">??</span> <span class="symbol">(</span>_categories <span class="symbol">=</span> <span class="keyword">new</span> ContentCategoryCollection <span class="symbol">{</span> Parent <span class="symbol">=</span> <span class="keyword">this</span> <span class="symbol">}</span><span class="symbol">)</span><span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> _categories <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="symbol">[</span>DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">]</span>
 <span class="symbol">[</span>DefaultValue<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">bool</span> HasCategories
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _categories <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> _categories<span class="symbol">.</span>Count <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="symbol">[</span>DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">]</span>
 <span class="symbol">[</span>DefaultValue<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">bool</span> HasTopics
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _topics <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> _topics<span class="symbol">.</span>Count <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">string</span> Name <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> ContentCategory Parent <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">string</span> Title <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> StringCollection Topics
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _topics <span class="symbol">??</span> <span class="symbol">(</span>_topics <span class="symbol">=</span> <span class="keyword">new</span> StringCollection<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> _topics <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The classes are fairly simple, but they do offer some small
challenges for serialisation</p>
<ul>
<li>Read-only properties</li>
<li>Parent references</li>
<li>Special values - child collections that are only initialised
when they are accessed and should be ignored if null or empty</li>
</ul>
<h2 id="basic-serialisation">Basic serialisation</h2>
<p>Using YamlDotNet, you can serialise an object graph quite simply
enough</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Serializer serializer<span class="symbol">;</span>
<span class="keyword">string</span> yaml<span class="symbol">;</span>

serializer <span class="symbol">=</span> <span class="keyword">new</span> SerializerBuilder<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">.</span>Build<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

yaml <span class="symbol">=</span> serializer<span class="symbol">.</span>Serialize<span class="symbol">(</span>_categories<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="basic-deserialisation">Basic deserialisation</h2>
<p>Deserialising a YAML document into a .NET object is also quite
straightforward</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Deserializer deserializer<span class="symbol">;</span>

deserializer <span class="symbol">=</span> <span class="keyword">new</span> DeserializerBuilder<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">.</span>Build<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">using</span> <span class="symbol">(</span>Stream stream <span class="symbol">=</span> File<span class="symbol">.</span>OpenRead<span class="symbol">(</span>fileName<span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>TextReader reader <span class="symbol">=</span> <span class="keyword">new</span> StreamReader<span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _categories <span class="symbol">=</span> deserializer<span class="symbol">.</span>Deserialize<span class="symbol">&lt;</span>ContentCategoryCollection<span class="symbol">&gt;</span><span class="symbol">(</span>reader<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="serialisation-shortcomings">Serialisation shortcomings</h2>
<p>The following is an example of the YAML produced by the above
classes with default serialisation</p>
<figure class="lang-yaml highlight"><figcaption><span>yaml</span></figcaption><pre class="code">
- Categories: []
 HasTopics: true
 Name: intro
 Title: Introducing {{ applicationname }}
 Topics:
 - whatis.md
 - licenseagreement.md
- &amp;o0
 Categories:
 - Categories: []
 Name: userinterface
 Parent: *o0
 Title: User Interface
 Topics: []
 HasCategories: true
 Name: gettingstarted
 Title: Getting Started
 Topics: []
- Categories: []
 Name: blank
 Title: Blank
 Topics: []
</pre>
</figure>
<p>For a format that is &quot;human friendly&quot; this is quite verbose with
a lot of extra clutter as the serialisation has included the
read-only properties (which will then cause a crash on
deserialisation), and our create-on-demand collections are being
created and serialised as empty values. It is also slightly
alien when you consider the alias references. While those are
undeniably cool (especially as YamlDotNet will recreate the
references), the nested nature of the properties implicitly
indicate the relationships and are therefore superfluous in this
case</p>
<p>It's also worth pointing out that the order of the serialised
values matches the ordering in code file - I always format my
code files to order members alphabetically, so the properties
are also serialised alphabetically.</p>
<p>You can also see that, for the most part, the <code>HasCategories</code>
and <code>HasTopics</code> properties were not serialised - although
YamlDotNet is ignoring the <code>BrowsableAttribute</code>, it is
processing the <code>DefaultValueAttribute</code> and skipping values which
are considered default, which is another nice feature.</p>
<h2 id="resolving-some-issues">Resolving some issues</h2>
<p>Similar to Json.NET, you can decorate your classes with
attributes to help control serialisation, and so we'll
investigate these first to see if they can resolve our problems
simply and easily.</p>
<h3 id="excluding-read-only-properties">Excluding read-only properties</h3>
<p>The <code>YamlIgnoreAttribute</code> class can be used to force certain
properties to be skipped, so applying this attribute to
properties with only getters is a good idea.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>YamlIgnore<span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">bool</span> HasCategories
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _categories <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> _categories<span class="symbol">.</span>Count <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="changing-serialisation-order">Changing serialisation order</h3>
<p>We can control the order in which YamlDotNet serialises using
the <code>YamlMemberAttribute</code>. This attribute has various options,
but for the time being I'm just looking at ordering - I'll
revisit this attribute in the next post.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>YamlMember<span class="symbol">(</span>Order <span class="symbol">=</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">string</span> Name <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>If you specify this attribute on one property to set an order
you'll most likely need to set it on all.</p>
</blockquote>
<h3 id="processing-the-collection-properties">Processing the collection properties</h3>
<p>Unfortunately, while I could make use of the <code>YamlIgnore</code> and
<code>YamlMember</code> attributes to control some of the serialisation, it
wouldn't stop the empty collection nodes from being created and
then serialised, which I didn't want. I suppose I could finally
work out how to make <code>DefaultValue</code> apply to collection classes
effectively, but then there wouldn't be much point in this
article!</p>
<p>Due to this requirement, I'm going to need to write some custom
serialisation code - enter the <code>IYamlTypeConveter</code> interface.</p>
<h2 id="creating-a-custom-converter">Creating a custom converter</h2>
<p>To create a custom converter for use with YamlDotNet, we start
by creating a new class and implementing <code>IYamlTypeConverter</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">sealed</span> <span class="keyword">class</span> ContentCategoryYamlTypeConverter <span class="symbol">:</span> IYamlTypeConverter
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">bool</span> Accepts<span class="symbol">(</span>Type type<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">object</span> ReadYaml<span class="symbol">(</span>IParser parser<span class="symbol">,</span> Type type<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">void</span> WriteYaml<span class="symbol">(</span>IEmitter emitter<span class="symbol">,</span> <span class="keyword">object</span> value<span class="symbol">,</span> Type type<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>First thing is to specify what types our class can handle via
the <code>Accepts</code> method.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> Type _contentCategoryNodeType <span class="symbol">=</span> <span class="keyword">typeof</span><span class="symbol">(</span>ContentCategory<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">public</span> <span class="keyword">bool</span> Accepts<span class="symbol">(</span>Type type<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> type <span class="symbol">==</span> _contentCategoryNodeType<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>In this case, we only care about our <code>ContentCategory</code> class so
I return <code>true</code> for this type and <code>false</code> for anything else.</p>
<p>Next, it's time to write the YAML content via the <code>WriteYaml</code>
method.</p>
<blockquote>
<p>The documentation for YamlDotNet is a little lacking and I
didn't find the serialisation support to be particularly
intuitive, so the code I'm presenting below is what worked for
me, but there may be better ways of doing it.</p>
</blockquote>
<p>First we need to get the value to serialise - this is via the
<code>value</code> and <code>type</code> parameters. In my example, I can ignore
<code>type</code> though as I'm only supporting the one type.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">void</span> WriteYaml<span class="symbol">(</span>IEmitter emitter<span class="symbol">,</span> <span class="keyword">object</span> value<span class="symbol">,</span> Type type<span class="symbol">)</span>
<span class="symbol">{</span>
 ContentCategory node<span class="symbol">;</span>

 node <span class="symbol">=</span> <span class="symbol">(</span>ContentCategory<span class="symbol">)</span>value<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The <code>IEmitter</code> interface (accessed via the <code>emitter</code> parameter)
is similar in principle to JSON.net's <code>JsonTextWriter</code> class
except it is less developer friendly. Rather than having a
number of <code>Write*</code> methods or overloads similar to BCL
serialisation classes, it has a single <code>Emit</code> method which takes
in a variety of objects.</p>
<h2 id="writing-property-value-maps">Writing property value maps</h2>
<p>To create our dictionary map, we start by emitting a
<code>MappingStart</code> object. Of course, if you have a start you need
an end so we'll close by emitting <code>MappingEnd</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
emitter<span class="symbol">.</span>Emit<span class="symbol">(</span><span class="keyword">new</span> MappingStart<span class="symbol">(</span><span class="keyword">null</span><span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">,</span> MappingStyle<span class="symbol">.</span>Block<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

<span class="comment">// reset of serialisation code</span>

emitter<span class="symbol">.</span>Emit<span class="symbol">(</span><span class="keyword">new</span> MappingEnd<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<blockquote>
<p>YAML supports block and flow styles. Block is essentially one
value per line, while flow is a more condensed comma separated
style. Block is much more readable for complex objects, but
flow is probably more valuable for short lists of simple
values.</p>
</blockquote>
<p>Next we need to write our key value pairs, which we do by
emitting pairs of <code>Scalar</code> objects.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">if</span> <span class="symbol">(</span>node<span class="symbol">.</span>Name <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
<span class="symbol">{</span>
 emitter<span class="symbol">.</span>Emit<span class="symbol">(</span><span class="keyword">new</span> Scalar<span class="symbol">(</span><span class="keyword">null</span><span class="symbol">,</span> <span class="string">&quot;Name&quot;</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 emitter<span class="symbol">.</span>Emit<span class="symbol">(</span><span class="keyword">new</span> Scalar<span class="symbol">(</span><span class="keyword">null</span><span class="symbol">,</span> node<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">if</span> <span class="symbol">(</span>node<span class="symbol">.</span>Title <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
<span class="symbol">{</span>
 emitter<span class="symbol">.</span>Emit<span class="symbol">(</span><span class="keyword">new</span> Scalar<span class="symbol">(</span><span class="keyword">null</span><span class="symbol">,</span> <span class="string">&quot;Title&quot;</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 emitter<span class="symbol">.</span>Emit<span class="symbol">(</span><span class="keyword">new</span> Scalar<span class="symbol">(</span><span class="keyword">null</span><span class="symbol">,</span> node<span class="symbol">.</span>Title<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>Although the YAML specification allows for null values,
attempting to emit a <code>Scalar</code> with a null value seems to
destabilise the emitter and it will promptly crash on
subsequent calls to <code>Emit</code>. For this reason, in the code above
I wrap each pair in a null check. (Not to mention if it is a
null value there is probably no need to serialise anything
anyway).</p>
</blockquote>
<h2 id="writing-lists">Writing lists</h2>
<p>With the basic properties serialised, we can now turn to our
child collections.</p>
<p>This time, after writing a single <code>Scalar</code> with the property
name instead of writing another <code>Scalar</code> we use the
<code>SequenceStart</code> and <code>SequenceEnd</code> classes to tell YamlDotNet
we're going to serialise a list of values.</p>
<p>For our <code>Topics</code> property, the values are simple strings so we
can just emit a <code>Scalar</code> for each entry in the list.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">if</span> <span class="symbol">(</span>node<span class="symbol">.</span>HasTopics<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteTopics<span class="symbol">(</span>emitter<span class="symbol">,</span> node<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> WriteTopics<span class="symbol">(</span>IEmitter emitter<span class="symbol">,</span> ContentCategory node<span class="symbol">)</span>
<span class="symbol">{</span>
 emitter<span class="symbol">.</span>Emit<span class="symbol">(</span><span class="keyword">new</span> Scalar<span class="symbol">(</span><span class="keyword">null</span><span class="symbol">,</span> <span class="string">&quot;Topics&quot;</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 emitter<span class="symbol">.</span>Emit<span class="symbol">(</span><span class="keyword">new</span> SequenceStart<span class="symbol">(</span><span class="keyword">null</span><span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">,</span> SequenceStyle<span class="symbol">.</span>Block<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">foreach</span> <span class="symbol">(</span><span class="keyword">string</span> child <span class="keyword">in</span> node<span class="symbol">.</span>Topics<span class="symbol">)</span>
 <span class="symbol">{</span>
 emitter<span class="symbol">.</span>Emit<span class="symbol">(</span><span class="keyword">new</span> Scalar<span class="symbol">(</span><span class="keyword">null</span><span class="symbol">,</span> child<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 emitter<span class="symbol">.</span>Emit<span class="symbol">(</span><span class="keyword">new</span> SequenceEnd<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>As the <code>Categories</code> property returns a collection of
<code>ContentCategory</code> objects, we can simply start a new list as we
did for topics and then recursively call <code>WriteYaml</code> to write
each child category object in the list.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">if</span> <span class="symbol">(</span>node<span class="symbol">.</span>HasCategories<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteChildren<span class="symbol">(</span>emitter<span class="symbol">,</span> node<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> WriteChildren<span class="symbol">(</span>IEmitter emitter<span class="symbol">,</span> ContentCategory node<span class="symbol">)</span>
<span class="symbol">{</span>
 emitter<span class="symbol">.</span>Emit<span class="symbol">(</span><span class="keyword">new</span> Scalar<span class="symbol">(</span><span class="keyword">null</span><span class="symbol">,</span> <span class="string">&quot;Categories&quot;</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 emitter<span class="symbol">.</span>Emit<span class="symbol">(</span><span class="keyword">new</span> SequenceStart<span class="symbol">(</span><span class="keyword">null</span><span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">,</span> SequenceStyle<span class="symbol">.</span>Block<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">foreach</span> <span class="symbol">(</span>ContentCategory child <span class="keyword">in</span> node<span class="symbol">.</span>Categories<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteYaml<span class="symbol">(</span>emitter<span class="symbol">,</span> child<span class="symbol">,</span> _contentCategoryNodeType<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 emitter<span class="symbol">.</span>Emit<span class="symbol">(</span><span class="keyword">new</span> SequenceEnd<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="deserialisation">Deserialisation</h2>
<p>In this article, I'm only covering custom serialisation.
However, the beauty of this code is that it doesn't generate
different YAML from default serialisation, it only excludes
values that it knows are defaults or that can't be read back,
and provides custom ordering of values. This means you can use
the basic deserialisation code presented at the start of this
article and it will just work, as demonstrated by the sample
program accompanying this post.</p>
<p>For this reason, for the time being I change the <code>ReadYaml</code>
method of our custom type converter to throw an exception
instead of actually doing anything.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">object</span> ReadYaml<span class="symbol">(</span>IParser parser<span class="symbol">,</span> Type type<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> NotImplementedException<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="using-the-custom-type-converter">Using the custom type converter</h2>
<p>Now we have a functioning type converter, we need to tell
YamlDotNet about it.</p>
<p>At the start of the article, I showed how you create a
<code>SerializerBuilder</code> object and call its <code>Build</code> method to get a
configured <code>Serializer</code> class. By calling the builder
objects<code>WithTypeConverter</code> method, we can enable the use of our
custom converter.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Serializer serializer<span class="symbol">;</span>
<span class="keyword">string</span> yaml<span class="symbol">;</span>

serializer <span class="symbol">=</span> <span class="keyword">new</span> SerializerBuilder<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">.</span>WithTypeConverter<span class="symbol">(</span><span class="keyword">new</span> ContentCategoryYamlTypeConverter<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">.</span>Build<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

yaml <span class="symbol">=</span> serializer<span class="symbol">.</span>Serialize<span class="symbol">(</span>_categories<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>See the attached demonstration program for a fully working sample.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-04-01 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/using-custom-type-converters-with-csharp-and-yamldotnet-part-1 .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comWriting Microsoft RIFF Palette (pal) files with C#urn:uuid:951c1104-90dd-47b2-ada5-73af19090e742017-03-04T06:24:00Z2017-03-04T06:24:00Z<p>A short follow up and sample program which demonstrates how to
write a RIFF palette with ease.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/riff-2a.png" class="gallery" title="Example program that generates a random palette and saves it into a RIFF form" ><img src="https://images.cyotek.com/image/thumbnail/devblog/riff-2a.png" alt="Example program that generates a random palette and saves it into a RIFF form" decoding="async" loading="lazy" /></a><figcaption>Example program that generates a random palette and saves it into a RIFF form</figcaption></figure><h2 id="about-riff-palettes">About RIFF Palettes</h2>
<p>I covered the basics of the RIFF specification and how to read
palettes in my <a href="/post/loading-microsoft-riff-palette-pal-files-with-csharp#the-riff-file-format">previous article</a>.</p>
<h2 id="performance-considerations">Performance Considerations</h2>
<p>When I first started this journey and wrote how to read and
write palette files in different formats, the code I provided
generally read and wrote bytes one at a time. At the start of
January (2016, time has a habit of getting away from me!) I
wrote an <a href="/post/reading-and-writing-farbfeld-images-using-csharp">article</a> which described how to read and write
<a href="http://tools.suckless.org/farbfeld/" rel="external nofollow noopener">farbfeld</a> images.</p>
<p>While updating the <a href="https://github.com/cyotek/Cyotek.Drawing.Imaging.Farbfeld" rel="external nofollow noopener">source</a> for this project, I created a
series of benchmarks testing the serialisation code and proved
the obvious fact that reading and writing a byte a time was
<em>really</em> inefficient.</p>
<p>As a result of this, I'm now a little more careful when reading
and writing files. The previous article on reading RIFF palettes
tried to be efficient both in terms of IO (reading blocks of
information at a time) and in terms of allocations (by using the
same buffer object as much as possible), so hopefully that code
is quite efficient.</p>
<p>Similarly, when writing the file as per the code below, I create
a buffer large enough to hold the entire RIFF form - palettes
generally aren't huge objects so this is fine. I then populate
the buffer with the form and write it all at once.</p>
<p>There aren't any guards around this code though to ensure that
buffers are reasonably sized and so if this code was being
adapted (for example to read WAVE audio or AVI videos) then
additional precautions would be required.</p>
<h2 id="writing-int-and-ushort-values-into-byte-arrays">Writing <code>int</code> and <code>ushort</code> values into byte arrays</h2>
<p>As we're going to construct the entire RIFF form in a byte
array, we can't use classes such as <code>StreamWriter</code> to write
values. I'm going to use a pair of helper methods that will
break down an <code>int</code> into four bytes or a <code>ushort</code> into a pair of
bytes which I will then place into the array at appropriate
offsets.</p>
<blockquote>
<p>Remember that RIFF uses little-endian ordering</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> PutInt<span class="number">32</span><span class="symbol">(</span><span class="keyword">int</span> value<span class="symbol">,</span> <span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">]</span> buffer<span class="symbol">,</span> <span class="keyword">int</span> offset<span class="symbol">)</span>
<span class="symbol">{</span>
 buffer<span class="symbol">[</span>offset <span class="symbol">+</span> <span class="number">3</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span><span class="symbol">(</span>value <span class="symbol">&amp;</span> <span class="number">0xFF000000</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">24</span><span class="symbol">)</span><span class="symbol">;</span>
 buffer<span class="symbol">[</span>offset <span class="symbol">+</span> <span class="number">2</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span><span class="symbol">(</span>value <span class="symbol">&amp;</span> <span class="number">0x00FF0000</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">16</span><span class="symbol">)</span><span class="symbol">;</span>
 buffer<span class="symbol">[</span>offset <span class="symbol">+</span> <span class="number">1</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span><span class="symbol">(</span>value <span class="symbol">&amp;</span> <span class="number">0x0000FF00</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">8</span><span class="symbol">)</span><span class="symbol">;</span>
 buffer<span class="symbol">[</span>offset<span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span><span class="symbol">(</span>value <span class="symbol">&amp;</span> <span class="number">0x000000FF</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> PutInt<span class="number">16</span><span class="symbol">(</span><span class="keyword">ushort</span> value<span class="symbol">,</span> <span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">]</span> buffer<span class="symbol">,</span> <span class="keyword">int</span> offset<span class="symbol">)</span>
<span class="symbol">{</span>
 buffer<span class="symbol">[</span>offset <span class="symbol">+</span> <span class="number">1</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span><span class="symbol">(</span>value <span class="symbol">&amp;</span> <span class="number">0x0000FF00</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">8</span><span class="symbol">)</span><span class="symbol">;</span>
 buffer<span class="symbol">[</span>offset<span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span><span class="symbol">(</span>value <span class="symbol">&amp;</span> <span class="number">0x000000FF</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>You could use the <code>BitConverter</code> class to break down the
values, but that means extra allocations for the byte array
returned by the <code>GetBytes</code> method.</p>
</blockquote>
<h2 id="writing-a-riff-palette">Writing a RIFF palette</h2>
<p>First we need to calculate the size of our <code>data</code> chunk for the
palette, which is <code>4 + number_of_colors * 4</code>. Each colour is
comprised of <code>4</code> bytes, which accounts for the bulk of the
chunk, but there's also <code>4</code> bytes for the <code>palVersion</code> and
<code>palNumEntries</code> fields of the <a href="https://msdn.microsoft.com/en-us/library/dd145040%28v=vs.85%29.aspx" rel="external nofollow noopener"><code>LOGPALETTE</code></a> structure.</p>
<p>Once we have that size, we calculate the size of the complete
RIFF form and create a byte array that will hold the entire
form.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">]</span> buffer<span class="symbol">;</span>
<span class="keyword">int</span> length<span class="symbol">;</span>
<span class="keyword">ushort</span> count<span class="symbol">;</span>
<span class="keyword">ushort</span> chunkSize<span class="symbol">;</span>

count <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">ushort</span><span class="symbol">)</span>_palette<span class="symbol">.</span>Length<span class="symbol">;</span>
chunkSize <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">ushort</span><span class="symbol">)</span><span class="symbol">(</span><span class="number">4</span> <span class="symbol">+</span> count <span class="symbol">*</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">;</span>

<span class="comment">// 4 bytes for RIFF</span>
<span class="comment">// 4 bytes for document size</span>
<span class="comment">// 4 bytes for PAL</span>
<span class="comment">// 4 bytes for data</span>
<span class="comment">// 4 bytes for chunk size</span>
<span class="comment">// 2 bytes for the version</span>
<span class="comment">// 2 bytes for the count</span>
<span class="comment">// (4*n) for the colors</span>
length <span class="symbol">=</span> <span class="number">4</span> <span class="symbol">+</span> <span class="number">4</span> <span class="symbol">+</span> <span class="number">4</span> <span class="symbol">+</span> <span class="number">4</span> <span class="symbol">+</span> <span class="number">4</span> <span class="symbol">+</span> <span class="number">2</span> <span class="symbol">+</span> <span class="number">2</span> <span class="symbol">+</span> count <span class="symbol">*</span> <span class="number">4</span><span class="symbol">;</span>
buffer <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">byte</span><span class="symbol">[</span>length<span class="symbol">]</span><span class="symbol">;</span>
</pre>
</figure>
<p>Next, we write the RIFF header. Remember that the document size
is the size of the entire form minus 8 bytes representing the
RIFF header.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="comment">// the riff header</span>
buffer<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="string">&#39;R&#39;</span><span class="symbol">;</span>
buffer<span class="symbol">[</span><span class="number">1</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="string">&#39;I&#39;</span><span class="symbol">;</span>
buffer<span class="symbol">[</span><span class="number">2</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="string">&#39;F&#39;</span><span class="symbol">;</span>
buffer<span class="symbol">[</span><span class="number">3</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="string">&#39;F&#39;</span><span class="symbol">;</span>
WordHelpers<span class="symbol">.</span>PutInt<span class="number">32</span><span class="symbol">(</span>length <span class="symbol">-</span> <span class="number">8</span><span class="symbol">,</span> buffer<span class="symbol">,</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// document size</span>
</pre>
</figure>
<p>We then follow this with the form type</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="comment">// the form type</span>
buffer<span class="symbol">[</span><span class="number">8</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="string">&#39;P&#39;</span><span class="symbol">;</span>
buffer<span class="symbol">[</span><span class="number">9</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="string">&#39;A&#39;</span><span class="symbol">;</span>
buffer<span class="symbol">[</span><span class="number">10</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="string">&#39;L&#39;</span><span class="symbol">;</span>
buffer<span class="symbol">[</span><span class="number">11</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="string">&#39; &#39;</span><span class="symbol">;</span>
</pre>
</figure>
<p>So far so good. We won't be writing any meta data, only the
<code>data</code> chunk with our basic RGB palette. First we'll write the
chunk header, and then we'll write the first two fields
describing the palette.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="comment">// data chunk header</span>
buffer<span class="symbol">[</span><span class="number">12</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="string">&#39;d&#39;</span><span class="symbol">;</span>
buffer<span class="symbol">[</span><span class="number">13</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="string">&#39;a&#39;</span><span class="symbol">;</span>
buffer<span class="symbol">[</span><span class="number">14</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="string">&#39;t&#39;</span><span class="symbol">;</span>
buffer<span class="symbol">[</span><span class="number">15</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="string">&#39;a&#39;</span><span class="symbol">;</span>
WordHelpers<span class="symbol">.</span>PutInt<span class="number">32</span><span class="symbol">(</span>chunkSize<span class="symbol">,</span> buffer<span class="symbol">,</span> <span class="number">16</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// chunk size</span>

<span class="comment">// logpalette</span>
buffer<span class="symbol">[</span><span class="number">20</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
buffer<span class="symbol">[</span><span class="number">21</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="number">3</span><span class="symbol">;</span> <span class="comment">// os version (always 03)</span>
WordHelpers<span class="symbol">.</span>PutInt<span class="number">16</span><span class="symbol">(</span>count<span class="symbol">,</span> buffer<span class="symbol">,</span> <span class="number">22</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// colour count</span>
</pre>
</figure>
<p>Now it's just a case of filling in the colour information</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> count<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
<span class="symbol">{</span>
 Color color<span class="symbol">;</span>
 <span class="keyword">int</span> offset<span class="symbol">;</span>

 color <span class="symbol">=</span> _palette<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">;</span>

 offset <span class="symbol">=</span> <span class="number">24</span> <span class="symbol">+</span> i <span class="symbol">*</span> <span class="number">4</span><span class="symbol">;</span>

 buffer<span class="symbol">[</span>offset<span class="symbol">]</span> <span class="symbol">=</span> color<span class="symbol">.</span>R<span class="symbol">;</span>
 buffer<span class="symbol">[</span>offset <span class="symbol">+</span> <span class="number">1</span><span class="symbol">]</span> <span class="symbol">=</span> color<span class="symbol">.</span>G<span class="symbol">;</span>
 buffer<span class="symbol">[</span>offset <span class="symbol">+</span> <span class="number">2</span><span class="symbol">]</span> <span class="symbol">=</span> color<span class="symbol">.</span>B<span class="symbol">;</span>
 
 <span class="comment">// TODO: use buffer[offset + 3] for flags</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>And finally, we can write our buffer to the destination stream.
Easy!</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
stream<span class="symbol">.</span>Write<span class="symbol">(</span>buffer<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> length<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-03-04 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/writing-microsoft-riff-palette-pal-files-with-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comLoading Microsoft RIFF Palette (pal) files with C#urn:uuid:144dbc8d-691d-45a2-aea9-6e41ea1e128c2017-02-18T10:24:16Z2017-02-18T10:24:16Z<p>At the start of 2014, I published an article describing how to
read colour palettes from <a href="/post/loading-the-color-palette-from-a-bbm-lbm-image-file-using-csharp">BBM/LBM</a> files. At the end of that
article I noted that Microsoft palette files used a similar
format, but I didn't investigate that at the time. Since then I
followed up with articles on reading and writing Adobe's <a href="/post/reading-photoshop-color-swatch-aco-files-using-csharp">Color
Swatch</a> and <a href="/post/reading-adobe-swatch-exchange-ase-files-using-csharp">Color Exchange</a> format files and I also
posted code for working with <a href="https://github.com/cyotek/Cyotek.Windows.Forms.ColorPicker/blob/master/Cyotek.Windows.Forms.ColorPicker/JascPaletteSerializer.cs" rel="external nofollow noopener">JASC</a>, <a href="https://github.com/cyotek/Cyotek.Windows.Forms.ColorPicker/blob/master/Cyotek.Windows.Forms.ColorPicker/GimpPaletteSerializer.cs" rel="external nofollow noopener">Gimp</a> and a couple
of other palette formats.</p>
<p>Now, finally, I decided to complete the collection and present
an article on reading Microsoft's palette files. These files are
RIFF forms containing colour data, similar to a BBM palette
being an IIF form.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/riff-1c.png" class="gallery" title="Example program that can read the contents of a RIFF palette" ><img src="https://images.cyotek.com/image/thumbnail/devblog/riff-1c.png" alt="Example program that can read the contents of a RIFF palette" decoding="async" loading="lazy" /></a><figcaption>Example program that can read the contents of a RIFF palette</figcaption></figure><h2 id="the-riff-file-format">The RIFF File Format</h2>
<p>The Resource Interchange File Format (RIFF), a tagged file
structure, is a general specification upon which many file
formats can be defined. The main advantage of RIFF is its
extensibility; file formats based on RIFF can be future-proofed,
as format changes can be ignored by existing applications.</p>
<p>The above paragraph is taken verbatim from the <em>Multimedia
Programming Interface and Data Specifications 1.0</em> document
co-produced by Microsoft and IBM around the time of Windows 3.0.</p>
<p>The RIFF format shares allows different file types to use the
same underlying structure. For example, as well as the palettes
we'll cover in this article, Wave audio (<code>.wav</code>) files are RIFF
forms as are some MIDI (<code>.mid</code>) and device independent bitmap
(<code>.dib</code>) files.</p>
<p>A RIFF form is comprised of chunks of data tagged with an ID and
a size. Some chunk types are globally defined and can apply to
all resource types, while others are resource specific. Global
tags include the ability to specify meta data, such as artist
information or to specify language options such as a character
set.</p>
<p>The screenshot below shows the structure of a Wave file
containing <code>fmt</code> and <code>data</code> chunks and then a list of meta tags.
Notice how the meta tags are in upper-case but the <code>fmt</code> and
<code>data</code> tags are in lower-case. By convention, RIFF suggests that
global tags used by more than one form type are in upper-case,
whilst those specific to a single form type are in lower-case.
An <code>ISFT</code> tag in a Wave file means exactly the same thing as an
<code>ISFT</code> tag in a palette file, but the Wave's <code>data</code> tag does not
correspond with a palettes <code>data</code> tag.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/riff-1b.png" class="gallery" title="Viewing the chunks in a Waveform audio file" ><img src="https://images.cyotek.com/image/thumbnail/devblog/riff-1b.png" alt="Viewing the chunks in a Waveform audio file" decoding="async" loading="lazy" /></a><figcaption>Viewing the chunks in a Waveform audio file</figcaption></figure>
<p>Chunks are word-aligned, so if the size of a chunk is odd, an
extra padding byte must be added at the end of the chunk. Note
that the chunk size does not include this alignment byte, so you
must manually check if the size is odd and handle this
accordingly.</p>
<p>The nature of the chunk format means a program can scan a file,
process the chunks it recognises, and ignore those it doesn't
with relative ease.</p>
<p>Most of the binary formats I've previously covered use
<a href="https://en.wikipedia.org/wiki/Endianness" rel="external nofollow noopener">big-endian</a> ordering (including the original <em>EA IFF 85
Standard for Interchange Format Files</em> that RIFF is derived
from), however RIFF is a noticeable exception as it uses
little-endian (which the spec refers to as Intel byte-ordering.
There is a counterpart format, RIFX that uses big-endian
(referred to as Motorola byte-ordering). I don't think I've ever
come across this variant, so I won't be covering it in this
article.</p>
<p>A more advanced version of RIFF exists which makes use of
compound elements and content tables, but that is also far out
of the scope of this article.</p>
<h3 id="obtaining-the-specification">Obtaining the specification</h3>
<p>Unless you happen to have a hard-copy of the book lying around,
you can get an electronic version from Nicholas J Humfrey's
<a href="https://www.aelius.com/njh/wavemetatools/" rel="external nofollow noopener">WAVE Meta Tools</a> page.</p>
<h2 id="about-the-riff-palette">About the RIFF Palette</h2>
<p>There are actually two variants of RIFF palettes, <em>simple</em> and
<em>extended</em>. As I've only come across simple palettes in the
wild, this article will concentrate only on the former.</p>
<blockquote>
<p>If anyone does have extended versions, please let me know,
would be interesting to test these.</p>
</blockquote>
<p>The simple format is an array of RGB colours, easily earning the
simple moniker.</p>
<p>The extended variant includes extra header data describing how
the palette should be used, and can include either the basic RGB
palette, or palettes using YUV or XYZ colour data.</p>
<p>The following form-specific chunk types are supported</p>
<table>
<thead>
<tr>
<th>Signature</th>
<th>Description</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>plth</code></td>
<td>Palette header</td>
<td>Extended</td>
</tr>
<tr>
<td><code>data</code></td>
<td>RGB palette</td>
<td>Basic or Extended</td>
</tr>
<tr>
<td><code>yuvp</code></td>
<td>YUV palette</td>
<td>Extended</td>
</tr>
<tr>
<td><code>xyzp</code></td>
<td>XYZ palette</td>
<td>Extended</td>
</tr>
</tbody>
</table>
<p>The screenshot below shows a basic palette file loaded into a
chunk viewer. Unlike the Wave screenshot above, only a single
format-specific tag is present.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/riff-1a.png" class="gallery" title="Viewing the chunks in a simple palette file. Can you spot a bug?" ><img src="https://images.cyotek.com/image/thumbnail/devblog/riff-1a.png" alt="Viewing the chunks in a simple palette file. Can you spot a bug?" decoding="async" loading="lazy" /></a><figcaption>Viewing the chunks in a simple palette file. Can you spot a bug?</figcaption></figure><h2 id="reading-a-riff-file">Reading a RIFF file</h2>
<h3 id="reading-the-form-type">Reading the form type</h3>
<p>The header of a RIFF file is 12 bytes comprised of the following
information</p>
<ul>
<li>Four bytes containing the signature <code>RIFF</code></li>
<li>32-bit unsigned integer which contains the size of the
document</li>
<li>Four bytes containing the form type, for example <code>WAVE</code> or
<code>MIDI</code></li>
</ul>
<p>The form type for a palette is <code>PAL</code>. As this is less than four
characters, it is padded with trailing spaces to make up the
difference.</p>
<p>We can test to see if a file is a valid RIFF form using code
similar to</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
stream<span class="symbol">.</span>Read<span class="symbol">(</span>buffer<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="number">12</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="keyword">if</span> <span class="symbol">(</span>buffer<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span> <span class="symbol">!=</span> <span class="string">&#39;R&#39;</span> <span class="symbol">||</span> buffer<span class="symbol">[</span><span class="number">1</span><span class="symbol">]</span> <span class="symbol">!=</span> <span class="string">&#39;I&#39;</span> <span class="symbol">||</span> buffer<span class="symbol">[</span><span class="number">2</span><span class="symbol">]</span> <span class="symbol">!=</span> <span class="string">&#39;F&#39;</span> <span class="symbol">||</span> buffer<span class="symbol">[</span><span class="number">3</span><span class="symbol">]</span> <span class="symbol">!=</span> <span class="string">&#39;F&#39;</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="string">&quot;Source stream is not a RIFF document.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">if</span> <span class="symbol">(</span>buffer<span class="symbol">[</span><span class="number">8</span><span class="symbol">]</span> <span class="symbol">!=</span> <span class="string">&#39;P&#39;</span> <span class="symbol">||</span> buffer<span class="symbol">[</span><span class="number">9</span><span class="symbol">]</span> <span class="symbol">!=</span> <span class="string">&#39;A&#39;</span> <span class="symbol">||</span> buffer<span class="symbol">[</span><span class="number">10</span><span class="symbol">]</span> <span class="symbol">!=</span> <span class="string">&#39;L&#39;</span> <span class="symbol">||</span> buffer<span class="symbol">[</span><span class="number">11</span><span class="symbol">]</span> <span class="symbol">!=</span> <span class="string">&#39; &#39;</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="string">&quot;Source stream is not a palette.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>In the above example, I'm ignoring the size read from the
header. If you wanted to perform some extra validation, you
could always check the read value against the size of the file
you are processing - the read value should match the file size,
minus 8 bytes to account for the RIFF signature.</p>
<p>I'm also comparing each byte to a character as that is more
readable, but you could always treat the 12 bytes as 3 unsigned
32-bit integers and compare the numbers - <code>1179011410</code> for
<code>RIFF</code> and and <code>541868368</code> for <code>PAL </code> (don't forget the trailing
space!).</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">if</span> <span class="symbol">(</span>buffer<span class="symbol">.</span>ToInt<span class="symbol">(</span><span class="number">0</span><span class="symbol">)</span> <span class="symbol">!=</span> <span class="number">1179011410</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="string">&quot;Source stream is not a RIFF document.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">if</span> <span class="symbol">(</span>buffer<span class="symbol">.</span>ToInt<span class="symbol">(</span><span class="number">8</span><span class="symbol">)</span> <span class="symbol">!=</span> <span class="number">541868368</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="string">&quot;Source stream is not a palette.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Not quite as readable and so I'll just stick with looking at the
individual characters.</p>
<h3 id="reading-the-chunks">Reading the chunks</h3>
<p>Although most palettes probably only contain the data chunk,
additional chunks (such as meta data) could be present, and I
have seen some RIFF files where custom chunks were present
before the main data. For this reason, I'm not going to blindly
assume that the palette is the first chunk and will iterate over
each one searching for palette data.</p>
<p>In a RIFF file, a chunk is identified by a four byte character
code, followed by a 32-bit unsigned integer describing the size
of the data. This means we can read the 8 byte header, decide if
we support the chunk or not, and if we don't we can simply skip
over the number of bytes identified by the size.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">while</span> <span class="symbol">(</span><span class="symbol">!</span>eof<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>stream<span class="symbol">.</span>Read<span class="symbol">(</span>buffer<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="number">8</span><span class="symbol">)</span> <span class="symbol">==</span> <span class="number">8</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 chunkSize <span class="symbol">=</span> buffer<span class="symbol">.</span>ToInt<span class="symbol">(</span><span class="number">4</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// see if we have the palette data</span>
 <span class="keyword">if</span> <span class="symbol">(</span>buffer<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span> <span class="symbol">==</span> <span class="string">&#39;d&#39;</span> <span class="symbol">&amp;&amp;</span> buffer<span class="symbol">[</span><span class="number">1</span><span class="symbol">]</span> <span class="symbol">==</span> <span class="string">&#39;a&#39;</span> <span class="symbol">&amp;&amp;</span> buffer<span class="symbol">[</span><span class="number">2</span><span class="symbol">]</span> <span class="symbol">==</span> <span class="string">&#39;t&#39;</span> <span class="symbol">&amp;&amp;</span> buffer<span class="symbol">[</span><span class="number">3</span><span class="symbol">]</span> <span class="symbol">==</span> <span class="string">&#39;a&#39;</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// we have a RGB palette, process the data and break</span>

 <span class="keyword">if</span> <span class="symbol">(</span>stream<span class="symbol">.</span>Read<span class="symbol">(</span>buffer<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> chunkSize<span class="symbol">)</span> <span class="symbol">!=</span> chunkSize<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="string">&quot;Failed to read enough data to match chunk size.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="comment">// TODO: Extract palette from the buffer</span>

 eof <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="comment">// not the palette data? advance the stream to the next chunk</span>

 <span class="comment">// advance the reader by a byte if the size is an odd number</span>
 <span class="keyword">if</span> <span class="symbol">(</span>chunkSize <span class="symbol">%</span> <span class="number">2</span> <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 chunkSize<span class="symbol">++</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 stream<span class="symbol">.</span>Position <span class="symbol">+=</span> chunkSize<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="comment">// nothing to read, abort</span>
 eof <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span><span class="symbol">;</span>
</pre>
</figure>
<h3 id="reading-the-palette">Reading the palette</h3>
<p>Once you have the chunk data, this needs converting into
something usable. For an RGB palette, the data is actually a
<a href="https://msdn.microsoft.com/en-us/library/dd145040%28v=vs.85%29.aspx" rel="external nofollow noopener"><code>LOGPALETTE</code></a> structure containing an array of
<a href="https://msdn.microsoft.com/en-us/library/dd162769(v=vs.85).aspx" rel="external nofollow noopener"><code>PALETTEENTRY</code></a> values. While this probably means there's a
cool way of converting that byte data directly into a
<code>LOGPALETTE</code>, we'll construct a <code>Color[]</code> array manually.</p>
<figure class="lang-cpp highlight"><figcaption><span>cpp</span></figcaption><pre class="code">
<span class="keyword">typedef</span> <span class="keyword">struct</span> tagLOGPALETTE {
 WORD palVersion;
 WORD palNumEntries;
 PALETTEENTRY palPalEntry[1];
} LOGPALETTE;

<span class="keyword">typedef</span> <span class="keyword">struct</span> tagPALETTEENTRY {
 BYTE peRed;
 BYTE peGreen;
 BYTE peBlue;
 BYTE peFlags;
} PALETTEENTRY;
</pre>
</figure>
<p>If you want more information on Windows data types, you can find
it on <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx" rel="external nofollow noopener">MSDN</a>, but suffice to say <code>WORD</code> is a 16-bit unsigned
integer, <code>BYTE</code> is as named, and <code>DWORD</code> is an unsigned 32-bit
integer.</p>
<p>Reading the palette is therefore as easy as pulling out the
number of colours and processing the bytes for each colour.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Color<span class="symbol">[</span><span class="symbol">]</span> palette<span class="symbol">;</span>
<span class="keyword">ushort</span> count<span class="symbol">;</span>

count <span class="symbol">=</span> buffer<span class="symbol">.</span>ToInt<span class="number">16</span><span class="symbol">(</span><span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>
palette <span class="symbol">=</span> <span class="keyword">new</span> Color<span class="symbol">[</span>count<span class="symbol">]</span><span class="symbol">;</span>

<span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> count<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">byte</span> r<span class="symbol">;</span>
 <span class="keyword">byte</span> g<span class="symbol">;</span>
 <span class="keyword">byte</span> b<span class="symbol">;</span>
 <span class="keyword">int</span> offset<span class="symbol">;</span>

 offset <span class="symbol">=</span> <span class="symbol">(</span>i <span class="symbol">*</span> <span class="number">4</span><span class="symbol">)</span> <span class="symbol">+</span> <span class="number">4</span><span class="symbol">;</span>
 r <span class="symbol">=</span> buffer<span class="symbol">[</span>offset<span class="symbol">]</span><span class="symbol">;</span>
 g <span class="symbol">=</span> buffer<span class="symbol">[</span>offset <span class="symbol">+</span> <span class="number">1</span><span class="symbol">]</span><span class="symbol">;</span>
 b <span class="symbol">=</span> buffer<span class="symbol">[</span>offset <span class="symbol">+</span> <span class="number">2</span><span class="symbol">]</span><span class="symbol">;</span>

 palette<span class="symbol">[</span>i<span class="symbol">]</span> <span class="symbol">=</span> Color<span class="symbol">.</span>FromArgb<span class="symbol">(</span>r<span class="symbol">,</span> g<span class="symbol">,</span> b<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>Although I included the <code>PALETTEENTRY</code> structure definition
above, I thought it was worth pointing out - each palette
entry is comprised of four bytes, but the fourth byte is
<strong>not</strong> an alpha channel, it is a set of flags describing how
Windows should process the palette.</p>
</blockquote>
<p>And that's pretty much all you need to handle reading a RIFF
palette file, although as usual I've included a sample
application for download.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-02-18 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/loading-microsoft-riff-palette-pal-files-with-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comFinding nearest colors using Euclidean distanceurn:uuid:973d6cfd-bf49-4bb6-921e-9d5bb2138cb22017-02-27T06:37:43Z2017-01-06T08:48:24Z<p>I've recently been updating our series on <a href="/tag/dither">dithering</a> to
include ordered dithering. However, in order to fully
demonstrate this I also updated the sample to include basic
color quantizing with a fixed palette.</p>
<p>While color reduction and dithering are related, I didn't want
to cover both topics in a single blog post, so here we are with
a first post on finding the nearest color via Euclidean
distance, and I'll follow up in another post on ordered
dithering.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/colordistance-1a.png" class="gallery" title="A demo showing the distance between two colors, and mapping those colors to the nearest color in a fixed palette" ><img src="https://images.cyotek.com/image/thumbnail/devblog/colordistance-1a.png" alt="A demo showing the distance between two colors, and mapping those colors to the nearest color in a fixed palette" decoding="async" loading="lazy" /></a><figcaption>A demo showing the distance between two colors, and mapping those colors to the nearest color in a fixed palette</figcaption></figure><h2 id="getting-the-distance-between-two-colors">Getting the distance between two colors</h2>
<p>Getting the distance between two colors is a matter of
multiplying the difference of each channel between the two
colors and then adding it all together, or if you want a
formula, <a href="https://en.wikipedia.org/wiki/Euclidean_distance" rel="external nofollow noopener">Wikipedia</a> obliges handily</p>
<p><img src="https://images.cyotek.com/image/devblog/d1d13a40a7b203b455ae6d4be8b3cce898bda625.svg" decoding="async" loading="lazy" alt="Three-dimensional Euclidean space formula" /></p>
<p>In C# terms, that translates to a helper function similar to the
below</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> GetDistance<span class="symbol">(</span>Color current<span class="symbol">,</span> Color match<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> redDifference<span class="symbol">;</span>
 <span class="keyword">int</span> greenDifference<span class="symbol">;</span>
 <span class="keyword">int</span> blueDifference<span class="symbol">;</span>

 redDifference <span class="symbol">=</span> current<span class="symbol">.</span>R <span class="symbol">-</span> match<span class="symbol">.</span>R<span class="symbol">;</span>
 greenDifference <span class="symbol">=</span> current<span class="symbol">.</span>G <span class="symbol">-</span> match<span class="symbol">.</span>G<span class="symbol">;</span>
 blueDifference <span class="symbol">=</span> current<span class="symbol">.</span>B <span class="symbol">-</span> match<span class="symbol">.</span>B<span class="symbol">;</span>

 <span class="keyword">return</span> redDifference <span class="symbol">*</span> redDifference <span class="symbol">+</span> greenDifference <span class="symbol">*</span> greenDifference <span class="symbol">+</span> blueDifference <span class="symbol">*</span> blueDifference<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Note that the distance is the same between two colours no matter
which way around you call <code>GetDistance</code> with them.</p>
<h2 id="finding-the-nearest-color">Finding the nearest color</h2>
<p>With the ability to identify the distance between two colours,
it is now a trivial matter to scan a fixed array of colors
looking for the closest match. The closest match is merely the
color with the lowest distance. A distance of zero means the
colors are a direct match.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> FindNearestColor<span class="symbol">(</span>Color<span class="symbol">[</span><span class="symbol">]</span> map<span class="symbol">,</span> Color current<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> shortestDistance<span class="symbol">;</span>
 <span class="keyword">int</span> index<span class="symbol">;</span>

 index <span class="symbol">=</span> <span class="symbol">-</span><span class="number">1</span><span class="symbol">;</span>
 shortestDistance <span class="symbol">=</span> <span class="keyword">int</span><span class="symbol">.</span>MaxValue<span class="symbol">;</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> map<span class="symbol">.</span>Length<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Color match<span class="symbol">;</span>
 <span class="keyword">int</span> distance<span class="symbol">;</span>

 match <span class="symbol">=</span> map<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">;</span>
 distance <span class="symbol">=</span> GetDistance<span class="symbol">(</span>current<span class="symbol">,</span> match<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>distance <span class="symbol">&lt;</span> shortestDistance<span class="symbol">)</span>
 <span class="symbol">{</span>
 index <span class="symbol">=</span> i<span class="symbol">;</span>
 shortestDistance <span class="symbol">=</span> distance<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> index<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="optimizing-finding-the-match">Optimizing finding the match</h2>
<p>While the initial code is simple, using it practically isn't. In
the demonstration program attached to this post, the
<code>FindNearestColor</code> is only called once and so you probably won't
notice any performance impact. However, if you are performing
many searches (for example to reduce the colors in an image),
then you may find the code quite slow. In this case, you
probably want to look at caching the value of <code>FindNearestColor</code>
along with the source color, so that future calls just look in
the cache rather than performing a full scan (a normal
<code>Dictionary&lt;Color, int&gt;</code> worked fine in my limited testing). Of
course the more colours in the map, the slower it will be as
well.</p>
<p>While I haven't tried this yet, using an ordered palette may
allow the use of linear searching. When combined with a cached
lookup, that ought to be enough for most scenarios.</p>
<h2 id="what-about-the-alpha-channel">What about the Alpha channel?</h2>
<p>For my purposes I don't need to consider the alpha value of a
color. However, if you do want to use it, then adjust
<code>GetDistance</code> to include the channel, and it will work just
fine.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> GetDistance<span class="symbol">(</span>Color current<span class="symbol">,</span> Color match<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> redDifference<span class="symbol">;</span>
 <span class="keyword">int</span> greenDifference<span class="symbol">;</span>
 <span class="keyword">int</span> blueDifference<span class="symbol">;</span>
 <span class="keyword">int</span> alphaDifference<span class="symbol">;</span>

 alphaDifference <span class="symbol">=</span> current<span class="symbol">.</span>A <span class="symbol">-</span> match<span class="symbol">.</span>A<span class="symbol">;</span>
 redDifference <span class="symbol">=</span> current<span class="symbol">.</span>R <span class="symbol">-</span> match<span class="symbol">.</span>R<span class="symbol">;</span>
 greenDifference <span class="symbol">=</span> current<span class="symbol">.</span>G <span class="symbol">-</span> match<span class="symbol">.</span>G<span class="symbol">;</span>
 blueDifference <span class="symbol">=</span> current<span class="symbol">.</span>B <span class="symbol">-</span> match<span class="symbol">.</span>B<span class="symbol">;</span>

 <span class="keyword">return</span> alphaDifference <span class="symbol">*</span> alphaDifference <span class="symbol">+</span> redDifference <span class="symbol">*</span> redDifference <span class="symbol">+</span> greenDifference <span class="symbol">*</span> greenDifference <span class="symbol">+</span> blueDifference <span class="symbol">*</span> blueDifference<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The images below were obtained by setting the value of the box
on the left to <code>0, 0, 220, 0</code>, and the right <code>255, 0, 220, 0</code> -
same RGB, just different alpha.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/colordistance-1b.png" class="gallery" title="Distance from the same color with different alpha" ><img src="https://images.cyotek.com/image/thumbnail/devblog/colordistance-1b.png" alt="Distance from the same color with different alpha" decoding="async" loading="lazy" /></a><figcaption>Distance from the same color with different alpha</figcaption></figure><h2 id="update-history">Update History</h2>
<ul>
<li>2017-01-06 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/finding-nearest-colors-using-euclidean-distance .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comError "DEP0001 : Unexpected Error: -1988945902" when deploying to Windows Mobile 10urn:uuid:09473578-4739-4a58-9ac7-8cbe9531f0d12016-08-14T16:17:10Z2016-08-14T16:17:10Z<p>Last month, I foolishly upgraded my Lumia 630 to a 650 even
though I had every intention of abandoning the Windows Mobile
platform after watching Microsoft flounder without hope.
However, after using an Android phone as an experiment for a
couple of weeks, I decided that despite the hardware (a Galaxy
S5) being much better than the budget phones I typically buy, I
just don't like Android. As Microsoft also reneged on their
promise of a Windows 10 upgrade for the 630, I grabbed a 650 to
amuse myself with.</p>
<p>Today I wrote a simple UWP application, which was multiple fun
learning curves for the price of one, such as XAML, forced use
of async/await, and of course the UWP paradigm itself.</p>
<p>After getting my application (a Notepad clone, a nice and simple
thing to start with!) working on my desktop, I decided to see
what would happen if I ran it on my phone - both the desktop and
the phone are running Windows 10 Anniversary Edition, so why
not.</p>
<p>However, each time I attempted to deploy, I received this
useless error:</p>
<blockquote>
<p>DEP0001 : Unexpected Error: -1988945902</p>
</blockquote>
<p><em>Sigh</em>. What a helpful error Microsoft! After trying multiple
times to deploy it finally occurred to me I was being a bit
silly. I had to enable Developer Mode on my <em>desktop</em> in order
to test the x86 version, so it stands to reason that I'd have to
do it on the <em>phone</em> as well. So, after doing a fairly good
Picard Facepalm, I enabled it on the phone.</p>
<ul>
<li>Open the settings app on the phone</li>
<li>Select the <strong>Upgrade &amp; security</strong> section</li>
<li>Select the <strong>For developers</strong> sub section</li>
<li>Select the <strong>Developer mode</strong> radio button</li>
<li>Confirm the security warning</li>
</ul>
<p>There are additional advanced options (<strong>Device discovery</strong> and
<strong>Device Portal</strong>) but they didn't seem to be required, even for
debugging. And, unlike the desktop, the phone didn't need a
reboot.</p>
<p>Now when I tried to deploy, it worked, and my application was
installed on the phone. Ran it and it looked identical to the
desktop version and worked fine, at least until I tried to save
a previously opened file and it promptly crashed. That aside, I
was actually rather impressed - Universal indeed. I was even
more impressed when I debugged said crash on the phone via the
desktop machine.</p>
<p>I decided to write this short post in case any one else was as
forgetful as I, and so I switched developer mode on the phone
off again so I could reproduce the original error in case there
was any extra information. Bad idea, Visual Studio really didn't
like that and just crashed and burned each time I tried to
deploy.</p>
<p>After several long waits while VS crashed and restarted,
eventually I uninstalled the application from the phone and
tried again, and to my surprise, while at least it didn't crash
VS this time, it did come out with a completely different error
message.</p>
<blockquote>
<p>DEP0200 : Ensure that the device is developer unlocked. For
details on developer unlock, visit
<a href="http://go.microsoft.com/fwlink/?LinkId=317976" rel="external nofollow noopener">http://go.microsoft.com/fwlink/?LinkId=317976</a>. 0x-2147009281:
To install this application you need either a Windows
developer license or a sideloading-enabled system. (Exception
from HRESULT: 0x80073CFF)</p>
</blockquote>
<p>Now that's more like it! Why on earth didn't it display that
error the first time around? Perhaps it was because that mode
had never been enabled previously, I don't know. And for the
record, everything worked fine when I switched developer mode
back on on the phone.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2016-08-14 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/error-dep0001-unexpected-error-1988945902-when-deploying-to-windows-mobile-10 .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comDisplaying multi-page tiff files using the ImageBox control and C#urn:uuid:3c3b9f09-50a4-4410-9a1e-3cb3543b71ed2016-07-30T08:25:51Z2016-07-30T08:25:51Z<p>Earlier this week I received a support request from a user
wanting to know if it was possible to display multi-page tiff
files using the <code>ImageBox</code> control. As I haven't wrote anything
about this control for a while, it seemed a good opportunity for
a short blog post.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/tiffview-1a.png" class="gallery" title="Viewing pages in a multi-page file" ><img src="https://images.cyotek.com/image/thumbnail/devblog/tiffview-1a.png" alt="Viewing pages in a multi-page file" decoding="async" loading="lazy" /></a><figcaption>Viewing pages in a multi-page file</figcaption></figure><h2 id="getting-the-number-of-pages-in-a-tiff-file">Getting the number of pages in a TIFF file</h2>
<p>One you have obtained an <code>Image</code> instance containing your tiff
graphic, you can use the <code>GetFrameCount</code> method in conjunction
with a predefined <code>FrameDimension</code> object in order to determine
how many pages there are in the image.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">int</span> GetPageCount<span class="symbol">(</span>Image image<span class="symbol">)</span>
<span class="symbol">{</span>
 FrameDimension dimension<span class="symbol">;</span>

 dimension <span class="symbol">=</span> FrameDimension<span class="symbol">.</span>Page<span class="symbol">;</span>

 <span class="keyword">return</span> image<span class="symbol">.</span>GetFrameCount<span class="symbol">(</span>dimension<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>I have tested this code on several images, and even types
which don't support pages (such as standard bitmaps) have
always return a valid value. However, I have no way of knowing
if this will always be the case (I have experienced first hand
differences in how GDI+ handles actions between different
versions of Windows). The <code>Image</code> object does offer a
<code>FrameDimensionsList</code> property which returns a list of GUID's
for the dimensions supported by the image, so you can always
check the contents of this property first if you want to be
extra sure.</p>
</blockquote>
<h2 id="selecting-a-page">Selecting a page</h2>
<p>To change the active page the <code>Image</code> object represents, you can
its <code>SelectActiveFrame</code> method, passing in a <code>FrameDimension</code>
object and the zero-based page index. Again, we can use the
predefined <code>FrameDimension.Page</code> property, similar to the
following</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
image<span class="symbol">.</span>SelectActiveFrame<span class="symbol">(</span>FrameDimension<span class="symbol">.</span>Page<span class="symbol">,</span> page <span class="symbol">-</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>After which, we need to instruct our <code>ImageBox</code> control (or
whatever control we have bound the image to) to repaint to pick
up the new image data.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
imageBox<span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<blockquote>
<p>You don't need to reassign the image (which probably won't
work anyway if the control does an equality check), simply
instructing it to repaint via <code>Invalidate</code> or <code>Refresh</code> ought
to be sufficient.</p>
</blockquote>
<h2 id="a-sample-multi-page-tiff-file">A sample multi-page tiff file</h2>
<p>As multi-page tiffs aren't exactly common images to be found in
plenty on the internet, I've prepared a sample image based on a
Newton's Cradle animation from
<a href="https://en.wikipedia.org/wiki/Newton%27s_cradle" rel="external nofollow noopener">Wikipedia</a>.</p>
<p><a href="https://images.cyotek.com/image/devblog/NewtonsCradle.tif">Download NewtonsCradle.tif</a> (4MB)</p>
<h2 id="short-and-sweet">Short and sweet</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/tiffview-1a.gif" class="gallery" title="The sample application in action" ><img src="https://images.cyotek.com/image/thumbnail/devblog/tiffview-1a.gif" alt="The sample application in action" decoding="async" loading="lazy" /></a><figcaption>The sample application in action</figcaption></figure>
<p>That is all the information we need to create a viewer - you can
download the project shown in the above animation from the links
below.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2016-07-30 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/displaying-multi-page-tiff-files-using-the-imagebox-control-and-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comAligning Windows Forms custom controls to text baselines using C#urn:uuid:19cf598d-70e5-4253-bb82-5acd9f4e63d52016-07-21T18:18:00Z2016-07-21T18:18:00Z<p>One of the nice things about the Visual Studio WinForms
designers are the guidelines it draws onto design surfaces,
aiding you in perfectly positioning your controls. These
guidelines are known internally as <a href="https://msdn.microsoft.com/en-us/library/system.windows.forms.design.behavior.snapline(v=vs.110).aspx" rel="external nofollow noopener">snap lines</a>, and by
default each visual component inheriting from <code>Control</code> gets
four of these, representing the values of the control's <code>Margin</code>
property.</p>
<p>A problem arises when you have multiple controls that have
different heights, and contain a display string - in this case
aligning along one edge isn't going to work and will probably
look pretty ugly. Instead, you more than likely want to align
the different controls so that the text appears on the same
line.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/snaplines-1a.png" class="gallery" title="Aligning everything along one edge just doesn't look right" ><img src="https://images.cyotek.com/image/devblog/snaplines-1a.png" alt="Aligning everything along one edge just doesn't look right" decoding="async" loading="lazy" /></a><figcaption>Aligning everything along one edge just doesn't look right</figcaption></figure>
<p>Fortunately for us developers, the designers do include this
functionality - just not by default. After all, while all
controls have a <code>Text</code> property, not all of them use it, and how
could the default designers know where your owner-draw control
is going to paint text?</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/snaplines-1b.png" class="gallery" title="Aligning the controls so all text is at the same level looks much better" ><img src="https://images.cyotek.com/image/devblog/snaplines-1b.png" alt="Aligning the controls so all text is at the same level looks much better" decoding="async" loading="lazy" /></a><figcaption>Aligning the controls so all text is at the same level looks much better</figcaption></figure>
<p>The image above shows a <code>Label</code>, <code>ComboBox</code> and <code>Button</code> control
all aligned along the text baseline (the magenta line). We can
achieve the same thing by creating a custom designer.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/snaplines-custom-v2.gif.png" class="gallery" title="Aligning a custom control with other controls using the text baseline" ><img src="https://images.cyotek.com/image/thumbnail/devblog/snaplines-custom-v2.gif" alt="Aligning a custom control with other controls using the text baseline" decoding="async" loading="lazy" /></a><figcaption>Aligning a custom control with other controls using the text baseline</figcaption></figure><h2 id="creating-the-designer">Creating the designer</h2>
<p>The first thing therefore is to create a new class and inherit
from <code>System.Windows.Forms.Design.ControlDesigner</code>. You may also
need to add a reference to <code>System.Design</code> to your project
(which rules out <em>Client Profile</em> targets).</p>
<blockquote>
<p>.NET conventions generally recommend that you put these types
of classes in a sub-namespace called Design.</p>
</blockquote>
<p>So, assuming I had a control named <code>BetterTextBox</code>, then the
associated designer would probably look similar to the
following.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> System<span class="symbol">.</span>Windows<span class="symbol">.</span>Forms<span class="symbol">.</span>Design<span class="symbol">;</span>

<span class="keyword">namespace</span> DesignerSnapLinesDemo<span class="symbol">.</span>Design
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">class</span> BetterTextBoxDesigner <span class="symbol">:</span> ControlDesigner
 <span class="symbol">{</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>If you use a tool such as Resharper to fill in namespaces,
note that by default it will try and use
<code>System.Web.UI.Design.ControlDesigner</code> which unsurprisingly
won't work for WinForms controls.</p>
</blockquote>
<h2 id="adding-a-snap-line">Adding a snap line</h2>
<p>To add or remove snap lines, we override the <code>SnapLines</code>
property and manipulate the list it returns. There are only a
few snap lines available, the one we want to add is <code>Baseline</code></p>
<p>For the baseline, you'll need to calculate where the control
will draw the text, taking into consideration padding, borders,
text alignments and of course the font. My previous article
<a href="http://www.cyotek.com/blog/retrieving-font-and-text-metrics-using-csharp">retrieving font and text metrics using C#</a> describes how to
do this.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> IList SnapLines
<span class="symbol">{</span>
 <span class="keyword">get</span>
 <span class="symbol">{</span>
 IList snapLines<span class="symbol">;</span>
 <span class="keyword">int</span> textBaseline<span class="symbol">;</span>
 SnapLine snapLine<span class="symbol">;</span>

 snapLines <span class="symbol">=</span> <span class="keyword">base</span><span class="symbol">.</span>SnapLines<span class="symbol">;</span>
 
 textBaseline <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetTextBaseline<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// Font ascent</span>

 <span class="comment">// TODO: Increase textBaseline by anything else that affects where your text is rendered, such as</span>
 <span class="comment">// * The value of the Padding.Top property</span>
 <span class="comment">// * If your control has a BorderStyle</span>
 <span class="comment">// * If you reposition the text vertically for centering etc</span>
 
 snapLine <span class="symbol">=</span> <span class="keyword">new</span> SnapLine<span class="symbol">(</span>SnapLineType<span class="symbol">.</span>Baseline<span class="symbol">,</span> textBaseline<span class="symbol">,</span> SnapLinePriority<span class="symbol">.</span>Medium<span class="symbol">)</span><span class="symbol">;</span>

 snapLines<span class="symbol">.</span>Add<span class="symbol">(</span>snapLine<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> snapLines<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>Note: Resharper seems to think the <code>SnapLines</code> property can
return a null object. At least for the base WinForms
<code>ControlDesigner</code>, this is not true and it will <em>always</em>
return a list containing every possible snapline <em>except</em> for
<code>BaseLine</code></p>
</blockquote>
<h2 id="linking-the-designer-to-your-control">Linking the designer to your control</h2>
<p>You can link your custom control to your designer by decorating
your class with the <code>System.ComponentModel.DesignerAttribute</code>.
If your designer type is in the same assembly as the control (or
is referenced), then you can call it with the direct type as
with the following example.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>Designer<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>BetterTextBoxDesigner<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">class</span> BetterTextBox <span class="symbol">:</span> Control
<span class="symbol">{</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>However, if the designer isn't directly available to your
control, all is not lost - the <code>DesignerAttribute</code> can also take
a string value that contains the assembly qualified designer
type name. Visual Studio will then figure out how to load the
type if it can.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>Designer<span class="symbol">(</span><span class="string">&quot;DesignerSnapLinesDemo.Design.BetterTextBoxDesigner, DesignerSnapLinesDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">class</span> BetterTextBox <span class="symbol">:</span> Control
<span class="symbol">{</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>After rebuilding the project, you'll find that your control now
uses your designer rather than the default.</p>
<blockquote>
<p>I seem to recall that when using older versions of Visual
Studio once the IDE had loaded my custom designer contained in
a source code project it seemed to cache it. This meant that
if I then changed the designer code and recompiled, it
wouldn't be picked up unless I restarted Visual Studio. I
haven't noticed that happening in VS2015, so either I'm
imagining the whole thing, or it was fixed. Regardless, if you
get odd behaviour in older versions of VS, a restart of the
IDE might be just what you need.</p>
</blockquote>
<p>The following image shows a zoomed version of the
<code>BetterTextbox</code> (which is just a garishly painted demo control
and so is several lies for the price of one) showing all three
controls are perfectly aligned to the magenta <code>BaseLine</code>
guideline.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/snaplines-1c.png" class="gallery" title="Aligning a custom control via its text baseline" ><img src="https://images.cyotek.com/image/devblog/snaplines-1c.png" alt="Aligning a custom control via its text baseline" decoding="async" loading="lazy" /></a><figcaption>Aligning a custom control via its text baseline</figcaption></figure><h2 id="bonus-chatter-locking-down-how-the-control-is-sized">Bonus Chatter: Locking down how the control is sized</h2>
<p>The default <code>ControlDesigner</code> allows controls to be resized
along any edge at will. If your control automatically sets its
height or width to fit its contents, then this behaviour can be
undesirable. By overriding the <a href="https://msdn.microsoft.com/en-us/library/system.windows.forms.design.controldesigner.selectionrules(v=vs.110).aspx" rel="external nofollow noopener"><code>SelectionRules</code></a> property,
you can define how the control can be processed. The following
code snippet shows an example which prevents the control from
being resized vertically, useful for single-line text box style
controls.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> SelectionRules SelectionRules
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> SelectionRules<span class="symbol">.</span>Visible <span class="symbol">|</span> SelectionRules<span class="symbol">.</span>Moveable <span class="symbol">|</span> SelectionRules<span class="symbol">.</span>LeftSizeable <span class="symbol">|</span> SelectionRules<span class="symbol">.</span>RightSizeable<span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="update-history">Update History</h2>
<ul>
<li>2016-07-21 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/aligning-windows-forms-custom-controls-to-text-baselines-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comRetrieving font and text metrics using C#urn:uuid:008f5e50-d41a-4f02-b882-2518ccfc930c2016-07-09T14:27:00Z2016-07-09T14:27:00Z<p>In several of my applications, I need to be able to line up
text, be it blocks of text using different fonts, or text
containers of differing heights. As far as I'm aware, there
isn't a way of doing this natively in .NET, however with a
little platform invoke we can get the information we need to do
it ourselves.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/gettextmetrics.png" class="gallery" title="Obtaining metrics using GetTextMetrics" ><img src="https://images.cyotek.com/image/thumbnail/devblog/gettextmetrics.png" alt="Obtaining metrics using GetTextMetrics" decoding="async" loading="lazy" /></a><figcaption>Obtaining metrics using GetTextMetrics</figcaption></figure>
<p>The <a href="https://msdn.microsoft.com/en-us/library/dd144941(v=vs.85).aspx" rel="external nofollow noopener"><code>GetTextMetrics</code></a> metrics function is used to obtain
metrics based on a font and a device context by populating a
<a href="https://msdn.microsoft.com/en-us/library/dd145132(v=vs.85).aspx" rel="external nofollow noopener"><code>TEXTMETRICW</code></a> structure.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;gdi32.dll&quot;</span><span class="symbol">,</span> CharSet <span class="symbol">=</span> CharSet<span class="symbol">.</span>Auto<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">bool</span> GetTextMetrics<span class="symbol">(</span>IntPtr hdc<span class="symbol">,</span> <span class="keyword">out</span> TEXTMETRICW lptm<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>StructLayout<span class="symbol">(</span>LayoutKind<span class="symbol">.</span>Sequential<span class="symbol">,</span> CharSet <span class="symbol">=</span> CharSet<span class="symbol">.</span>Unicode<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">struct</span> TEXTMETRICW
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">int</span> tmHeight<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> tmAscent<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> tmDescent<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> tmInternalLeading<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> tmExternalLeading<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> tmAveCharWidth<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> tmMaxCharWidth<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> tmWeight<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> tmOverhang<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> tmDigitizedAspectX<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> tmDigitizedAspectY<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">ushort</span> tmFirstChar<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">ushort</span> tmLastChar<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">ushort</span> tmDefaultChar<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">ushort</span> tmBreakChar<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">byte</span> tmItalic<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">byte</span> tmUnderlined<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">byte</span> tmStruckOut<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">byte</span> tmPitchAndFamily<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">byte</span> tmCharSet<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Although there's a lot of information available (as you can see
in the demonstration program), for the most part I tend to use
just the <code>tmAscent</code> value which returns the pixels above the
base line of characters.</p>
<h2 id="a-quick-note-on-leaks">A quick note on leaks</h2>
<p>I don't know how relevant clean up is in modern versions of
Windows, but in older versions of Windows it used to be very
important to clean up behind you. If you get a handle to
something, release it when you're done. If you create a GDI
object, delete it when you're done. If you select GDI objects
into a DC, store and restore the original objects when you're
done. Not doing these actions used to be a good source of leaks.
I don't use GDI anywhere near as much as I used to years ago as
a VB6 developer, but I assume the principles still apply even in
the latest versions of Windows.</p>
<h2 id="calling-gettextmetrics">Calling GetTextMetrics</h2>
<p>As <code>GetTextMetrics</code> is a Win32 GDI API call, it requires a
device context, which is basically a bunch of graphical objects
such as pens, brushes - and fonts. Generally you would use the
<code>GetDC</code> or <code>CreateDC</code> API calls, but fortunately the .NET
<code>Graphics</code> object is essentially a wrapper around a device
context, so we can use this.</p>
<p>A DC can only have one object of a specific type activate at a
time. For example, in order to draw a line, you need to tell the
DC the handle of the pen to draw with. When you do this, Windows
will tell <em>you</em> the handle of the pen that was originally in the
DC. After you have finished drawing your line, it is up to you
to both restore the state of the DC, and to destroy your pen.
The GDI calls <a href="https://msdn.microsoft.com/en-us/library/dd162957(v=vs.85).aspx" rel="external nofollow noopener"><code>SelectObject</code></a> and <a href="https://msdn.microsoft.com/en-us/library/dd183539(v=vs.85).aspx" rel="external nofollow noopener"><code>DeleteObject</code></a> can do
this.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;gdi32.dll&quot;</span><span class="symbol">,</span> CharSet <span class="symbol">=</span> CharSet<span class="symbol">.</span>Auto<span class="symbol">,</span> SetLastError <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">bool</span> DeleteObject<span class="symbol">(</span>IntPtr hObject<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;gdi32.dll&quot;</span><span class="symbol">,</span> CharSet <span class="symbol">=</span> CharSet<span class="symbol">.</span>Auto<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">extern</span> IntPtr SelectObject<span class="symbol">(</span>IntPtr hdc<span class="symbol">,</span> IntPtr hgdiObj<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>The following helper functions can be used to get the font ascent, either for the specified <code>Control</code> or for a <code>IDeviceContext</code> and <code>Font</code> combination.</p>
<blockquote>
<p>I haven't tested the performance of using <code>Control.CreateGraphics</code> versus directly creating a DC. If you are calling this functionality a lot it may be worth caching the values or avoiding <code>CreateGraphics</code> and trying pure Win32 API calls.</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">int</span> GetFontAscent<span class="symbol">(</span>Control control<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>Graphics graphics <span class="symbol">=</span> control<span class="symbol">.</span>CreateGraphics<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>GetFontAscent<span class="symbol">(</span>graphics<span class="symbol">,</span> control<span class="symbol">.</span>Font<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">int</span> GetFontAscent<span class="symbol">(</span>IDeviceContext dc<span class="symbol">,</span> Font font<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> result<span class="symbol">;</span>
 IntPtr hDC<span class="symbol">;</span>
 IntPtr hFont<span class="symbol">;</span>
 IntPtr hFontDefault<span class="symbol">;</span>

 hDC <span class="symbol">=</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">;</span>
 hFont <span class="symbol">=</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">;</span>
 hFontDefault <span class="symbol">=</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 NativeMethods<span class="symbol">.</span>TEXTMETRICW textMetric<span class="symbol">;</span>

 hDC <span class="symbol">=</span> dc<span class="symbol">.</span>GetHdc<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 hFont <span class="symbol">=</span> font<span class="symbol">.</span>ToHfont<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 hFontDefault <span class="symbol">=</span> NativeMethods<span class="symbol">.</span>SelectObject<span class="symbol">(</span>hDC<span class="symbol">,</span> hFont<span class="symbol">)</span><span class="symbol">;</span>

 NativeMethods<span class="symbol">.</span>GetTextMetrics<span class="symbol">(</span>hDC<span class="symbol">,</span> <span class="keyword">out</span> textMetric<span class="symbol">)</span><span class="symbol">;</span>

 result <span class="symbol">=</span> textMetric<span class="symbol">.</span>tmAscent<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">finally</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>hFontDefault <span class="symbol">!=</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span>
 <span class="symbol">{</span>
 NativeMethods<span class="symbol">.</span>SelectObject<span class="symbol">(</span>hDC<span class="symbol">,</span> hFontDefault<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>hFont <span class="symbol">!=</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span>
 <span class="symbol">{</span>
 NativeMethods<span class="symbol">.</span>DeleteObject<span class="symbol">(</span>hFont<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 dc<span class="symbol">.</span>ReleaseHdc<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>In the above code you can see how we first get the handle of the
underlying device context by calling <code>GetDC</code>. This essentially
locks the device context, as in the same way that only a single
GDI object of each type can be associated with a GDI, only one
thread can use the DC at a time. (It's little more complicated
than that, but this will suffice for this post).</p>
<p>Next, we convert the managed .NET <code>Font</code> into an unmanaged
<code>HFONT</code>.</p>
<blockquote>
<p>You are responsible for deleting the handle returned by
<code>Font.ToHfont</code></p>
</blockquote>
<p>Once we have our font handle, we set that to be the current font
of the device context using <code>SelectObject</code>, which returns the
existing font handle - we store this for later.</p>
<p>Now we can call <code>GetTextMetrics</code> passing in the handle of the
DC, and a <code>TEXTMETRIC</code> instance to populate. Note that the
<code>GetTextMetrics</code> call <em>could</em> fail, and if so the function call
will return false. In this demonstration code, I'm not checking
for success or failure and assuming the call will always
succeed.</p>
<p>Once we've called <code>GetTextMetrics</code>, it's time to reverse some of
the steps we did earlier.</p>
<blockquote>
<p>Note the use of a finally block, so even if a crash occurs
during processing, our clean up operations will still get
called</p>
</blockquote>
<p>First we restore the original font handle that we obtained from
the first call to <code>SelectObject</code>.</p>
<p>Now it's safe to delete our <code>HFONT</code> - so we do that with
<code>DeleteObject</code>.</p>
<blockquote>
<p>It's important to do these steps in order - deleting the
handle to a GDI object that is currently associated with a
device context isn't a great idea!</p>
</blockquote>
<p>Finally, we release the DC handle we created earlier via
<code>ReleaseDC</code>.</p>
<p>And that's pretty much all there is to it - we've got our font
ascent, cleaned up everything behind us and can now get on with
the whatever purpose we needed that value for!</p>
<h2 id="what-about-the-other-information">What about the other information?</h2>
<p>The example code above focuses on the <code>tmAscent</code> value as this
is mostly what I use. However, you could adapt the function to
return the <code>TEXTMETRICW</code> structure directly, or to populate a
more .NET friendly object using .NET naming conventions and
converting things like <code>tmPitchAndFamily</code> to friendly enums etc.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2016-07-09 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/retrieving-font-and-text-metrics-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comAdding keyboard accelerators and visual cues to a WinForms controlurn:uuid:d0dff781-581a-43c1-ad3b-96180ebc20e32016-06-03T22:18:28Z2016-06-03T22:18:28Z<style>
.syntax {max-height: 25em;}
</style>
<p>Some weeks ago I was trying to make parts of <a href="https://cyotek.com/cyotek-webcopy">WebCopy's</a> UI a little bit simpler via the expedient of hiding some of the more advanced (and consequently less used) options. And to do this, I created a basic toggle panel control. This worked rather nicely, and while I was writing it I also thought I'd write a short article on adding keyboard support to WinForm controls - controls that are mouse only are a particular annoyance of mine.</p>
<h2 id="a-demonstration-control">A demonstration control</h2>
<p>Below is an fairly simple (but functional) button control that works - as long as you're a mouse user. The rest of the article will discuss how to extend the control to more thoroughly support keyboard users, and you what I describe below in your own controls.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/kbd-mouseonly.gif" class="gallery" title="A button control that currently only supports the mouse" ><img src="https://images.cyotek.com/image/thumbnail/devblog/kbd-mouseonly.gif" alt="A button control that currently only supports the mouse" decoding="async" loading="lazy" /></a><figcaption>A button control that currently only supports the mouse</figcaption></figure><figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">sealed</span> <span class="keyword">class</span> Button <span class="symbol">:</span> Control<span class="symbol">,</span> IButtonControl
<span class="symbol">{</span>
 <span class="keyword">#region</span> Constants

 <span class="keyword">private</span> <span class="keyword">const</span> TextFormatFlags _defaultFlags <span class="symbol">=</span> TextFormatFlags<span class="symbol">.</span>NoPadding <span class="symbol">|</span> TextFormatFlags<span class="symbol">.</span>SingleLine <span class="symbol">|</span> TextFormatFlags<span class="symbol">.</span>HorizontalCenter <span class="symbol">|</span> TextFormatFlags<span class="symbol">.</span>VerticalCenter <span class="symbol">|</span> TextFormatFlags<span class="symbol">.</span>EndEllipsis<span class="symbol">;</span>

 <span class="keyword">private</span> <span class="keyword">bool</span> _isDefault<span class="symbol">;</span>

 <span class="keyword">private</span> ButtonState _state<span class="symbol">;</span>

 <span class="keyword">public</span> Button<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>SetStyle<span class="symbol">(</span>ControlStyles<span class="symbol">.</span>AllPaintingInWmPaint <span class="symbol">|</span> ControlStyles<span class="symbol">.</span>OptimizedDoubleBuffer <span class="symbol">|</span> ControlStyles<span class="symbol">.</span>ResizeRedraw<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>SetStyle<span class="symbol">(</span>ControlStyles<span class="symbol">.</span>StandardDoubleClick<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>
 _state <span class="symbol">=</span> ButtonState<span class="symbol">.</span>Normal<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="symbol">[</span>DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">new</span> <span class="keyword">event</span> EventHandler DoubleClick
 <span class="symbol">{</span>
 add <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>DoubleClick <span class="symbol">+=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 remove <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>DoubleClick <span class="symbol">-=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="symbol">[</span>DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">new</span> <span class="keyword">event</span> MouseEventHandler MouseDoubleClick
 <span class="symbol">{</span>
 add <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>MouseDoubleClick <span class="symbol">+=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 remove <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>MouseDoubleClick <span class="symbol">-=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnBackColorChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnBackColorChanged<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnEnabledChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnEnabledChanged<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>SetState<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Enabled <span class="symbol">?</span> ButtonState<span class="symbol">.</span>Normal <span class="symbol">:</span> ButtonState<span class="symbol">.</span>Inactive<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnFontChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnFontChanged<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnForeColorChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnForeColorChanged<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseDown<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnMouseDown<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>SetState<span class="symbol">(</span>ButtonState<span class="symbol">.</span>Pushed<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseUp<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnMouseUp<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>SetState<span class="symbol">(</span>ButtonState<span class="symbol">.</span>Normal<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnPaint<span class="symbol">(</span>PaintEventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 Graphics g<span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnPaint<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 g <span class="symbol">=</span> e<span class="symbol">.</span>Graphics<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>PaintButton<span class="symbol">(</span>g<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>PaintText<span class="symbol">(</span>g<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnTextChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnTextChanged<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">void</span> PaintButton<span class="symbol">(</span>Graphics g<span class="symbol">)</span>
 <span class="symbol">{</span>
 Rectangle bounds<span class="symbol">;</span>

 bounds <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ClientRectangle<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_isDefault<span class="symbol">)</span>
 <span class="symbol">{</span>
 g<span class="symbol">.</span>DrawRectangle<span class="symbol">(</span>SystemPens<span class="symbol">.</span>WindowFrame<span class="symbol">,</span> bounds<span class="symbol">.</span>X<span class="symbol">,</span> bounds<span class="symbol">.</span>Y<span class="symbol">,</span> bounds<span class="symbol">.</span>Width <span class="symbol">-</span> <span class="number">1</span><span class="symbol">,</span> bounds<span class="symbol">.</span>Height <span class="symbol">-</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 bounds<span class="symbol">.</span>Inflate<span class="symbol">(</span><span class="symbol">-</span><span class="number">1</span><span class="symbol">,</span> <span class="symbol">-</span><span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 ControlPaint<span class="symbol">.</span>DrawButton<span class="symbol">(</span>g<span class="symbol">,</span> bounds<span class="symbol">,</span> _state<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">void</span> PaintText<span class="symbol">(</span>Graphics g<span class="symbol">)</span>
 <span class="symbol">{</span>
 Color textColor<span class="symbol">;</span>
 Rectangle textBounds<span class="symbol">;</span>
 Size size<span class="symbol">;</span>

 size <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ClientSize<span class="symbol">;</span>
 textColor <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Enabled <span class="symbol">?</span> <span class="keyword">this</span><span class="symbol">.</span>ForeColor <span class="symbol">:</span> SystemColors<span class="symbol">.</span>GrayText<span class="symbol">;</span>
 textBounds <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span><span class="number">3</span><span class="symbol">,</span> <span class="number">3</span><span class="symbol">,</span> size<span class="symbol">.</span>Width <span class="symbol">-</span> <span class="number">6</span><span class="symbol">,</span> size<span class="symbol">.</span>Height <span class="symbol">-</span> <span class="number">6</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_state <span class="symbol">==</span> ButtonState<span class="symbol">.</span>Pushed<span class="symbol">)</span>
 <span class="symbol">{</span>
 textBounds<span class="symbol">.</span>X<span class="symbol">++</span><span class="symbol">;</span>
 textBounds<span class="symbol">.</span>Y<span class="symbol">++</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 TextRenderer<span class="symbol">.</span>DrawText<span class="symbol">(</span>g<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Text<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Font<span class="symbol">,</span> textBounds<span class="symbol">,</span> textColor<span class="symbol">,</span> _defaultFlags<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">void</span> SetState<span class="symbol">(</span>ButtonState state<span class="symbol">)</span>
 <span class="symbol">{</span>
 _state <span class="symbol">=</span> state<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">void</span> NotifyDefault<span class="symbol">(</span><span class="keyword">bool</span> value<span class="symbol">)</span>
 <span class="symbol">{</span>
 _isDefault <span class="symbol">=</span> value<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">void</span> PerformClick<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnClick<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Behavior&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="symbol">[</span>DefaultValue<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>DialogResult<span class="symbol">)</span><span class="symbol">,</span> <span class="string">&quot;None&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> DialogResult DialogResult <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="about-mnemonic-characters">About mnemonic characters</h2>
<p>I'm fairly sure most developers would know about mnemonic
characters / keyboard accelerators, but I'll quickly outline
regardless. When attached to a UI element, the mnemonic
character tells users what key (usually combined with
<kbd>Alt</kbd>) to press in order to activate it. Windows shows
the mnemonic character with an underline, and this is known as
a keyboard cue.</p>
<p>For example, <strong><u>F</u>ile</strong> would mean press
<kbd>Alt</kbd>+<kbd>F</kbd>.</p>
<h2 id="specifying-the-keyboard-accelerator">Specifying the keyboard accelerator</h2>
<p>In Windows programming, you generally use the <code>&amp;</code> character to
denote the mnemonic in a string. So for example, <code>&amp;Demo</code> means
the <code>d</code> character is the mnemonic. If you actually wanted to
display the <code>&amp;</code> character, then you'd just double them up, e.g.
<code>Hello &amp;&amp; Goodbye</code>.</p>
<p>While the underlying Win32 API uses the <code>&amp;</code> character, and most
other platforms such as classic Visual Basic or Windows Forms do
the same, WPF uses the <code>_</code> character instead. Which pretty much
sums up all of my knowledge of WPF in that one little fact.</p>
<h2 id="painting-keyboard-cues">Painting keyboard cues</h2>
<p>If you use<code>TextRenderer.DrawText</code> to render text in your
controls (which produces <a href="https://theartofdev.com/2014/04/21/text-rendering-methods-comparison-or-gdi-vs-gdi-revised/" rel="external nofollow noopener">better output</a> than
<code>Graphics.DrawString</code>) then by default it will render keyboard
cues.</p>
<p>Older versions of Windows used to always render these cues.
However, at some point (with Window 2000 if I remember
correctly) Microsoft changed the rules so that applications
would only render cues after the user had first pressed the
<kbd>Alt</kbd> character. In practice, this means you need to
check to see if cues should be rendered and act accordingly.
There used to be an option to specify if they should always be
shown or not, but that seems to have disappeared with the march
towards dumbing the OS down to mobile-esque levels.</p>
<p>The first order of business then is to update our <code>PaintText</code>
method to include or exclude keyboard cues as necessary.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">const</span> TextFormatFlags _defaultFlags <span class="symbol">=</span> TextFormatFlags<span class="symbol">.</span>NoPadding <span class="symbol">|</span> TextFormatFlags<span class="symbol">.</span>SingleLine <span class="symbol">|</span> TextFormatFlags<span class="symbol">.</span>HorizontalCenter <span class="symbol">|</span> TextFormatFlags<span class="symbol">.</span>VerticalCenter <span class="symbol">|</span> TextFormatFlags<span class="symbol">.</span>EndEllipsis<span class="symbol">;</span>

<span class="keyword">private</span> <span class="keyword">void</span> PaintText<span class="symbol">(</span>Graphics g<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// .. snip ..</span>
 
 TextRenderer<span class="symbol">.</span>DrawText<span class="symbol">(</span>g<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Text<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Font<span class="symbol">,</span> textBounds<span class="symbol">,</span> textColor<span class="symbol">,</span> _defaultFlags<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p><code>TextRenderer.DrawText</code> is a managed wrapper around the
<a href="https://msdn.microsoft.com/en-us/library/dd162499(v=vs.85).aspx" rel="external nofollow noopener"><code>DrawTextEx</code></a> Win32 API, and most of the members of
<code>TextFormatFlags</code> map to various <code>DT_*</code> constants. (Except for
<code>NoPadding</code>... I really don't know why <code>TextRenderer</code> adds left
and right padding by default but it's really annoying - I always
set <code>NoPadding</code> (when I'm not directly calling GDI via p/invoke)</p>
<p>As I noted the default behaviour is to <em>draw</em> the cues, so we
need to detect when cues should not be displayed and instruct
our paint code to skip them. To determine whether or not to
display keyboard cues, we can check the <code>ShowKeyboardCues</code>
property of the <code>Control</code> class. To stop <code>DrawText</code> from
painting the underline, we use the <code>TextFormatFlags.HidePrefix</code>
flag (<code>DT_HIDEPREFIX</code>).</p>
<p>So we can update our <code>PaintText</code> method accordingly</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> PaintText<span class="symbol">(</span>Graphics g<span class="symbol">)</span>
<span class="symbol">{</span>
 TextFormatFlags flags<span class="symbol">;</span>
 
 <span class="comment">// .. snip ..</span>

 flags <span class="symbol">=</span> _defaultFlags<span class="symbol">;</span>
 
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>ShowKeyboardCues<span class="symbol">)</span>
 <span class="symbol">{</span>
 flags <span class="symbol">|=</span> TextFormatFlags<span class="symbol">.</span>HidePrefix<span class="symbol">;</span>
 <span class="symbol">}</span>

 TextRenderer<span class="symbol">.</span>DrawText<span class="symbol">(</span>g<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Text<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Font<span class="symbol">,</span> textBounds<span class="symbol">,</span> textColor<span class="symbol">,</span> flags<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Now our button will now hide and show accelerators based on how
the end user is working.</p>
<p>If for some reason you <em>want</em> to use <code>Graphics.DrawString</code>, then
you can use something similar to the below - just set the
<code>HotkeyPrefix</code> property of a <code>StringFormat</code> object to be
<code>HotkeyPrefix.Show</code> or <code>HotkeyPrefix.Hide</code>. Note that the
default <code>StringFormat</code> object <em>doesn't</em> show prefixes, in a nice
contradiction to <code>TextRenderer</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> <span class="symbol">(</span>StringFormat format <span class="symbol">=</span> <span class="keyword">new</span> StringFormat<span class="symbol">(</span>StringFormat<span class="symbol">.</span>GenericDefault<span class="symbol">)</span>
<span class="symbol">{</span>
 HotkeyPrefix <span class="symbol">=</span> HotkeyPrefix<span class="symbol">.</span>Show<span class="symbol">,</span>
 Alignment <span class="symbol">=</span> StringAlignment<span class="symbol">.</span>Center<span class="symbol">,</span>
 LineAlignment <span class="symbol">=</span>StringAlignment<span class="symbol">.</span>Center<span class="symbol">,</span>
 Trimming <span class="symbol">=</span> StringTrimming<span class="symbol">.</span>EllipsisCharacter
<span class="symbol">}</span><span class="symbol">)</span>
<span class="symbol">{</span>
 g<span class="symbol">.</span>DrawString<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Text<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Font<span class="symbol">,</span> SystemBrushes<span class="symbol">.</span>ControlText<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ClientRectangle<span class="symbol">,</span> format<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/kbd-keyboardcue.gif" class="gallery" title="The button control now reacts to keyboard cues" ><img src="https://images.cyotek.com/image/thumbnail/devblog/kbd-keyboardcue.gif" alt="The button control now reacts to keyboard cues" decoding="async" loading="lazy" /></a><figcaption>The button control now reacts to keyboard cues</figcaption></figure>
<p>As the above animation is just a GIF file, there's no audio -
but when I ran that demo, pressing <kbd>Alt</kbd>+<kbd>D</kbd>
triggered a beep sound as there was nothing on the form that
could handle the accelerator.</p>
<h2 id="painting-focus-cues">Painting focus cues</h2>
<p>Focus cues are highlights that show which element has the
keyboard focus. Traditionally Windows would draw a dotted
outline around the text of an element that performs a single
action (such as a button or checkbox), or draws an item using
both a different background and foreground colours for an
element that has multiple items (such as a listbox or a menu).
Normally (for single action controls at least) focus cues only
appear after the <kbd>Tab</kbd> key has been pressed, memory
fails me as to whether this has always been the case or if
Windows use to always show a focus cue.</p>
<p>You can use the <code>Focused</code> property of a <code>Control</code> to determine
if it currently has keyboard focus and the <code>ShowFocusCues</code>
property to see if the focus state should be rendered.</p>
<p>After that, the simplest way of drawing a focus rectangle would
be to use the <code>ControlPaint.DrawFocusRectangle</code>. However, this
draws using fixed colours. Old-school focus rectangles inverted
the pixels by drawing with a dotted XOR pen, meaning you could
erase the focus rectangle by simply drawing it again - this was
great for rubber banding (or dancing ants if you prefer). If you
want <em>that</em> type of effect then you can use the
<a href="https://msdn.microsoft.com/en-us/library/dd162479(v=vs.85).aspx" rel="external nofollow noopener"><code>DrawFocusRect</code></a>
Win32 API.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> PaintButton<span class="symbol">(</span>Graphics g<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// .. snip ..</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ShowFocusCues <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>Focused<span class="symbol">)</span>
 <span class="symbol">{</span>
 bounds<span class="symbol">.</span>Inflate<span class="symbol">(</span><span class="symbol">-</span><span class="number">3</span><span class="symbol">,</span> <span class="symbol">-</span><span class="number">3</span><span class="symbol">)</span><span class="symbol">;</span>

 ControlPaint<span class="symbol">.</span>DrawFocusRectangle<span class="symbol">(</span>g<span class="symbol">,</span> bounds<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/kbd-focuscue.gif" class="gallery" title="The button control showing focus cues as focus is cycled with the tab key" ><img src="https://images.cyotek.com/image/thumbnail/devblog/kbd-focuscue.gif" alt="The button control showing focus cues as focus is cycled with the tab key" decoding="async" loading="lazy" /></a><figcaption>The button control showing focus cues as focus is cycled with the tab key</figcaption></figure>
<p>Notice in the demo above how focus cues and keyboard cues are
independent from each other.</p>
<h2 id="so-about-those-accelerators">So, about those accelerators</h2>
<p>Now that we've covered painting our control to show focus /
keyboard cues as appropriate, it's time to actually handle
accelerators. Once again, the <code>Control</code> class has everything we
need built right into it.</p>
<p>To start with, we override the <code>ProcessMnemonic</code> method. This
method is automatically called by .NET when a user presses an
<kbd>Alt</kbd> key combination and it is up to your component to
determine if it should process it or not. If the component can't
handle the accelerator, then it should return <code>false</code>. If it
can, then it should perform the action and return <code>true</code>. The
method includes a <code>char</code> argument that contains the accelerator
key (e.g. just the character code, not the alt modifier).</p>
<p>So how do you know if your component can handle it? Luckily the
<code>Control</code> class offers a static <code>IsMnemonic</code> method that takes a
<code>char</code> and a <code>string</code> as arguments. It will return <code>true</code> if the
source string contains a mnemonic matching the passed character.
Note that it expects the <code>&amp;</code> character is used to identify the
mnemonic. I assume WPF has a matching version of this method,
but I don't know where.</p>
<p>We can now implement the accelerator handling quite simply using
the following snippet</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">bool</span> ProcessMnemonic<span class="symbol">(</span><span class="keyword">char</span> charCode<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">bool</span> processed<span class="symbol">;</span>

 processed <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>CanFocus <span class="symbol">&amp;&amp;</span> IsMnemonic<span class="symbol">(</span>charCode<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Text<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>processed<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Focus<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>PerformClick<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> processed<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>We check to make sure the control can be focused in addition to
checking if our control has a match for the incoming mnemonic,
and if both are true then we set focus to the control and raise
the <code>Click</code> event. If you don't need (or want) to set focus to
the control, then you can skip the <code>CanFocus</code> check and <code>Focus</code>
call.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/kbd-accelerator.gif" class="gallery" title="In this final demonstration, we see pressing Alt+D triggering the Click event of the button. Mission accomplished!" ><img src="https://images.cyotek.com/image/thumbnail/devblog/kbd-accelerator.gif" alt="In this final demonstration, we see pressing Alt+D triggering the Click event of the button. Mission accomplished!" decoding="async" loading="lazy" /></a><figcaption>In this final demonstration, we see pressing Alt+D triggering the Click event of the button. Mission accomplished!</figcaption></figure><h2 id="bonus-points-other-keys">Bonus Points: Other Keys</h2>
<p>Some controls accept other keyboard conventions. For example, a
button accepts the <kbd>Enter</kbd> or <kbd>Space</kbd> keys to
click the button (the former acting as an accelerator, the
latter acting as though the mouse were being pressed and
released), combo boxes accept <kbd>F4</kbd> to display drop
downs and so on. If your control mimics any standard controls,
it's always worthwhile adding support for these conventions too.
And don't forget about focus!</p>
<p>For example, in the sample button, I modify <code>OnMouseDown</code> to set
focus to the control if it isn't already set</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseDown<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnMouseDown<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>CanFocus<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Focus<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">this</span><span class="symbol">.</span>SetState<span class="symbol">(</span>ButtonState<span class="symbol">.</span>Pushed<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>I also add overrides for <code>OnKeyDown</code> and <code>OnKeyUp</code> to mimic the
button being pushed and then released when the user presses and
releases the space bar</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnKeyDown<span class="symbol">(</span>KeyEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnKeyDown<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span><span class="symbol">(</span>e<span class="symbol">.</span>KeyCode <span class="symbol">==</span> Keys<span class="symbol">.</span>Space <span class="symbol">&amp;&amp;</span> e<span class="symbol">.</span>Modifiers <span class="symbol">==</span> Keys<span class="symbol">.</span>None<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>SetState<span class="symbol">(</span>ButtonState<span class="symbol">.</span>Pushed<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnKeyUp<span class="symbol">(</span>KeyEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnKeyUp<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span><span class="symbol">(</span><span class="symbol">(</span>e<span class="symbol">.</span>KeyCode <span class="symbol">&amp;</span> Keys<span class="symbol">.</span>Space<span class="symbol">)</span> <span class="symbol">==</span> Keys<span class="symbol">.</span>Space<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>SetState<span class="symbol">(</span>ButtonState<span class="symbol">.</span>Normal<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>PerformClick<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>However, I'm not adding anything to handle the enter key. This
is because I don't need to - in this example, the <code>Button</code>
control implements the <code>IButtonControl</code> interface and so it's
handled for me without any special actions. For non-button
controls, I would need to explicitly handle enter key presses if
appropriate.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2016-06-03 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/adding-keyboard-accelerators-and-visual-cues-to-a-winforms-control .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comImplementing events more efficiently in .NET applications urn:uuid:d2b78de5-6867-4ff0-9151-d842c54a42622016-06-02T06:37:11Z2016-05-20T19:46:31Z<p>One of the things that frequently annoys me about third party
controls (including those built into the .NET Framework) are
properties that either aren't <code>virtual</code>, or don't have
corresponding change events / virtual methods. Quite often I
find myself wanting to perform an action when a property is
changed, and if neither of those are present I end up having to
create a custom version of the property, and as a rule, I don't
like using the <code>new</code> keyword unless there is no other
alternative.</p>
<p>As a result of this, whenever I add properties to my WinForm
controls, I tend to ensure they have a change event, and most
often they are also virtual as I have a custom code snippet to
build the boilerplate. That can mean some controls have an awful
lot of events (for example, the <a href="https://github.com/cyotek/Cyotek.Windows.Forms.ImageBox" rel="external nofollow noopener">ImageBox</a> control has (at
the time of writing) 42 custom events on top of those it
inherits, some for actions but the majority for properties).
Many of these events will be rarely used.</p>
<p>As an example, here is a typical property and backing event</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">bool</span> _allowUnfocusedMouseWheel<span class="symbol">;</span>

<span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Behavior&quot;</span><span class="symbol">)</span><span class="symbol">,</span> DefaultValue<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">virtual</span> <span class="keyword">bool</span> AllowUnfocusedMouseWheel
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _allowUnfocusedMouseWheel<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_allowUnfocusedMouseWheel <span class="symbol">!=</span> value<span class="symbol">)</span>
 <span class="symbol">{</span>
 _allowUnfocusedMouseWheel <span class="symbol">=</span> value<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>OnAllowUnfocusedMouseWheelChanged<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Property Changed&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">event</span> EventHandler AllowUnfocusedMouseWheelChanged<span class="symbol">;</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> OnAllowUnfocusedMouseWheelChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 EventHandler handler<span class="symbol">;</span>

 handler <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>AllowUnfocusedMouseWheelChanged<span class="symbol">;</span>

 handler<span class="symbol">?.</span>Invoke<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">,</span> e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Quite straightforward - a backing field, a property definition,
a change event, and a protected virtual method to raise the
change event the &quot;safe&quot; way. It's an example of an event that
will be rarely used, but you never know and so I continue to
follow this pattern.</p>
<p>Despite all the years I've been writing C# code, I never
actually thought about <em>how</em> the C# compiler implements events,
beyond the fact that I knew it created <code>add</code> and <code>remove</code>
methods, in a similar fashion to how a property creates <code>get</code>
and <code>set</code> methods.</p>
<p>From browsing the <a href="http://referencesource.microsoft.com" rel="external nofollow noopener">.NET Reference Source</a> in the past, I knew
the <code>Control</code> class implemented events slightly differently to
above, but I never thought about why. I assumed it was something
they had done in .NET 1.0 and never changed with Microsoft's
mania for backwards compatibility.</p>
<p>I am currently just under halfway through CLR via C# by Jeffrey
Richter. It's a nicely written book, and probably would have
been of great help many years ago when I first started using C#
(and no doubt as I get through the last third of the book I'm
going to find some new goodies). As it is, I've been ploughing
through it when I hit the chapter on Events. This chapter
started off by describing how events are implemented by the CLR
and expanding on what I already knew. It then dropped the slight
bombshell that this is quite inefficient as it requires more
memory, especially for events that are never used. Given I
liberally sprinkle my WinForms controls with events and I have
lots of other classes with events, mainly custom observable
collections and classes implementing <code>INotifyPropertyChanged</code>
(many of those!), it's a safe bet that I'm using a goodly chunk
of ram for no good reason. And if I can save some memory &quot;for
free&quot; as it were... well, every little helps.</p>
<p>The book then continued with a description of how to explicitly
implement an event, which is how the base <code>Control</code> class I
mentioned earlier does it, and why the reference source code
looked different to typical. While the functionality is
therefore clearly built into .NET, he also proposes and
demonstrates code for a custom approach which is possibly better
than the built in version.</p>
<p>In this article, I'm only going to cover what is built into the
.NET Framework. Firstly, because I don't believe in taking
someone else's written content, deleting the introductions and
copyright information and them passing it off as my own work.
And secondly, as I'm going to start using this approach with my
myriad libraries of WinForm controls, their base implementations
already have this built in, so I just need to bolt my bits on
top of it.</p>
<h3 id="how-big-is-my-class">How big is my class?</h3>
<p>Before I made any changes to my code, I decided I wanted to know
how much memory the <code>ImageBox</code> control required. (Not that I
doubted Jeffrey, but it doesn't hurt to be cautious, especially
given the mountain of work this will entail if I start
converting all my existing code). There isn't really a simple
way of getting the size of an object, but <a href="http://stackoverflow.com/a/1128674/148962" rel="external nofollow noopener">this post</a> on
Stack Overflow (where else!) has one method.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">unsafe</span>
<span class="symbol">{</span>
 RuntimeTypeHandle th <span class="symbol">=</span> <span class="keyword">typeof</span><span class="symbol">(</span>ImageBox<span class="symbol">)</span><span class="symbol">.</span>TypeHandle<span class="symbol">;</span>
 <span class="keyword">int</span> size <span class="symbol">=</span> <span class="symbol">*</span><span class="symbol">(</span><span class="symbol">*</span><span class="symbol">(</span><span class="keyword">int</span><span class="symbol">**</span><span class="symbol">)</span><span class="symbol">&amp;</span>th <span class="symbol">+</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>

 Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span>size<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>When running this code in the current version of the <code>ImageBox</code>,
I get a value of <strong>968</strong>. It's a fairly meaningless number, but
does give me something to compare. However, as I didn't quite
trust it I also profiled the demo program with a <a href="https://www.jetbrains.com/dotmemory/" rel="external nofollow noopener">memory
profiler</a>. After profiling, dotMemory also showed the size of
the ImageBox control to be 968 bytes. Lucky me.</p>
<h3 id="explicitly-implementing-an-event">Explicitly implementing an event</h3>
<p>At the start of the article, I showed a typical compiler
generated event. Now I'm going to explicitly implement it. This
is done by using a proxy class to store the event delegates. So
instead of having delegates automatically created for each
event, they will only be created when explicitly binding the
event. This is where Jeffrey prefers a custom approach, but I'm
going to stick with the class provided by the .NET Framework,
the <a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.eventhandlerlist(v=vs.110).aspx" rel="external nofollow noopener">EventHandlerList class</a>.</p>
<p>As the proxy class is essentially a dictionary, we need a key to
identify the event. As we're trying to save memory, we create a
static object which will be used for all occurrences of this
event, no matter how many instances of our component are
created.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> <span class="keyword">object</span> EventAllowUnfocusedMouseWheelChanged <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">object</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>Next, we need to implement the <code>add</code> and <code>remove</code> accessors of
the event ourselves</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">event</span> EventHandler AllowUnfocusedMouseWheelChanged
<span class="symbol">{</span>
 add
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Events<span class="symbol">.</span>AddHandler<span class="symbol">(</span>EventAllowUnfocusedMouseWheelChanged<span class="symbol">,</span> value<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 remove
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Events<span class="symbol">.</span>RemoveHandler<span class="symbol">(</span>EventAllowUnfocusedMouseWheelChanged<span class="symbol">,</span> value<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>As you can see, the definition is the same, but now we have
created <code>add</code> and <code>remove</code> accessors which call either the
<code>AddHandler</code> or <code>RemoveHandler</code> methods of a per-instance
<code>EventHandlerList</code> component, using the key we defined earlier,
and of course the delegate value to add or remove.</p>
<blockquote>
<p>In a WinForm's control, this is automatically provided via the
protected <code>Events</code> property. If you're explicitly implementing
events in a class which doesn't offer this functionality,
you'll need to create and manage an instance of the
<code>EventHandlerList</code> class yourself</p>
</blockquote>
<p>Finally, when it's time to invoke the method, we need to
retrieve the delegate from the <code>EventHandlerList</code>, once again
with our event key, and if it isn't null, invoke it as normal.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> OnAllowUnfocusedMouseWheelChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 EventHandler handler<span class="symbol">;</span>

 handler <span class="symbol">=</span> <span class="symbol">(</span>EventHandler<span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>Events<span class="symbol">[</span>EventAllowUnfocusedMouseWheelChanged<span class="symbol">]</span><span class="symbol">;</span>

 handler<span class="symbol">?.</span>Invoke<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">,</span> e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>There are no generic overloads, so you'll need to cast the
returned <code>Delegate</code> into the appropriate <code>EventHandler</code>,
<code>EventHandler&lt;T&gt;</code> or custom delegate.</p>
<p>Simple enough, and you can easily have a code snippet do all the
grunt work. The pain will come from if you decide to convert
existing code.</p>
<h3 id="does-this-break-anything">Does this break anything?</h3>
<p>No. You're only changing the implementation, not how other
components interact with your events. You won't need to make any
code changes to any code that interacts with your updated
component, and possibly won't even need to recompile the other
code (strong naming and binding issues aside!).</p>
<p>In other words, unless you do something daft like change your
the visibility of your event, or accidentally rename it,
explicitly implementing a previously implicitly defined event is
not a breaking change.</p>
<h3 id="how-big-is-my-class-redux">How big is my class, redux</h3>
<p>I modified the <code>ImageBox</code> control (you can see the changed
version on <a href="https://github.com/cyotek/Cyotek.Windows.Forms.ImageBox/blob/EventsOverhaul/Cyotek.Windows.Forms.ImageBox/ImageBox.cs" rel="external nofollow noopener">this branch</a> in GitHub) so that all the events
were explicitly implemented. After running the new version of
the code through the memory profiler / magic unsafe code, the
size of the <code>ImageBox</code> is now 632 bytes, knocking nearly a third
of the size off. No magic bullet, and isn't a full picture, but
I'll take it!</p>
<p>In all honesty, I don't know if this has really saved memory or
not. But I do know I have a plethora of controls with varying
numbers of events. And I know Jeffrey's CLR book is widely
touted as a rather good tome. And I know this is how Microsoft
have implemented events in the base <code>Control</code> classes (possibly
elsewhere too, I haven't looked). So with all these &quot;I knows&quot;, I
also know I'm going to have all new events follow this pattern
in future, and I'll be retrofitting existing code when I can.</p>
<h3 id="an-all-you-can-eat-code-snippet">An all-you-can-eat code snippet</h3>
<p>I love <a href="https://msdn.microsoft.com/en-us/library/ms165392.aspx" rel="external nofollow noopener">code snippets</a> and tend to create them whenever I
have boilerplate code to implement repeatedly. In fact, most of
my snippets actually are variations of property and event
implementations, to handle things like properties with change
events, or properties in classes that implement
<code>INotifyPropertyChanged</code> and other similar scenarios. I have now
retired my venerable basic property-with-event and
standalone-event snippets with new versions that do explicit
event implementing. As I haven't prepared a demonstration
program for this article, I instead present this code snippet
for generating properties with backing events - I hope someone
finds them as useful as I do.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;?</span><span class="name">xml</span> <span class="name">version</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1.0</span><span class="symbol">&quot;</span> <span class="name">encoding</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">utf-8</span><span class="symbol">&quot;</span> <span class="symbol">?&gt;</span>
<span class="symbol">&lt;</span><span class="name">CodeSnippets</span> <span class="name">xmlns</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">CodeSnippet</span> <span class="name">Format</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1.0.0</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Header</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Title</span><span class="symbol">&gt;</span>Property with Backing Event<span class="symbol">&lt;/</span><span class="name">Title</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Shortcut</span><span class="symbol">&gt;</span>prope<span class="symbol">&lt;/</span><span class="name">Shortcut</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Description</span><span class="symbol">&gt;</span>Code snippet for property with backing field and a change event<span class="symbol">&lt;/</span><span class="name">Description</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Author</span><span class="symbol">&gt;</span>Richard Moss<span class="symbol">&lt;/</span><span class="name">Author</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">SnippetTypes</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">SnippetType</span><span class="symbol">&gt;</span>Expansion<span class="symbol">&lt;/</span><span class="name">SnippetType</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">SnippetTypes</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Header</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Snippet</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Declarations</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Literal</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">ID</span><span class="symbol">&gt;</span>type<span class="symbol">&lt;/</span><span class="name">ID</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">ToolTip</span><span class="symbol">&gt;</span>Property type<span class="symbol">&lt;/</span><span class="name">ToolTip</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Default</span><span class="symbol">&gt;</span>int<span class="symbol">&lt;/</span><span class="name">Default</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Literal</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Literal</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">ID</span><span class="symbol">&gt;</span>name<span class="symbol">&lt;/</span><span class="name">ID</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">ToolTip</span><span class="symbol">&gt;</span>Property name<span class="symbol">&lt;/</span><span class="name">ToolTip</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Default</span><span class="symbol">&gt;</span>MyProperty<span class="symbol">&lt;/</span><span class="name">Default</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Literal</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Literal</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">ID</span><span class="symbol">&gt;</span>field<span class="symbol">&lt;/</span><span class="name">ID</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">ToolTip</span><span class="symbol">&gt;</span>The variable backing this property<span class="symbol">&lt;/</span><span class="name">ToolTip</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Default</span><span class="symbol">&gt;</span>myVar<span class="symbol">&lt;/</span><span class="name">Default</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Literal</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Declarations</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Code</span> <span class="name">Language</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">csharp</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span><span class="symbol">&lt;![CDATA[</span><span class="section">private $type$ $field$;

 [Category(&quot;&quot;)]
 [DefaultValue(&quot;&quot;)]
 public $type$ $name$
 {
 get { return $field$; }
 set
 {
 if ($field$ != value)
 {
 $field$ = value;

 this.On$name$Changed(EventArgs.Empty);
 }
 }
 }

 private static readonly object Event$name$Changed = new object();

 /// &lt;summary&gt;
 /// Occurs when the $name$ property value changes
 /// &lt;/summary&gt;
 [Category(&quot;Property Changed&quot;)]
 public event EventHandler $name$Changed
 {
 add
 {
 this.Events.AddHandler(Event$name$Changed, value);
 }
 remove
 {
 this.Events.RemoveHandler(Event$name$Changed, value);
 }
 }

 /// &lt;summary&gt;
 /// Raises the &lt;see cref=&quot;$name$Changed&quot; /&gt; event.
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;e&quot;&gt;The &lt;see cref=&quot;EventArgs&quot; /&gt; instance containing the event data.&lt;/param&gt;
 protected virtual void On$name$Changed(EventArgs e)
 {
 EventHandler handler;

 handler = (EventHandler)this.Events[Event$name$Changed];

 handler?.Invoke(this, e);
 }

 $end$</span><span class="symbol">]]&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Code</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Snippet</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">CodeSnippet</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">CodeSnippets</span><span class="symbol">&gt;</span>
</pre>
</figure>
<h2 id="update-history">Update History</h2>
<ul>
<li>2016-05-20 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/implementing-events-more-efficiently-in-net-applications .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comGenerating code using T4 templatesurn:uuid:b29d688c-04be-4626-a622-154dab1c7cbe2016-03-20T07:09:05Z2016-03-20T07:09:05Z<p>Recently I was updating a library that contains two keyed
collection classes. These collections aren't the usual
run-of-the-mill collections as they need to be able to support
duplicate keys. Normally I'd inherit from <code>KeyedCollection</code> but
as with most collection implementations, duplicate keys are not
permitted in this class.</p>
<p>I'd initially solved the problem by simply creating my own base
class to fit my requirements, and this works absolutely fine.
However, this wasn't going to suffice as a long term solution as
I don't want that base class to be part of a public API,
especially a public API that has nothing to do with offering
custom base collections to consumers.</p>
<p>Another way I could have solved the problem would be to just
duplicate all that boilerplate code, but that was pretty much a
last resort. If there's one thing I really don't like doing it's
fixing the same bugs over and over again in duplicated code!</p>
<p>Then I remembered about <a href="https://msdn.microsoft.com/en-us/library/bb126445.aspx" rel="external nofollow noopener">T4 Templates</a>, which has been a
feature of Visual Studio for some time I believe. Previously my
only interaction with them has been via <a href="http://www.toptensoftware.com/petapoco/" rel="external nofollow noopener">PetaPoco</a>, a rather
marvellous library which generates C# classes based on a
database model, provides a micro ORM, and has powered cyotek.com
for years. This proved to be a nice solution for my collection
issue, and I thought I'd document the process here, firstly as
it's been a while since I blogged, and secondly as a reference
for &quot;next time&quot;.</p>
<h2 id="creating-the-template">Creating the template</h2>
<p>First, we need to create a template. To do this from Visual
Studio, open the <strong>Project</strong> menu and click <strong>Add New Item</strong>.
The select <em>Text Template</em> from the list of templates, give it a
name, and click <strong>Add</strong>.</p>
<p>This will create a simple file containing something similar to
the following</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">&lt;</span>#<span class="symbol">@</span> template debug<span class="symbol">=</span><span class="string">&quot;false&quot;</span> hostspecific<span class="symbol">=</span><span class="string">&quot;false&quot;</span> language<span class="symbol">=</span><span class="string">&quot;C#&quot;</span> #<span class="symbol">&gt;</span>
<span class="symbol">&lt;</span>#<span class="symbol">@</span> assembly name<span class="symbol">=</span><span class="string">&quot;System.Core&quot;</span> #<span class="symbol">&gt;</span>
<span class="symbol">&lt;</span>#<span class="symbol">@</span> import <span class="keyword">namespace</span><span class="symbol">=</span><span class="string">&quot;System.Linq&quot;</span> #<span class="symbol">&gt;</span>
<span class="symbol">&lt;</span>#<span class="symbol">@</span> import <span class="keyword">namespace</span><span class="symbol">=</span><span class="string">&quot;System.Text&quot;</span> #<span class="symbol">&gt;</span>
<span class="symbol">&lt;</span>#<span class="symbol">@</span> import <span class="keyword">namespace</span><span class="symbol">=</span><span class="string">&quot;System.Collections.Generic&quot;</span> #<span class="symbol">&gt;</span>
<span class="symbol">&lt;</span>#<span class="symbol">@</span> output extension<span class="symbol">=</span><span class="string">&quot;.txt&quot;</span> #<span class="symbol">&gt;</span>
</pre>
</figure>
<p>A T4 template is basically the content you want to output, with
one or more control blocks for dynamically changing the content.
In other words, it's just like a Razor HTML file, WebForms,
Classic ASP, PHP... the list is probably endless.</p>
<p>Each block is delimited by <code>&lt;#</code> and <code>#&gt;</code>, the <code>@</code> symbols above
are directives. We can use the <code>=</code> symbol to inject content. For
example, if modify the template to include the following lines</p>
<figure class="lang-html highlight"><figcaption><span>html</span></figcaption><pre class="code">
<span class="literal">&lt;</span><span class="name">html</span><span class="literal">&gt;</span>
<span class="literal">&lt;</span><span class="name">head</span><span class="literal">&gt;</span>
<span class="literal">&lt;</span><span class="name">title</span><span class="literal">&gt;</span>&lt;#=DateTime.Now#&gt;<span class="literal">&lt;/</span><span class="name">title</span><span class="literal">&gt;</span>
<span class="literal">&lt;/</span><span class="name">head</span><span class="literal">&gt;</span>
<span class="literal">&lt;/</span><span class="name">html</span><span class="literal">&gt;</span>
</pre>
</figure>
<p>Save the file, then in the Project Explorer, expand the node for
the file - by default the auto generated content will be nested
beneath your template file, as with any other designer code.
Open the generated file and you should see something like this</p>
<figure class="lang-html highlight"><figcaption><span>html</span></figcaption><pre class="code">
<span class="literal">&lt;</span><span class="name">html</span><span class="literal">&gt;</span>
<span class="literal">&lt;</span><span class="name">head</span><span class="literal">&gt;</span>
<span class="literal">&lt;</span><span class="name">title</span><span class="literal">&gt;</span>03/12/2016 12:41:07<span class="literal">&lt;/</span><span class="name">title</span><span class="literal">&gt;</span>
<span class="literal">&lt;/</span><span class="name">head</span><span class="literal">&gt;</span>
<span class="literal">&lt;/</span><span class="name">html</span><span class="literal">&gt;</span>
</pre>
</figure>
<h2 id="changing-the-file-name">Changing the file name</h2>
<p>The name of the auto-generated file is based on the underlying
template, so make sure your template is named appropriately. You
can get the desired file extension by including the following
directive in the template</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">&lt;</span>#<span class="symbol">@</span> output extension<span class="symbol">=</span><span class="string">&quot;.txt&quot;</span> #<span class="symbol">&gt;</span>
</pre>
</figure>
<p>If no directive at all is present, then <code>.cs</code> will be used.</p>
<h2 id="including-other-files">Including other files</h2>
<p>So far, things are looking positive - we can create a template
that will spit out our content, and dynamically manipulate it.
But it's still one file, and in my use case I'll need at least
two. Enter - the include directive. By including this directive,
the contents of another file will be injected, allowing us to
have multiple templates generated from one common file.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">&lt;</span>#<span class="symbol">@</span> include file<span class="symbol">=</span><span class="string">&quot;CollectionBase.ttinclude&quot;</span> #<span class="symbol">&gt;</span>
</pre>
</figure>
<p>If your include file makes use of variables, they are
automatically inherited from the parent template, which is the
key piece of magic I need.</p>
<h2 id="adding-conditional-logic">Adding conditional logic</h2>
<p>So far I've mentioned the <code>&lt;%@ ... %&gt;</code> directives, and the <code>&lt;%= ... %&gt;</code> insertion blocks. But what about if you want to include
code for decision making, branching, and so on? For this, you
use the <code>&lt;% ... %&gt;</code> syntax without any symbols on the opening
delimiter. For example, I use the following code to include a
certain <code>using</code> statement if a variable has been set</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> System<span class="symbol">.</span>Collections<span class="symbol">.</span>Generic<span class="symbol">;</span>
<span class="symbol">&lt;</span># <span class="keyword">if</span> <span class="symbol">(</span>UsePropertyChanged<span class="symbol">)</span> <span class="symbol">{</span> #<span class="symbol">&gt;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>ComponentModel<span class="symbol">;</span>
<span class="symbol">&lt;</span># <span class="symbol">}</span> #<span class="symbol">&gt;</span>
</pre>
</figure>
<p>In the above example, the line using
<em>System.Collections.Generic;</em> will always be written. On the
other hand, the <em>using System.ComponentModel;</em> line will only be
written if the <code>UsePropertyChanged</code> variable has been set.</p>
<blockquote>
<p>Note: Remember that T4 templates are compiled and executed. So
syntax errors in your C# code (such as forgetting to assign
(or define) the <code>UsePropertyChanged</code> variable above) will
cause the template generation to fail, and any related output
files to be only partially generated, if at all.</p>
</blockquote>
<h2 id="debugging-templates">Debugging templates</h2>
<p>I haven't really tested this much, as my own templates were
fairly straight forward and didn't have any complicated logic.
However, you can stick breakpoints in your <code>.tt</code> or <code>.ttinclude</code>
files, and then debug the template generation by context
clicking the template file and choosing <strong>Debug T4 Template</strong>
from the menu. For example, this may be useful if you create
helper methods in your templates for performing calculations.</p>
<h2 id="putting-it-all-together">Putting it all together</h2>
<p>The two collections I want to end up with are
<code>ColorEntryCollection</code> and <code>ColorEntryContainerCollection</code>. Both
will share a lot of boilerplate code, but also some custom code,
so I'll need to include dedicated CS files in addition to the
auto-generated ones.</p>
<p>To start with, I create my <code>ColorEntryCollection.cs</code> and
<code>ColorEntryContainerCollection.cs</code> files with the following
class definitions. Note the use of the <code>partial</code> keyword so I
can have the classes built from multiple code files.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> ColorEntryCollection
<span class="symbol">{</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> ColorEntryContainerCollection
<span class="symbol">{</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Next, I created two T4 template files,
<code>ColorEntryCollectionBase.tt</code> and
<code>ColorEntryContainerCollectionBase.tt</code>. I made sure these had
different file names to avoid the auto-generated <code>.cs</code> files
from overwriting the custom ones (I didn't test to see if VS
handles this, better safe than sorry).</p>
<p>The contents of the <code>ColorEntryCollectionBase.tt</code> file looks
like this</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">&lt;</span>#
<span class="keyword">string</span> ClassName <span class="symbol">=</span> <span class="string">&quot;ColorEntryCollection&quot;</span><span class="symbol">;</span>
<span class="keyword">string</span> CollectionItemType <span class="symbol">=</span> <span class="string">&quot;ColorEntry&quot;</span><span class="symbol">;</span>
<span class="keyword">bool</span> UsePropertyChanged <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
#<span class="symbol">&gt;</span>

<span class="symbol">&lt;</span>#<span class="symbol">@</span> include file<span class="symbol">=</span><span class="string">&quot;CollectionBase.ttinclude&quot;</span> #<span class="symbol">&gt;</span>
</pre>
</figure>
<p>The contents of <code>ColorEntryContainerCollectionBase.tt</code> are</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">&lt;</span>#
<span class="keyword">string</span> ClassName <span class="symbol">=</span> <span class="string">&quot;ColorEntryContainerCollection&quot;</span><span class="symbol">;</span>
<span class="keyword">string</span> CollectionItemType <span class="symbol">=</span> <span class="string">&quot;ColorEntryContainer&quot;</span><span class="symbol">;</span>
<span class="keyword">bool</span> UsePropertyChanged <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
#<span class="symbol">&gt;</span>

<span class="symbol">&lt;</span>#<span class="symbol">@</span> include file<span class="symbol">=</span><span class="string">&quot;CollectionBase.ttinclude&quot;</span> #<span class="symbol">&gt;</span>
</pre>
</figure>
<p>As you can see, the templates are very simple - basically just
setting it up the key information that is required to generate
the template, then including another file - and it is this file
that has the true content.</p>
<p>The final piece of the puzzle therefore, was to create my
<code>CollectionBase.ttinclude</code> file. I copied into this my original
base class, then pretty much did a search and replace to replace
hard coded class names to use T4 text blocks. The file is too
big to include in-line in this article, so I've just included
the first few lines to show how the different blocks fit
together.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> System<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Collections<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Collections<span class="symbol">.</span>Generic<span class="symbol">;</span>
<span class="symbol">&lt;</span># <span class="keyword">if</span> <span class="symbol">(</span>UsePropertyChanged<span class="symbol">)</span> <span class="symbol">{</span> #<span class="symbol">&gt;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>ComponentModel<span class="symbol">;</span>
<span class="symbol">&lt;</span># <span class="symbol">}</span> #<span class="symbol">&gt;</span>

<span class="keyword">namespace</span> Cyotek<span class="symbol">.</span>Drawing
<span class="symbol">{</span>
 <span class="keyword">partial</span> <span class="keyword">class</span> <span class="symbol">&lt;</span>#<span class="symbol">=</span>ClassName#<span class="symbol">&gt;</span> <span class="symbol">:</span> IList<span class="symbol">&lt;&lt;</span>#<span class="symbol">=</span>CollectionItemType#<span class="symbol">&gt;&gt;</span>
 <span class="symbol">{</span>
 <span class="keyword">private</span> <span class="keyword">readonly</span> IList<span class="symbol">&lt;&lt;</span>#<span class="symbol">=</span>CollectionItemType#<span class="symbol">&gt;&gt;</span> _items<span class="symbol">;</span>
 <span class="keyword">private</span> <span class="keyword">readonly</span> IDictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> SmallList<span class="symbol">&lt;&lt;</span>#<span class="symbol">=</span>CollectionItemType#<span class="symbol">&gt;&gt;&gt;</span> _nameLookup<span class="symbol">;</span>

 <span class="keyword">public</span> <span class="symbol">&lt;</span>#<span class="symbol">=</span>ClassName#<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _items <span class="symbol">=</span> <span class="keyword">new</span> List<span class="symbol">&lt;&lt;</span>#<span class="symbol">=</span>CollectionItemType#<span class="symbol">&gt;&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 _nameLookup <span class="symbol">=</span> <span class="keyword">new</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> SmallList<span class="symbol">&lt;&lt;</span>#<span class="symbol">=</span>CollectionItemType#<span class="symbol">&gt;&gt;&gt;</span><span class="symbol">(</span>StringComparer<span class="symbol">.</span>OrdinalIgnoreCase<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>All the <code>&lt;#=ClassName#&gt;</code> blocks get replaced with the
<code>ClassName</code> value from the parent <code>.tt</code> file, as do the
<code>&lt;#=CollectionItemType#&gt;</code> blocks. You can also see the
<code>UsePropertyChanged</code> variable logic I described earlier for
inserting a <code>using</code> statement - I used the same functionality in
other places to include entire methods or just extra lines where
appropriate.</p>
<p>Then it was just a case of right clicking the two <code>.tt</code> files I
created earlier and selecting <strong>Run Custom Tool</strong> from the
content menu which caused the contents of my two collections to
be fully generated from the template. The only thing left to do
was to then add the custom implementation code to the two main
class definitions and job done.</p>
<p>I also used the same process to create a bunch of standard tests
for those collections rather than having to duplicate those too.</p>
<h2 id="thats-all-folks">That's all folks</h2>
<p>Although normally you probably won't need this sort of
functionality, the fact that it is built right into Visual
Studio and so easy to use is pretty nice. It has certainly
solved my collection issue and I'll probably use it again in the
future.</p>
<p>While writing this article, I had a quick look around the <a href="https://msdn.microsoft.com/en-us/library/bb126445.aspx" rel="external nofollow noopener">MSDN
documentation</a> and there's plenty of advanced functionality
you can use with template generation which I haven't covered, as
just the basics were sufficient for me.</p>
<p>Although I haven't included the usual sample download with this
article, I think it's straightforward enough that it doesn't
need one. The final code will be available on our <a href="https://github.com/cyotek" rel="external nofollow noopener">GitHub</a>
page at some point in the future, once I've finished adding more
tests, and refactored a whole bunch of extremely awkwardly named
classes.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2016-03-20 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/generating-code-using-t4-templates .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comRotating an array using C#urn:uuid:f85fc30f-d6e6-4b93-b0e6-d51cee6818b32015-12-24T10:06:49Z2015-12-24T10:06:49Z<p>I've recently been working on a number of small test programs
for the different sections which make up a game I'm planning on
writing. One of these test systems involved a series of
<a href="https://en.wikipedia.org/wiki/Polyomino" rel="external nofollow noopener">polyominoes</a> which I needed to rotate. Internally, the data
for these shapes are stored as a simple boolean array, which I
access as though <a href="/post/converting-2d-arrays-to-1d-and-accessing-as-either-2d-or-1d">it were two dimensions</a>.</p>
<p>One of the requirements was that the player needs to be able to
rotate these shapes at 90° intervals, and so there were two
ways I could have solved this</p>
<ul>
<li>Define pre-rotated versions of all shapes</li>
<li>Rotate the shapes on the fly</li>
</ul>
<p>Clearly, I went with option two otherwise there would be no need
for this article! I choose not to go with the pre-rotated
approach, as firstly I'm using a lot of shapes and creating up
to 4 versions of each of these is not really worthwhile, and
secondly I don't want to store them either, or have to care
which orientation is currently in use.</p>
<p>This article describes how to rotate a 2D array in fixed
90° intervals, and also how to rotate 1D arrays that
masquerade as 2D arrays.</p>
<blockquote>
<p>Note: The code in this article will only work with rectangle
arrays. I don't usually use jagged arrays, so this code has no
special provisions to work with them.</p>
</blockquote>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/rotatearray.gif" class="gallery" title="A demonstration program rotating arrays representing tetrominoes" ><img src="https://images.cyotek.com/image/thumbnail/devblog/rotatearray.gif" alt="A demonstration program rotating arrays representing tetrominoes" decoding="async" loading="lazy" /></a><figcaption>A demonstration program rotating arrays representing tetrominoes</figcaption></figure><h2 id="creating-a-simple-sample">Creating a simple sample</h2>
<p>First up, we need an array to rotate. For the purposes of our
demo, we'll use the following array - note that the width and
the height of the array don't match.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">bool</span><span class="symbol">[</span><span class="symbol">,</span><span class="symbol">]</span> src<span class="symbol">;</span>

src <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">bool</span><span class="symbol">[</span><span class="number">2</span><span class="symbol">,</span> <span class="number">3</span><span class="symbol">]</span><span class="symbol">;</span>

src<span class="symbol">[</span><span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
src<span class="symbol">[</span><span class="number">0</span><span class="symbol">,</span> <span class="number">1</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
src<span class="symbol">[</span><span class="number">0</span><span class="symbol">,</span> <span class="number">2</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
src<span class="symbol">[</span><span class="number">1</span><span class="symbol">,</span> <span class="number">2</span><span class="symbol">]</span> <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
</pre>
</figure>
<p>We can visualize the contents of the array but dumping it in a
friendly fashion to the console</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> PrintArray<span class="symbol">(</span><span class="keyword">bool</span><span class="symbol">[</span><span class="symbol">,</span><span class="symbol">]</span> src<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> width<span class="symbol">;</span>
 <span class="keyword">int</span> height<span class="symbol">;</span>

 width <span class="symbol">=</span> src<span class="symbol">.</span>GetUpperBound<span class="symbol">(</span><span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span>
 height <span class="symbol">=</span> src<span class="symbol">.</span>GetUpperBound<span class="symbol">(</span><span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> row <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> row <span class="symbol">&lt;</span> height <span class="symbol">+</span> <span class="number">1</span><span class="symbol">;</span> row<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> col <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> col <span class="symbol">&lt;</span> width <span class="symbol">+</span> <span class="number">1</span><span class="symbol">;</span> col<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">char</span> c<span class="symbol">;</span>

 c <span class="symbol">=</span> src<span class="symbol">[</span>col<span class="symbol">,</span> row<span class="symbol">]</span> <span class="symbol">?</span> <span class="string">&#39;#&#39;</span> <span class="symbol">:</span> <span class="string">&#39;.&#39;</span><span class="symbol">;</span>

 Console<span class="symbol">.</span>Write<span class="symbol">(</span>c<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

PrintArray<span class="symbol">(</span>src<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>All of which provides the following stunning output</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
#.
#.
##
</pre>
</figure>
<h2 id="rotating-the-array-clockwise">Rotating the array clockwise</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/rotatearray-1a.png" class="gallery" title="The original program used to test rotating an array" ><img src="https://images.cyotek.com/image/thumbnail/devblog/rotatearray-1a.png" alt="The original program used to test rotating an array" decoding="async" loading="lazy" /></a><figcaption>The original program used to test rotating an array</figcaption></figure>
<p>This function will rotate an array 90° clockwise</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">bool</span><span class="symbol">[</span><span class="symbol">,</span><span class="symbol">]</span> RotateArrayClockwise<span class="symbol">(</span><span class="keyword">bool</span><span class="symbol">[</span><span class="symbol">,</span><span class="symbol">]</span> src<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> width<span class="symbol">;</span>
 <span class="keyword">int</span> height<span class="symbol">;</span>
 <span class="keyword">bool</span><span class="symbol">[</span><span class="symbol">,</span><span class="symbol">]</span> dst<span class="symbol">;</span>

 width <span class="symbol">=</span> src<span class="symbol">.</span>GetUpperBound<span class="symbol">(</span><span class="number">0</span><span class="symbol">)</span> <span class="symbol">+</span> <span class="number">1</span><span class="symbol">;</span>
 height <span class="symbol">=</span> src<span class="symbol">.</span>GetUpperBound<span class="symbol">(</span><span class="number">1</span><span class="symbol">)</span> <span class="symbol">+</span> <span class="number">1</span><span class="symbol">;</span>
 dst <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">bool</span><span class="symbol">[</span>height<span class="symbol">,</span> width<span class="symbol">]</span><span class="symbol">;</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> row <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> row <span class="symbol">&lt;</span> height<span class="symbol">;</span> row<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> col <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> col <span class="symbol">&lt;</span> width<span class="symbol">;</span> col<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> newRow<span class="symbol">;</span>
 <span class="keyword">int</span> newCol<span class="symbol">;</span>

 newRow <span class="symbol">=</span> col<span class="symbol">;</span>
 newCol <span class="symbol">=</span> height <span class="symbol">-</span> <span class="symbol">(</span>row <span class="symbol">+</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>

 dst<span class="symbol">[</span>newCol<span class="symbol">,</span> newRow<span class="symbol">]</span> <span class="symbol">=</span> src<span class="symbol">[</span>col<span class="symbol">,</span> row<span class="symbol">]</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> dst<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>How does it work? First we get the width and height of the array
using the <code>GetUpperBound</code> method of the <code>Array</code> class. As arrays
are zero based, we add <code>1</code> to each of these results, otherwise
the new array will be too small to hold the data.</p>
<p>Next, we create a new array - with the width and height ready
previously swapped, allowing us to correctly handle non-square
arrays.</p>
<p>Finally, we loop through each row and each column. For each
entry, we calculate the new row and column, then assign the
value from the source array to the transposed location in the
destination array</p>
<ul>
<li>To calculate the new row, we simply set the row to the
existing column value</li>
<li>To calculate the new column, we take the current row, add one
to it, then subtract that value from the original array's
height</li>
</ul>
<p>If we now call <code>RotateArrayClockwise</code> using our source array,
we'll get the following output</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
###
#..
</pre>
</figure>
<p>Perfect!</p>
<h2 id="rotating-the-array-anti-clockwise">Rotating the array anti-clockwise</h2>
<p>Rotating the array anti-clockwise (or counter clockwise
depending on your terminology) uses most of the same code as
previous, but the calculation for the new row and column is
slightly different</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
newRow <span class="symbol">=</span> width <span class="symbol">-</span> <span class="symbol">(</span>col <span class="symbol">+</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
newCol <span class="symbol">=</span> row<span class="symbol">;</span>
</pre>
</figure>
<ul>
<li>To calculate the new row we take the current column, add one
to it, then subtract that value from the original array's
width</li>
<li>The new column is the current row</li>
</ul>
<p>Using our trusty source array, this is what we get</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
..#
###
</pre>
</figure>
<h2 id="rotating-1d-arrays">Rotating 1D arrays</h2>
<p>Rotating a 1D array follows the same principles outlined above,
with the following differences</p>
<ul>
<li>As the array has only a single dimension, you cannot get the
width and the height automatically - you must know these in
advance</li>
<li>When calculating the new index position using <a href="https://en.wikipedia.org/wiki/Row-major_order" rel="external nofollow noopener">row-major
order</a> remember that as the width and the height have been
swapped, the calculation will be something similar to
<code>newIndex = newRow * height + newCol</code></li>
</ul>
<p>The following functions show how I rotate a 1D boolean array.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> Polyomino RotateAntiClockwise<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>Rotate<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> Polyomino RotateClockwise<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>Rotate<span class="symbol">(</span><span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> Polyomino Rotate<span class="symbol">(</span><span class="keyword">bool</span> clockwise<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">byte</span> width<span class="symbol">;</span>
 <span class="keyword">byte</span> height<span class="symbol">;</span>
 <span class="keyword">bool</span><span class="symbol">[</span><span class="symbol">]</span> result<span class="symbol">;</span>
 <span class="keyword">bool</span><span class="symbol">[</span><span class="symbol">]</span> matrix<span class="symbol">;</span>

 matrix <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Matrix<span class="symbol">;</span>
 width <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Width<span class="symbol">;</span>
 height <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Height<span class="symbol">;</span>
 result <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">bool</span><span class="symbol">[</span>matrix<span class="symbol">.</span>Length<span class="symbol">]</span><span class="symbol">;</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> row <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> row <span class="symbol">&lt;</span> height<span class="symbol">;</span> row<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> col <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> col <span class="symbol">&lt;</span> width<span class="symbol">;</span> col<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> index<span class="symbol">;</span>

 index <span class="symbol">=</span> row <span class="symbol">*</span> width <span class="symbol">+</span> col<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>matrix<span class="symbol">[</span>index<span class="symbol">]</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> newRow<span class="symbol">;</span>
 <span class="keyword">int</span> newCol<span class="symbol">;</span>
 <span class="keyword">int</span> newIndex<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>clockwise<span class="symbol">)</span>
 <span class="symbol">{</span>
 newRow <span class="symbol">=</span> col<span class="symbol">;</span>
 newCol <span class="symbol">=</span> height <span class="symbol">-</span> <span class="symbol">(</span>row <span class="symbol">+</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 newRow <span class="symbol">=</span> width <span class="symbol">-</span> <span class="symbol">(</span>col <span class="symbol">+</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 newCol <span class="symbol">=</span> row<span class="symbol">;</span>
 <span class="symbol">}</span>

 newIndex <span class="symbol">=</span> newRow <span class="symbol">*</span> height <span class="symbol">+</span> newCol<span class="symbol">;</span>

 result<span class="symbol">[</span>newIndex<span class="symbol">]</span> <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> <span class="keyword">new</span> Polyomino<span class="symbol">(</span>result<span class="symbol">,</span> height<span class="symbol">,</span> width<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="update-history">Update History</h2>
<ul>
<li>2015-12-24 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/rotating-an-array-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comWriting Adobe Swatch Exchange (ase) files using C#urn:uuid:c5d37d48-c255-4429-9908-24737ace603c2015-10-21T20:02:41Z2015-10-21T20:02:41Z<p>In my last post, I <a href="/post/reading-adobe-swatch-exchange-ase-files-using-csharp">described</a> how to read Adobe Swatch
Exchange files using C#. Now I'm going to update that sample
program to save <code>ase</code> files as well as load them.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ase-writer-1a.png" class="gallery" title="An example of a multi-group ASE file created by the sample application" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ase-writer-1a.png" alt="An example of a multi-group ASE file created by the sample application" decoding="async" loading="lazy" /></a><figcaption>An example of a multi-group ASE file created by the sample application</figcaption></figure><h2 id="writing-big-endian-values">Writing big endian values</h2>
<p>I covered the basics of writing big-endian values in my original
post on <a href="/post/writing-photoshop-color-swatch-aco-files-using-csharp">writing Photoshop aco files</a>, so I'll not cover that
again but only mention the new bits.</p>
<p>Firstly, we now need to store float values. I mentioned the
trick that <code>BitConverter.ToSingle</code> does where it converts a
<code>int</code> to a pointer, and then the pointer to a <code>float</code>. I'm going
to do exactly the reverse in order to write the float to a
stream - convert the <code>float</code> to a pointer, then convert it to an
<code>int</code>, then write the bytes of the <code>int</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> WriteBigEndian<span class="symbol">(</span><span class="keyword">this</span> Stream stream<span class="symbol">,</span> <span class="keyword">float</span> value<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">unsafe</span>
 <span class="symbol">{</span>
 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span><span class="symbol">*</span><span class="symbol">(</span><span class="keyword">int</span><span class="symbol">*</span><span class="symbol">)</span><span class="symbol">&amp;</span>value<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>We also need to store unsigned 2-byte integers, so we have
another extension for that.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> WriteBigEndian<span class="symbol">(</span><span class="keyword">this</span> Stream stream<span class="symbol">,</span> <span class="keyword">ushort</span> value<span class="symbol">)</span>
<span class="symbol">{</span>
 stream<span class="symbol">.</span>WriteByte<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span>value <span class="symbol">&gt;&gt;</span> <span class="number">8</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteByte<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span>value <span class="symbol">&gt;&gt;</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Finally, lets not forget our length prefixed strings!</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> WriteBigEndian<span class="symbol">(</span><span class="keyword">this</span> Stream stream<span class="symbol">,</span> <span class="keyword">string</span> value<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">]</span> data<span class="symbol">;</span>

 data <span class="symbol">=</span> Encoding<span class="symbol">.</span>BigEndianUnicode<span class="symbol">.</span>GetBytes<span class="symbol">(</span>value<span class="symbol">)</span><span class="symbol">;</span>

 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span>value<span class="symbol">.</span>Length<span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>Write<span class="symbol">(</span>data<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> data<span class="symbol">.</span>Length<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="saving-the-file">Saving the file</h2>
<p>I covered the format of an <code>ase</code> file in the previous post, so I
won't cover that again either. In summary, you have a version
header, a block count, then a number of blocks - of which a
block can either be a group (start or end) or a colour.</p>
<p>Saving the version header is rudimentary</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> WriteVersionHeader<span class="symbol">(</span>Stream stream<span class="symbol">)</span>
<span class="symbol">{</span>
 stream<span class="symbol">.</span>Write<span class="symbol">(</span><span class="string">&quot;ASEF&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">ushort</span><span class="symbol">)</span><span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">ushort</span><span class="symbol">)</span><span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

</pre>
</figure>
<p>After this, we write the number of blocks, then cycle each group
and colour in our document.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> WriteBlocks<span class="symbol">(</span>Stream stream<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> blockCount<span class="symbol">;</span>

 blockCount <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Groups<span class="symbol">.</span>Count <span class="symbol">*</span> <span class="number">2</span><span class="symbol">)</span> <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>Colors<span class="symbol">.</span>Count <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>Groups<span class="symbol">.</span>Sum<span class="symbol">(</span><span class="keyword">group</span> <span class="symbol">=&gt;</span> <span class="keyword">group</span><span class="symbol">.</span>Colors<span class="symbol">.</span>Count<span class="symbol">)</span><span class="symbol">;</span>

 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span>blockCount<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// write the global colors first</span>
 <span class="comment">// not sure if global colors + groups is a supported combination however</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span>ColorEntry color <span class="keyword">in</span> <span class="keyword">this</span><span class="symbol">.</span>Colors<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteBlock<span class="symbol">(</span>stream<span class="symbol">,</span> color<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="comment">// now write the groups</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span>ColorGroup <span class="keyword">group</span> <span class="keyword">in</span> <span class="keyword">this</span><span class="symbol">.</span>Groups<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteBlock<span class="symbol">(</span>stream<span class="symbol">,</span> <span class="keyword">group</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Writing a block is slightly complicated as you need to know - up
front - the final size of all of the data belonging to that
block. Originally I wrote the block to a temporary
<code>MemoryStream</code>, then copied the length and the data into the
real stream but that isn't a very efficient approach, so now I
just calculate the block size.</p>
<h3 id="writing-groups">Writing Groups</h3>
<p>If you recall from the previous article, a group is comprised of
at least two blocks - one that starts the group (and includes
the name), and one that finishes the group. There can also be
any number of colour blocks in between. Potentially you can have
nested groups, but I haven't coded for this - I need to grab
myself a Creative Cloud subscription and experiment with <code>ase</code>
files, at which point I'll update these samples if need be.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">int</span> GetBlockLength<span class="symbol">(</span>Block block<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> blockLength<span class="symbol">;</span>

 <span class="comment">// name data (2 bytes per character + null terminator, plus 2 bytes to describe that first number )</span>
 blockLength <span class="symbol">=</span> <span class="number">2</span> <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span><span class="symbol">(</span>block<span class="symbol">.</span>Name <span class="symbol">??</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">.</span>Length <span class="symbol">+</span> <span class="number">1</span><span class="symbol">)</span> <span class="symbol">*</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>block<span class="symbol">.</span>ExtraData <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 blockLength <span class="symbol">+=</span> block<span class="symbol">.</span>ExtraData<span class="symbol">.</span>Length<span class="symbol">;</span> <span class="comment">// data we can&#39;t process but keep anyway</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> blockLength<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> WriteBlock<span class="symbol">(</span>Stream stream<span class="symbol">,</span> ColorGroup block<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> blockLength<span class="symbol">;</span>

 blockLength <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetBlockLength<span class="symbol">(</span>block<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// write the start group block</span>
 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">ushort</span><span class="symbol">)</span>BlockType<span class="symbol">.</span>GroupStart<span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span>blockLength<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteNullTerminatedString<span class="symbol">(</span>stream<span class="symbol">,</span> block<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteExtraData<span class="symbol">(</span>stream<span class="symbol">,</span> block<span class="symbol">.</span>ExtraData<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// write the colors in the group</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span>ColorEntry color <span class="keyword">in</span> block<span class="symbol">.</span>Colors<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteBlock<span class="symbol">(</span>stream<span class="symbol">,</span> color<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="comment">// and write the end group block</span>
 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">ushort</span><span class="symbol">)</span>BlockType<span class="symbol">.</span>GroupEnd<span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span><span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// there isn&#39;t any data, but we still need to specify that</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="writing-colours">Writing Colours</h3>
<p>Writing a colour block is fairly painless, at least for RGB
colours. As with loading an <code>ase</code> file, I'm completely ignoring
the existence of Lab, CMYK and Gray scale colours.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">int</span> GetBlockLength<span class="symbol">(</span>ColorEntry block<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> blockLength<span class="symbol">;</span>

 blockLength <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetBlockLength<span class="symbol">(</span><span class="symbol">(</span>Block<span class="symbol">)</span>block<span class="symbol">)</span><span class="symbol">;</span>

 blockLength <span class="symbol">+=</span> <span class="number">6</span><span class="symbol">;</span> <span class="comment">// 4 bytes for the color space and 2 bytes for the color type</span>

 <span class="comment">// TODO: Include support for other color spaces</span>

 blockLength <span class="symbol">+=</span> <span class="number">12</span><span class="symbol">;</span> <span class="comment">// length of RGB data (3 * 4 bytes)</span>

 <span class="keyword">return</span> blockLength<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> WriteBlock<span class="symbol">(</span>Stream stream<span class="symbol">,</span> ColorEntry block<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> blockLength<span class="symbol">;</span>

 blockLength <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetBlockLength<span class="symbol">(</span>block<span class="symbol">)</span><span class="symbol">;</span>

 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">ushort</span><span class="symbol">)</span>BlockType<span class="symbol">.</span>Color<span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span>blockLength<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>WriteNullTerminatedString<span class="symbol">(</span>stream<span class="symbol">,</span> block<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">;</span>

 stream<span class="symbol">.</span>Write<span class="symbol">(</span><span class="string">&quot;RGB &quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">float</span><span class="symbol">)</span><span class="symbol">(</span>block<span class="symbol">.</span>R <span class="symbol">/</span> <span class="number">255.0</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">float</span><span class="symbol">)</span><span class="symbol">(</span>block<span class="symbol">.</span>G <span class="symbol">/</span> <span class="number">255.0</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">float</span><span class="symbol">)</span><span class="symbol">(</span>block<span class="symbol">.</span>B <span class="symbol">/</span> <span class="number">255.0</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">ushort</span><span class="symbol">)</span>block<span class="symbol">.</span>Type<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>WriteExtraData<span class="symbol">(</span>stream<span class="symbol">,</span> block<span class="symbol">.</span>ExtraData<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="caveats-or-why-this-took-longer-than-it-should-have-done">Caveats, or why this took longer than it should have done</h2>
<p>When I originally tested this code, I added a simple compare
function which compared the bytes of a source <code>ase</code> file with a
version written by the new code. For two of the three samples I
was using, this was fine, but for the third the files didn't
match. As this didn't help me in any way diagnose the issue, I
ended up writing a very basic (and inefficient!) hex viewer,
artfully highlighted using the same colours as the <code>ase</code> format
description on <a href="http://www.selapa.net/swatches/colors/fileformats.php#adobe_ase" rel="external nofollow noopener">sepla.net</a>.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ase-writer-1b.png" class="gallery" title="Comparing a third party ASE file with the version created by the sample application" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ase-writer-1b.png" alt="Comparing a third party ASE file with the version created by the sample application" decoding="async" loading="lazy" /></a><figcaption>Comparing a third party ASE file with the version created by the sample application</figcaption></figure>
<p>This allowed me to easily view the files side by side and be
able to break the files down into their sections and see what
was wrong. The example screenshot above shows an identical
comparison.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ase-writer-1c.png" class="gallery" title="Another compare of a third party ASE file with the version created by the sample application, showing the colour data is the same, but the raw file differs" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ase-writer-1c.png" alt="Another compare of a third party ASE file with the version created by the sample application, showing the colour data is the same, but the raw file differs" decoding="async" loading="lazy" /></a><figcaption>Another compare of a third party ASE file with the version created by the sample application, showing the colour data is the same, but the raw file differs</figcaption></figure>
<p>With that third sample file, it was more complicated. In the
first case, the file sizes were different - the hex viewer very
clearly showed that the sample file has 3 extra null bytes at
the end of the file, which my version doesn't bother writing.
I'm not entirely sure what these bytes are for, but I can't
imagine they are official as it's an odd number.</p>
<p>The second issue was potentially more problematic. In the
screenshot above, you can see all the orange values which are
the float point representations of the RGB colours, and the last
byte of each of these values does not match. However, the
translated RGB values <strong>do</strong> match, so I guess it is a rounding
/ precision issue.</p>
<p>When I turn this into something more production ready, I will
probably store the original floating point values and write them
back, rather than loosing precision by converting them to
integers (well, bytes really as the range is 0-255) and back
again.</p>
<h2 id="on-with-the-show">On with the show</h2>
<p>The updated demonstration application is available for download
below, including new sample files generated directly by the
program.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2015-10-21 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/writing-adobe-swatch-exchange-ase-files-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comReading Adobe Swatch Exchange (ase) files using C#urn:uuid:24615ad2-52ae-4bc9-9526-582fa6c161922015-10-16T17:14:07Z2015-10-16T17:14:07Z<p>Previously I wrote how to <a href="/post/reading-photoshop-color-swatch-aco-files-using-csharp">read</a> and <a href="/post/writing-photoshop-color-swatch-aco-files-using-csharp">write</a> files using
the Photoshop Color Swatch file format. In this article
mini-series, I'm now going to take a belated look at Adobe's
Swatch Exchange file format and show how to read and write these
files using C#. This first article covers reading an existing
<code>ase</code> file.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ase-loader-1a.png" class="gallery" title="An example of an ASE file with a single group containing 5 RGB colours" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ase-loader-1a.png" alt="An example of an ASE file with a single group containing 5 RGB colours" decoding="async" loading="lazy" /></a><figcaption>An example of an ASE file with a single group containing 5 RGB colours</figcaption></figure><h2 id="caveat-emptor">Caveat Emptor</h2>
<p>Unlike some of Adobe's other specifications, they don't seem to
have published an official specification for the <code>ase</code> format
themselves. For the purposes of this article, I've been using
unofficial details available from <a href="http://www.selapa.net/swatches/colors/fileformats.php#adobe_ase" rel="external nofollow noopener">Olivier Berten</a> and
<a href="http://mh-nexus.de/en/" rel="external nofollow noopener">HxD</a> to poke around in sample files I have downloaded.</p>
<p>And, as with my previous articles, the code I'm about to present
doesn't handle CMYK or Lab colour spaces. It's also received a
very limited amount of testing.</p>
<h2 id="structure-of-a-adobe-swatch-exchange-file">Structure of a Adobe Swatch Exchange file</h2>
<p><code>ase</code> files support the notion of groups, so you can have
multiple groups containing colours. Judging from the files I
have tested, you can also just have a bunch of colours without a
group at all. I'm uncertain if groups can be nested, so I have
assumed they cannot be.</p>
<p>With that said, the structure is relatively straight forward,
and helpfully includes data that means I can skip the bits that
I have no idea at all what they are. The format comprises of a
basic version header, then a number of blocks. Each block
includes a type, data length, the block name, and then
additional data specific to the block type, and optionally
custom data specific to that particular block.</p>
<p>Blocks can either be a colour, the start of a group, or the end
of a group.</p>
<p>Colour blocks include the colour space, 1-4 floating point
values that describe the colour (3 for RGB and LAB, 4 for CMYK
and 1 for grayscale), and a type.</p>
<p>Finally, all blocks can carry custom data. I have no idea what
this data is, but it doesn't seem to be essential nor are you
required to know what it is for in order to pull out the colour
information. Fortunately, as you know how large each block is,
you can skip the remaining bytes from the block and move onto
the next one. As there seems to be little difference between the
purposes of <code>aco</code> and <code>ase</code> files (the obvious one being that
the former is just a list of colours while the latter supports
grouping) I assume this data is meta data from the application
that created the <code>ase</code> file, but it is all supposition.</p>
<p>The following table attempts to describe the layout, although I
actually found the highlighted hex grid displayed at
<a href="http://www.selapa.net/swatches/colors/fileformats.php#adobe_ase" rel="external nofollow noopener">selapa.net</a> to potentially be easier to read.</p>
<table>
 <thead>
 <tr>
 <th>Length
 </th>
 <th>Description
 </th>
 </tr>
 </thead>
 <tbody>
 <tr>
 <td>4</td>
 <td>Signature</td>
 </tr>
 <tr>
 <td>2</td>
 <td>Major Version</td>
 </tr>
 <tr>
 <td>2</td>
 <td>Minor Version</td>
 </tr>
 <tr>
 <td>4</td>
 <td>Number of blocks</td>
 </tr>
 <tr>
 <td rowspan="3">variable</td>
 <td>
 <p> Block data</p>
 <table>
 <thead>
 <tr>
 <th>Length
 </th>
 <th>Description
 </th>
 </tr>
 </thead>
 <tbody>
 <tr>
 <td>2</td>
 <td>Type</td>
 </tr>
 <tr>
 <td>4</td>
 <td>Block length</td>
 </tr>
 <tr>
 <td>2</td>
 <td>Name length</td>
 </tr>
 <tr>
 <td>(name length)</td>
 <td>Name</td>
 </tr>
 </tbody>
 </table>
 </td>
 </tr>
 <tr>
 <td>
 <p><em>Colour blocks only</em></p>
 <table>
 <thead>
 <tr>
 <th>Length
 </th>
 <th>Description
 </th>
 </tr>
 </thead>
 <tbody>
 <tr>
 <td>4</td>
 <td>Colour space</td>
 </tr>
 <tr>
 <td>12 (RGB, LAB), 16 (CMYK), 4 (Grayscale)</td>
 <td>Colour data. Every four bytes represents one floating
 point value</td>
 </tr>
 <tr>
 <td>2</td>
 <td>Colour type</td>
 </tr>
 </tbody>
 </table>
 </td>
 </tr>
 <tr>
 <td>
 <p><em>All blocks</em></p>
 <table>
 <thead>
 <tr>
 <th>Length
 </th>
 <th>Description
 </th>
 </tr>
 </thead>
 <tbody>
 <tr>
 <td>variable (Block length - previously read data)</td>
 <td>Unknown</td>
 </tr>
 </tbody>
 </table>
 </td>
 </tr>
 </tbody>
</table>
<p>As with <code>aco</code> files, all the data in an <code>ase</code> file is stored in
<a href="http://en.wikipedia.org/wiki/Endianness" rel="external nofollow noopener">big-endian</a> format and therefore needs to be reversed on
Windows systems. Unlike the <code>aco</code> files where four values are
present for each colour even if not required by the appropriate
colour space, the <code>ase</code> format uses between one and four values,
making it slightly more compact that <code>aso</code>.</p>
<h2 id="colour-spaces">Colour Spaces</h2>
<p>I mentioned above that each colour has a description of what
colour space it belongs to. There appear to be four supported
colour spaces. Note that space names are 4 characters long in an
<code>ase</code> file, shorter names are therefore padded with spaces.</p>
<ul>
<li>RGB</li>
<li>LAB</li>
<li>CMYK</li>
<li>Gray</li>
</ul>
<p>In my experiments, RGB was easy enough - just multiply the value
read from the file by 255 to get the right value to use with
.NET's <code>Color</code> structure. I have no idea on the other 3 types
however - I need more samples!</p>
<h2 id="big-endian-conversion">Big-endian conversion</h2>
<p>I covered the basics of reading shorts, ints, and strings in
big-endian format in my <a href="/post/reading-photoshop-color-swatch-aco-files-using-csharp">previous article</a> on <code>aco</code> files so
I won't cover that here.</p>
<p>However, this time around I do need to read floats from the
files too. While the <code>BitConverter</code> class has a <code>ToSingle</code>
method that will convert a 4-byte array to a float, of course it
is for little-endian.</p>
<p>I looked at the <a href="http://referencesource.microsoft.com/#mscorlib/system/bitconverter.cs#7d2958fc09cde954" rel="external nofollow noopener">reference source</a> for this method and saw it
does a really neat trick - it converts the four bytes into an
integer, then creates a float from that integer via pointers.</p>
<p>So, I used the same approach - read an int in big-endian, then
convert it to a float. The only caveat is that you are using
pointers, meaning unsafe code. By default you can't use the
<code>unsafe</code> keyword without enabling a special option in project
properties. I use unsafe code quite frequently for working with
image data and generally don't have a problem, if you are
unwilling to enable this option then you can always take the
four bytes, reverse them, and then call <code>BitConverter.ToSingle</code>
with the reversed array.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">float</span> ReadSingleBigEndian<span class="symbol">(</span><span class="keyword">this</span> Stream stream<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">unsafe</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> value<span class="symbol">;</span>

 value <span class="symbol">=</span> stream<span class="symbol">.</span>ReadUInt<span class="number">32</span>BigEndian<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> <span class="symbol">*</span><span class="symbol">(</span><span class="keyword">float</span><span class="symbol">*</span><span class="symbol">)</span><span class="symbol">&amp;</span>value<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Another slight difference between <code>aco</code> and <code>ase</code> files is that
in <code>ase</code> files, strings are null terminated, and the name length
includes that terminator. Of course, when reading the strings
back out, we really don't want that terminator to be included.
So I added another helper method to deal with that.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">string</span> ReadStringBigEndian<span class="symbol">(</span><span class="keyword">this</span> Stream stream<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> length<span class="symbol">;</span>
 <span class="keyword">string</span> value<span class="symbol">;</span>

 <span class="comment">// string is null terminated, value saved in file includes the terminator</span>

 length <span class="symbol">=</span> stream<span class="symbol">.</span>ReadUInt<span class="number">16</span>BigEndian<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">-</span> <span class="number">1</span><span class="symbol">;</span>
 value <span class="symbol">=</span> stream<span class="symbol">.</span>ReadStringBigEndian<span class="symbol">(</span>length<span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>ReadUInt<span class="number">16</span>BigEndian<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// read and discard the terminator</span>

 <span class="keyword">return</span> value<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="storage-classes">Storage classes</h2>
<p>In my previous examples on reading colour data from files, I've
kept it simple and returned arrays of colours, discarding
incidental details such as names. This time, I've created a
small set of helper classes, to preserve this information and to
make it easier to serialize it.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">abstract</span> <span class="keyword">class</span> Block
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">]</span> ExtraData <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">public</span> <span class="keyword">string</span> Name <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">internal</span> <span class="keyword">class</span> ColorEntry <span class="symbol">:</span> Block
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">int</span> B <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">public</span> <span class="keyword">int</span> G <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">public</span> <span class="keyword">int</span> R <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">public</span> ColorType Type <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> Color ToColor<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">return</span> Color<span class="symbol">.</span>FromArgb<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>R<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>G<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>B<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">internal</span> <span class="keyword">class</span> ColorEntryCollection <span class="symbol">:</span> Collection<span class="symbol">&lt;</span>ColorEntry<span class="symbol">&gt;</span>
<span class="symbol">{</span> <span class="symbol">}</span>

<span class="keyword">internal</span> <span class="keyword">class</span> ColorGroup <span class="symbol">:</span> Block<span class="symbol">,</span> IEnumerable<span class="symbol">&lt;</span>ColorEntry<span class="symbol">&gt;</span>
<span class="symbol">{</span>
 <span class="keyword">public</span> ColorGroup<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Colors <span class="symbol">=</span> <span class="keyword">new</span> ColorEntryCollection<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> ColorEntryCollection Colors <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> IEnumerator<span class="symbol">&lt;</span>ColorEntry<span class="symbol">&gt;</span> GetEnumerator<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>Colors<span class="symbol">.</span>GetEnumerator<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 IEnumerator IEnumerable<span class="symbol">.</span>GetEnumerator<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>GetEnumerator<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">internal</span> <span class="keyword">class</span> ColorGroupCollection <span class="symbol">:</span> Collection<span class="symbol">&lt;</span>ColorGroup<span class="symbol">&gt;</span>
<span class="symbol">{</span> <span class="symbol">}</span>

<span class="keyword">internal</span> <span class="keyword">class</span> SwatchExchangeData
<span class="symbol">{</span>
 <span class="keyword">public</span> SwatchExchangeData<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Groups <span class="symbol">=</span> <span class="keyword">new</span> ColorGroupCollection<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Colors <span class="symbol">=</span> <span class="keyword">new</span> ColorEntryCollection<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> ColorEntryCollection Colors <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">public</span> ColorGroupCollection Groups <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>That should be all we need, time to load some files!</p>
<h2 id="reading-the-file">Reading the file</h2>
<p>To start with, we create a new <code>ColorEntryCollection</code> that will
be used for global colours (i.e. colour blocks that don't appear
within a group). To make things simple, I'm also creating a
<code>Stack&lt;ColorEntryCollection&gt;</code> to which I push this global
collection. Later on, when I encounter a start group block, I'll
<code>Push</code> a new <code>ColorEntryCollection</code> to this stack, and when I
encounter an end group block, I'll <code>Pop</code> the value at the top of
the stack. This way, when I encounter a colour block, I can
easily add it to the right collection without needing to
explicitly keep track of the active group or lack thereof.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">void</span> Load<span class="symbol">(</span><span class="keyword">string</span> fileName<span class="symbol">)</span>
<span class="symbol">{</span>
 Stack<span class="symbol">&lt;</span>ColorEntryCollection<span class="symbol">&gt;</span> colors<span class="symbol">;</span>
 ColorGroupCollection groups<span class="symbol">;</span>
 ColorEntryCollection globalColors<span class="symbol">;</span>

 groups <span class="symbol">=</span> <span class="keyword">new</span> ColorGroupCollection<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 globalColors <span class="symbol">=</span> <span class="keyword">new</span> ColorEntryCollection<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 colors <span class="symbol">=</span> <span class="keyword">new</span> Stack<span class="symbol">&lt;</span>ColorEntryCollection<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// add the global collection to the bottom of the stack to handle color blocks outside of a group</span>
 colors<span class="symbol">.</span>Push<span class="symbol">(</span>globalColors<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Stream stream <span class="symbol">=</span> File<span class="symbol">.</span>OpenRead<span class="symbol">(</span>fileName<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> blockCount<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>ReadAndValidateVersion<span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">;</span>

 blockCount <span class="symbol">=</span> stream<span class="symbol">.</span>ReadUInt<span class="number">32</span>BigEndian<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> blockCount<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ReadBlock<span class="symbol">(</span>stream<span class="symbol">,</span> groups<span class="symbol">,</span> colors<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">this</span><span class="symbol">.</span>Groups <span class="symbol">=</span> groups<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Colors <span class="symbol">=</span> globalColors<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>After opening a <code>Stream</code> containing our file data, we need to
check that the stream contains both <code>ase</code> data, and that the
data is a version we can read. This is done by reading 8 bytes
from the start of the data. The first four are ASCII characters
which should match the string <code>ASEF</code>, the next two are the major
version and the final two the minor version.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> ReadAndValidateVersion<span class="symbol">(</span>Stream stream<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">string</span> signature<span class="symbol">;</span>
 <span class="keyword">int</span> majorVersion<span class="symbol">;</span>
 <span class="keyword">int</span> minorVersion<span class="symbol">;</span>

 <span class="comment">// get the signature (4 ascii characters)</span>
 signature <span class="symbol">=</span> stream<span class="symbol">.</span>ReadAsciiString<span class="symbol">(</span><span class="number">4</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>signature <span class="symbol">!=</span> <span class="string">&quot;ASEF&quot;</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="string">&quot;Invalid file format.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="comment">// read the version</span>
 majorVersion <span class="symbol">=</span> stream<span class="symbol">.</span>ReadUInt<span class="number">16</span>BigEndian<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 minorVersion <span class="symbol">=</span> stream<span class="symbol">.</span>ReadUInt<span class="number">16</span>BigEndian<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>majorVersion <span class="symbol">!=</span> <span class="number">1</span> <span class="symbol">&amp;&amp;</span> minorVersion <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="string">&quot;Invalid version information.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Assuming the data is valid, we read the number of blocks in the
file, and enter a loop to process each block. For each block,
first we read the type of the block, and then the length of the
block's data.</p>
<p>How we continue reading from the stream depends on the block
type (more on that later), after which we work out how much data
is left in the block, read it, and store it as raw bytes on the
off-chance the consuming application can do something with it,
or for saving back into the file.</p>
<blockquote>
<p>This technique assumes that the source stream is seekable. If
this is not the case, you'll need to manually keep track of
how many bytes you have read from the block to calculate the
remaining custom data left to read.</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> ReadBlock<span class="symbol">(</span>Stream stream<span class="symbol">,</span> ColorGroupCollection groups<span class="symbol">,</span> Stack<span class="symbol">&lt;</span>ColorEntryCollection<span class="symbol">&gt;</span> colorStack<span class="symbol">)</span>
<span class="symbol">{</span>
 BlockType blockType<span class="symbol">;</span>
 <span class="keyword">int</span> blockLength<span class="symbol">;</span>
 <span class="keyword">int</span> offset<span class="symbol">;</span>
 <span class="keyword">int</span> dataLength<span class="symbol">;</span>
 Block block<span class="symbol">;</span>

 blockType <span class="symbol">=</span> <span class="symbol">(</span>BlockType<span class="symbol">)</span>stream<span class="symbol">.</span>ReadUInt<span class="number">16</span>BigEndian<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 blockLength <span class="symbol">=</span> stream<span class="symbol">.</span>ReadUInt<span class="number">32</span>BigEndian<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// store the current position of the stream, so we can calculate the offset</span>
 <span class="comment">// from bytes read to the block length in order to skip the bits we can&#39;t use</span>
 offset <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>stream<span class="symbol">.</span>Position<span class="symbol">;</span>

 <span class="comment">// process the actual block</span>
 <span class="keyword">switch</span> <span class="symbol">(</span>blockType<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> BlockType<span class="symbol">.</span>Color<span class="symbol">:</span>
 block <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ReadColorBlock<span class="symbol">(</span>stream<span class="symbol">,</span> colorStack<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> BlockType<span class="symbol">.</span>GroupStart<span class="symbol">:</span>
 block <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ReadGroupBlock<span class="symbol">(</span>stream<span class="symbol">,</span> groups<span class="symbol">,</span> colorStack<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> BlockType<span class="symbol">.</span>GroupEnd<span class="symbol">:</span>
 block <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
 colorStack<span class="symbol">.</span>Pop<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">default</span><span class="symbol">:</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="symbol">$</span><span class="string">&quot;Unsupported block type &#39;{blockType}&#39;.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="comment">// load in any custom data and attach it to the</span>
 <span class="comment">// current block (if available) as raw byte data</span>
 dataLength <span class="symbol">=</span> blockLength <span class="symbol">-</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span>stream<span class="symbol">.</span>Position <span class="symbol">-</span> offset<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>dataLength <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">]</span> extraData<span class="symbol">;</span>

 extraData <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">byte</span><span class="symbol">[</span>dataLength<span class="symbol">]</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>Read<span class="symbol">(</span>extraData<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> dataLength<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>block <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 block<span class="symbol">.</span>ExtraData <span class="symbol">=</span> extraData<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="processing-groups">Processing groups</h3>
<p>If we have found a &quot;start group&quot; block, then we create a new
<code>ColorGroup</code> object and read the group name. We also push the
group's <code>ColorEntryCollection</code> to the stack I mentioned earlier.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> Block ReadGroupBlock<span class="symbol">(</span>Stream stream<span class="symbol">,</span> ColorGroupCollection groups<span class="symbol">,</span> Stack<span class="symbol">&lt;</span>ColorEntryCollection<span class="symbol">&gt;</span> colorStack<span class="symbol">)</span>
<span class="symbol">{</span>
 ColorGroup block<span class="symbol">;</span>
 <span class="keyword">string</span> name<span class="symbol">;</span>

 <span class="comment">// read the name of the group</span>
 name <span class="symbol">=</span> stream<span class="symbol">.</span>ReadStringBigEndian<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// create the group and add it to the results set</span>
 block <span class="symbol">=</span> <span class="keyword">new</span> ColorGroup
 <span class="symbol">{</span>
 Name <span class="symbol">=</span> name
 <span class="symbol">}</span><span class="symbol">;</span>

 groups<span class="symbol">.</span>Add<span class="symbol">(</span>block<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// add the group color collection to the stack, so when subsequent colour blocks</span>
 <span class="comment">// are read, they will be added to the correct collection</span>
 colorStack<span class="symbol">.</span>Push<span class="symbol">(</span>block<span class="symbol">.</span>Colors<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> block<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>For &quot;end group&quot; blocks, we don't do any custom processing as I
do not think there is any data associated with these. Instead,
we just pop the last value from our colour stack. (Of course,
that means if there is a malformed <code>ase</code> file containing a group
end without a group start, this procedure is going to crash
sooner or later!</p>
<h3 id="processing-colours">Processing colours</h3>
<p>When we hit a colour block, we read the colour's name and the
colour mode.</p>
<p>Then, depending on the mode, we read between 1 and 4 float
values which describe the colour. As anything other than RGB
processing is beyond the scope of this article, I'm throwing an
exception for the LAB, CMYK and Gray colour spaces.</p>
<p>For RGB colours, I take each value and multiple it by 255 to get
a value suitable for use with the .NET <code>Color</code> struct.</p>
<p>After reading the colour data, there's one official value left
to read, which is the colour type. This can either be Global
(0), Spot (1) or Normal (2).</p>
<p>Finally, I construct a new <code>ColorEntry</code> object containing the
colour information and add it to whatever <code>ColorEntryCollection</code>
is on the top of the stack.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> Block ReadColorBlock<span class="symbol">(</span>Stream stream<span class="symbol">,</span> Stack<span class="symbol">&lt;</span>ColorEntryCollection<span class="symbol">&gt;</span> colorStack<span class="symbol">)</span>
<span class="symbol">{</span>
 ColorEntry block<span class="symbol">;</span>
 <span class="keyword">string</span> colorMode<span class="symbol">;</span>
 <span class="keyword">int</span> r<span class="symbol">;</span>
 <span class="keyword">int</span> g<span class="symbol">;</span>
 <span class="keyword">int</span> b<span class="symbol">;</span>
 ColorType colorType<span class="symbol">;</span>
 <span class="keyword">string</span> name<span class="symbol">;</span>
 ColorEntryCollection colors<span class="symbol">;</span>

 <span class="comment">// get the name of the color</span>
 <span class="comment">// this is stored as a null terminated string</span>
 <span class="comment">// with the length of the byte data stored before</span>
 <span class="comment">// the string data in a 16bit int</span>
 name <span class="symbol">=</span> stream<span class="symbol">.</span>ReadStringBigEndian<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// get the mode of the color, which is stored</span>
 <span class="comment">// as four ASCII characters</span>
 colorMode <span class="symbol">=</span> stream<span class="symbol">.</span>ReadAsciiString<span class="symbol">(</span><span class="number">4</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// read the color data</span>
 <span class="comment">// how much data we need to read depends on the</span>
 <span class="comment">// color mode we previously read</span>
 <span class="keyword">switch</span> <span class="symbol">(</span>colorMode<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> <span class="string">&quot;RGB &quot;</span><span class="symbol">:</span>
 <span class="comment">// RGB is comprised of three floating point values ranging from 0-1.0</span>
 <span class="keyword">float</span> value<span class="number">1</span><span class="symbol">;</span>
 <span class="keyword">float</span> value<span class="number">2</span><span class="symbol">;</span>
 <span class="keyword">float</span> value<span class="number">3</span><span class="symbol">;</span>
 value<span class="number">1</span> <span class="symbol">=</span> stream<span class="symbol">.</span>ReadSingleBigEndian<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 value<span class="number">2</span> <span class="symbol">=</span> stream<span class="symbol">.</span>ReadSingleBigEndian<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 value<span class="number">3</span> <span class="symbol">=</span> stream<span class="symbol">.</span>ReadSingleBigEndian<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 r <span class="symbol">=</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span>value<span class="number">1</span> <span class="symbol">*</span> <span class="number">255</span><span class="symbol">)</span><span class="symbol">;</span>
 g <span class="symbol">=</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span>value<span class="number">2</span> <span class="symbol">*</span> <span class="number">255</span><span class="symbol">)</span><span class="symbol">;</span>
 b <span class="symbol">=</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span>value<span class="number">3</span> <span class="symbol">*</span> <span class="number">255</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="string">&quot;CMYK&quot;</span><span class="symbol">:</span>
 <span class="comment">// CMYK is comprised of four floating point values</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="symbol">$</span><span class="string">&quot;Unsupported color mode &#39;{colorMode}&#39;.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="string">&quot;LAB &quot;</span><span class="symbol">:</span>
 <span class="comment">// LAB is comprised of three floating point values</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="symbol">$</span><span class="string">&quot;Unsupported color mode &#39;{colorMode}&#39;.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="string">&quot;Gray&quot;</span><span class="symbol">:</span>
 <span class="comment">// Grayscale is comprised of a single floating point value</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="symbol">$</span><span class="string">&quot;Unsupported color mode &#39;{colorMode}&#39;.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">default</span><span class="symbol">:</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="symbol">$</span><span class="string">&quot;Unsupported color mode &#39;{colorMode}&#39;.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="comment">// the final &quot;official&quot; piece of data is a color type</span>
 colorType <span class="symbol">=</span> <span class="symbol">(</span>ColorType<span class="symbol">)</span>stream<span class="symbol">.</span>ReadUInt<span class="number">16</span>BigEndian<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 block <span class="symbol">=</span> <span class="keyword">new</span> ColorEntry
 <span class="symbol">{</span>
 R <span class="symbol">=</span> r<span class="symbol">,</span>
 G <span class="symbol">=</span> g<span class="symbol">,</span>
 B <span class="symbol">=</span> b<span class="symbol">,</span>
 Name <span class="symbol">=</span> name<span class="symbol">,</span>
 Type <span class="symbol">=</span> colorType
 <span class="symbol">}</span><span class="symbol">;</span>

 colors <span class="symbol">=</span> colorStack<span class="symbol">.</span>Peek<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 colors<span class="symbol">.</span>Add<span class="symbol">(</span>block<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> block<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="and-done">And done</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ase-loader-1b.png" class="gallery" title="An example of a group-less ASE file" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ase-loader-1b.png" alt="An example of a group-less ASE file" decoding="async" loading="lazy" /></a><figcaption>An example of a group-less ASE file</figcaption></figure>
<p>The <code>ase</code> format is pretty simple to process, although the fact
there is still data in these files with an unknown purpose could
be a potential issue. Unfortunately, I don't have a recent
version of PhotoShop to actually generate some of these files to
investigate further (and to test if groups can be nested so I
can adapt this code accordingly).</p>
<p>However, I have tested this code on a number of files downloaded
from the internet and have been able to pull out all the colour
information, so I suspect the <a href="https://cyotek.com/cyotek-palette-editor">Color Palette Editor</a> and
<a href="https://github.com/cyotek/Cyotek.Windows.Forms.ColorPicker" rel="external nofollow noopener">Color Picker Controls</a> will be getting <code>ase</code> support fairly
soon!</p>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/reading-adobe-swatch-exchange-ase-files-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comTargeting multiple versions of the .NET Framework from the same projecturn:uuid:847314d1-21a3-4194-8973-eee03766636e2015-08-18T06:53:19Z2015-08-18T06:53:19Z<p>The new exception management library I've been working on was
originally targeted for .NET 4.6, changing to .NET 4.5.2 when I
found that Azure websites don't support 4.6 yet. Regardless of
4.5 or 4.6, this meant trouble when I tried to integrate it with
WebCopy - this product uses a mix of 3.5 and 4.0 targeted
assemblies, meaning it couldn't actually reference the new
library due the higher framework version.</p>
<p>Rather than creating several different project files with the
same source but different configuration settings, I decided that
I would modify the library to target multiple framework versions
from the same source project.</p>
<h2 id="bits-you-need-to-change">Bits you need to change</h2>
<p>In order to get multi targeting working properly, you'll need to
tinker a few things</p>
<ul>
<li>The output path - no good having all your libraries compiling
to the same location otherwise one compile will overwrite the
previous</li>
<li>Reference paths - you may need to reference different versions
of third party assemblies</li>
<li>Compile constants - in case you need to conditionally include
or exclude lines of code</li>
<li>Custom files - if the changes are so great you might as well
have separate files (or bridging files providing functionality
that doesn't exist in your target platform)</li>
</ul>
<p>Possibly there's other things too, but this is all I have needed
to do so far in order to produce multiple versions of the
library.</p>
<blockquote>
<p>I wrote this article against Visual Studio 2015 / MSBuild
14.0, but it should work in at least some earlier versions as
well</p>
</blockquote>
<h2 id="conditions-conditions-conditions">Conditions, Conditions, Conditions</h2>
<p>The magic that makes multi-targeting work (at least how I'm
doing it, there might be better ways) is by using
<a href="https://msdn.microsoft.com/en-us/library/7szfhaft.aspx" rel="external nofollow noopener">conditions</a>. Remember that your solution and project files
are really just MSBuild files - so (probably) anything you can
do with MSBuild, you can do in these files.</p>
<p>Conditions are fairly basic, but they have enough functionality
to get the job done. In a nutshell, you add a <code>Condition</code>
attribute containing an expression to a supported element. If
the expression evaluates to <code>true</code>, then the element will be
fully processed by the build.</p>
<blockquote>
<p>As conditions are XML attribute values, this means you have to
encode non-conformant characters such as <code>&lt;</code> and <code>&gt;</code> (use
<code>&amp;lt;</code> and <code>&amp;gt;</code> respectively). If you don't, then Visual Studio
will issue an error and refuse to load the project.</p>
</blockquote>
<h2 id="getting-started">Getting Started</h2>
<p>You can either edit your project files directly in Visual
Studio, or with an external editor such as Notepad++. While the
former approach makes it easier to detect errors (your XML will
be validated against the relevant schema) and provides
intellisense, I personally think that Visual Studio makes it
unnecessarily difficult to directly edit project files as you
have to unload the project, before opening it for editing. In
order to reload the project, you have to close the editing
window. I find it much more convenient to edit them in an
external application, then allow Visual Studio to reload the
project when it detects the changes.</p>
<p>Also, you probably want to settle on a &quot;default&quot; target version
for when using the raw project. Generally this would either be
the highest or lowest framework version you support. I choose to
do the lowest, that way I can reference the same source library
in WebCopy and other projects that are either .NET 4.0 or 4.5.2.
(Of course, it would be better to use a NuGet package with the
multi-targeted binaries, but that's the next step!)</p>
<h2 id="conditional-constants">Conditional Constants</h2>
<p>To set up my multi-targeting, I'm going to define a dedicated
<code>PropertyGroup</code> for each target, with a condition stating that
the <code>TargetFrameworkVersion</code> value must match the version I'm
targeting.</p>
<p>I'm doing this for two reasons - firstly to define a numerical
value for the version (e.g. <code>3.5</code> instead of <code>v3.5</code>), which I'll
cover in a subsequent section. The second reason is to define a
new constant for the project, so that I can use conditional
compilation if required.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="comment">&lt;!-- 3.5 Specific --&gt;</span>
<span class="symbol">&lt;</span><span class="name">PropertyGroup</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute"> &#39;$(TargetFrameworkVersion)&#39; == &#39;v3.5&#39; </span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">DefineConstants</span><span class="symbol">&gt;</span>$(DefineConstants);NET35<span class="symbol">&lt;/</span><span class="name">DefineConstants</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">TargetFrameworkVersionNumber</span><span class="symbol">&gt;</span>3.5<span class="symbol">&lt;/</span><span class="name">TargetFrameworkVersionNumber</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">PropertyGroup</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>In the above XML block, we can see the condition expression
<code>'$(TargetFrameworkVersion)' == 'v3.5'</code>. This means that the
<code>PropertyGroup</code> will only be processed if the target framework
version is 3.5. Well, that's not quite true but it will suffice
for now.</p>
<p>Next, I change the constants for the project to include a new
<code>NET35</code> constant. Note however, that I'm also embedding the
<em>existing</em> constants into the new value - if I didn't do this,
then my new value would overwrite all existing properties (such
as <strong>DEBUG</strong> or <strong>TRACE</strong>). You probably don't want that to
happen!</p>
<blockquote>
<p>Constants are separated with a semi-colon</p>
</blockquote>
<p>The third line creates a new configuration value named
<code>TargetFrameworkVersionNumber</code> with our numeric framework
version.</p>
<blockquote>
<p>If you are editing the project through Visual Studio, it will
highlight the <code>TargetFrameworkVersionNumber</code> element as being
invalid as it isn't part of the schema. This is a harmless
error which you can ignore.</p>
</blockquote>
<h2 id="conditional-compilation">Conditional Compilation</h2>
<p>With the inclusion of new constants from the previous section,
it's quite easy to conditionally include or exclude code. If you
are targeting an older version of the .NET Framework, it's
possible that it doesn't have the functionality you require. For
example, .NET 4.0 and above have <code>Is64BitOperatingSystem</code> and
<code>IsIs64BitProcess</code> properties available on the <code>Environment</code>
object, while previous versions do not.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">bool</span> is<span class="number">64</span>BitOperatingSystem<span class="symbol">;</span>
<span class="keyword">bool</span> is<span class="number">64</span>BitProcess<span class="symbol">;</span>

<span class="keyword">#if</span> NET20 || NET35
 is<span class="number">64</span>BitOperatingSystem <span class="symbol">=</span> NativeMethods<span class="symbol">.</span>Is<span class="number">64</span>BitOperatingSystem<span class="symbol">,</span>
 is<span class="number">64</span>BitProcess <span class="symbol">=</span> NativeMethods<span class="symbol">.</span>Is<span class="number">64</span>BitProcess<span class="symbol">,</span>
<span class="keyword">#else</span>
 is<span class="number">64</span>BitOperatingSystem <span class="symbol">=</span> Environment<span class="symbol">.</span>Is<span class="number">64</span>BitOperatingSystem<span class="symbol">,</span>
 is<span class="number">64</span>BitProcess <span class="symbol">=</span> Environment<span class="symbol">.</span>Is<span class="number">64</span>BitProcess<span class="symbol">,</span>
<span class="keyword">#endif</span>
</pre>
</figure>
<p>The appropriate code will then be used by the compile process.</p>
<h2 id="including-or-excluding-entire-source-files">Including or Excluding Entire Source Files</h2>
<p>Sometimes the code might be too complex to make good use of
conditional compilation, or perhaps you need to include extra
code to support the feature in one version that you don't in
another such as bridging or interop classes. You can use
condition attributes to conditionally include these too.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">ItemGroup</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Compile</span> <span class="name">Include</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">NativeMethods.cs</span><span class="symbol">&quot;</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute"> &#39;$(TargetFrameworkVersionNumber)&#39; &lt;= &#39;3.5&#39; </span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
<span class="symbol">&lt;/</span><span class="name">ItemGroup</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>One of the limitations of MSBuild conditions is that the <code>&gt;</code>,
<code>&gt;=</code>, <code>&lt;</code> and <code>&lt;=</code> operators only work on numbers, not strings.
And it is much easier to say &quot;<em>greater than 3.5</em>&quot; than it is to
say &quot;<em>is 4.0 or is 4.5 or is 4.5.1 or is 4.5.2</em>&quot; or &quot;<em>not 2.0
and not 3.5</em>&quot; and so on. By creating that
<code>TargetFrameworkVersionNumber</code> property, we make it much easier
to use greater / less than expressions in conditions.</p>
<p>Even if the source file is excluded by a specific configuration,
it will still appear in the IDE, but unless the condition is
met, it will not be compiled into your project, nor prevent
compilation if it has syntax errors.</p>
<h2 id="external-references">External References</h2>
<p>If your library depends on any external references (or even some
of the default ones), then you'll possibly need to exclude the
reference outright, or include a different version of it. In my
case, I'm using Newtonsoft's <a href="http://www.newtonsoft.com/json" rel="external nofollow noopener">Json.NET</a> library, which very
helpfully comes in different versions for each platform - I just
need to make sure I include the right one.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">ItemGroup</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute"> &#39;$(TargetFrameworkVersionNumber)&#39; == &#39;3.5&#39; </span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Reference</span> <span class="name">Include</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">HintPath</span><span class="symbol">&gt;</span>..\..\packages\Newtonsoft.Json.7.0.1\lib\net35\Newtonsoft.Json.dll<span class="symbol">&lt;/</span><span class="name">HintPath</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Private</span><span class="symbol">&gt;</span>True<span class="symbol">&lt;/</span><span class="name">Private</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Reference</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">ItemGroup</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>Here we can see an <code>ItemGroup</code> element which describes a single
reference along with a now familiar <code>Condition</code> attribute to
target a specific .NET version. By changing the <code>HintPath</code>
element to point to the <strong>net35</strong> folder of the Json package, I
can be sure that I'm pulling out the right reference.</p>
<blockquote>
<p>Even though these references are &quot;excluded&quot;, Visual Studio
will still display them, along with a warning that you cannot
suppress. However, just like with the code file of the
previous section, the duplication / warnings are completely
ignored.</p>
</blockquote>
<p>The non-suppressible warnings are actually really annoying -
fortunately I aim to consume this library via a NuGet package
eventually so it will become a moot point.</p>
<h2 id="core-references">Core References</h2>
<p>In most cases, if your project references .NET Framework
assemblies such as <code>System.Xml</code>, you don't need to worry about
them; they will automatically use the appropriate version
without you lifting a finger. However, there are some special
references such as <code>System.Core</code> or <code>Microsoft.CSharp</code> which
aren't available in earlier versions and should be excluded. (Or
removed if you aren't using them at all)</p>
<p>As <code>Microsoft.CSharp</code> is not supported by .NET 3.5, I change the
<code>Reference</code> element for <code>Microsoft.CSharp</code> to include a
condition to exclude it for anything below 4.0.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">Reference</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute"> &#39;$(TargetFrameworkVersionNumber)&#39; &amp;gt;= &#39;4.0&#39; </span><span class="symbol">&quot;</span> <span class="name">Include</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Microsoft.CSharp</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
</pre>
</figure>
<p>If I was targeting 2.0 then I would exclude <code>System.Core</code> in a
similar fashion.</p>
<h2 id="output-paths">Output Paths</h2>
<p>One last task to change in our project - the output paths.
Fortunately we can again utilize MSBuild's property system to
avoid having to create different platform configurations.</p>
<p>All we need to do is find the <code>OutputPath</code> element(s) and change
their values to include the <code>$(TargetFrameworkVersion)</code> variable
- this will then ensure our binaries are created in sub-folders
named after the .NET version.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">OutputPath</span><span class="symbol">&gt;</span>bin\Release\$(TargetFrameworkVersion)\<span class="symbol">&lt;/</span><span class="name">OutputPath</span><span class="symbol">&gt;</span>
</pre>
</figure>
<blockquote>
<p>Generally, there will be at least two <code>OutputPath</code> elements in
a project. If you have defined additional platforms (such as
explicit targeting of x86 or x64 then there may be even more).
You will need to update all of these, or at least the ones
targeting <strong>Release</strong> builds.</p>
</blockquote>
<h2 id="building-the-libraries">Building the libraries</h2>
<p>The final part of our multi-targeting puzzle is to compile the
different versions of our project. Although I expect you could
trigger MSBuild using the <code>AfterBuild</code> target, I decided not to
do this as when I'm developing and testing in the IDE I only
need one version. I'll save the fancy stuff for dedicated
release builds, which I always do externally of Visual Studio
using batch files.</p>
<p>Below is a sample batch file which will take a solution
(<strong>SolutionFile.sln</strong>) and compile 3.5, 4.0 and 4.5.2 versions
of a single project (<strong>AwesomeLibary</strong>).</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
@ECHO <span class="keyword">OFF</span>

<span class="keyword">CALL</span> :build 3.5
<span class="keyword">CALL</span> :build 4.0
<span class="keyword">CALL</span> :build 4.5.2

<span class="keyword">GOTO</span> :eof

:build
<span class="keyword">ECHO</span> Building .NET <span class="string">%1 client:
MSBUILD &quot;SolutionFile.sln&quot; /p:Configuration=&quot;Release&quot; /p:TargetFrameworkVersion=&quot;v%</span>1<span class="string">&quot; /t:&quot;</span>AwesomeLibary:Clean<span class="string">&quot;,&quot;</span>AwesomeLibary:Rebuild&quot; /v:m /nologo
<span class="keyword">ECHO</span>.
</pre>
</figure>
<p>The <code>/p:name=value</code> arguments are used to override properties in
the solution file, so I use <code>/p:TargetFrameworkVersion</code> to
change the .NET version of the output library, and as I always
want these to be release builds, I also use the
<code>/p:Configuration</code> argument to force the <strong>Release</strong>
configuration.</p>
<p>The <code>/t</code> argument specifies a comma separated list of targets.
Generally, I just use <strong>Clean,Rebuild</strong> to do a full clean of
the solution following by a build. However, by including a
project name, I can skip everything but that one project, which
avoids having to have a separate slimmed down solution file to
avoid fully compiling a massive solution.</p>
<blockquote>
<p>Note that you shouldn't include the project extension in the
target, and if your project name includes any other periods,
then you must change these into underscores instead. For
example, <code>Cyotek.Windows.Forms.csproj</code> would be referenced as
<code>Cyotek_Windows_Forms</code>. I also believe that if you have sited
your project within a solution folder, you need to include the
folder hierarchy too</p>
</blockquote>
<h2 id="a-fuller-example">A fuller example</h2>
<p>This is a more-or-less complete C# project file that
demonstrates multi targeting, and may help in a sort of &quot;big
picture way&quot;.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;?</span><span class="name">xml</span> <span class="name">version</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1.0</span><span class="symbol">&quot;</span> <span class="name">encoding</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">utf-8</span><span class="symbol">&quot;</span><span class="symbol">?&gt;</span>
<span class="symbol">&lt;</span><span class="name">Project</span> <span class="name">ToolsVersion</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">14.0</span><span class="symbol">&quot;</span> <span class="name">DefaultTargets</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Build</span><span class="symbol">&quot;</span> <span class="name">xmlns</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://schemas.microsoft.com/developer/msbuild/2003</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Import</span> <span class="name">Project</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props</span><span class="symbol">&quot;</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Exists(&#39;$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props&#39;)</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">PropertyGroup</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Configuration</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute"> &#39;$(Configuration)&#39; == &#39;&#39; </span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>Debug<span class="symbol">&lt;/</span><span class="name">Configuration</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Platform</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute"> &#39;$(Platform)&#39; == &#39;&#39; </span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>AnyCPU<span class="symbol">&lt;/</span><span class="name">Platform</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">ProjectGuid</span><span class="symbol">&gt;</span>{DA5D3442-D7E1-4436-9364-776732BD3FF5}<span class="symbol">&lt;/</span><span class="name">ProjectGuid</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">OutputType</span><span class="symbol">&gt;</span>Library<span class="symbol">&lt;/</span><span class="name">OutputType</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">AppDesignerFolder</span><span class="symbol">&gt;</span>Properties<span class="symbol">&lt;/</span><span class="name">AppDesignerFolder</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">RootNamespace</span><span class="symbol">&gt;</span>Cyotek.ErrorHandler.Client<span class="symbol">&lt;/</span><span class="name">RootNamespace</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">AssemblyName</span><span class="symbol">&gt;</span>Cyotek.ErrorHandler.Client<span class="symbol">&lt;/</span><span class="name">AssemblyName</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">TargetFrameworkVersion</span><span class="symbol">&gt;</span>v3.5<span class="symbol">&lt;/</span><span class="name">TargetFrameworkVersion</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">FileAlignment</span><span class="symbol">&gt;</span>512<span class="symbol">&lt;/</span><span class="name">FileAlignment</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">TargetFrameworkProfile</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">PropertyGroup</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">PropertyGroup</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute"> &#39;$(Configuration)|$(Platform)&#39; == &#39;Debug|AnyCPU&#39; </span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">DebugSymbols</span><span class="symbol">&gt;</span>true<span class="symbol">&lt;/</span><span class="name">DebugSymbols</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">DebugType</span><span class="symbol">&gt;</span>full<span class="symbol">&lt;/</span><span class="name">DebugType</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Optimize</span><span class="symbol">&gt;</span>false<span class="symbol">&lt;/</span><span class="name">Optimize</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">OutputPath</span><span class="symbol">&gt;</span>bin\Debug\$(TargetFrameworkVersion)\<span class="symbol">&lt;/</span><span class="name">OutputPath</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">DefineConstants</span><span class="symbol">&gt;</span>DEBUG;TRACE<span class="symbol">&lt;/</span><span class="name">DefineConstants</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">ErrorReport</span><span class="symbol">&gt;</span>prompt<span class="symbol">&lt;/</span><span class="name">ErrorReport</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">WarningLevel</span><span class="symbol">&gt;</span>4<span class="symbol">&lt;/</span><span class="name">WarningLevel</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">PropertyGroup</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">PropertyGroup</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute"> &#39;$(Configuration)|$(Platform)&#39; == &#39;Release|AnyCPU&#39; </span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">DebugType</span><span class="symbol">&gt;</span>pdbonly<span class="symbol">&lt;/</span><span class="name">DebugType</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Optimize</span><span class="symbol">&gt;</span>true<span class="symbol">&lt;/</span><span class="name">Optimize</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">OutputPath</span><span class="symbol">&gt;</span>bin\Release\$(TargetFrameworkVersion)\<span class="symbol">&lt;/</span><span class="name">OutputPath</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">DefineConstants</span><span class="symbol">&gt;</span>TRACE<span class="symbol">&lt;/</span><span class="name">DefineConstants</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">ErrorReport</span><span class="symbol">&gt;</span>prompt<span class="symbol">&lt;/</span><span class="name">ErrorReport</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">WarningLevel</span><span class="symbol">&gt;</span>4<span class="symbol">&lt;/</span><span class="name">WarningLevel</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">PropertyGroup</span><span class="symbol">&gt;</span>

 <span class="comment">&lt;!-- 3.5 Specific --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">PropertyGroup</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute"> &#39;$(TargetFrameworkVersion)&#39; == &#39;v3.5&#39; </span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">DefineConstants</span><span class="symbol">&gt;</span>$(DefineConstants);NET35<span class="symbol">&lt;/</span><span class="name">DefineConstants</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">TargetFrameworkVersionNumber</span><span class="symbol">&gt;</span>3.5<span class="symbol">&lt;/</span><span class="name">TargetFrameworkVersionNumber</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">PropertyGroup</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">ItemGroup</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute"> &#39;$(TargetFrameworkVersionNumber)&#39; == &#39;3.5&#39; </span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Reference</span> <span class="name">Include</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">HintPath</span><span class="symbol">&gt;</span>..\..\packages\Newtonsoft.Json.7.0.1\lib\net35\Newtonsoft.Json.dll<span class="symbol">&lt;/</span><span class="name">HintPath</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Private</span><span class="symbol">&gt;</span>True<span class="symbol">&lt;/</span><span class="name">Private</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Reference</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">ItemGroup</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">ItemGroup</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Compile</span> <span class="name">Include</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">NativeMethods.cs</span><span class="symbol">&quot;</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute"> &#39;$(TargetFrameworkVersionNumber)&#39; &lt;= &#39;3.5&#39; </span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">ItemGroup</span><span class="symbol">&gt;</span>

 <span class="comment">&lt;!-- 4.0 Specific --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">PropertyGroup</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute"> &#39;$(TargetFrameworkVersion)&#39; == &#39;v4.0&#39; </span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">DefineConstants</span><span class="symbol">&gt;</span>$(DefineConstants);NET40<span class="symbol">&lt;/</span><span class="name">DefineConstants</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">TargetFrameworkVersionNumber</span><span class="symbol">&gt;</span>4.0<span class="symbol">&lt;/</span><span class="name">TargetFrameworkVersionNumber</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">PropertyGroup</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">ItemGroup</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute"> &#39;$(TargetFrameworkVersionNumber)&#39; == &#39;4.0&#39; </span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Reference</span> <span class="name">Include</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">HintPath</span><span class="symbol">&gt;</span>..\..\packages\Newtonsoft.Json.7.0.1\lib\net40\Newtonsoft.Json.dll<span class="symbol">&lt;/</span><span class="name">HintPath</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Private</span><span class="symbol">&gt;</span>True<span class="symbol">&lt;/</span><span class="name">Private</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Reference</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">ItemGroup</span><span class="symbol">&gt;</span>

 <span class="comment">&lt;!-- 4.5 Specific --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">PropertyGroup</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute"> &#39;$(TargetFrameworkVersion)&#39; == &#39;v4.5.2&#39; </span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">DefineConstants</span><span class="symbol">&gt;</span>$(DefineConstants);NET45<span class="symbol">&lt;/</span><span class="name">DefineConstants</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">TargetFrameworkVersionNumber</span><span class="symbol">&gt;</span>4.0<span class="symbol">&lt;/</span><span class="name">TargetFrameworkVersionNumber</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">PropertyGroup</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">ItemGroup</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute"> &#39;$(TargetFrameworkVersionNumber)&#39; &amp;gt;= &#39;4.5&#39; </span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Reference</span> <span class="name">Include</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">HintPath</span><span class="symbol">&gt;</span>..\..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll<span class="symbol">&lt;/</span><span class="name">HintPath</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Private</span><span class="symbol">&gt;</span>True<span class="symbol">&lt;/</span><span class="name">Private</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Reference</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">ItemGroup</span><span class="symbol">&gt;</span>

 <span class="symbol">&lt;</span><span class="name">ItemGroup</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Reference</span> <span class="name">Include</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">System</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Reference</span> <span class="name">Include</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">System.Configuration</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Reference</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute"> &#39;$(TargetFrameworkVersionNumber)&#39; &amp;gt; &#39;2.0&#39; </span><span class="symbol">&quot;</span> <span class="name">Include</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">System.Core</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Reference</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute"> &#39;$(TargetFrameworkVersionNumber)&#39; &amp;gt; &#39;3.5&#39; </span><span class="symbol">&quot;</span> <span class="name">Include</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Microsoft.CSharp</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">ItemGroup</span><span class="symbol">&gt;</span>

 <span class="symbol">&lt;</span><span class="name">ItemGroup</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Compile</span> <span class="name">Include</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Client.cs</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Compile</span> <span class="name">Include</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Utilities.cs</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">ItemGroup</span><span class="symbol">&gt;</span>

 <span class="symbol">&lt;</span><span class="name">ItemGroup</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">None</span> <span class="name">Include</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">packages.config</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">ItemGroup</span><span class="symbol">&gt;</span>
 
 <span class="symbol">&lt;</span><span class="name">Import</span> <span class="name">Project</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">$(MSBuildToolsPath)\Microsoft.CSharp.targets</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="comment">&lt;!-- To modify your build process, add your task inside one of the targets below and uncomment it.
 Other similar extension points exist, see Microsoft.Common.targets.
 &lt;Target Name=&quot;BeforeBuild&quot;&gt;
 &lt;/Target&gt;
 &lt;Target Name=&quot;AfterBuild&quot;&gt;
 &lt;/Target&gt;
 --&gt;</span>
<span class="symbol">&lt;/</span><span class="name">Project</span><span class="symbol">&gt;</span>
</pre>
</figure>
<h2 id="final-notes-and-caveats">Final Notes and Caveats</h2>
<p>Unfortunately, Visual Studio doesn't really seem to support
these conditions very gracefully - firstly you can't suppress
reference warnings (that I know of), and secondly you have zero
visibility of the conditions in the IDE.</p>
<p>Each time Visual Studio saves your project file, it will
reformat the XML, removing any white space. It might also decide
to insert elements between the elements you have created. For
this reason, you might want to use XML comments to identify your
custom condition blocks.</p>
<p>Visual Studio seems reasonably competent when you change your
project, for example by adding new code files or references so
that it doesn't break any of your conditional stuff. However, if
you use the IDE to directly manipulate something that you have
bound to a condition (for example the Json.NET references) then
I imagine it will be less forgiving and may need to be manually
resolved. I haven't tried this yet, I'll probably find out when
I need to install an update to the Json.NET NuGet package!</p>
<p>This principle seems sound and not to difficult, at least for
smaller libraries and I suspect I'll make more use of this for
any independent libraries that I create in the future. It is a
manual process to set up and maintain, and slightly unfriendly
to Visual Studio though, so I would wait until a library was
complete before doing this, and I probably would not do it to
product assemblies (for example to make WebCopy work on Windows
XP again) although it is feasible.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2015-08-18 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/targeting-multiple-versions-of-the-net-framework-from-the-same-project .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comWorking around System.ArgumentException: Only TrueType fonts are supported. This is not a TrueType fonturn:uuid:fc271a73-cdca-4181-9f84-0f757017b00e2015-08-15T16:35:23Z2015-08-15T16:35:23Z<p>One of the exceptions I see with a reasonable frequency (usually
in <a href="https://cyotek.com/cyotek-gif-animator">Gif Animator</a>) is <strong>Only TrueType fonts are supported.
This is not a TrueType font</strong>.</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
System.ArgumentException: Only TrueType fonts are supported. This is not a TrueType font.
 at System.Drawing.Font.FromLogFont(Object lf, IntPtr hdc)
 at System.Windows.Forms.FontDialog.UpdateFont(LOGFONT lf)
 at System.Windows.Forms.FontDialog.RunDialog(IntPtr hWndOwner)
 at System.Windows.Forms.CommonDialog.ShowDialog(IWin32Window owner)
</pre>
</figure>
<p>This exception is thrown when using the
<code>System.Windows.Forms.FontDialog</code> component and you select an
invalid font. And you can't do a thing about it*, as
this exception is buried in a private method of the <code>FontDialog</code>
that isn't handled.</p>
<p>As the bug has been there for years without being fixed, and
given that fact that Windows Forms isn't exactly high on the
list of priorities for Microsoft, I suspect it will never be
fixed. This is one wheel I'd prefer not to reinvent, but... here
it is anyway.</p>
<p>The <code>Cyotek.Windows.Forms.FontDialog</code> component is a drop in
replacement for the original <code>System.Windows.Forms.FontDialog</code>,
but without the crash that occurs when selecting a non-True Type
font.</p>
<p>This version uses the native Win32 dialog via <code>ChooseFont</code> - the
hook procedure to handle the <code>Apply</code> event and hiding the colour
combobox has been taken directly from the original component. As
I'm inheriting from the same base component and have replicated
the API completely, you should simply be able to replace
<code>System.Windows.Forms.FontDialog</code> with
<code>Cyotek.Windows.Forms.FontDialog</code> and it will work.</p>
<p>There's also a fully managed solution buried in one of the
branches of the repository. It is incomplete, mainly because I
wasn't able to determine which fonts are hidden by settings, and
how to combine families with non standard styles such as
<em>Light</em>. It's still interesting in its own right, showing how to
use <code>EnumFontFamiliesEx</code> and other interop calls, but for now it
is on hold as a work in progress.</p>
<h2 id="have-you-experienced-this-crash">Have you experienced this crash?</h2>
<blockquote>
<p>I haven't actually managed to find a font that causes this
type of crash, although I have quite a few automated error
reports from users who experience it. If you know of such a
font that is (legally!) available for download, please let me
know so that I can test this myself. I assume my version fixes
the problem but at this point I don't actually know for sure.</p>
</blockquote>
<h2 id="getting-the-source">Getting the source</h2>
<p>The source is available from <a href="https://github.com/cyotek/Cyotek.Windows.Forms.FontDialog" rel="external nofollow noopener">GitHub</a>.</p>
<h2 id="nuget-package">NuGet Package</h2>
<p>A NuGet package <a href="https://www.nuget.org/packages/Cyotek.Windows.Forms.FontDialog" rel="external nofollow noopener">is available</a>.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
Install-Package Cyotek.Windows.Forms.FontDialog
</pre>
</figure>
<h2 id="license">License</h2>
<p>The <code>FontDialog</code> component is licensed under the MIT License.
See <code>LICENSE.txt</code> for the full text.</p>
<hr />
<p>* You might be able to catch it in
<code>Application.ThreadException</code> or
<code>AppDomain.CurrentDomain.UnhandledException</code> (or even by just
wrapping the call to <code>ShowDialog</code> in a <code>try</code> ... <code>catch</code> block),
but as I haven't been able to reproduce this crash I have no way
of knowing for sure. Plus I have no idea if it will leave the
Win32 dialog open or destabilize it in some way</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2015-08-15 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/working-around-system-argumentexception-only-truetype-fonts-are-supported-this-is-not-a-truetype-font .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comSending SMS messages with Twiliourn:uuid:df7f7c44-f463-4adc-9a6b-e20e79497af92015-07-25T07:46:40Z2015-07-24T19:11:38Z<p>Last week I attended the <a href="http://nebytes.net/" rel="external nofollow noopener">NEBytes</a> technology user group for
the first time. Despite the fact I didn't actually say more than
two words (speaking to a real live human is only marginally
easier than flying without wings) I did enjoy the two talks that
were given.</p>
<p>The first of these was for <a href="https://www.twilio.com/" rel="external nofollow noopener">Twilio</a>, a platform for text
messaging and Voice over IP (VoIP). This platform provides you
with the ability to send and receive SMS messages, or even
create convoluted telephone call services where you can prompt
the user with options, capture input, record messages, redirect
to other phones... and all fairly painlessly. I can see all
sorts of interesting uses for the services they offer. Oh, and
the prices seem reasonable as well.</p>
<p>All of this is achieved using a simple REST API which is pretty
impressive.</p>
<p>My immediate use case for this is for alert notifications as,
like any technology, sometimes emails fail or are not
accessible. I also added two factor authentication to cyotek.com
in under 5 minutes which I thought was neat (although in
fairness, with the Identity Framework all I had to do was fill
in the blanks for the <code>Smsservice</code> and uncomment some
boilerplate code).</p>
<p>In this article, I'll show you just how incredibly easy it is to
send text messages.</p>
<h2 id="getting-an-account">Getting an account</h2>
<p>The first thing you need is a Twilio account - so go sign up.
You don't need to shell out any money at this stage, the example
program I will present below will work perfectly well with their
trial account and not cost a penny.</p>
<p>Once you've signed up you'll need to validate a real phone
number of your own for security purposes, and then you'll need
to buy a phone number that you will use for your SMS services.</p>
<blockquote>
<p>You get one phone number for free with your trial account.
When you are ready to upgrade to a unrestricted account, each
phone number you buy costs $1 a month (yes, that's one
dollar), then $0.0075 to receive a SMS message or $0.04 to
send one. (<em>Prices correct at time of writing</em>). For high
volume businesses, short codes are also available, but these
are very expensive.</p>
</blockquote>
<p>You'll need to get your API credentials too - this is slightly
hidden, but if you go to your Twilio <a href="https://www.twilio.com/user/account/" rel="external nofollow noopener">account portal</a> and
look in the upper right section of the page there is a link
titled <strong>Show API Credentials</strong> - click this to get your
<em>Account SID</em> and <em>Auth Token</em>.</p>
<h2 id="creating-a-simple-application">Creating a simple application</h2>
<p>Twilio offers client libraries for a raft of languages, and
support for .NET is no exception by using the
<a href="https://github.com/twilio/twilio-csharp" rel="external nofollow noopener">twilio-csharp</a> client,
which of course has a NuGet package. Lots of packages actually,
but we just need the core.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
Install-Package Twilio
</pre>
</figure>
<p>Now you're set!</p>
<p>To send a message, you create an instance of the
<code>TwilioRestClient</code> using your Account SID and Auth Token and
call <code>SendSmsMessage</code> with your Twilio phone number, the number
of the phone to send the message to, and of course the message
itself. And that's pretty much it.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">static</span> <span class="keyword">void</span> Main<span class="symbol">(</span><span class="keyword">string</span><span class="symbol">[</span><span class="symbol">]</span> args<span class="symbol">)</span>
<span class="symbol">{</span>
 SendSms<span class="symbol">(</span><span class="string">&quot;077xxxxxxxx&quot;</span><span class="symbol">,</span> <span class="string">&quot;Sending messages couldn&#39;t be simpler!&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> SendSms<span class="symbol">(</span><span class="keyword">string</span> to<span class="symbol">,</span> <span class="keyword">string</span> message<span class="symbol">)</span>
<span class="symbol">{</span>
 TwilioRestClient client<span class="symbol">;</span>
 <span class="keyword">string</span> accountSid<span class="symbol">;</span>
 <span class="keyword">string</span> authToken<span class="symbol">;</span>
 <span class="keyword">string</span> fromNumber<span class="symbol">;</span>

 accountSid <span class="symbol">=</span> <span class="string">&quot;DF8A228F5D66403E973E714324D5816D&quot;</span><span class="symbol">;</span> <span class="comment">// no, these are not real</span>
 authToken <span class="symbol">=</span> <span class="string">&quot;942CA384E3CC4107A10BA58177ACF88B&quot;</span><span class="symbol">;</span>
 fromNumber <span class="symbol">=</span> <span class="string">&quot;+44191xxxxxxx&quot;</span><span class="symbol">;</span>

 client <span class="symbol">=</span> <span class="keyword">new</span> TwilioRestClient<span class="symbol">(</span>accountSid<span class="symbol">,</span> authToken<span class="symbol">)</span><span class="symbol">;</span>

 client<span class="symbol">.</span>SendSmsMessage<span class="symbol">(</span>fromNumber<span class="symbol">,</span> to<span class="symbol">,</span> message<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The <code>SendSmsMessage</code> method returns a <code>SMSMessage</code> object which
has various attributes relating to the sent message - such as
the cost of sending it.</p>
<p>Apologies for the less-than-perfect photo, but the image below
shows my Lumia 630 with the received message.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/twilio-sendsms.png" class="gallery" title="Not the best photo in the world, but here is a sample message" ><img src="https://images.cyotek.com/image/thumbnail/devblog/twilio-sendsms.png" alt="Not the best photo in the world, but here is a sample message" decoding="async" loading="lazy" /></a><figcaption>Not the best photo in the world, but here is a sample message</figcaption></figure>
<blockquote>
<p>Sharp eyes will note that the message is prefixed with <em>Sent
from your Twilio trial account -</em> this prefix is only for
trial accounts, and there will be no adjustment of your
messages once you've upgraded.</p>
</blockquote>
<h2 id="simple-apis-arent-so-simple">Simple API's aren't so simple</h2>
<p>There's one fairly awkward caveat with this library however -
exception handling. I did a test using invalid credentials, and
to my surprise nothing happened when I ran the sample program. I
didn't receive a SMS message of course, but neither did the
sample program crash.</p>
<p>This is because for whatever reason, the client doesn't raise an
exception if the call fails. Instead, it is essentially
returned as a result code. I mentioned above that the
<code>SendSmsMessage</code> return a <code>SMSMessage</code> object. This object has a
property named <code>RestException</code>. If the value of this property is
<code>null</code>, everything is fine, if not, then your request wasn't
successful.</p>
<p>I really don't like this behaviour, as it means now I'm
responsible for checking the response every time I send a
message, instead of the client throwing an exception and forcing
me to deal with issues.</p>
<p>The other thing that irks me with this library is that the
<code>RestException</code> class has <code>Status</code> and <code>Code</code> properties, which
are the HTTP status code and Twilio status code respectively.
But for some curious reason, these numeric properties are
defined as strings, and so if you want to process them you'll
have to both convert them to integers and make sure that the
underlying value is a number in the first place.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> SendSms<span class="symbol">(</span><span class="keyword">string</span> to<span class="symbol">,</span> <span class="keyword">string</span> message<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="symbol">...</span> <span class="symbol">&lt;</span>snip<span class="symbol">&gt;</span> <span class="symbol">...</span>
 SMSMessage result<span class="symbol">;</span>

 <span class="symbol">...</span> <span class="symbol">&lt;</span>snip<span class="symbol">&gt;</span> <span class="symbol">...</span>

 result <span class="symbol">=</span> client<span class="symbol">.</span>SendSmsMessage<span class="symbol">(</span>fromNumber<span class="symbol">,</span> to<span class="symbol">,</span> message<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>result<span class="symbol">.</span>RestException <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ApplicationException<span class="symbol">(</span>result<span class="symbol">.</span>RestException<span class="symbol">.</span>Message<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Although I don't recommend you use <code>ApplicationException</code>!
Something like this may be more appropriate:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">if</span> <span class="symbol">(</span>result<span class="symbol">.</span>RestException <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> httpStatus<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">int</span><span class="symbol">.</span>TryParse<span class="symbol">(</span>result<span class="symbol">.</span>RestException<span class="symbol">.</span>Status<span class="symbol">,</span> <span class="keyword">out</span> httpStatus<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 httpStatus <span class="symbol">=</span> <span class="number">500</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">throw</span> <span class="keyword">new</span> HttpException<span class="symbol">(</span>httpStatus<span class="symbol">,</span> result<span class="symbol">.</span>RestException<span class="symbol">.</span>Message<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>There's also a <code>Status</code> property on the underlying <code>SMSMessage</code>
class which can be <code>failed</code>. Hopefully the <code>RestException</code>
property is always set for failed statuses otherwise that's
something else you'd have to remember to check.</p>
<p>However you choose to do it, you probably should ensure that you
do check for a failed / exception response, especially if the
messages are important (for example two-factor authentication
codes).</p>
<h2 id="long-codes-vs-short-codes">Long Codes vs Short Codes</h2>
<p>By default, Twilio uses long codes (also known as &quot;normal&quot; phone
numbers). According to their docs, these are rate limited to 1
message per second. I did a sample test where I spammed 10
messages one after another. I received the first 5 right away,
and the next five about a minute later. So if you have a high
volume service, it's possible that your messages may be slightly
delayed. One the plus side, it does seem to be fire and forget,
you don't need to manually queue messages yourself and they
don't get lost.</p>
<p>Twilio also supports short codes (e.g. send STOP to 123456 to
opt out of this list you never opted into in the first place),
which are suitable for high traffic - 30 messages a second
apparently. However, these are very expensive and have to be
leased from the mobile operators, a process which takes several
weeks.</p>
<h2 id="advanced-scenarios">Advanced Scenarios</h2>
<p>As I mentioned in my intro, there's a lot more to Twilio than
just sending SMS messages, although for me personally that's
going to be a big part of it. But you can also read and process
messages, in other words when someone sends a SMS to your Twilio
phone number, it will call a custom HTTP endpoint in your
application code, where you can then read the message and
process it. This too is something I will find value in, and I'll
cover that in another post.</p>
<p>And then there's some pretty impressive options for working with
real phone calls (along with the worst robot sounding voice in
history). Not entirely sure I will cover this as it's not
immediately something I'd make use of.</p>
<p>Take a look at their <a href="https://www.twilio.com/docs" rel="external nofollow noopener">documentation</a> to see how to use their
API's to build SMS/VoIP functionality into your services.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2015-07-24 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/sending-sms-messages-with-twilio .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comEven more algorithms for dithering images using C#urn:uuid:13fbfc7c-65f9-47a2-99fb-2a23bb07e05b2015-06-13T10:55:53Z2015-06-13T10:55:53Z<p>Although I should really be working on adding the dithering
algorithms into <a href="https://cyotek.com/cyotek-gif-animator">Gif Animator</a>, I thought it would be useful
to expand the repertoire of algorithms available for use with it
and the other projects I'm working on.</p>
<h2 id="adding-a-general-purpose-base-class">Adding a general purpose base class</h2>
<p>I decided to re-factor the class I created for the <a href="/post/dithering-an-image-using-the-burkes-algorithm-in-csharp">Burkes</a>
algorithm to make it suitable for adding other error diffusion
filters with a minimal amount of code.</p>
<p>First, I added a new abstract class, <code>ErrorDiffusionDithering</code>.
The constructor of this class requires you to pass in the matrix
used to disperse the error to neighbouring pixels, the divisor,
and whether or not to use bit shifting. The reason for the last
parameter is the <a href="/post/dithering-an-image-using-the-floyd-steinberg-algorithm-in-csharp">Floyd-Steinberg</a> and Burkes algorithms
covered in my earlier posts had divisors that were powers of
two, and so could therefore be bit shifted for faster division.
Not all algorithms use a power of two divisor though and so we
need to be flexible.</p>
<p>The constructor then stores the matrix, and pre-calculates a
couple of other values to avoid repeating these each time the
<code>Diffuse</code> method is called.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> ErrorDiffusionDithering<span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">,</span><span class="symbol">]</span> matrix<span class="symbol">,</span> <span class="keyword">byte</span> divisor<span class="symbol">,</span> <span class="keyword">bool</span> useShifting<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>matrix <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException<span class="symbol">(</span><span class="string">&quot;matrix&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>matrix<span class="symbol">.</span>Length <span class="symbol">==</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentException<span class="symbol">(</span><span class="string">&quot;Matrix is empty.&quot;</span><span class="symbol">,</span> <span class="string">&quot;matrix&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 _matrix <span class="symbol">=</span> matrix<span class="symbol">;</span>
 _matrixWidth <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span>matrix<span class="symbol">.</span>GetUpperBound<span class="symbol">(</span><span class="number">1</span><span class="symbol">)</span> <span class="symbol">+</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 _matrixHeight <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span>matrix<span class="symbol">.</span>GetUpperBound<span class="symbol">(</span><span class="number">0</span><span class="symbol">)</span> <span class="symbol">+</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 _divisor <span class="symbol">=</span> divisor<span class="symbol">;</span>
 _useShifting <span class="symbol">=</span> useShifting<span class="symbol">;</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> _matrixWidth<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>matrix<span class="symbol">[</span><span class="number">0</span><span class="symbol">,</span> i<span class="symbol">]</span> <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _startingOffset <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span>i <span class="symbol">-</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The actual dithering implementation is unchanged from original
matrix handling code, with the exception of supporting bit
shifting or integer division, and not having to work out the
current pixel in the matrix, width or height.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">void</span> IErrorDiffusion<span class="symbol">.</span>Diffuse<span class="symbol">(</span>ArgbColor<span class="symbol">[</span><span class="symbol">]</span> data<span class="symbol">,</span> ArgbColor original<span class="symbol">,</span> ArgbColor transformed<span class="symbol">,</span> <span class="keyword">int</span> x<span class="symbol">,</span> <span class="keyword">int</span> y<span class="symbol">,</span> <span class="keyword">int</span> width<span class="symbol">,</span> <span class="keyword">int</span> height<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> redError<span class="symbol">;</span>
 <span class="keyword">int</span> blueError<span class="symbol">;</span>
 <span class="keyword">int</span> greenError<span class="symbol">;</span>

 redError <span class="symbol">=</span> original<span class="symbol">.</span>R <span class="symbol">-</span> transformed<span class="symbol">.</span>R<span class="symbol">;</span>
 blueError <span class="symbol">=</span> original<span class="symbol">.</span>G <span class="symbol">-</span> transformed<span class="symbol">.</span>G<span class="symbol">;</span>
 greenError <span class="symbol">=</span> original<span class="symbol">.</span>B <span class="symbol">-</span> transformed<span class="symbol">.</span>B<span class="symbol">;</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> row <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> row <span class="symbol">&lt;</span> _matrixHeight<span class="symbol">;</span> row<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> offsetY<span class="symbol">;</span>

 offsetY <span class="symbol">=</span> y <span class="symbol">+</span> row<span class="symbol">;</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> col <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> col <span class="symbol">&lt;</span> _matrixWidth<span class="symbol">;</span> col<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> coefficient<span class="symbol">;</span>
 <span class="keyword">int</span> offsetX<span class="symbol">;</span>

 coefficient <span class="symbol">=</span> _matrix<span class="symbol">[</span>row<span class="symbol">,</span> col<span class="symbol">]</span><span class="symbol">;</span>
 offsetX <span class="symbol">=</span> x <span class="symbol">+</span> <span class="symbol">(</span>col <span class="symbol">-</span> _startingOffset<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>coefficient <span class="symbol">!=</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> offsetX <span class="symbol">&gt;</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> offsetX <span class="symbol">&lt;</span> width <span class="symbol">&amp;&amp;</span> offsetY <span class="symbol">&gt;</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> offsetY <span class="symbol">&lt;</span> height<span class="symbol">)</span>
 <span class="symbol">{</span>
 ArgbColor offsetPixel<span class="symbol">;</span>
 <span class="keyword">int</span> offsetIndex<span class="symbol">;</span>
 <span class="keyword">int</span> newR<span class="symbol">;</span>
 <span class="keyword">int</span> newG<span class="symbol">;</span>
 <span class="keyword">int</span> newB<span class="symbol">;</span>

 offsetIndex <span class="symbol">=</span> offsetY <span class="symbol">*</span> width <span class="symbol">+</span> offsetX<span class="symbol">;</span>
 offsetPixel <span class="symbol">=</span> data<span class="symbol">[</span>offsetIndex<span class="symbol">]</span><span class="symbol">;</span>

 <span class="comment">// if the UseShifting property is set, then bit shift the values by the specified</span>
 <span class="comment">// divisor as this is faster than integer division. Otherwise, use integer division</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_useShifting<span class="symbol">)</span>
 <span class="symbol">{</span>
 newR <span class="symbol">=</span> <span class="symbol">(</span>redError <span class="symbol">*</span> coefficient<span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> _divisor<span class="symbol">;</span>
 newG <span class="symbol">=</span> <span class="symbol">(</span>greenError <span class="symbol">*</span> coefficient<span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> _divisor<span class="symbol">;</span>
 newB <span class="symbol">=</span> <span class="symbol">(</span>blueError <span class="symbol">*</span> coefficient<span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> _divisor<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 newR <span class="symbol">=</span> <span class="symbol">(</span>redError <span class="symbol">*</span> coefficient<span class="symbol">)</span> <span class="symbol">/</span> _divisor<span class="symbol">;</span>
 newG <span class="symbol">=</span> <span class="symbol">(</span>greenError <span class="symbol">*</span> coefficient<span class="symbol">)</span> <span class="symbol">/</span> _divisor<span class="symbol">;</span>
 newB <span class="symbol">=</span> <span class="symbol">(</span>blueError <span class="symbol">*</span> coefficient<span class="symbol">)</span> <span class="symbol">/</span> _divisor<span class="symbol">;</span>
 <span class="symbol">}</span>

 offsetPixel<span class="symbol">.</span>R <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>R <span class="symbol">+</span> newR<span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 offsetPixel<span class="symbol">.</span>G <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>G <span class="symbol">+</span> newG<span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 offsetPixel<span class="symbol">.</span>B <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>B <span class="symbol">+</span> newB<span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 data<span class="symbol">[</span>offsetIndex<span class="symbol">]</span> <span class="symbol">=</span> offsetPixel<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="burkes-dithering-redux">Burkes Dithering, redux</h2>
<p>The <code>BurkesDithering</code> class now looks like this</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">sealed</span> <span class="keyword">class</span> BurksDithering <span class="symbol">:</span> ErrorDiffusionDithering
<span class="symbol">{</span>
 <span class="keyword">public</span> BurksDithering<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">:</span> <span class="keyword">base</span><span class="symbol">(</span><span class="keyword">new</span> <span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">,</span><span class="symbol">]</span>
 <span class="symbol">{</span>
 <span class="symbol">{</span>
 <span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="number">8</span><span class="symbol">,</span> <span class="number">4</span>
 <span class="symbol">}</span><span class="symbol">,</span>
 <span class="symbol">{</span>
 <span class="number">2</span><span class="symbol">,</span> <span class="number">4</span><span class="symbol">,</span> <span class="number">8</span><span class="symbol">,</span> <span class="number">4</span><span class="symbol">,</span> <span class="number">2</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span><span class="symbol">,</span> <span class="number">5</span><span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span>
 <span class="symbol">{</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>No code, just the matrix and the bit shifted divisor of 5, which
will divide each result by 32. Nice!</p>
<h2 id="more-algorithms">More Algorithms</h2>
<p>As well as opening the door to allowing a user to define a
custom dither matrix, it also makes it trivial to implement a
number of other common error diffusion matrixes. The <a href="https://github.com/cyotek/Dithering" rel="external nofollow noopener">GitHub
Repository</a> now offers the
following algorithms</p>
<ul>
<li>Atkinson</li>
<li>Burkes</li>
<li>Floyd-Steinberg</li>
<li>Jarvis, Judice &amp; Ninke</li>
<li>Sierra</li>
<li>Two Row Sierra</li>
<li>Sierra Light</li>
<li>Stucki</li>
</ul>
<p>Which is a fairly nice array.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/dithering-atkinson.png" class="gallery" title="An example of Atkinson dithering" ><img src="https://images.cyotek.com/image/thumbnail/devblog/dithering-atkinson.png" alt="An example of Atkinson dithering" decoding="async" loading="lazy" /></a><figcaption>An example of Atkinson dithering</figcaption></figure><figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">sealed</span> <span class="keyword">class</span> AtkinsonDithering <span class="symbol">:</span> ErrorDiffusionDithering
<span class="symbol">{</span>
 <span class="keyword">public</span> AtkinsonDithering<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">:</span> <span class="keyword">base</span><span class="symbol">(</span><span class="keyword">new</span> <span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">,</span><span class="symbol">]</span>
 <span class="symbol">{</span>
 <span class="symbol">{</span>
 <span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="number">1</span><span class="symbol">,</span> <span class="number">1</span>
 <span class="symbol">}</span><span class="symbol">,</span>
 <span class="symbol">{</span>
 <span class="number">1</span><span class="symbol">,</span> <span class="number">1</span><span class="symbol">,</span> <span class="number">1</span><span class="symbol">,</span> <span class="number">0</span>
 <span class="symbol">}</span><span class="symbol">,</span>
 <span class="symbol">{</span>
 <span class="number">0</span><span class="symbol">,</span> <span class="number">1</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="number">0</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span><span class="symbol">,</span> <span class="number">3</span><span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span>
 <span class="symbol">{</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="random-dithering">Random Dithering</h2>
<p>There's a rather old (in internet terms anyway!) text file
floating around named <a href="http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT" rel="external nofollow noopener">DHALF.TXT</a> (based in turn on an even
older document named <code>DITHER.TXT</code>) that has a ton of useful
information on dithering, and with the exception of the
Altkinson algorithm (I took that from <a href="http://www.tannerhelland.com/4660/dithering-eleven-algorithms-source-code/#attachment_4677" rel="external nofollow noopener">here</a> is where I have
pulled all the error weights and divisors from.</p>
<p>One of the sections in this document dealt with random
dithering. Although I didn't think I would ever use it myself, I
thought I'd add an implementation of it anyway to see what it's
like.</p>
<p>Unlike the error diffusion methods, random dithering affects
only a single pixel at a time, and does not consider or modify
its neighbours. You also have a modicum of control over it too,
if you can control the initial seed of the random number
generator.</p>
<p>The <code>DHALF.TXT</code> text sums it up succinctly: For each dot in our
grayscale image, we generate a random number in the range 0 -
255: if the random number is greater than the image value at
that dot, the display device plots the dot white; otherwise, it
plots it black. That's it.</p>
<p>And here's our implementation (ignoring the fact that it isn't
error diffusion and all of a sudden our <code>IErrorDiffusion</code>
interface is named wrong!)</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">void</span> IErrorDiffusion<span class="symbol">.</span>Diffuse<span class="symbol">(</span>ArgbColor<span class="symbol">[</span><span class="symbol">]</span> data<span class="symbol">,</span> ArgbColor original<span class="symbol">,</span> ArgbColor transformed<span class="symbol">,</span> <span class="keyword">int</span> x<span class="symbol">,</span> <span class="keyword">int</span> y<span class="symbol">,</span> <span class="keyword">int</span> width<span class="symbol">,</span> <span class="keyword">int</span> height<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">byte</span> gray<span class="symbol">;</span>

 gray <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span><span class="number">0.299</span> <span class="symbol">*</span> original<span class="symbol">.</span>R <span class="symbol">+</span> <span class="number">0.587</span> <span class="symbol">*</span> original<span class="symbol">.</span>G <span class="symbol">+</span> <span class="number">0.114</span> <span class="symbol">*</span> original<span class="symbol">.</span>B<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>gray <span class="symbol">&gt;</span> _random<span class="symbol">.</span>Next<span class="symbol">(</span><span class="number">0</span><span class="symbol">,</span> <span class="number">255</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 data<span class="symbol">[</span>y <span class="symbol">*</span> width <span class="symbol">+</span> x<span class="symbol">]</span> <span class="symbol">=</span> _white<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 data<span class="symbol">[</span>y <span class="symbol">*</span> width <span class="symbol">+</span> x<span class="symbol">]</span> <span class="symbol">=</span> _black<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>(Although I reversed black and white from the original
description as otherwise it looked completely wrong)</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/dithering-random.png" class="gallery" title="Random dithering - it doesn't actually look too bad" ><img src="https://images.cyotek.com/image/thumbnail/devblog/dithering-random.png" alt="Random dithering - it doesn't actually look too bad" decoding="async" loading="lazy" /></a><figcaption>Random dithering - it doesn't actually look too bad</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/dithering-random-color.png" class="gallery" title="Another example of random dithering, this time using colour" ><img src="https://images.cyotek.com/image/thumbnail/devblog/dithering-random-color.png" alt="Another example of random dithering, this time using colour" decoding="async" loading="lazy" /></a><figcaption>Another example of random dithering, this time using colour</figcaption></figure>
<p>I was surprised to see it actually doesn't look that bad.</p>
<h2 id="continuation">Continuation</h2>
<p>I've almost got a full house of useful dithering algorithms now.
About the only thing left for me to do is to implement a ordered
Bayer dithering as I really like the look of this type, and
reminds me of games and computers of yesteryear. So there's
still at least one more article to follow in this series!</p>
<p>The updated source code with all these algorithms is available
from the <a href="https://github.com/cyotek/Dithering" rel="external nofollow noopener">GitHub repository</a>.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2015-06-13 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/even-more-algorithms-for-dithering-images-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comDithering an image using the Burkes algorithm in C#urn:uuid:119ddf2c-d60c-459f-9438-743b434a8c752015-06-06T11:02:11Z2015-06-06T11:02:11Z<p>In my <a href="/post/dithering-an-image-using-the-floyd-steinberg-algorithm-in-csharp">previous post</a>, I described how to dither an image in
C# using the Floyd‑Steinberg algorithm. Continuing this theme,
this post will cover the Burkes algorithm.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/dithering-threshold.png" class="gallery" title="An example of 1bit conversion via a threshold" ><img src="https://images.cyotek.com/image/thumbnail/devblog/dithering-threshold.png" alt="An example of 1bit conversion via a threshold" decoding="async" loading="lazy" /></a><figcaption>An example of 1bit conversion via a threshold</figcaption></figure>
<p>I will be using the same demonstration application as from the
previous post, so I won't go over how this works again.</p>
<h2 id="burkes-dithering">Burkes dithering</h2>
<p>As with Floyd‑Steinberg, the Burkes algorithm is an error
diffusion algorithm, which is to say for each pixel an &quot;<em>error</em>&quot;
is generated and then distributed to pixels around the source.
Unlike Floyd‑Steinberg however (which modifies 4 surrounding
pixels), Burkes modifies 7 pixels.</p>
<blockquote>
<p>Burkes is actually a modified version of the Stucki algorithm,
which in turn is an evolution of the Jarvis algorithms.</p>
</blockquote>
<p>The diagram below shows the distribution of the error
coefficients.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/dithering-burkes-diagram.png" class="gallery" title="How the error of the current pixel is diffused to its neighbours" ><img src="https://images.cyotek.com/image/devblog/dithering-burkes-diagram.png" alt="How the error of the current pixel is diffused to its neighbours" decoding="async" loading="lazy" /></a><figcaption>How the error of the current pixel is diffused to its neighbours</figcaption></figure>
<ul>
<li>8 for the pixel to the right of the current pixel</li>
<li>4 for the second pixel to the right</li>
<li>2 for the pixel below and two to the left</li>
<li>4 for the pixel below and to the left</li>
<li>8 for the pixel below</li>
<li>4 for the pixel below and to the right</li>
<li>2 for the pixel below and two to the right</li>
</ul>
<p>Unlike Floyd‑Steinberg, the error result in this algorithm is
divided by 32. But as that's still a power of two, once again we
can use bit shifting to perform the division.</p>
<p>Due to the additional calculations I would assume that this
algorithm will be slightly slower than Floyd‑Steinberg, but as
of yet I haven't ran any form of benchmarks to test this.</p>
<h2 id="applying-the-algorithm">Applying the algorithm</h2>
<p>In my Floyd‑Steinberg example, I replicated the calculations
four times for each pixel. As there are now seven sets of
calculations with Burkes, I decided to store the coefficients in
a 2D array mimicing the diagram above, then iterating this. I'm
not entirely convinced this is the best approach, but it does
seem to be working.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> <span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">,</span><span class="symbol">]</span> _matrix <span class="symbol">=</span>
<span class="symbol">{</span>
 <span class="symbol">{</span>
 <span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="number">8</span><span class="symbol">,</span> <span class="number">4</span>
 <span class="symbol">}</span><span class="symbol">,</span>
 <span class="symbol">{</span>
 <span class="number">2</span><span class="symbol">,</span> <span class="number">4</span><span class="symbol">,</span> <span class="number">8</span><span class="symbol">,</span> <span class="number">4</span><span class="symbol">,</span> <span class="number">2</span>
 <span class="symbol">}</span>
<span class="symbol">}</span><span class="symbol">;</span>

<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">int</span> _matrixHeight <span class="symbol">=</span> <span class="number">2</span><span class="symbol">;</span>

<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">int</span> _matrixStartX <span class="symbol">=</span> <span class="number">2</span><span class="symbol">;</span>

<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">int</span> _matrixWidth <span class="symbol">=</span> <span class="number">5</span><span class="symbol">;</span>
</pre>
</figure>
<p>This sets up the matrix as a static that is only created once.
I've also added some constants to control the offsets as I can't
create an array with a non-zero lower bound. This does smell a
bit so I'll be revisiting this!</p>
<p>Below is the code to dither a single pixel. Remember that the
demonstration program uses a 1D array of <code>ArgbColor</code> structs to
make it easy to read and understand, but you could equally use
direct pointer manipulation on a bitmap's bits, with lots of
extra code to handle different colour depths.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">int</span> redError<span class="symbol">;</span>
<span class="keyword">int</span> blueError<span class="symbol">;</span>
<span class="keyword">int</span> greenError<span class="symbol">;</span>

redError <span class="symbol">=</span> originalPixel<span class="symbol">.</span>R <span class="symbol">-</span> transformedPixel<span class="symbol">.</span>R<span class="symbol">;</span>
blueError <span class="symbol">=</span> originalPixel<span class="symbol">.</span>G <span class="symbol">-</span> transformedPixel<span class="symbol">.</span>G<span class="symbol">;</span>
greenError <span class="symbol">=</span> originalPixel<span class="symbol">.</span>B <span class="symbol">-</span> transformedPixel<span class="symbol">.</span>B<span class="symbol">;</span>

<span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> row <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> row <span class="symbol">&lt;</span> _matrixHeight<span class="symbol">;</span> row<span class="symbol">++</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> offsetY<span class="symbol">;</span>

 offsetY <span class="symbol">=</span> y <span class="symbol">+</span> row<span class="symbol">;</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> col <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> col <span class="symbol">&lt;</span> _matrixWidth<span class="symbol">;</span> col<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> coefficient<span class="symbol">;</span>
 <span class="keyword">int</span> offsetX<span class="symbol">;</span>

 coefficient <span class="symbol">=</span> _matrix<span class="symbol">[</span>row<span class="symbol">,</span> col<span class="symbol">]</span><span class="symbol">;</span>
 offsetX <span class="symbol">=</span> x <span class="symbol">+</span> <span class="symbol">(</span>col <span class="symbol">-</span> _matrixStartX<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>coefficient <span class="symbol">!=</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> offsetX <span class="symbol">&gt;</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> offsetX <span class="symbol">&lt;</span> width <span class="symbol">&amp;&amp;</span> offsetY <span class="symbol">&gt;</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> offsetY <span class="symbol">&lt;</span> height<span class="symbol">)</span>
 <span class="symbol">{</span>
 ArgbColor offsetPixel<span class="symbol">;</span>
 <span class="keyword">int</span> offsetIndex<span class="symbol">;</span>

 offsetIndex <span class="symbol">=</span> offsetY <span class="symbol">*</span> width <span class="symbol">+</span> offsetX<span class="symbol">;</span>
 offsetPixel <span class="symbol">=</span> original<span class="symbol">[</span>offsetIndex<span class="symbol">]</span><span class="symbol">;</span>
 offsetPixel<span class="symbol">.</span>R <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>R <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>redError <span class="symbol">*</span> coefficient<span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">5</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 offsetPixel<span class="symbol">.</span>G <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>G <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>greenError <span class="symbol">*</span> coefficient<span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">5</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 offsetPixel<span class="symbol">.</span>B <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>B <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>blueError <span class="symbol">*</span> coefficient<span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">5</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 original<span class="symbol">[</span>offsetIndex<span class="symbol">]</span> <span class="symbol">=</span> offsetPixel<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Due to the loop this code is now shorter than the
Floyd‑Steinberg version. It's also less readable due the
coefficients being stored in a 2D matrix. Of course, the
algorithm is fixed and won't change so perhaps that's not an
issue, but if performance really was a concern you can unroll
the loop and duplicate all that code. I'll stick with the loop!</p>
<h2 id="final-output">Final Output</h2>
<p>The image below shows our sample image dithered using the Burkes
algorithm. It's very similar to the output created via
Floyd–Steinberg, albeit darker.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/dithering-burkes.png" class="gallery" title="The final result - a bitmap transformed with Burkes dithering" ><img src="https://images.cyotek.com/image/thumbnail/devblog/dithering-burkes.png" alt="The final result - a bitmap transformed with Burkes dithering" decoding="async" loading="lazy" /></a><figcaption>The final result - a bitmap transformed with Burkes dithering</figcaption></figure>
<p>Again, by changing the threshold at which colours are converted
to black or white, we can affect the output of the dithering
even if the conversion is to solid black.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/dithering-burkes-extreme.png" class="gallery" title="The non-dithered version of this image is solid black" ><img src="https://images.cyotek.com/image/thumbnail/devblog/dithering-burkes-extreme.png" alt="The non-dithered version of this image is solid black" decoding="async" loading="lazy" /></a><figcaption>The non-dithered version of this image is solid black</figcaption></figure><h2 id="source-code">Source Code</h2>
<p>The latest source code for this demonstration (which will be
extended over time to include additional algorithms) can be
found at our <a href="https://github.com/cyotek/Dithering" rel="external nofollow noopener">GitHib page</a>.</p>
<p>The source code from the time this article was created is
available from the link below, however may not be fully up to
date.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2015-06-06 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/dithering-an-image-using-the-burkes-algorithm-in-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comDithering an image using the Floyd‑Steinberg algorithm in C#urn:uuid:24c8a964-e1ca-424d-9200-e58642d4d0982015-06-06T11:01:41Z2015-06-06T11:01:41Z<p>In my <a href="/post/an-introduction-to-dithering-images">previous introductory post</a>, I briefly described the
concept of dithering an image. In this article, I will describe
how to dither an image in C# using the Floyd–Steinberg
algorithm.</p>
<h2 id="the-demo-application">The Demo Application</h2>
<p>For this series of articles, I'll be using the same demo
application, the source of which can be found on <a href="https://github.com/cyotek/Dithering" rel="external nofollow noopener">GitHib</a>.
There's a few things about the demo I wish to cover before I get
onto the actual topic of dithering.</p>
<p>Algorithms can be a tricky thing to learn about, and so I don't
want the demo to be horribly complicated by including a
additional complex code unrelated to dithering. At the same
time, bitmap operations are expensive, so there is already some
advanced code present.</p>
<p>As I mentioned in my introduction, dithering is part of a
process. For this demo, the process will be converting a 32bit
image into a 1bit image as this is the simplest conversion I can
stick in a demo. <strong>This does not mean that the dithering
techniques can only be used to convert an image to black and
white, it is simply to make the demo easier to understand</strong>.</p>
<p>I have however broken this rule when it comes to the actual
image processing. The .NET <code>Bitmap</code> object offers <code>SetPixel</code> and
<code>GetPixel</code> methods. You should try and avoid using these as they
will utterly destroy the performance of whatever it is you are
trying to do. The best way of accessing pixel data is to access
it directly using <code>Bitmap.LockBits</code>, pointer manipulation, then
<code>Bitmap.UnlockBits</code>. In this demo, I use this approach to create
a custom array of colours, and while it is very fast, if you
want better performance it is probably better to manipulate
individual bytes via pointers. However, this requires much more
complex code to account for different colour depths and is well
beyond the scope of this demo.</p>
<blockquote>
<p>I did a version of the demo program using <code>SetPixel</code> and
<code>GetPixel</code>. Saying it was slow was an understatement. Just
pretend these methods don't exist!</p>
</blockquote>
<h2 id="converting-a-colour-to-black-or-white">Converting a colour to black or white</h2>
<p>In order to convert the image to 2 colours, I scan each pixel
and convert it to grayscale. If the grayscale value is around
50% (127 in .NET's 0 - 255 range), then the transformed pixel
will be black, otherwise it will be white.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">byte</span> gray<span class="symbol">;</span>

gray <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span><span class="number">0.299</span> <span class="symbol">*</span> pixel<span class="symbol">.</span>R <span class="symbol">+</span> <span class="number">0.587</span> <span class="symbol">*</span> pixel<span class="symbol">.</span>G <span class="symbol">+</span> <span class="number">0.114</span> <span class="symbol">*</span> pixel<span class="symbol">.</span>B<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">return</span> gray <span class="symbol">&lt;</span> <span class="number">128</span> <span class="symbol">?</span> <span class="keyword">new</span> ArgbColor<span class="symbol">(</span>pixel<span class="symbol">.</span>A<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span> <span class="symbol">:</span> <span class="keyword">new</span> ArgbColor<span class="symbol">(</span>pixel<span class="symbol">.</span>A<span class="symbol">,</span> <span class="number">255</span><span class="symbol">,</span> <span class="number">255</span><span class="symbol">,</span> <span class="number">255</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>This actually creates quite a nice result from our demonstration
image, but results will vary depending on the image.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/dithering-threshold.png" class="gallery" title="An example of 1bit conversion via a threshold" ><img src="https://images.cyotek.com/image/thumbnail/devblog/dithering-threshold.png" alt="An example of 1bit conversion via a threshold" decoding="async" loading="lazy" /></a><figcaption>An example of 1bit conversion via a threshold</figcaption></figure><h2 id="floydsteinberg-dithering">Floyd‑Steinberg dithering</h2>
<p>The Floyd‑Steinberg algorithm is an error diffusion algorithm,
meaning for each pixel an &quot;<em>error</em>&quot; is generated and then
distributed to four pixels around the surrounding the current
pixel. Each of the four offset pixels has a different weight -
the error is multiplied by the weight, divided by 16 and then
added to the existing value of the offset pixel.</p>
<p>As a picture is definitely worth a thousand words, the diagram
below shows the weights.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/dithering-floyd-steinberg-diagram.png" class="gallery" title="How the error of the current pixel is diffused to its neighbours" ><img src="https://images.cyotek.com/image/devblog/dithering-floyd-steinberg-diagram.png" alt="How the error of the current pixel is diffused to its neighbours" decoding="async" loading="lazy" /></a><figcaption>How the error of the current pixel is diffused to its neighbours</figcaption></figure>
<ul>
<li>7 for the pixel to the right of the current pixel</li>
<li>3 for the pixel below and to the left</li>
<li>5 for the pixel below</li>
<li>1 for the pixel below and to the right</li>
</ul>
<h2 id="calculating-the-error">Calculating the error</h2>
<p>The error calculation in our demonstration program is simple,
although in actuality it's 3 errors, one for the red, green and
blue channels. All we are doing is taking the difference between
the channels transformed value from the original value.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
redError <span class="symbol">=</span> originalPixel<span class="symbol">.</span>R <span class="symbol">-</span> transformedPixel<span class="symbol">.</span>R<span class="symbol">;</span>
blueError <span class="symbol">=</span> originalPixel<span class="symbol">.</span>G <span class="symbol">-</span> transformedPixel<span class="symbol">.</span>G<span class="symbol">;</span>
greenError <span class="symbol">=</span> originalPixel<span class="symbol">.</span>B <span class="symbol">-</span> transformedPixel<span class="symbol">.</span>B<span class="symbol">;</span>
</pre>
</figure>
<h2 id="applying-the-error">Applying the error</h2>
<p>Once we have our error, it's just a case of getting each
neighbouring pixels to adjust, and applying each error the
appropriate channel. The <code>ToByte</code> extension method in the
snippet below simply converts the calculated integer to a byte,
while ensuring it is in the 0-255 range.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
offsetPixel<span class="symbol">.</span>R <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>R <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>redError <span class="symbol">*</span> <span class="number">7</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
offsetPixel<span class="symbol">.</span>G <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>G <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>greenError <span class="symbol">*</span> <span class="number">7</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
offsetPixel<span class="symbol">.</span>B <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>B <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>blueError <span class="symbol">*</span> <span class="number">7</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<blockquote>
<h3 id="bit-shifting-for-division">Bit shifting for division</h3>
<p>As 16 is a power of two, it means we can use bit shifting to
do the division. While this may be slightly less readable if
you aren't hugely familiar with it, it ought to be faster. I
did a quick benchmark test using a sample of 1 million, 10
million and then 100 million random numbers. Using bit
shifting to divide each sample by 16 took roughly two thirds
of the time it took to do the same sets with integer division.
This is probably a useful thing to know when performing
thousands of operations processing an image.</p>
</blockquote>
<h2 id="dithering-a-single-pixel">Dithering a single pixel</h2>
<p>Here's the code used by the demonstration program to dither a
single source pixel - the <code>ArbColor</code> data representing each
pixel is stored in a one-dimensional array using <a href="/post/converting-2d-arrays-to-1d-and-accessing-as-either-2d-or-1d">row-major
order</a>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
ArgbColor offsetPixel<span class="symbol">;</span>
<span class="keyword">int</span> redError<span class="symbol">;</span>
<span class="keyword">int</span> blueError<span class="symbol">;</span>
<span class="keyword">int</span> greenError<span class="symbol">;</span>
<span class="keyword">int</span> offsetIndex<span class="symbol">;</span>
<span class="keyword">int</span> index<span class="symbol">;</span>

index <span class="symbol">=</span> y <span class="symbol">*</span> width <span class="symbol">+</span> x<span class="symbol">;</span>
redError <span class="symbol">=</span> originalPixel<span class="symbol">.</span>R <span class="symbol">-</span> transformedPixel<span class="symbol">.</span>R<span class="symbol">;</span>
blueError <span class="symbol">=</span> originalPixel<span class="symbol">.</span>G <span class="symbol">-</span> transformedPixel<span class="symbol">.</span>G<span class="symbol">;</span>
greenError <span class="symbol">=</span> originalPixel<span class="symbol">.</span>B <span class="symbol">-</span> transformedPixel<span class="symbol">.</span>B<span class="symbol">;</span>

<span class="keyword">if</span> <span class="symbol">(</span>x <span class="symbol">+</span> <span class="number">1</span> <span class="symbol">&lt;</span> width<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// right</span>
 offsetIndex <span class="symbol">=</span> index <span class="symbol">+</span> <span class="number">1</span><span class="symbol">;</span>
 offsetPixel <span class="symbol">=</span> original<span class="symbol">[</span>offsetIndex<span class="symbol">]</span><span class="symbol">;</span>
 offsetPixel<span class="symbol">.</span>R <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>R <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>redError <span class="symbol">*</span> <span class="number">7</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 offsetPixel<span class="symbol">.</span>G <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>G <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>greenError <span class="symbol">*</span> <span class="number">7</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 offsetPixel<span class="symbol">.</span>B <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>B <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>blueError <span class="symbol">*</span> <span class="number">7</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 original<span class="symbol">[</span>offsetIndex<span class="symbol">]</span> <span class="symbol">=</span> offsetPixel<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">if</span> <span class="symbol">(</span>y <span class="symbol">+</span> <span class="number">1</span> <span class="symbol">&lt;</span> height<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>x <span class="symbol">-</span> <span class="number">1</span> <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// left and down</span>
 offsetIndex <span class="symbol">=</span> index <span class="symbol">+</span> width <span class="symbol">-</span> <span class="number">1</span><span class="symbol">;</span>
 offsetPixel <span class="symbol">=</span> original<span class="symbol">[</span>offsetIndex<span class="symbol">]</span><span class="symbol">;</span>
 offsetPixel<span class="symbol">.</span>R <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>R <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>redError <span class="symbol">*</span> <span class="number">3</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 offsetPixel<span class="symbol">.</span>G <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>G <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>greenError <span class="symbol">*</span> <span class="number">3</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 offsetPixel<span class="symbol">.</span>B <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>B <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>blueError <span class="symbol">*</span> <span class="number">3</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 original<span class="symbol">[</span>offsetIndex<span class="symbol">]</span> <span class="symbol">=</span> offsetPixel<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="comment">// down</span>
 offsetIndex <span class="symbol">=</span> index <span class="symbol">+</span> width<span class="symbol">;</span>
 offsetPixel <span class="symbol">=</span> original<span class="symbol">[</span>offsetIndex<span class="symbol">]</span><span class="symbol">;</span>
 offsetPixel<span class="symbol">.</span>R <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>R <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>redError <span class="symbol">*</span> <span class="number">5</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 offsetPixel<span class="symbol">.</span>G <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>G <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>greenError <span class="symbol">*</span> <span class="number">5</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 offsetPixel<span class="symbol">.</span>B <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>B <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>blueError <span class="symbol">*</span> <span class="number">5</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 original<span class="symbol">[</span>offsetIndex<span class="symbol">]</span> <span class="symbol">=</span> offsetPixel<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>x <span class="symbol">+</span> <span class="number">1</span> <span class="symbol">&lt;</span> width<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// right and down</span>
 offsetIndex <span class="symbol">=</span> index <span class="symbol">+</span> width <span class="symbol">+</span> <span class="number">1</span><span class="symbol">;</span>
 offsetPixel <span class="symbol">=</span> original<span class="symbol">[</span>offsetIndex<span class="symbol">]</span><span class="symbol">;</span>
 offsetPixel<span class="symbol">.</span>R <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>R <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>redError <span class="symbol">*</span> <span class="number">1</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 offsetPixel<span class="symbol">.</span>G <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>G <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>greenError <span class="symbol">*</span> <span class="number">1</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 offsetPixel<span class="symbol">.</span>B <span class="symbol">=</span> <span class="symbol">(</span>offsetPixel<span class="symbol">.</span>B <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span>blueError <span class="symbol">*</span> <span class="number">1</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>ToByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 original<span class="symbol">[</span>offsetIndex<span class="symbol">]</span> <span class="symbol">=</span> offsetPixel<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Much of the code is duplicated, with a different co-efficient
for the multiplication, and (importantly!) guards to skip pixels
when the current pixel is either the first or last pixel in the
row, or is within the final row.</p>
<h2 id="and-the-result">And the result?</h2>
<p>The image below shows our sample image dithered using the
Floyd–Steinberg algorithm. It doesn't look too bad!</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/dithering-floyd-steinberg.png" class="gallery" title="The final result - a bitmap transformed with Floyd–Steinberg dithering" ><img src="https://images.cyotek.com/image/thumbnail/devblog/dithering-floyd-steinberg.png" alt="The final result - a bitmap transformed with Floyd–Steinberg dithering" decoding="async" loading="lazy" /></a><figcaption>The final result - a bitmap transformed with Floyd–Steinberg dithering</figcaption></figure>
<p>By changing the threshold at which colours are converted to
black or white, we can affect the output of the dithering even
if the conversion is to solid black.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/dithering-floyd-steinberg-extreme.png" class="gallery" title="A slightly more extreme black and white conversion still dithers fairly well" ><img src="https://images.cyotek.com/image/thumbnail/devblog/dithering-floyd-steinberg-extreme.png" alt="A slightly more extreme black and white conversion still dithers fairly well" decoding="async" loading="lazy" /></a><figcaption>A slightly more extreme black and white conversion still dithers fairly well</figcaption></figure>
<p><em>(Note: The thumbnail hasn't resized well, the actual size
version looks better)</em></p>
<h2 id="source-code">Source Code</h2>
<p>The latest source code for this demonstration (which will be
extended over time to include additional algorithms) can be
found at our <a href="https://github.com/cyotek/Dithering" rel="external nofollow noopener">GitHib page</a>.</p>
<p>The source code from the time this article was created is
available from the link below, however may not be fully up to
date.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2015-06-06 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/dithering-an-image-using-the-floyd-steinberg-algorithm-in-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comQuick and simple sprite sheet packer sourceurn:uuid:09c11be3-04fb-406e-803c-fe92fe2b6f9e2015-05-24T16:56:47Z2015-05-24T16:56:47Z<p>For some time now, I've started moving away from monolithic and
complex GUI tools in favour of more streamlined command line
interfaces, generally using text based inputs like JSON or YAML.</p>
<p>While there is still a need for GUI tools for performing complex
actions, sometimes you just want something simple without a load
of bells and whistles. I especially make use of CLI tools in
build processes, and it is so much easier when such tools are
simple <code>exe</code> files that can be deployed via the package manager
of your choice, rather than requiring dozens of DLL's, registry
settings and who knows what.</p>
<p>While my own tools are certainly guilty of some of the above,
they do at least include CLI tools, some of which are powerful
in their own right (and perhaps some not powerful enough).
Sometimes though, even that is too much - such tools generally
have dependencies of their own (although much fewer than the GUI
versions), and there's no easy way to just get CLI versions
without the extra components.</p>
<p>Recently I had need of generating some sprite sheets for use
with HTML pages as part of a build process, but installing
<a href="https://cyotek.com/cyotek-spriter">Spriter</a> was going to be overkill as I didn't
need anything it offers bar the absolute core - pack some images
together and generate some usable CSS. So I opted to create a
small console tool to do just that, and have released the
source.</p>
<h3 id="about-the-tool">About the tool</h3>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/sprpack.png" class="gallery" title="A simple example using simple codes" ><img src="https://images.cyotek.com/image/thumbnail/devblog/sprpack.png" alt="A simple example using simple codes" decoding="async" loading="lazy" /></a><figcaption>A simple example using simple codes</figcaption></figure>
<p>The tool, <code>sprpack.exe</code>, is a stand alone (well, it needs
Microsoft .NET 4, so as stand alone as you can get with that
involved) tool that you can point at a given directory and it
will suck in the image files and spit out a sprite sheet
containing all the images neatly laid out to take the least
amount of space possible. It will also create some basic CSS if
required.</p>
<p>However, there's obviously a big caveat - it will do that one
job and it will do it well enough. But it doesn't include any
form of advanced functionality, for example templates to control
output CSS, advanced file patterns for including only specify
files, layout options, in fact pretty much no options at all. It
has one(ish) job.</p>
<h3 id="options">Options</h3>
<p>The following list details the arguments that <code>sprpack.exe</code>
accepts. All are optional.</p>
<ul>
<li><code>path</code> - specifies the path to process. Defaults to the
current directory</li>
<li><code>mask</code> - comma separated list of file masks to search.
Defaults to <code>*.png</code></li>
<li><code>out</code> - the file name of the sprite sheet graphic. Defaults to
<code>sheet.png</code></li>
<li><code>css</code> - the file name where the CSS will be written. If not
specified, CSS will not be generated</li>
<li><code>class</code> - the base CSS class name. Ignored if <code>/css</code> is not
set</li>
</ul>
<p>As you can see, it is a very simple affair!</p>
<blockquote>
<p><strong>Note!</strong> The tool will overwrite output files without
prompting</p>
</blockquote>
<h3 id="source-code">Source Code</h3>
<p>The source code for the simple packer can be found on the
<a href="https://github.com/cyotek/SpriteSheetPacker" rel="external nofollow noopener">project page</a>.</p>
<p>I haven't done it yet, but I'll probably add a NuGet package so
I can more easily drop it into a build process. At this point I
don't know if I'll expand the source to include any more options
but I suppose I'll build a few extra ones in over time.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2015-05-24 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/quick-and-simple-sprite-sheet-packer-source .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comColorEcho - adding colour to echoed batch texturn:uuid:532af147-0199-42a8-a1c4-f2b7e717af062015-04-09T20:13:47Z2015-04-09T20:13:47Z<p>We use batch files for... well, pretty much everything. From
simple files that simple optimize modified graphics, to the
tendril-like files that build our software. For some time now,
I've been using <code>cecho.exe</code> from a <a href="http://www.codeproject.com/Articles/17033/Add-Colors-to-Batch-Files" rel="external nofollow noopener">CodeProject article</a> so
that I highlight errors and successes. And this has worked fine
- as long as I was running the scripts in a console window.</p>
<p>However, when <a href="http://www.rickglos.com/post/How-to-run-windows-batch-files-from-Visual-Studio-2010-Solution-Explorer.aspx" rel="external nofollow noopener">running batch files through Visual Studio</a> any
output from <code>cecho.exe</code> simply wasn't displayed. That was
something I ignored. However, over the last couple of days I've
been finally setting up a CI server and have been testing both
<a href="http://jenkins-ci.org/" rel="external nofollow noopener">Jenkins</a> and <a href="https://www.jetbrains.com/teamcity/" rel="external nofollow noopener">TeamCity</a> and I had the exact same
behaviour of blank lines when running builds in both of these
tools - that I can't ignore.</p>
<p>I had a cursory glance through the C++ code from the original
article and while it looks fine, I make no pretence of being a
C++ developer. Given that the past two weeks I've been working
with PHP and F#, I not in a hurry to study a 3rd extra language!</p>
<p>I had observed that my own console tools which used coloured
output appeared perfectly well in Visual Studio, Jenkins and
TeamCity so I decided I would replicate the <code>cecho.exe</code> tool
using C#.</p>
<h2 id="using-the-tool">Using the tool</h2>
<p>As I'm not in a hurry to change all the batch files calling
<code>cecho</code> I've kept the exact same syntax (and for the time being
the same annoying behaviour regarding having to manually reset
colours and include line breaks).</p>
<ul>
<li><code>{XX}</code>: colours coded as two hexadecimal digits. E.g., <code>{0A}</code>
light green</li>
<li><code>{color}</code>: colour information as understandable text. E.g.,
<code>{light red on black}</code></li>
<li><code>{\n}</code>: New line character</li>
<li><code>{\t}</code>: Tab character</li>
<li><code>{\u0000}</code>: Unicode character code</li>
<li><code>{{</code>: escape character <code>{</code></li>
<li><code>{#}</code>: restore foreground colour</li>
<li><code>{##}</code>: restore foreground and background colour</li>
</ul>
<p>Colours are defined as</p>
<ul>
<li><code>0</code>: Black (<code>black</code>)</li>
<li><code>1</code>: Dark Blue (<code>navy</code>, <code>dark blue</code>)</li>
<li><code>2</code>: Dark Green (<code>green</code>, <code>dark green</code>)</li>
<li><code>3</code>: Dark Cyan (<code>teal</code>, <code>dark cyan</code>)</li>
<li><code>4</code>: Dark Red (<code>maroon</code>, <code>dark red</code>)</li>
<li><code>5</code>: Dark Magenta (<code>purple</code>, <code>dark magenta</code>)</li>
<li><code>6</code>: Dark Yellow (<code>olive</code>, <code>brown</code>, <code>dark yellow</code>)</li>
<li><code>7</code>: Gray (<code>silver</code>, <code>light gray</code>, <code>light grey</code>)</li>
<li><code>8</code>: Dark Gray (<code>gray</code>, <code>grey</code>, <code>dark gray</code>, <code>dark grey</code>)</li>
<li><code>9</code>: Blue (<code>blue</code>, <code>light blue</code>)</li>
<li><code>A</code>: Green (<code>lime</code>, <code>light green</code>)</li>
<li><code>B</code>: Cyan (<code>aqua</code>, <code>light cyan</code>)</li>
<li><code>C</code>: Red (<code>red</code>, <code>light red</code>)</li>
<li><code>D</code>: Magenta (<code>fuschia</code>, <code>magenta</code>, <code>light magenta</code>)</li>
<li><code>E</code>: Yellow (<code>yellow</code>)</li>
<li><code>F</code>: White (<code>white</code>)</li>
</ul>
<p>The names in brackets are alternatives you can use for
understandable text.</p>
<h2 id="note">Note</h2>
<p>For backwards compatibility, this program behaves the same way
as the original <code>cecho</code> - lines are not terminated with a
carriage return and the colours are not reset. Therefore you
should ensure you include <code>{#}</code> or <code>{##}</code> and <code>{\n}</code> at the end
of your statements.</p>
<p>Or of course, just modify the source to do this automatically if
you don't need compatibility.</p>
<h2 id="samples">Samples</h2>
<p>This first example uses the shorthand notation to change
<code>ERROR:</code> into red.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
cecho {0c}ERROR:{#} Signing failed for program1.exe, retrying{\n}
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/colorecho-1a.png" class="gallery" title="Basic example using hex codes" ><img src="https://images.cyotek.com/image/thumbnail/devblog/colorecho-1a.png" alt="Basic example using hex codes" decoding="async" loading="lazy" /></a><figcaption>Basic example using hex codes</figcaption></figure>
<p>This example uses named colours instead of hex codes.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
cecho This {yellow <span class="keyword">on</span> teal}word{##} is yellow <span class="keyword">on</span> a teal background{\n}
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/colorecho-1b.png" class="gallery" title="Basic example using colour names" ><img src="https://images.cyotek.com/image/thumbnail/devblog/colorecho-1b.png" alt="Basic example using colour names" decoding="async" loading="lazy" /></a><figcaption>Basic example using colour names</figcaption></figure>
<p>This final example prints out an extended character.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
cecho {\u2593}
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/colorecho-1c.png" class="gallery" title="Printing extended characters" ><img src="https://images.cyotek.com/image/thumbnail/devblog/colorecho-1c.png" alt="Printing extended characters" decoding="async" loading="lazy" /></a><figcaption>Printing extended characters</figcaption></figure><h2 id="getting-the-source">Getting the source</h2>
<p>The source code this tool is available on our <a href="https://github.com/cyotek/ColorEcho" rel="external nofollow noopener">GitHub</a> page.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2015-04-09 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/colorecho-adding-colour-to-echoed-batch-text .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comHosting a ColorGrid control in a ToolStripurn:uuid:ead00749-49e1-4b1f-bf44-81d222f6c89e2015-02-28T17:49:06Z2015-02-28T17:49:06Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/toolstrip-colorgrid-1b.png" class="gallery" title="Displaying a ColorGrid control in the drop down of a ToolStrip" ><img src="https://images.cyotek.com/image/thumbnail/devblog/toolstrip-colorgrid-1b.png" alt="Displaying a ColorGrid control in the drop down of a ToolStrip" decoding="async" loading="lazy" /></a><figcaption>Displaying a ColorGrid control in the drop down of a ToolStrip</figcaption></figure>
<p>The <code>ColorGrid</code> control is a fairly useful control for selecting
from a predefined list of colours. However, it can take up quite
a bit of screen real estate depending on how many colours it
contains. This article describes how you can host a <code>ColorGrid</code>
in a standard <code>ToolStrip</code> control, providing access to both the
<code>ColorGrid</code> and the <code>ColorPickerDialog</code>.</p>
<p>The <code>ToolStrip</code> control makes this surprisingly easy to
accomplish. First, we're going to need a component to host the
<code>ColorGrid</code> which we can ably achieve by inheriting from
<code>ToolStripDropDown</code>. So lets get started!</p>
<h2 id="the-drop-down">The Drop Down</h2>
<p>The <code>ToolStripDropDown</code> class &quot;<em>represents a control that allows
the user to select a single item from a list that is displayed
when the user clicks a ToolStripDropDownButton</em>&quot; and is just
what we need to save use reinventing at least one wheel. This
class will essentially manage the interactions to the
<code>ColorGrid</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">class</span> ToolStripColorPickerDropDown <span class="symbol">:</span> ToolStripDropDown
<span class="symbol">{</span>
 <span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="symbol">[</span>DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> ColorGrid Host <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">private</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> ToolStripColorPickerDropDown<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Host <span class="symbol">=</span> <span class="keyword">new</span> ColorGrid
 <span class="symbol">{</span>
 AutoSize <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">,</span>
 Columns <span class="symbol">=</span> <span class="number">10</span><span class="symbol">,</span>
 Palette <span class="symbol">=</span> ColorPalette<span class="symbol">.</span>Office<span class="number">2010</span>
 <span class="symbol">}</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Host<span class="symbol">.</span>MouseClick <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>HostMouseClickHandler<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Host<span class="symbol">.</span>KeyDown <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>HostKeyDownHandler<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> ToolStripControlHost<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Host<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>When the <code>ToolStripColorPickerDropDown</code> is created we
automatically create a <code>ColorGrid</code> control, set some default
properties and then add it to the <code>ToolStripItemCollection</code> of
the <code>ToolStripDropDown</code>.</p>
<p>If we simply bound the <code>ColorChanged</code> event of the <code>ColorGrid</code>
to select a colour, then you'd probably have great difficulty in
using the control properly - keyboard support is immediately out
of the question, and even some mouse support would be affected.</p>
<p>For this reason, I'm binding the <code>MouseClick</code> and <code>KeyDown</code>
events to allow for a nicer editing experience. I'll also add a
<code>Color</code> property so that I can track color independently of the
<code>ColorGrid</code>, to enable cancel support.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> HostKeyDownHandler<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> KeyEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">switch</span> <span class="symbol">(</span>e<span class="symbol">.</span>KeyCode<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> Keys<span class="symbol">.</span>Enter<span class="symbol">:</span>
 <span class="keyword">this</span><span class="symbol">.</span>Close<span class="symbol">(</span>ToolStripDropDownCloseReason<span class="symbol">.</span>Keyboard<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Color <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Host<span class="symbol">.</span>Color<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> Keys<span class="symbol">.</span>Escape<span class="symbol">:</span>
 <span class="keyword">this</span><span class="symbol">.</span>Close<span class="symbol">(</span>ToolStripDropDownCloseReason<span class="symbol">.</span>Keyboard<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>In the key handler, I'm closing the drop down if either the
<kbd>Enter</kbd> or <kbd>Escape</kbd> keys are pressed. If it's
the former, we update our true <code>Color</code> property. If the latter,
we don't. This way a user can cancel the drop down without
updating anything.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> HostMouseClickHandler<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 ColorHitTestInfo info<span class="symbol">;</span>

 info <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Host<span class="symbol">.</span>HitTest<span class="symbol">(</span>e<span class="symbol">.</span>Location<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>info<span class="symbol">.</span>Index <span class="symbol">!=</span> ColorGrid<span class="symbol">.</span>InvalidIndex<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Close<span class="symbol">(</span>ToolStripDropDownCloseReason<span class="symbol">.</span>ItemClicked<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Color <span class="symbol">=</span> info<span class="symbol">.</span>Color<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The mouse handling is fairly similar, with the exception we
don't cover a cancel case. If the user clicks outside the bounds
of the drop down it will be automatically closed.</p>
<p>Here we do a hit test, and if a colour was clicked, we close the
drop down and update the internal colour.</p>
<blockquote>
<p>Notice that I close the drop down <em>before</em> setting the colour.
This is deliberate, as originally I had it the other way
around (as would seem more logical). The problem with that is
that change events will be raised for the modified colour -
but the drop down palette is still visible on the screen which
I found a hindrance while debugging.</p>
</blockquote>
<p>I also noted that when the drop down opened, the <code>ColorGrid</code> did
not have focus. That was easy enough to resolve by overriding
<code>OnOpened</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnOpened<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnOpened<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Host<span class="symbol">.</span>Focus<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Now that the drop down is handled, we need a new <code>ToolStripItem</code>
to interact with it.</p>
<h2 id="a-custom-toolstripsplitbutton">A custom ToolStripSplitButton</h2>
<p>For the actual button, I choose to inherit from
<code>ToolStripSplitButton</code>. This gives me two interactions, a drop
down, and a button. We will display the <code>ColorGrid</code> via the drop
down, and the <code>ColorPickerDialog</code> via the button, giving the
user both a simple and an advanced way of choosing a colour.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>DefaultProperty<span class="symbol">(</span><span class="string">&quot;Color&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="symbol">[</span>DefaultEvent<span class="symbol">(</span><span class="string">&quot;ColorChanged&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="symbol">[</span>ToolStripItemDesignerAvailability<span class="symbol">(</span>ToolStripItemDesignerAvailability<span class="symbol">.</span>ToolStrip <span class="symbol">|</span> ToolStripItemDesignerAvailability<span class="symbol">.</span>StatusStrip<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">class</span> ToolStripColorPickerSplitButton <span class="symbol">:</span> ToolStripSplitButton
<span class="symbol">{</span>
 <span class="keyword">public</span> ToolStripColorPickerSplitButton<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Color <span class="symbol">=</span> Color<span class="symbol">.</span>Black<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Data&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="symbol">[</span>DefaultValue<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>Color<span class="symbol">)</span><span class="symbol">,</span> <span class="string">&quot;Black&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">virtual</span> Color Color
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _color<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Color <span class="symbol">!=</span> value<span class="symbol">)</span>
 <span class="symbol">{</span>
 _color <span class="symbol">=</span> value<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>OnColorChanged<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>As with the <code>ToolStripColorPickerDropDown</code> class, our new
<code>ToolStripColorPickerSplitButton</code> also has a dedicated colour
property. The reason for this is I don't want to create the drop
down component unless it's actually going to be used. After all,
why waste resources creating objects we're not going to need?</p>
<p>The <code>ToolStripSplitButton</code> class calls <code>CreateDefaultDropDown</code>
in order to set the <code>DropDown</code> property if it doesn't have a
value. We'll override this to create our custom drop down.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> ToolStripColorPickerDropDown _dropDown<span class="symbol">;</span>

<span class="keyword">protected</span> <span class="keyword">override</span> ToolStripDropDown CreateDefaultDropDown<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>EnsureDropDownIsCreated<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> _dropDown<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> EnsureDropDownIsCreated<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_dropDown <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _dropDown <span class="symbol">=</span> <span class="keyword">new</span> ToolStripColorPickerDropDown<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 _dropDown<span class="symbol">.</span>ColorChanged <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>DropDownColorChangedHandler<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>In order to allow the developer to customise the <code>ColorGrid</code> if
required, we need to expose the control so they can access it.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="symbol">[</span>DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> ColorGrid Host
<span class="symbol">{</span>
 <span class="keyword">get</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>EnsureDropDownIsCreated<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> _dropDown<span class="symbol">.</span>Host<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The <code>Browsable</code> attribute prevents it from appearing in property
grids, while <code>DesignerSerializationVisibility</code> prevents the
property from being serialized.</p>
<p>Both the <code>Host</code> property and the <code>CreateDefaultDropDown</code>method
make use of the private <code>EnsureDropDownIsCreated</code> method, so
that the drop down is created on demand.</p>
<p>This means you can only customise the control from actual code
(such as from your forms <code>Load</code> event, not by setting properties
on the designer.</p>
<h2 id="toolstrip-designer-support">ToolStrip Designer Support</h2>
<p>As long as the <code>ToolStripColorPickerSplitButton</code> is <code>public</code>,
the existing designers will automatically detect it and allow
you to add them to your <code>ToolStrip</code> or <code>StatusStrip</code> controls.
(Although interestingly it seems to automatically remove the
&quot;ToolStrip&quot; prefix).</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/toolstrip-colorgrid-1c.png" class="gallery" title="Designer support is handled for you" ><img src="https://images.cyotek.com/image/thumbnail/devblog/toolstrip-colorgrid-1c.png" alt="Designer support is handled for you" decoding="async" loading="lazy" /></a><figcaption>Designer support is handled for you</figcaption></figure>
<p>There is a caveat however - the
<code>ToolStripColorPickerSplitButton</code> class must be <code>public</code>.
Originally I had it as <code>internal</code> (as it is part of a
non-library project) but then it never showed up in designers.</p>
<blockquote>
<p>If you display the drop down at design time, you'll find that
you can continue to add items to the drop down underneath the
hosted <code>ColorGrid</code>. I couldn't find a way to disable this,
unless I created a new designer myself.</p>
</blockquote>
<h2 id="displaying-the-colorpickerdialog">Displaying the ColorPickerDialog</h2>
<p>Once the <code>DropDown</code> property of a <code>ToolStripSplitButton</code> has
been set, it will take care of the details of showing it, so
there's nothing more for us to do there. However, we do need to
add some code to display the <code>ColorPickerDialog</code> if a user
clicks the main body of the button. This can be done by
overriding <code>OnButtonClick</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnButtonClick<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnButtonClick<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>ColorPickerDialog dialog <span class="symbol">=</span> <span class="keyword">new</span> ColorPickerDialog<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 dialog<span class="symbol">.</span>Color <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Color<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>dialog<span class="symbol">.</span>ShowDialog<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>GetCurrentParent<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span> <span class="symbol">==</span> DialogResult<span class="symbol">.</span>OK<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Color <span class="symbol">=</span> dialog<span class="symbol">.</span>Color<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="custom-painting">Custom Painting</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/toolstrip-colorgrid-1a.png" class="gallery" title="A series of ToolStripColorPickerSplitButton's in different styles to demonstrate the custom painting" ><img src="https://images.cyotek.com/image/thumbnail/devblog/toolstrip-colorgrid-1a.png" alt="A series of ToolStripColorPickerSplitButton's in different styles to demonstrate the custom painting" decoding="async" loading="lazy" /></a><figcaption>A series of ToolStripColorPickerSplitButton's in different styles to demonstrate the custom painting</figcaption></figure>
<p>Typically, buttons which display an editor for a colour also
display a preview of the active colour as a thick band
underneath the buttons icon. Although the <code>ToolStripSplitButton</code>
makes this a little harder than it should, we can add this to
our <code>ToolStripColorPickerSplitButton</code> class by overriding the
<code>OnPaint</code> method.</p>
<p>The difficulty comes from the fact that the class doesn't give
us access to its internal layout information, so we have to
guess where the image is in order to draw our line. As there are
quite a few display styles for these items, it can be a little
tricky.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnPaint<span class="symbol">(</span>PaintEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 Rectangle underline<span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnPaint<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 underline <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetUnderlineRectangle<span class="symbol">(</span>e<span class="symbol">.</span>Graphics<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Brush brush <span class="symbol">=</span> <span class="keyword">new</span> SolidBrush<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Color<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>FillRectangle<span class="symbol">(</span>brush<span class="symbol">,</span> underline<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">private</span> Rectangle GetUnderlineRectangle<span class="symbol">(</span>Graphics g<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> x<span class="symbol">;</span>
 <span class="keyword">int</span> y<span class="symbol">;</span>
 <span class="keyword">int</span> w<span class="symbol">;</span>
 <span class="keyword">int</span> h<span class="symbol">;</span>

 <span class="comment">// TODO: These are approximate values and may not work with different font sizes or image sizes etc</span>

 h <span class="symbol">=</span> <span class="number">4</span><span class="symbol">;</span> <span class="comment">// static height!</span>
 x <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ContentRectangle<span class="symbol">.</span>Left<span class="symbol">;</span>
 y <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ContentRectangle<span class="symbol">.</span>Bottom <span class="symbol">-</span> <span class="symbol">(</span>h <span class="symbol">+</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>DisplayStyle <span class="symbol">==</span> ToolStripItemDisplayStyle<span class="symbol">.</span>ImageAndText <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>Image <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Text<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> innerHeight<span class="symbol">;</span>

 innerHeight <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Height <span class="symbol">-</span> h<span class="symbol">;</span>

 <span class="comment">// got both an image and some text to deal with</span>
 w <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Width<span class="symbol">;</span>
 y <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ButtonBounds<span class="symbol">.</span>Top <span class="symbol">+</span> innerHeight <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ButtonBounds<span class="symbol">.</span>Height <span class="symbol">-</span> <span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Height<span class="symbol">)</span> <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">switch</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>TextImageRelation<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> TextImageRelation<span class="symbol">.</span>TextBeforeImage<span class="symbol">:</span>
 x <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ButtonBounds<span class="symbol">.</span>Right <span class="symbol">-</span> <span class="symbol">(</span>w <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>ButtonBounds<span class="symbol">.</span>Left <span class="symbol">+</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> TextImageRelation<span class="symbol">.</span>ImageAboveText<span class="symbol">:</span>
 x <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ButtonBounds<span class="symbol">.</span>Left <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ButtonBounds<span class="symbol">.</span>Width <span class="symbol">-</span> <span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Width<span class="symbol">)</span> <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>
 y <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ButtonBounds<span class="symbol">.</span>Top <span class="symbol">+</span> innerHeight <span class="symbol">+</span> <span class="number">2</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> TextImageRelation<span class="symbol">.</span>TextAboveImage<span class="symbol">:</span>
 x <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ButtonBounds<span class="symbol">.</span>Left <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ButtonBounds<span class="symbol">.</span>Width <span class="symbol">-</span> <span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Width<span class="symbol">)</span> <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>
 y <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ContentRectangle<span class="symbol">.</span>Bottom <span class="symbol">-</span> h<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> TextImageRelation<span class="symbol">.</span>Overlay<span class="symbol">:</span>
 x <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ButtonBounds<span class="symbol">.</span>Left <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ButtonBounds<span class="symbol">.</span>Width <span class="symbol">-</span> <span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Width<span class="symbol">)</span> <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>
 y <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ButtonBounds<span class="symbol">.</span>Top <span class="symbol">+</span> innerHeight <span class="symbol">+</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ButtonBounds<span class="symbol">.</span>Height <span class="symbol">-</span> <span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Height<span class="symbol">)</span> <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>DisplayStyle <span class="symbol">==</span> ToolStripItemDisplayStyle<span class="symbol">.</span>Image <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>Image <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// just the image</span>
 w <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Width<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>DisplayStyle <span class="symbol">==</span> ToolStripItemDisplayStyle<span class="symbol">.</span>Text <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Text<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// just the text</span>
 w <span class="symbol">=</span> TextRenderer<span class="symbol">.</span>MeasureText<span class="symbol">(</span>g<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Text<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Font<span class="symbol">)</span><span class="symbol">.</span>Width<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="comment">// who knows, use what we have</span>
 <span class="comment">// TODO: ButtonBounds (and SplitterBounds for that matter) seem to return the wrong</span>
 <span class="comment">// values when painting first occurs, so the line is too narrow until after you </span>
 <span class="comment">// hover the mouse over the button</span>
 w <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ButtonBounds<span class="symbol">.</span>Width <span class="symbol">-</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ContentRectangle<span class="symbol">.</span>Left <span class="symbol">*</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>x<span class="symbol">,</span> y<span class="symbol">,</span> w<span class="symbol">,</span> h<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The <code>GetUnderlineRectangle</code> method show above does a decent job
of guessing where the image should be and should work without
much in the way of tinkering.</p>
<blockquote>
<p>If you are drawing a custom underline, you should make sure
the bottom four pixels of your image are blank, as any details
in these will be covered over by the image.</p>
</blockquote>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/toolstrip-colorgrid-1d.png" class="gallery" title="Keep the bottom pixels of the image clear to avoid loosing details" ><img src="https://images.cyotek.com/image/thumbnail/devblog/toolstrip-colorgrid-1d.png" alt="Keep the bottom pixels of the image clear to avoid loosing details" decoding="async" loading="lazy" /></a><figcaption>Keep the bottom pixels of the image clear to avoid loosing details</figcaption></figure><h2 id="downloading-the-full-source">Downloading the full source</h2>
<p>The full source code can be found in the demonstration program
for the ColorPicker controls on <a href="https://github.com/cyotek/Cyotek.Windows.Forms.ColorPicker" rel="external nofollow noopener">GitHub</a>. Just add the
<code>ToolStripColorPickerDropDown.cs</code> and
<code>ToolStripColorPickerSplitButton.cs</code> files to your project and
you should be good to go!</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2015-02-28 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/hosting-a-colorgrid-control-in-a-toolstrip .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comAdding Double Click support to the ComboBox controlurn:uuid:ea23b0c5-bc2a-40ba-9fb2-ca1533294f222014-10-11T07:30:31Z2014-10-11T07:30:31Z<p>I was recently using a <code>ComboBox</code> control with the
<code>DropDownStyle</code> set to <code>Simple</code>, effectively turning into a
combined text box and list box.</p>
<p>However, when I wanted an action to occur on double clicking an
item in the list I found that the control doesn't actually offer
double click support. I suppose I should have just ripped out
the combo box at that point and went with dedicated controls but
instead I decided to extend <code>ComboBox</code> to support double clicks.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/combobox-double-click.png" class="gallery" title="Double click events from a simple mode ComboBox control" ><img src="https://images.cyotek.com/image/thumbnail/devblog/combobox-double-click.png" alt="Double click events from a simple mode ComboBox control" decoding="async" loading="lazy" /></a><figcaption>Double click events from a simple mode ComboBox control</figcaption></figure><h2 id="hmm-no-wm_lbuttondblclk-message">Hmm, no WM_LBUTTONDBLCLK message?</h2>
<p>I had assumed I could simply get the handle of the list
component, set the <code>CS_DBLCLKS</code> style, and start receiving
<code>WM_LBUTTONDBLCLK</code> messages. Unfortunately I couldn't get this
to work. Something to revisit another day perhaps.</p>
<h2 id="fine-lets-fake-it-with-wm_lbuttonup-instead">Fine, lets fake it with WM_LBUTTONUP instead</h2>
<p>So plan A was a bust. Not to worry, I had another idea. In a
<a href="/post/getting-the-hwnd-of-the-edit-component-within-a-combobox-control">previous post</a> I described how to use the <code>GetComboBoxInfo</code>
Win32 API call to obtain the handles to the integrated controls.
We'll use this along with a <code>NativeWindow</code> to watch for
<code>WM_LBUTTONUP</code> messages and handle our double clicks that way.</p>
<h2 id="what-is-nativewindow">What is NativeWindow?</h2>
<p>I haven't described <code>NativeWindow</code> in any previous post, so I'll
briefly cover it now. <code>NativeWindow</code> is a managed wrapper around
a Win32 window handle, and allows you to easily hook into it's
window procedure (WndProc) in order to capture and process
messages sent to the window. Very tidy. The most important class
members are</p>
<ul>
<li><code>AssignHandle</code> - attaches the class to a window</li>
<li><code>ReleaseHandle</code> - detaches the handle once you're finished
with it</li>
<li><code>WndProc</code> - allows you to process messages, otherwise there's
not really much point in using the class!</li>
</ul>
<p>One final point, in most cases you're probably going to want to
subclass <code>NativeWindow</code> as <code>WndProc</code> is protected. And that's
what we'll do here, using a new <code>ListBoxNativeWindow</code> class.</p>
<h2 id="attaching-the-handle">Attaching the handle</h2>
<p>As I mentioned above, you have to explicitly attached your
<code>NativeWindow</code> implementation to a window. For this
demonstration control we'll do it when the control handle is
created, and when the drop down list style is changed. I'll also
add a <code>AllowDoubleClick</code> property to control the new behaviour,
so we'll also set it from there.</p>
<blockquote>
<p><code>NativeWindow</code> doesn't implement <code>IDisposable</code> so for best
practice you should make sure you manually clean up by calling
<code>ReleaseHandle</code> when you are done.</p>
</blockquote>
<p>As I've previously covered the <code>COMBOBOXINFO</code> structure and
<code>GetComboBoxInfo</code> call I won't go over these again - please
refer to my <a href="/post/getting-the-hwnd-of-the-edit-component-within-a-combobox-control">previous post</a> if you need more info.</p>
<p>Assuming we successfully obtain the combo box information, we
instantiate a new instance of our <code>ListBoxNativeWindow</code> and
attach it to the handle of the list box.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> ListBoxNativeWindow _listBoxWindow<span class="symbol">;</span>

<span class="keyword">private</span> <span class="keyword">void</span> AttachHandle<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ReleaseHandle<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsHandleCreated <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>AllowDoubleClick <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>DropDownStyle <span class="symbol">==</span> ComboBoxStyle<span class="symbol">.</span>Simple<span class="symbol">)</span>
 <span class="symbol">{</span>
 COMBOBOXINFO info<span class="symbol">;</span>

 info <span class="symbol">=</span> <span class="keyword">new</span> COMBOBOXINFO<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 info<span class="symbol">.</span>cbSize <span class="symbol">=</span> Marshal<span class="symbol">.</span>SizeOf<span class="symbol">(</span>info<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>GetComboBoxInfo<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> <span class="keyword">ref</span> info<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 IntPtr hWnd<span class="symbol">;</span>

 hWnd <span class="symbol">=</span> info<span class="symbol">.</span>hwndList<span class="symbol">;</span>

 _listBoxWindow <span class="symbol">=</span> <span class="keyword">new</span> ListBoxNativeWindow<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">)</span><span class="symbol">;</span>
 _listBoxWindow<span class="symbol">.</span>AssignHandle<span class="symbol">(</span>hWnd<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Our new class is also storing a reference to the owner
<code>ComboBox</code> control so that we can raise events as appropriate
later on.</p>
<p>As we should clean up behind ourselves, there's a helper method
to release any existing handles which we will call when
assigning a new handle, or when disposing of the control.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> ReleaseHandle<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_listBoxWindow <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _listBoxWindow<span class="symbol">.</span>ReleaseHandle<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 _listBoxWindow <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Now it's time to watch for some messages.</p>
<h2 id="intercepting-messages">Intercepting messages</h2>
<p>Intercepting messages in a <code>NativeWindow</code> is no different to
that of a normal control - just override <code>WndProc</code> and wait for
something interesting.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">const</span> <span class="keyword">int</span> WM_LBUTTONUP <span class="symbol">=</span> <span class="number">0x0202</span><span class="symbol">;</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> WndProc<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>Msg <span class="symbol">==</span> WM_LBUTTONUP<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// do stuff!</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>WndProc<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="double-clicks">Double clicks</h2>
<p>A double click is a pretty simple thing - it is the second click
to occur within a defined interval and with the cursor within
the region of the first click. These system values are
configurable by the end user so we shouldn't hard code our own
values.</p>
<p>The <code>DoubleClickSize</code> and <code>DoubleClickTime</code> properties of the
<code>SystemInformation</code> class provide managed access to these system
values, and so we can now populate our <code>WndProc</code> template with
some real code.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>Msg <span class="symbol">==</span> NativeMethods<span class="symbol">.</span>WM_LBUTTONUP<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">long</span> previousMessageTime<span class="symbol">;</span>
 <span class="keyword">long</span> currentMessageTime<span class="symbol">;</span>
 Point currentLocation<span class="symbol">;</span>

 previousMessageTime <span class="symbol">=</span> _lastMessageTime<span class="symbol">;</span>
 currentMessageTime <span class="symbol">=</span> DateTime<span class="symbol">.</span>Now<span class="symbol">.</span>Ticks<span class="symbol">;</span>
 currentLocation <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetPoint<span class="symbol">(</span>m<span class="symbol">.</span>LParam<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_lastMessageTime <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Rectangle doubleClickBounds<span class="symbol">;</span>
 Size doubleClickSize<span class="symbol">;</span>

 doubleClickSize <span class="symbol">=</span> SystemInformation<span class="symbol">.</span>DoubleClickSize<span class="symbol">;</span>
 doubleClickBounds <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>_lastMousePosition<span class="symbol">.</span>X <span class="symbol">-</span> <span class="symbol">(</span>doubleClickSize<span class="symbol">.</span>Width <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">,</span> _lastMousePosition<span class="symbol">.</span>Y <span class="symbol">-</span> <span class="symbol">(</span>doubleClickSize<span class="symbol">.</span>Height <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">,</span> doubleClickSize<span class="symbol">.</span>Width<span class="symbol">,</span> doubleClickSize<span class="symbol">.</span>Height<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>previousMessageTime <span class="symbol">+</span> <span class="symbol">(</span>SystemInformation<span class="symbol">.</span>DoubleClickTime <span class="symbol">*</span> TimeSpan<span class="symbol">.</span>TicksPerMillisecond<span class="symbol">)</span> <span class="symbol">&gt;</span> currentMessageTime <span class="symbol">&amp;&amp;</span> doubleClickBounds<span class="symbol">.</span>Contains<span class="symbol">(</span>currentLocation<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 MouseEventArgs e<span class="symbol">;</span>

 e <span class="symbol">=</span> <span class="keyword">new</span> MouseEventArgs<span class="symbol">(</span>MouseButtons<span class="symbol">.</span>Left<span class="symbol">,</span> <span class="number">2</span><span class="symbol">,</span> currentLocation<span class="symbol">.</span>X<span class="symbol">,</span> currentLocation<span class="symbol">.</span>Y<span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span>

 _owner<span class="symbol">.</span>RaiseDoubleClick<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 _lastMessageTime <span class="symbol">=</span> currentMessageTime<span class="symbol">;</span>
 _lastMousePosition <span class="symbol">=</span> currentLocation<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Although it might look a little complicated at first glance, it
should be straight forward.</p>
<ul>
<li>The very first time you click with the left mouse button, we
record the current time and the cursor location</li>
<li>Each subsequent click then
<ul>
<li>Compares the current cursor position against a rectangle
centered on the previous position</li>
<li>Compares the previous click time with the current time
subtracted from the interval</li>
<li>If both the interval since the last click has not elapsed
and the cursor is in the same general area, then we have our
double click</li>
<li>Regards of if an event is to be raised or not, we then
update the time and position for the next click</li>
</ul>
</li>
</ul>
<h2 id="raising-the-event">Raising the event</h2>
<p>Although I'd like to do the &quot;right thing&quot; and trigger a
<code>WM_LBUTTONDBLCLK </code> message, the control doesn't support it and
there's not really much point in adding it when it's not going
to have any real value. So we'll manually do it.</p>
<p>I start by adding an internal method to our <code>ComboBox</code> control -
I tend to avoid internals where possible but I don't really see
a need to expose this publicly.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">void</span> RaiseDoubleClick<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnDoubleClick<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>OnMouseDoubleClick<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Short and to the point, it simply raises the two different
events .NET controls have for double clicks.</p>
<p>And back in our <code>WndProc</code>, we construct a new <code>MouseEventArgs</code>
object and then call the new method.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
MouseEventArgs e<span class="symbol">;</span>

e <span class="symbol">=</span> <span class="keyword">new</span> MouseEventArgs<span class="symbol">(</span>MouseButtons<span class="symbol">.</span>Left<span class="symbol">,</span> <span class="number">2</span><span class="symbol">,</span> currentLocation<span class="symbol">.</span>X<span class="symbol">,</span> currentLocation<span class="symbol">.</span>Y<span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span>

_owner<span class="symbol">.</span>RaiseDoubleClick<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>It's worth pointing out the fudge in this - the magic number <code>2</code>
which represents the number of times the button was clicked. The
<code>0</code>, while still magic, represents a mouse wheel delta which is
not appropriate for this event.</p>
<p>And with that code in place, this slightly long winded article
has gotten to the point and you now have fully working events.</p>
<h2 id="really-i-cant-see-them">Really? I can't see them</h2>
<p>Oh of course. As the <code>ComboBox</code> control doesn't support the
<code>DoubleClick</code> and <code>MouseDoubleClick</code> events, the <code>DoubleClick</code>
event has been hidden (but not <code>MouseDoubleClick</code> for some
reason). Easy enough to bring it back - just redefine
<code>DoubleClick</code> with the <code>new</code> keyword set the <code>EditorBrowsable</code>
and <code>Browsable</code> attributes so it will appear in designers.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>EditorBrowsable<span class="symbol">(</span>EditorBrowsableState<span class="symbol">.</span>Always<span class="symbol">)</span><span class="symbol">]</span>
<span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">true</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">new</span> <span class="keyword">event</span> EventHandler DoubleClick
<span class="symbol">{</span>
 add <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>DoubleClick <span class="symbol">+=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 remove <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>DoubleClick <span class="symbol">-=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="always-a-catch">Always a catch</h2>
<p>This was yet another blog post that was written in a hurry after
writing some code in a hurry. I'm positive there must be a
better way using normal window styles and messages rather than
the manual approach I've taken.</p>
<p>There's also a flaw in the code - if you triple click (or more)
then you'll get two (or more) double click events. I don't know
of too many people who spam double clicks so I'm going to ignore
this for now. Possibly at some point I'll be bored enough to
take another look at this and see where I went wrong with the
pure API approach.</p>
<p>Finally, given the hurry with which both of these items were
written, it hasn't had any robust testing, and so may be a
flawed piece of work.</p>
<p>As always, a demonstration project accompanies this article.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2014-10-11 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/adding-double-click-support-to-the-combobox-control .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comDragging items in a ListBox control with visual insertion guidesurn:uuid:120dddbf-cf89-43e7-892b-e0a6453d2a3e2014-07-27T20:31:50Z2014-07-27T20:31:50Z<p>In my last post, I described how to <a href="/post/dragging-items-in-a-listview-control-with-visual-insertion-guides">drag and drop items to
reorder a <code>ListView</code> control</a>. This time I'm going to
describe the exact same technique, but this time for the more
humble <code>ListBox</code>.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/listbox-item-drag.gif" class="gallery" title="The demonstration project in action" ><img src="https://images.cyotek.com/image/thumbnail/devblog/listbox-item-drag.gif" alt="The demonstration project in action" decoding="async" loading="lazy" /></a><figcaption>The demonstration project in action</figcaption></figure><h2 id="getting-started">Getting Started</h2>
<p>The code below assumes you are working in a new class named
<code>ListBox</code> that inherits from <code>System.Windows.Forms.ListBox</code>.</p>
<p>As it's only implementation details that are different between
the two versions, I'll include the pertinent code and point out
the differences but that's about it. As always a full example
project is available from the link at the end of the article.</p>
<blockquote>
<p>As with the previous article, you must set <code>AllowDrop</code> to
<code>true</code> on any <code>ListBox</code> you wish to make use of this
functionality.</p>
</blockquote>
<h2 id="drawing-on-a-listbox">Drawing on a ListBox</h2>
<p>Just like the <code>ListView</code>, the <code>ListBox</code> control is a native
control that is drawn by the operating system and so overriding
<code>OnPaint</code> doesn't work. The <code>ListBox</code> also has a unique
behaviour of built in owner draw support, so you have to make
sure your painting works with all modes.</p>
<p>Fortunately, the exact same method of painting I used with the
<code>ListView</code> works fine here too - that is, I capture <code>WM_PAINT</code>
messages and use <code>Graphics.FromControl</code> to get something I can
work with.</p>
<p>The only real difference is getting the boundaries of the item
to draw due to the differences in the API's of the two controls
- the <code>ListView</code> uses <code>ListViewItem.GetBounds</code> whilst the
<code>ListBox</code> version is <code>ListView.GetItemRectangle</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> DrawInsertionLine<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>InsertionIndex <span class="symbol">!=</span> InvalidIndex<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> index<span class="symbol">;</span>

 index <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>InsertionIndex<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>index <span class="symbol">&gt;=</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> index <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count<span class="symbol">)</span>
 <span class="symbol">{</span>
 Rectangle bounds<span class="symbol">;</span>
 <span class="keyword">int</span> x<span class="symbol">;</span>
 <span class="keyword">int</span> y<span class="symbol">;</span>
 <span class="keyword">int</span> width<span class="symbol">;</span>

 bounds <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetItemRectangle<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>InsertionIndex<span class="symbol">)</span><span class="symbol">;</span>
 x <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> <span class="comment">// aways fit the line to the client area, regardless of how the user is scrolling</span>
 y <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>InsertionMode <span class="symbol">==</span> InsertionMode<span class="symbol">.</span>Before <span class="symbol">?</span> bounds<span class="symbol">.</span>Top <span class="symbol">:</span> bounds<span class="symbol">.</span>Bottom<span class="symbol">;</span>
 width <span class="symbol">=</span> Math<span class="symbol">.</span>Min<span class="symbol">(</span>bounds<span class="symbol">.</span>Width <span class="symbol">-</span> bounds<span class="symbol">.</span>Left<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ClientSize<span class="symbol">.</span>Width<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// again, make sure the full width fits in the client area</span>

 <span class="keyword">this</span><span class="symbol">.</span>DrawInsertionLine<span class="symbol">(</span>x<span class="symbol">,</span> y<span class="symbol">,</span> width<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="flicker-flicker-flicker">Flicker flicker flicker</h2>
<p>The <code>ListBox</code> is a flickery old beast when owner draw is being
used. Unlike the <code>ListView</code> control where I just invalidate the
entire control and trust the double buffering, unfortunately
setting double buffering on the <code>ListBox</code> seems to have no
effect and it flickers like crazy as you drag things around.</p>
<p>To help combat this, I've added a custom <code>Invalidate</code> method
that accepts the index of a single item to redraw. It also
checks if an insertion mode is set, and if so adjusts the bounds
of the rectangle to include the next/previous item (otherwise,
bits of the insertion guides will be left behind as it tries to
flicker free paint). It will then invalidate only that specific
rectangle and reduce overall flickering. It's not perfect but
it's a lot better than invalidating the whole control.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">void</span> Invalidate<span class="symbol">(</span><span class="keyword">int</span> index<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>index <span class="symbol">!=</span> InvalidIndex<span class="symbol">)</span>
 <span class="symbol">{</span>
 Rectangle bounds<span class="symbol">;</span>

 bounds <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetItemRectangle<span class="symbol">(</span>index<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>InsertionMode <span class="symbol">==</span> InsertionMode<span class="symbol">.</span>Before <span class="symbol">&amp;&amp;</span> index <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 bounds <span class="symbol">=</span> Rectangle<span class="symbol">.</span>Union<span class="symbol">(</span>bounds<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>GetItemRectangle<span class="symbol">(</span>index <span class="symbol">-</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>InsertionMode <span class="symbol">==</span> InsertionMode<span class="symbol">.</span>After <span class="symbol">&amp;&amp;</span> index <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count <span class="symbol">-</span> <span class="number">1</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 bounds <span class="symbol">=</span> Rectangle<span class="symbol">.</span>Union<span class="symbol">(</span>bounds<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>GetItemRectangle<span class="symbol">(</span>index <span class="symbol">+</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span>bounds<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>When you call <code>Control.Invalidate</code> it <em>does not</em> trigger an
immediate repaint. Instead it sends a <code>WM_PAINT</code> message to
the control to do a paint when next possible. This means
multiple calls to <code>Invalidate</code> with custom rectangles will
more than likely have them all combined into a single large
rectangle, thus repainting more of the control that you might
anticipate.</p>
</blockquote>
<h2 id="initiating-a-drag-operation">Initiating a drag operation</h2>
<p>Unlike the<code>ListView</code> control and its <code>ItemDrag</code> event, the
<code>ListBox</code> doesn't have one. So we'll roll our own <a href="/post/adding-drag-handles-to-an-imagebox-to-allow-resizing-of-selection-regions">using similar
techniques to those I've described before</a>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">int</span> DragIndex <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

<span class="keyword">protected</span> Point DragOrigin <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseDown<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnMouseDown<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Button <span class="symbol">==</span> MouseButtons<span class="symbol">.</span>Left<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>DragOrigin <span class="symbol">=</span> e<span class="symbol">.</span>Location<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>DragIndex <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>IndexFromPoint<span class="symbol">(</span>e<span class="symbol">.</span>Location<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>DragOrigin <span class="symbol">=</span> Point<span class="symbol">.</span>Empty<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>DragIndex <span class="symbol">=</span> InvalidIndex<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>```

When the user first presses a button<span class="symbol">,</span> I record both the position of the cursor and which item <span class="keyword">is</span> under it<span class="symbol">.</span>

```csharp
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseMove<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>AllowItemDrag <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>IsDragging <span class="symbol">&amp;&amp;</span> e<span class="symbol">.</span>Button <span class="symbol">==</span> MouseButtons<span class="symbol">.</span>Left <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>IsOutsideDragZone<span class="symbol">(</span>e<span class="symbol">.</span>Location<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>IsDragging <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>DoDragDrop<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>DragIndex<span class="symbol">,</span> DragDropEffects<span class="symbol">.</span>Move<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnMouseMove<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">bool</span> IsOutsideDragZone<span class="symbol">(</span>Point location<span class="symbol">)</span>
<span class="symbol">{</span>
 Rectangle dragZone<span class="symbol">;</span>
 <span class="keyword">int</span> dragWidth<span class="symbol">;</span>
 <span class="keyword">int</span> dragHeight<span class="symbol">;</span>

 dragWidth <span class="symbol">=</span> SystemInformation<span class="symbol">.</span>DragSize<span class="symbol">.</span>Width<span class="symbol">;</span>
 dragHeight <span class="symbol">=</span> SystemInformation<span class="symbol">.</span>DragSize<span class="symbol">.</span>Height<span class="symbol">;</span>
 dragZone <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>DragOrigin<span class="symbol">.</span>X <span class="symbol">-</span> <span class="symbol">(</span>dragWidth <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragOrigin<span class="symbol">.</span>Y <span class="symbol">-</span> <span class="symbol">(</span>dragHeight <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">,</span> dragWidth<span class="symbol">,</span> dragHeight<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> <span class="symbol">!</span>dragZone<span class="symbol">.</span>Contains<span class="symbol">(</span>location<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>As it would be somewhat confusing to the user (not to mention
rude) if we suddenly initiated drag events whenever they click
the control and their mouse wiggles during it, we check to see
if the mouse cursor has moved sufficient pixels away from the
drag origin using metrics obtained from <code>SystemInformation</code>.</p>
<p>If the user has dragged the mouse outside this region, then we
call <code>DoDragDrop</code> to initialize the drag and drop operation.</p>
<h2 id="updating-the-insertion-index">Updating the insertion index</h2>
<p>In exactly the same way as with the <code>ListView</code> version, we can
use the <code>DragOver</code> event to determine which item the mouse is
hovered over, and from there calculate if this is a &quot;before&quot; or
&quot;after&quot; action.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnDragOver<span class="symbol">(</span>DragEventArgs drgevent<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsDragging<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> insertionIndex<span class="symbol">;</span>
 InsertionMode insertionMode<span class="symbol">;</span>
 Point clientPoint<span class="symbol">;</span>

 clientPoint <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>PointToClient<span class="symbol">(</span><span class="keyword">new</span> Point<span class="symbol">(</span>drgevent<span class="symbol">.</span>X<span class="symbol">,</span> drgevent<span class="symbol">.</span>Y<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 insertionIndex <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>IndexFromPoint<span class="symbol">(</span>clientPoint<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>insertionIndex <span class="symbol">!=</span> InvalidIndex<span class="symbol">)</span>
 <span class="symbol">{</span>
 Rectangle bounds<span class="symbol">;</span>

 bounds <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetItemRectangle<span class="symbol">(</span>insertionIndex<span class="symbol">)</span><span class="symbol">;</span>
 insertionMode <span class="symbol">=</span> clientPoint<span class="symbol">.</span>Y <span class="symbol">&lt;</span> bounds<span class="symbol">.</span>Top <span class="symbol">+</span> <span class="symbol">(</span>bounds<span class="symbol">.</span>Height <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span> <span class="symbol">?</span> InsertionMode<span class="symbol">.</span>Before <span class="symbol">:</span> InsertionMode<span class="symbol">.</span>After<span class="symbol">;</span>

 drgevent<span class="symbol">.</span>Effect <span class="symbol">=</span> DragDropEffects<span class="symbol">.</span>Move<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 insertionIndex <span class="symbol">=</span> InvalidIndex<span class="symbol">;</span>
 insertionMode <span class="symbol">=</span> InsertionMode<span class="symbol">.</span>None<span class="symbol">;</span>

 drgevent<span class="symbol">.</span>Effect <span class="symbol">=</span> DragDropEffects<span class="symbol">.</span>None<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>insertionIndex <span class="symbol">!=</span> <span class="keyword">this</span><span class="symbol">.</span>InsertionIndex <span class="symbol">||</span> insertionMode <span class="symbol">!=</span> <span class="keyword">this</span><span class="symbol">.</span>InsertionMode<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>InsertionIndex<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// clear the previous item</span>
 <span class="keyword">this</span><span class="symbol">.</span>InsertionMode <span class="symbol">=</span> insertionMode<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>InsertionIndex <span class="symbol">=</span> insertionIndex<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>InsertionIndex<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// draw the new item</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnDragOver<span class="symbol">(</span>drgevent<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The logic is the same, just the implementation differences in
getting the hovered item (use <code>ListBox.IndexFromPoint</code> and the
item bounds). I've also added a dedicated <code>InsertionMode.None</code>
option this time, which is mainly so I don't unnecessarily
invalidate larger regions that I wanted as described in &quot;Flicker
flicker flicker&quot; above.</p>
<p>If the mouse leaves the confines of the control, then we use the
<code>DragLeave</code> event to reset the insertion status. Again no
differences per se, I set the insertion mode now, and I also
call <code>Invalidate</code> first with the current index before resetting
it.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnDragLeave<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>InsertionIndex<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>InsertionIndex <span class="symbol">=</span> InvalidIndex<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>InsertionMode <span class="symbol">=</span> InsertionMode<span class="symbol">.</span>None<span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnDragLeave<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="handling-the-drop">Handling the drop</h2>
<p>When the user releases the mouse, the <code>DragDrop</code> event is
raised. Here, we'll do the actual removal and re-insertion of
the source item.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnDragDrop<span class="symbol">(</span>DragEventArgs drgevent<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsDragging<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">try</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>InsertionIndex <span class="symbol">!=</span> InvalidIndex<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> dragIndex<span class="symbol">;</span>
 <span class="keyword">int</span> dropIndex<span class="symbol">;</span>

 dragIndex <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>drgevent<span class="symbol">.</span>Data<span class="symbol">.</span>GetData<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 dropIndex <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>InsertionIndex<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>dragIndex <span class="symbol">&lt;</span> dropIndex<span class="symbol">)</span>
 <span class="symbol">{</span>
 dropIndex<span class="symbol">--</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>InsertionMode <span class="symbol">==</span> InsertionMode<span class="symbol">.</span>After <span class="symbol">&amp;&amp;</span> dragIndex <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count <span class="symbol">-</span> <span class="number">1</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 dropIndex<span class="symbol">++</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>dropIndex <span class="symbol">!=</span> dragIndex<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">object</span> dragItem<span class="symbol">;</span>

 dragItem <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">[</span>dragIndex<span class="symbol">]</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Remove<span class="symbol">(</span>dragItem<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Insert<span class="symbol">(</span>dropIndex<span class="symbol">,</span> dragItem<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>SelectedItem <span class="symbol">=</span> dragItem<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">finally</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>InsertionIndex<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>InsertionIndex <span class="symbol">=</span> InvalidIndex<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>InsertionMode <span class="symbol">=</span> InsertionMode<span class="symbol">.</span>None<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>IsDragging <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnDragDrop<span class="symbol">(</span>drgevent<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Just as simple as the <code>ListView</code> version!</p>
<h2 id="sample-project">Sample Project</h2>
<p>An example demonstration project with an extended version of the
above code is available for download from the link below.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2014-07-27 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/dragging-items-in-a-listbox-control-with-visual-insertion-guides .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comDragging items in a ListView control with visual insertion guidesurn:uuid:2f61c77c-d1ca-4ce4-af2e-4932323fcd152014-07-27T14:47:09Z2014-07-27T14:47:09Z<p>I can't remember when it was I first saw something being dragged
with an insertion mark for guidance. Whenever it was, it was a
long long time ago and I'm just catching up now.</p>
<p>This article describes how to extend a <code>ListView</code> control to
allow the items within it to be reordered, using insertion
guides.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/listview-item-drag.gif" class="gallery" title="The demonstration project in action" ><img src="https://images.cyotek.com/image/thumbnail/devblog/listview-item-drag.gif" alt="The demonstration project in action" decoding="async" loading="lazy" /></a><figcaption>The demonstration project in action</figcaption></figure><h2 id="drag-drop-vs-mouse-events">Drag Drop vs Mouse Events</h2>
<p>When I first decided that one of my applications needed the
ability to move items around in a list, I knocked together some
quick code by using the <code>MouseDown</code>, <code>MouseMove</code> and <code>MouseUp</code>
events. It worked nicely but I ended up not using it, for the
simple reason I couldn't work out how to change the cursor one
of the standard drag/drop icons - such as <code>Move</code>, <code>Scroll</code>, or
<code>Link</code>. So if anyone knows how to do this, I'd be happy to hear
how (note I don't mean assigning a custom cursor, I mean using
the true OS cursor). As it turns out, apart from the cursor
business (and the incidental fact it looks... awful... if you
<a href="/post/enabling-shell-styles-for-the-listview-and-treeview-controls-in-csharp">enable shell styles</a>), it's also reinventing a fairly large
wheel as the <code>ListView</code> control already provides most of what we
need.</p>
<h2 id="an-elephant-never-forgets-the-allowdrop-property">An Elephant Never Forgets: The AllowDrop Property</h2>
<blockquote>
<p>I should probably have this tattooed upon my forehead as I
think that whenever I try and add drag and drop to anything
and it doesn't work, it's invariably because I didn't set the
<code>AllowDrop</code> property of the respective object to <code>true</code>. And
invariably it takes forever until I remember that that has to
be done.</p>
<p>So... don't forget to set it!</p>
</blockquote>
<h2 id="getting-started">Getting Started</h2>
<p>The code below assumes you are working in a new class named
<code>ListView</code> that inherits from <code>System.Windows.Forms.ListView</code>.
You could do most of this by hooking into the events of an
existing control, but that way leads to madness (and more
complicated code). Or at least duplicate code, and more than
likely duplicate bugs.</p>
<h2 id="drawing-insertion-marks">Drawing insertion marks</h2>
<p>I originally started writing this article with the drag sections
at the start, followed by the sections on drawing.
Unfortunately, it was somewhat confusing to read as the drag is
so heavily dependant on the drawing and insertion bits. So I'll
talk about that first instead.</p>
<p>In order to draw our guides, and to know what to do when the
drag is completed, we need to store some extra information - the
index of the item where the item is to be inserted, and whether
the item is to be inserted before or after the insertion item.
Leaving behind the question on if that sentence even makes
sense, on with some code!</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">enum</span> InsertionMode
<span class="symbol">{</span>
 Before<span class="symbol">,</span>

 After
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">int</span> InsertionIndex <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

<span class="keyword">protected</span> InsertionMode InsertionMode <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">bool</span> IsRowDragInProgress <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
</pre>
</figure>
<p>As we'll be drawing a nice guide so the user is clear on what is
happening, we'll also provide the property to configure the
colour of said guide.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Appearance&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="symbol">[</span>DefaultValue<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>Color<span class="symbol">)</span><span class="symbol">,</span> <span class="string">&quot;Red&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">virtual</span> Color InsertionLineColor
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _insertionLineColor<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> _insertionLineColor <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>We'll also need to initialize default values for these.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> ListView<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>DoubleBuffered <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>InsertionLineColor <span class="symbol">=</span> Color<span class="symbol">.</span>Red<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>InsertionIndex <span class="symbol">=</span> <span class="symbol">-</span><span class="number">1</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Notice the call to set the <code>DoubleBuffered</code> property? This has
to be done, otherwise your drag operation will be an epic
exercise of Major Flickering. In my library code I use the
<code>LVS_EX_DOUBLEBUFFER</code> style when creating the window, but in
this example <code>DoubleBuffered</code> has worked just as well and is
much easier to do.</p>
<h2 id="drawing-on-a-listview">Drawing on a ListView</h2>
<p>The <code>ListView</code> control is a native control that is drawn by the
operating system. In otherwords, overriding <code>OnPaint</code> isn't
working to work.</p>
<p>So how do you draw on it? Well, you could always go even more
old school than using Windows Forms in the first place, and use
the Win32 to do some custom painting. However, it's a touch
overkill and we can get around it for the most part.</p>
<p>Instead, we'll hook into <code>WndProc</code>, watch for the <code>WM_PAINT</code>
message and then use the <code>Control.CreateGraphics</code> method to get
a <code>Graphics</code> object bound to the window and do our painting that
way.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">int</span> WM_PAINT <span class="symbol">=</span> <span class="number">0xF</span><span class="symbol">;</span>

<span class="symbol">[</span>DebuggerStepThrough<span class="symbol">]</span>
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> WndProc<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>WndProc<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">switch</span> <span class="symbol">(</span>m<span class="symbol">.</span>Msg<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> WM_PAINT<span class="symbol">:</span>
 <span class="keyword">this</span><span class="symbol">.</span>DrawInsertionLine<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>I'm not really sure that using <code>CreateGraphics</code> is the best way
to approach this, but it seems to work and it was quicker than
trying to recall all the Win32 GDI work I've done in the past.</p>
<blockquote>
<p><em>Tip:</em> The <code>DebuggerStepThrough</code> is useful for stopping the
debugger from stepping into a method (including any manual
breakpoints you have created). <code>WndProc</code> can be called
thousands of times in a &quot;busy&quot; control, and if you're trying
to debug and suddenly end up in here, it can be a pain.</p>
</blockquote>
<p>The code for actually drawing the insertion line is in itself
simple enough - we just draw a horizontal line with arrow heads
at either side. We adjust the start and end of the line to
ensure it always fits within the client area of the control,
regardless of if the control is horizontally scrolled or the
total width of the item.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> DrawInsertionLine<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>InsertionIndex <span class="symbol">!=</span> <span class="symbol">-</span><span class="number">1</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> index<span class="symbol">;</span>

 index <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>InsertionIndex<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>index <span class="symbol">&gt;=</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> index <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count<span class="symbol">)</span>
 <span class="symbol">{</span>
 Rectangle bounds<span class="symbol">;</span>
 <span class="keyword">int</span> x<span class="symbol">;</span>
 <span class="keyword">int</span> y<span class="symbol">;</span>
 <span class="keyword">int</span> width<span class="symbol">;</span>

 bounds <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">[</span>index<span class="symbol">]</span><span class="symbol">.</span>GetBounds<span class="symbol">(</span>ItemBoundsPortion<span class="symbol">.</span>Entire<span class="symbol">)</span><span class="symbol">;</span>
 x <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> <span class="comment">// aways fit the line to the client area, regardless of how the user is scrolling</span>
 y <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>InsertionMode <span class="symbol">==</span> InsertionMode<span class="symbol">.</span>Before <span class="symbol">?</span> bounds<span class="symbol">.</span>Top <span class="symbol">:</span> bounds<span class="symbol">.</span>Bottom<span class="symbol">;</span>
 width <span class="symbol">=</span> Math<span class="symbol">.</span>Min<span class="symbol">(</span>bounds<span class="symbol">.</span>Width <span class="symbol">-</span> bounds<span class="symbol">.</span>Left<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ClientSize<span class="symbol">.</span>Width<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// again, make sure the full width fits in the client area</span>

 <span class="keyword">this</span><span class="symbol">.</span>DrawInsertionLine<span class="symbol">(</span>x<span class="symbol">,</span> y<span class="symbol">,</span> width<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> DrawInsertionLine<span class="symbol">(</span><span class="keyword">int</span> x<span class="number">1</span><span class="symbol">,</span> <span class="keyword">int</span> y<span class="symbol">,</span> <span class="keyword">int</span> width<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>Graphics g <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>CreateGraphics<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Point<span class="symbol">[</span><span class="symbol">]</span> leftArrowHead<span class="symbol">;</span>
 Point<span class="symbol">[</span><span class="symbol">]</span> rightArrowHead<span class="symbol">;</span>
 <span class="keyword">int</span> arrowHeadSize<span class="symbol">;</span>
 <span class="keyword">int</span> x<span class="number">2</span><span class="symbol">;</span>

 x<span class="number">2</span> <span class="symbol">=</span> x<span class="number">1</span> <span class="symbol">+</span> width<span class="symbol">;</span>
 arrowHeadSize <span class="symbol">=</span> <span class="number">7</span><span class="symbol">;</span>
 leftArrowHead <span class="symbol">=</span> <span class="keyword">new</span><span class="symbol">[</span><span class="symbol">]</span>
 <span class="symbol">{</span>
 <span class="keyword">new</span> Point<span class="symbol">(</span>x<span class="number">1</span><span class="symbol">,</span> y <span class="symbol">-</span> <span class="symbol">(</span>arrowHeadSize <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">,</span> <span class="keyword">new</span> Point<span class="symbol">(</span>x<span class="number">1</span> <span class="symbol">+</span> arrowHeadSize<span class="symbol">,</span> y<span class="symbol">)</span><span class="symbol">,</span> <span class="keyword">new</span> Point<span class="symbol">(</span>x<span class="number">1</span><span class="symbol">,</span> y <span class="symbol">+</span> <span class="symbol">(</span>arrowHeadSize <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">}</span><span class="symbol">;</span>
 rightArrowHead <span class="symbol">=</span> <span class="keyword">new</span><span class="symbol">[</span><span class="symbol">]</span>
 <span class="symbol">{</span>
 <span class="keyword">new</span> Point<span class="symbol">(</span>x<span class="number">2</span><span class="symbol">,</span> y <span class="symbol">-</span> <span class="symbol">(</span>arrowHeadSize <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">,</span> <span class="keyword">new</span> Point<span class="symbol">(</span>x<span class="number">2</span> <span class="symbol">-</span> arrowHeadSize<span class="symbol">,</span> y<span class="symbol">)</span><span class="symbol">,</span> <span class="keyword">new</span> Point<span class="symbol">(</span>x<span class="number">2</span><span class="symbol">,</span> y <span class="symbol">+</span> <span class="symbol">(</span>arrowHeadSize <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">}</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Pen pen <span class="symbol">=</span> <span class="keyword">new</span> Pen<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>InsertionLineColor<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 g<span class="symbol">.</span>DrawLine<span class="symbol">(</span>pen<span class="symbol">,</span> x<span class="number">1</span><span class="symbol">,</span> y<span class="symbol">,</span> x<span class="number">2</span> <span class="symbol">-</span> <span class="number">1</span><span class="symbol">,</span> y<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Brush brush <span class="symbol">=</span> <span class="keyword">new</span> SolidBrush<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>InsertionLineColor<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 g<span class="symbol">.</span>FillPolygon<span class="symbol">(</span>brush<span class="symbol">,</span> leftArrowHead<span class="symbol">)</span><span class="symbol">;</span>
 g<span class="symbol">.</span>FillPolygon<span class="symbol">(</span>brush<span class="symbol">,</span> rightArrowHead<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>And that's all there is to that part of the code. Don't forget
to ensure the control is double buffered!</p>
<h2 id="initiating-a-drag-operation">Initiating a drag operation</h2>
<p>The <code>ListView</code> control has an <code>ItemDrag</code> event that is
automatically raised when the user tries to drag an item. We'll
use this to initiate our own drag and drop operation.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnItemDrag<span class="symbol">(</span>ItemDragEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count <span class="symbol">&gt;</span> <span class="number">1</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>IsRowDragInProgress <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>DoDragDrop<span class="symbol">(</span>e<span class="symbol">.</span>Item<span class="symbol">,</span> DragDropEffects<span class="symbol">.</span>Move<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnItemDrag<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>Note: The code snippets in this article are kept concise to
show only the basics of the technique. In most cases, I've
expanded upon this to include extra support (in this case for
raising an event allowing the operation to be cancelled),
please download the sample project for the full class.</p>
</blockquote>
<p>When the <code>DroDragDrop</code> is called, execution will halt at that
point until the drag is complete or cancelled. You can use the
<code>DragEnter</code>, <code>DragOver</code>, <code>DragLeave</code>, <code>DragDrop</code> and
<code>GiveFeedback</code> events to control the drag, for example to
specify the action that is currently occurring, and to handle
what happens when the user releases the mouse cursor.</p>
<h2 id="updating-the-insertion-index">Updating the insertion index</h2>
<p>We can use the <code>DragOver</code> event to determine which item the
mouse is hovered over, and from there calculate if this is a
&quot;before&quot; or &quot;after&quot; action.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnDragOver<span class="symbol">(</span>DragEventArgs drgevent<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsRowDragInProgress<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> insertionIndex<span class="symbol">;</span>
 InsertionMode insertionMode<span class="symbol">;</span>
 ListViewItem dropItem<span class="symbol">;</span>
 Point clientPoint<span class="symbol">;</span>

 clientPoint <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>PointToClient<span class="symbol">(</span><span class="keyword">new</span> Point<span class="symbol">(</span>drgevent<span class="symbol">.</span>X<span class="symbol">,</span> drgevent<span class="symbol">.</span>Y<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 dropItem <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetItemAt<span class="symbol">(</span><span class="number">0</span><span class="symbol">,</span> Math<span class="symbol">.</span>Min<span class="symbol">(</span>clientPoint<span class="symbol">.</span>Y<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">[</span><span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count <span class="symbol">-</span> <span class="number">1</span><span class="symbol">]</span><span class="symbol">.</span>GetBounds<span class="symbol">(</span>ItemBoundsPortion<span class="symbol">.</span>Entire<span class="symbol">)</span><span class="symbol">.</span>Bottom <span class="symbol">-</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>dropItem <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Rectangle bounds<span class="symbol">;</span>

 bounds <span class="symbol">=</span> dropItem<span class="symbol">.</span>GetBounds<span class="symbol">(</span>ItemBoundsPortion<span class="symbol">.</span>Entire<span class="symbol">)</span><span class="symbol">;</span>
 insertionIndex <span class="symbol">=</span> dropItem<span class="symbol">.</span>Index<span class="symbol">;</span>
 insertionMode <span class="symbol">=</span> clientPoint<span class="symbol">.</span>Y <span class="symbol">&lt;</span> bounds<span class="symbol">.</span>Top <span class="symbol">+</span> <span class="symbol">(</span>bounds<span class="symbol">.</span>Height <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span> <span class="symbol">?</span> InsertionMode<span class="symbol">.</span>Before <span class="symbol">:</span> InsertionMode<span class="symbol">.</span>After<span class="symbol">;</span>

 drgevent<span class="symbol">.</span>Effect <span class="symbol">=</span> DragDropEffects<span class="symbol">.</span>Move<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 insertionIndex <span class="symbol">=</span> <span class="symbol">-</span><span class="number">1</span><span class="symbol">;</span>
 insertionMode <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>InsertionMode<span class="symbol">;</span>

 drgevent<span class="symbol">.</span>Effect <span class="symbol">=</span> DragDropEffects<span class="symbol">.</span>None<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>insertionIndex <span class="symbol">!=</span> <span class="keyword">this</span><span class="symbol">.</span>InsertionIndex <span class="symbol">||</span> insertionMode <span class="symbol">!=</span> <span class="keyword">this</span><span class="symbol">.</span>InsertionMode<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>InsertionMode <span class="symbol">=</span> insertionMode<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>InsertionIndex <span class="symbol">=</span> insertionIndex<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnDragOver<span class="symbol">(</span>drgevent<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The code is a little long, but simple enough. We get the
<code>ListViewItem</code> underneath the cursor. If there isn't one, we
clear any existing insertion data. If we do have one, we check
if the cursor is above or below half of the total height of the
item in order to decide &quot;before&quot; or &quot;after&quot; status.</p>
<p>We also inform the underlying drag operation so that the
appropriate cursor &quot;Move&quot; or &quot;No Drag&quot; is displayed.</p>
<p>Finally, we issue a call to <code>Invalidate</code> to force the control to
repaint so that the new indicator is drawn (or the existing
indicator cleared).</p>
<p>If the mouse leaves the confines of the control, then we use the
<code>DragLeave</code> event to reset the insertion status. We don't need
to use <code>DragEnter</code> as <code>DragOver</code> covers us in this case.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnDragLeave<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>InsertionIndex <span class="symbol">=</span> <span class="symbol">-</span><span class="number">1</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnDragLeave<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="handling-the-drop">Handling the drop</h2>
<p>When the user releases the mouse, the <code>DragDrop</code> event is
raised. Here, we'll do the actual removal and re-insertion of
the source item.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnDragDrop<span class="symbol">(</span>DragEventArgs drgevent<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsRowDragInProgress<span class="symbol">)</span>
 <span class="symbol">{</span>
 ListViewItem dropItem<span class="symbol">;</span>

 dropItem <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>InsertionIndex <span class="symbol">!=</span> <span class="symbol">-</span><span class="number">1</span> <span class="symbol">?</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">[</span><span class="keyword">this</span><span class="symbol">.</span>InsertionIndex<span class="symbol">]</span> <span class="symbol">:</span> <span class="keyword">null</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>dropItem <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 ListViewItem dragItem<span class="symbol">;</span>
 <span class="keyword">int</span> dropIndex<span class="symbol">;</span>

 dragItem <span class="symbol">=</span> <span class="symbol">(</span>ListViewItem<span class="symbol">)</span>drgevent<span class="symbol">.</span>Data<span class="symbol">.</span>GetData<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>ListViewItem<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 dropIndex <span class="symbol">=</span> dropItem<span class="symbol">.</span>Index<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>dragItem<span class="symbol">.</span>Index <span class="symbol">&lt;</span> dropIndex<span class="symbol">)</span>
 <span class="symbol">{</span>
 dropIndex<span class="symbol">--</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>InsertionMode <span class="symbol">==</span> InsertionMode<span class="symbol">.</span>After <span class="symbol">&amp;&amp;</span> dragItem<span class="symbol">.</span>Index <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count <span class="symbol">-</span> <span class="number">1</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 dropIndex<span class="symbol">++</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>dropIndex <span class="symbol">!=</span> dragItem<span class="symbol">.</span>Index<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Remove<span class="symbol">(</span>dragItem<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Insert<span class="symbol">(</span>dropIndex<span class="symbol">,</span> dragItem<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>SelectedItem <span class="symbol">=</span> dragItem<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">this</span><span class="symbol">.</span>InsertionIndex <span class="symbol">=</span> <span class="symbol">-</span><span class="number">1</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>IsRowDragInProgress <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnDragDrop<span class="symbol">(</span>drgevent<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>We reuse the <code>InsertionIndex</code> and <code>InsertionMode</code> values we
calculated in <code>OnDragOver</code> and then determine the index of the
new item from these. Remove the source item, reinsert it at the
new index, then clear the insertion values, force and repaint
and we're done. Easy!</p>
<h2 id="sample-project">Sample Project</h2>
<p>An example demonstration project with an extended version of the
above code is available for download from the link below.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2014-07-27 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/dragging-items-in-a-listview-control-with-visual-insertion-guides .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comConfiguring the emulation mode of an Internet Explorer WebBrowser controlurn:uuid:66dfda59-d846-47d7-9dce-5c19ca3a15ef2014-06-28T09:47:00Z2014-06-28T09:47:00Z<p>Occasionally I need to embed HTML in my applications. If it is
just to display some simple layout with basic interactions, I
might use a component such as <a href="https://github.com/ArthurHub/HTML-Renderer" rel="external nofollow noopener">HtmlRenderer</a>. In most cases
however, I need a more complex layout, JavaScript or I might
want to display real pages from the internet - in which case I'm
lumbered with the <code>WebBrowser</code> control.</p>
<blockquote>
<p>I'm aware other embeddable browsers exist, but the idea of
shipping additional multi-MB dependencies doesn't make sense
unless an application makes heavy use of HTML interfaces</p>
</blockquote>
<p>The <code>WebBrowser</code> control annoys me in myriad ways, but it does
get the job done. One of the things that occasionally frustrates
me is that by default it is essentially an embedded version of
Internet Explorer 7 - or enabling Compatibility Mode in a modern
IE session. Not so good as more and more sites use HTML5 and
other goodies.</p>
<p>Rather fortunately however, Microsoft provide the ability to
configure the emulation mode your application will use. It's not
as simple as setting some properties on a control as it involves
setting some registry values and other caveats, but it is still
a reasonable process.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/browseremulation-1a.png" class="gallery" title="Do you really want to greet your users with script errors when displaying modern websites in the WebBrowser control?" ><img src="https://images.cyotek.com/image/thumbnail/devblog/browseremulation-1a.png" alt="Do you really want to greet your users with script errors when displaying modern websites in the WebBrowser control?" decoding="async" loading="lazy" /></a><figcaption>Do you really want to greet your users with script errors when displaying modern websites in the WebBrowser control?</figcaption></figure><h2 id="about-browser-emulation-versions">About browser emulation versions</h2>
<p>The table below (<a href="http://msdn.microsoft.com/en-us/library/ee330730(v=vs.85).aspx#browser_emulation" rel="external nofollow noopener">source</a>) lists the currently supported
emulation versions at the time of writing. As you can see, it's
possible to emulate all &quot;recent&quot; versions of Internet Explorer
in one of two ways - either by forcing a standards mode, or
allowing <code>!DOCTYPE</code> directives to control the mode. The
exception to this dual behaviour is version 7 which is as is.</p>
<p>According to the documentation the IE8 (8000) and IE9 (9000) modes will switch to IE10 (10000) mode if installed. The documentation doesn't mention if this is still the case regarding IE11 so I'm not sure on the behaviour in that regard.</p>
<table>
<thead>
<tr>
<th style="text-align: right;">Value</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: right;">11001</td>
<td>Internet Explorer 11. Webpages are displayed in IE11 edge mode, regardless of the !DOCTYPE directive.</td>
</tr>
<tr>
<td style="text-align: right;">11000</td>
<td>IE11. Webpages containing standards-based !DOCTYPE directives are displayed in IE11 edge mode. Default value for IE11.</td>
</tr>
<tr>
<td style="text-align: right;">10001</td>
<td>Internet Explorer 10. Webpages are displayed in IE10 Standards mode, regardless of the !DOCTYPE directive.</td>
</tr>
<tr>
<td style="text-align: right;">10000</td>
<td>Internet Explorer 10. Webpages containing standards-based !DOCTYPE directives are displayed in IE10 Standards mode. Default value for Internet Explorer 10.</td>
</tr>
<tr>
<td style="text-align: right;">9999</td>
<td>Windows Internet Explorer 9. Webpages are displayed in IE9 Standards mode, regardless of the !DOCTYPE directive.</td>
</tr>
<tr>
<td style="text-align: right;">9000</td>
<td>Internet Explorer 9. Webpages containing standards-based !DOCTYPE directives are displayed in IE9 mode. Default value for Internet Explorer 9.</td>
</tr>
<tr>
<td style="text-align: right;">8888</td>
<td>Webpages are displayed in IE8 Standards mode, regardless of the !DOCTYPE directive.</td>
</tr>
<tr>
<td style="text-align: right;">8000</td>
<td>Webpages containing standards-based !DOCTYPE directives are displayed in IE8 mode. Default value for Internet Explorer 8</td>
</tr>
<tr>
<td style="text-align: right;">7000</td>
<td>Webpages containing standards-based !DOCTYPE directives are displayed in IE7 Standards mode. Default value for applications hosting the WebBrowser Control.</td>
</tr>
</tbody>
</table>
<h2 id="setting-the-browser-emulation-version">Setting the browser emulation version</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/browseremulation-1c.png" class="gallery" title="Browser emulation support is configured in the Registry" ><img src="https://images.cyotek.com/image/thumbnail/devblog/browseremulation-1c.png" alt="Browser emulation support is configured in the Registry" decoding="async" loading="lazy" /></a><figcaption>Browser emulation support is configured in the Registry</figcaption></figure>
<p>Setting the emulation version is very straightforward - add a
value to the registry in the below key containing the name of
your executable file and a value from the table above.</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
HKEY_LOCAL_MACHINE (or HKEY_CURRENT_USER)
 SOFTWARE
 Microsoft
 Internet Explorer
 Main
 FeatureControl
 FEATURE_BROWSER_EMULATION
 yourapp.exe = (DWORD) version
</pre>
</figure>
<blockquote>
<p>Note: If you do this from an application you're debugging
using Visual Studio and the Visual Studio Hosting Process
option is enabled you'll find the executable name may not be
what you expect. When enabled, a stub process with a slightly
modified name is used instead. For example, if your
application is named <code>calc.exe,</code> you'll need to add the value
<code>calc.vshost.exe</code> in order to set the emulated version for the
correct process.</p>
</blockquote>
<h2 id="getting-the-internet-explorer-version">Getting the Internet Explorer version</h2>
<p>As it makes more sense to detect the version of IE installed on
the user's computer and set the emulation version to match,
first we need a way of detecting the IE version.</p>
<p>There are various ways of getting the installed IE version, but
the sensible method is reading the value from the registry as
everything else we are doing in this article involves the
registry in some fashion.</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
HKEY_LOCAL_MACHINE
 SOFTWARE
 Microsoft
 Internet Explorer
 svcVersion or Version
</pre>
</figure>
<p>Older versions of IE used the <code>Version</code> value, while newer
versions use <code>svcVersion</code>. In either case, this value contains
the version string.</p>
<p>We can use the following version to pull out the major digit.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">string</span> InternetExplorerRootKey <span class="symbol">=</span> <span class="string">@&quot;Software\Microsoft\Internet Explorer&quot;</span><span class="symbol">;</span>

<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> GetInternetExplorerMajorVersion<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> result<span class="symbol">;</span>

 result <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 RegistryKey key<span class="symbol">;</span>

 key <span class="symbol">=</span> Registry<span class="symbol">.</span>LocalMachine<span class="symbol">.</span>OpenSubKey<span class="symbol">(</span>InternetExplorerRootKey<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>key <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">object</span> value<span class="symbol">;</span>

 value <span class="symbol">=</span> key<span class="symbol">.</span>GetValue<span class="symbol">(</span><span class="string">&quot;svcVersion&quot;</span><span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span> <span class="symbol">??</span> key<span class="symbol">.</span>GetValue<span class="symbol">(</span><span class="string">&quot;Version&quot;</span><span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>value <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> version<span class="symbol">;</span>
 <span class="keyword">int</span> separator<span class="symbol">;</span>

 version <span class="symbol">=</span> value<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 separator <span class="symbol">=</span> version<span class="symbol">.</span>IndexOf<span class="symbol">(</span><span class="string">&#39;.&#39;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>separator <span class="symbol">!=</span> <span class="symbol">-</span><span class="number">1</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span><span class="symbol">.</span>TryParse<span class="symbol">(</span>version<span class="symbol">.</span>Substring<span class="symbol">(</span><span class="number">0</span><span class="symbol">,</span> separator<span class="symbol">)</span><span class="symbol">,</span> <span class="keyword">out</span> result<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>SecurityException<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// The user does not have the permissions required to read from the registry key.</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>UnauthorizedAccessException<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// The user does not have the necessary registry rights.</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="points-to-note">Points to note</h3>
<ul>
<li>I'm returning an <code>int</code> with the major version component rather
a <code>Version</code> class. In this example, I don't need a full
version to start with and it avoids crashes if the version
string is invalid</li>
<li>For the same reason, I'm explicitly catching (and ignoring)
<code>SecurityException</code> and <code>UnauthorizedAccessException</code>
exceptions which will be thrown if the user doesn't have
permission to access those keys. Again, I don't really want
the function crashing for those reasons.</li>
</ul>
<p>You can always remove the <code>try</code> block to have all exceptions
thrown instead of the access exceptions being ignored.</p>
<h2 id="getting-the-browser-emulation-version">Getting the browser emulation version</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/browseremulation-1b.png" class="gallery" title="By using Internet Explorer's browser emulation support, we can choose how the WebControl behaves" ><img src="https://images.cyotek.com/image/thumbnail/devblog/browseremulation-1b.png" alt="By using Internet Explorer's browser emulation support, we can choose how the WebControl behaves" decoding="async" loading="lazy" /></a><figcaption>By using Internet Explorer's browser emulation support, we can choose how the WebControl behaves</figcaption></figure>
<blockquote>
<p>The functions to get and set the emulation version are using
<code>HKEY_CURRENT_USER</code> to make them per user rather than for the
entire machine.</p>
</blockquote>
<p>First we'll create an enumeration to handle the different
versions described above so that we don't have to deal with
magic numbers.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">enum</span> BrowserEmulationVersion
<span class="symbol">{</span>
 Default <span class="symbol">=</span> <span class="number">0</span><span class="symbol">,</span>
 Version<span class="number">7</span> <span class="symbol">=</span> <span class="number">7000</span><span class="symbol">,</span>
 Version<span class="number">8</span> <span class="symbol">=</span> <span class="number">8000</span><span class="symbol">,</span>
 Version<span class="number">8</span>Standards <span class="symbol">=</span> <span class="number">8888</span><span class="symbol">,</span>
 Version<span class="number">9</span> <span class="symbol">=</span> <span class="number">9000</span><span class="symbol">,</span>
 Version<span class="number">9</span>Standards <span class="symbol">=</span> <span class="number">9999</span><span class="symbol">,</span>
 Version<span class="number">10</span> <span class="symbol">=</span> <span class="number">10000</span><span class="symbol">,</span>
 Version<span class="number">10</span>Standards <span class="symbol">=</span> <span class="number">10001</span><span class="symbol">,</span>
 Version<span class="number">11</span> <span class="symbol">=</span> <span class="number">11000</span><span class="symbol">,</span>
 Version<span class="number">11</span>Edge <span class="symbol">=</span> <span class="number">11001</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Next, a function to detect the current emulation version in use
by our application, and another to quickly tell if an emulation
version has previously been set.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">string</span> BrowserEmulationKey <span class="symbol">=</span> InternetExplorerRootKey <span class="symbol">+</span> <span class="string">@&quot;\Main\FeatureControl\FEATURE_BROWSER_EMULATION&quot;</span><span class="symbol">;</span>

<span class="keyword">public</span> <span class="keyword">static</span> BrowserEmulationVersion GetBrowserEmulationVersion<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 BrowserEmulationVersion result<span class="symbol">;</span>

 result <span class="symbol">=</span> BrowserEmulationVersion<span class="symbol">.</span>Default<span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 RegistryKey key<span class="symbol">;</span>

 key <span class="symbol">=</span> Registry<span class="symbol">.</span>CurrentUser<span class="symbol">.</span>OpenSubKey<span class="symbol">(</span>BrowserEmulationKey<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>key <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> programName<span class="symbol">;</span>
 <span class="keyword">object</span> value<span class="symbol">;</span>

 programName <span class="symbol">=</span> Path<span class="symbol">.</span>GetFileName<span class="symbol">(</span>Environment<span class="symbol">.</span>GetCommandLineArgs<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span>
 value <span class="symbol">=</span> key<span class="symbol">.</span>GetValue<span class="symbol">(</span>programName<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>value <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="symbol">(</span>BrowserEmulationVersion<span class="symbol">)</span>Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span>value<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>SecurityException<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// The user does not have the permissions required to read from the registry key.</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>UnauthorizedAccessException<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// The user does not have the necessary registry rights.</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">bool</span> IsBrowserEmulationSet<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> GetBrowserEmulationVersion<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">!=</span> BrowserEmulationVersion<span class="symbol">.</span>Default<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="setting-the-emulation-version">Setting the emulation version</h2>
<p>And finally, we need to be able to set the emulation version.
I've provided two functions for doing this, one which allows you
to explicitly set a value, and another that uses the best
matching value for the installed version of Internet Explorer.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">bool</span> SetBrowserEmulationVersion<span class="symbol">(</span>BrowserEmulationVersion browserEmulationVersion<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>

 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 RegistryKey key<span class="symbol">;</span>

 key <span class="symbol">=</span> Registry<span class="symbol">.</span>CurrentUser<span class="symbol">.</span>OpenSubKey<span class="symbol">(</span>BrowserEmulationKey<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>key <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> programName<span class="symbol">;</span>

 programName <span class="symbol">=</span> Path<span class="symbol">.</span>GetFileName<span class="symbol">(</span>Environment<span class="symbol">.</span>GetCommandLineArgs<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>browserEmulationVersion <span class="symbol">!=</span> BrowserEmulationVersion<span class="symbol">.</span>Default<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// if it&#39;s a valid value, update or create the value</span>
 key<span class="symbol">.</span>SetValue<span class="symbol">(</span>programName<span class="symbol">,</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>browserEmulationVersion<span class="symbol">,</span> RegistryValueKind<span class="symbol">.</span>DWord<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="comment">// otherwise, remove the existing value</span>
 key<span class="symbol">.</span>DeleteValue<span class="symbol">(</span>programName<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 result <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>SecurityException<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// The user does not have the permissions required to read from the registry key.</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>UnauthorizedAccessException<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// The user does not have the necessary registry rights.</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">bool</span> SetBrowserEmulationVersion<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> ieVersion<span class="symbol">;</span>
 BrowserEmulationVersion emulationCode<span class="symbol">;</span>

 ieVersion <span class="symbol">=</span> GetInternetExplorerMajorVersion<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>ieVersion <span class="symbol">&gt;=</span> <span class="number">11</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 emulationCode <span class="symbol">=</span> BrowserEmulationVersion<span class="symbol">.</span>Version<span class="number">11</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="keyword">switch</span> <span class="symbol">(</span>ieVersion<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> <span class="number">10</span><span class="symbol">:</span>
 emulationCode <span class="symbol">=</span> BrowserEmulationVersion<span class="symbol">.</span>Version<span class="number">10</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="number">9</span><span class="symbol">:</span>
 emulationCode <span class="symbol">=</span> BrowserEmulationVersion<span class="symbol">.</span>Version<span class="number">9</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="number">8</span><span class="symbol">:</span>
 emulationCode <span class="symbol">=</span> BrowserEmulationVersion<span class="symbol">.</span>Version<span class="number">8</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">default</span><span class="symbol">:</span>
 emulationCode <span class="symbol">=</span> BrowserEmulationVersion<span class="symbol">.</span>Version<span class="number">7</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> SetBrowserEmulationVersion<span class="symbol">(</span>emulationCode<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>As mentioned previously, I don't really want these functions
crashing for anticipated reasons, so these functions will also
catch and ignore <code>SecurityException</code> and
<code>UnauthorizedAccessException</code> exceptions. The
<code>SetBrowserEmulationVersion</code> function will return <code>true</code> if a
value was updated.</p>
<h2 id="simple-usage">Simple Usage</h2>
<p>If you just want &quot;fire and forget&quot; updating of the browser
emulation version, you can use the following lines.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>InternetExplorerBrowserEmulation<span class="symbol">.</span>IsBrowserEmulationSet<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 InternetExplorerBrowserEmulation<span class="symbol">.</span>SetBrowserEmulationVersion<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>This will apply the best matching IE version if an emulation
version isn't set. However, it means if the user updates their
copy if IE to something newer, your application will potentially
continue to use the older version. I shall leave that as an
exercise for another day!</p>
<h2 id="caveats-and-points-to-note">Caveats and points to note</h2>
<h3 id="changing-the-emulation-version-while-your-application-is-running">Changing the emulation version while your application is running</h3>
<p>While experimenting with this code, I did hit a major caveat.</p>
<p>In the original application this code was written for, I was
applying the emulation version just before the first window
containing a <code>WebBrowser</code> control was loaded, and this worked
perfectly well.</p>
<p>However, setting the emulation version doesn't seem to work if
an instance of the <code>WebBrowser</code> control has already been created
in your application. I tried various things such as recreating
the <code>WebBrowser</code> control or reloading the <code>Form</code> the control was
hosted on, but couldn't get the new instance to honour the
setting without an application restart.</p>
<p>The attached demonstration program has gone with the &quot;restart
after making a selection&quot; hack - please don't do this in
production applications!</p>
<h3 id="should-i-change-the-emulation-version-of-my-application">Should I change the emulation version of my application?</h3>
<p>You should carefully consider where or not to change the
emulation version of your application. If it's currently working
fine, then it's probably better to leave it as is. If however,
you wish to make use of modern standards compliant HTML, CSS or
JavaScript then setting the appropriate emulation version will
save you a lot of trouble.</p>
<h2 id="further-reading">Further Reading</h2>
<p>The are <em>a lot</em> of different options you can apply to Internet
Explorer and the <code>WebBrowser</code> control. These options allow you
to change behaviours, supported features and quite a few more.
This article has touched upon one of the more common
requirements, but there are a number of other options that are
worth looking at for advanced application scenarios.</p>
<p>An index of all available configuration options can be found on
<a href="http://msdn.microsoft.com/en-us/library/ee330733(v=vs.85).aspx" rel="external nofollow noopener">MSDN</a>.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2014-06-28 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/configuring-the-emulation-mode-of-an-internet-explorer-webbrowser-control .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comBatch Syntax Highlighting in the DigitalRune Text Editor Controlurn:uuid:b060a717-827a-4506-a2d2-20f85e2e0b4b2014-06-23T20:00:26Z2014-06-23T20:00:26Z<p>In a <a href="/post/css-syntax-highlighting-in-the-digitalrune-text-editor-control">previous article</a> I described how to create a CSS
syntax highlighting definition file for use with the open source
<a href="http://www.digitalrune.com/Products/TextEditorControl/Overview.aspx" rel="external nofollow noopener">DigitalRune Text Editor Control</a>.</p>
<p>Today I was testing events in one of our myriad of prototypes,
which triggered an sample <code>TextEditorControl</code> to apply the BAT
syntax definition found in <code>BAT-Mode.xshd</code>.</p>
<p>While it validated the particular piece of code I was working on
rather nicely, I was less then enamoured with the highlighting.
The image below shows an example of this.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/bat-syntax-original.png" class="gallery" title="My eyes! The original (and illegible) BAT syntax highlighting" ><img src="https://images.cyotek.com/image/thumbnail/devblog/bat-syntax-original.png" alt="My eyes! The original (and illegible) BAT syntax highlighting" decoding="async" loading="lazy" /></a><figcaption>My eyes! The original (and illegible) BAT syntax highlighting</figcaption></figure>
<p>Pretty ugly isn't it? However, I was actually doing a vaguely
real-world test as I did want to see some simple batch file
syntax highlighting. Perfect timing for a distraction to go look
at another problem.</p>
<p>The following screenshot shows my new and improved syntax
highlighting file, based on my experiences creating the CSS
version. I couldn't do everything I tried/wanted, and I suspect
this will be limitations of the <code>TextEditorControl</code>, but fixing
that is &quot;rainy day&quot; stuff. Otherwise, simple as this was, it was
a nice distraction with immediate benefits!</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/bat-syntax-new.png" class="gallery" title="New and improved Joker products! Or, at least new and improved syntax highlighting" ><img src="https://images.cyotek.com/image/thumbnail/devblog/bat-syntax-new.png" alt="New and improved Joker products! Or, at least new and improved syntax highlighting" decoding="async" loading="lazy" /></a><figcaption>New and improved Joker products! Or, at least new and improved syntax highlighting</figcaption></figure><h2 id="the-xml-definition">The XML Definition</h2>
<p>The definition is very straightforward, as it's basically just
keyword tokens and a single highlighting span for environment
variables.</p>
<p>I was trying for something similar to how Notepad++ highlights
batch files, in which if the first &quot;word&quot; on a line isn't a
command, it's assumed to be a program and highlighted
accordingly, but I couldn't get that working correctly so it's
commented out in the definition - if you know of a trick or code
patch to make it work, please let me know!</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;?</span><span class="name">xml</span> <span class="name">version</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1.0</span><span class="symbol">&quot;</span> <span class="name">encoding</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">utf-8</span><span class="symbol">&quot;</span><span class="symbol">?&gt;</span>

<span class="comment">&lt;!-- Batch File syntax highlighting for DigitalRune TextEditor. Created by Cyotek (http://cyotek.com/) --&gt;</span>

<span class="symbol">&lt;</span><span class="name">SyntaxDefinition</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Batch</span><span class="symbol">&quot;</span> <span class="name">extensions</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">*.bat;*.cmd</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>

 <span class="symbol">&lt;</span><span class="name">Environment</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Default</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">#000000</span><span class="symbol">&quot;</span> <span class="name">bgcolor</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">#ffffff</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Environment</span><span class="symbol">&gt;</span>

 <span class="symbol">&lt;</span><span class="name">RuleSets</span><span class="symbol">&gt;</span>

 <span class="symbol">&lt;</span><span class="name">RuleSet</span> <span class="name">ignorecase</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>

 <span class="comment">&lt;!-- Adding @ as a delimiter means the &quot;ECHO&quot; in &quot;@ECHO OFF&quot; will be highlighted. --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Delimiters</span><span class="symbol">&gt;</span>@:<span class="symbol">&lt;/</span><span class="name">Delimiters</span><span class="symbol">&gt;</span> 

 <span class="comment">&lt;!-- REM comment --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Span</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">LineComment</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">#008000</span><span class="symbol">&quot;</span> <span class="name">stopateol</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Begin</span><span class="symbol">&gt;</span>REM<span class="symbol">&lt;/</span><span class="name">Begin</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Span</span><span class="symbol">&gt;</span>

 <span class="comment">&lt;!-- :: style comment --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Span</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">LineComment2</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">#008000</span><span class="symbol">&quot;</span> <span class="name">stopateol</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Begin</span><span class="symbol">&gt;</span>::<span class="symbol">&lt;/</span><span class="name">Begin</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Span</span><span class="symbol">&gt;</span>

 <span class="comment">&lt;!-- label --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Span</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Label</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">#FF0000</span><span class="symbol">&quot;</span> <span class="name">bgcolor</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">#FFFF80</span><span class="symbol">&quot;</span> <span class="name">stopateol</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Begin</span> <span class="name">startofline</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>:<span class="symbol">&lt;/</span><span class="name">Begin</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Span</span><span class="symbol">&gt;</span>

 <span class="comment">&lt;!-- Environment Variable --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Span</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Variable</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">#FF8000</span><span class="symbol">&quot;</span> <span class="name">bgcolor</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">#FCFFF0</span><span class="symbol">&quot;</span> <span class="name">stopateol</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Begin</span><span class="symbol">&gt;</span>%<span class="symbol">&lt;/</span><span class="name">Begin</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">End</span><span class="symbol">&gt;</span>%<span class="symbol">&lt;/</span><span class="name">End</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Span</span><span class="symbol">&gt;</span>

 <span class="comment">&lt;!-- Programs --&gt;</span>
 <span class="comment">&lt;!-- The idea here is the first word on a line that isn&#39;t a keyword is a program. 
 Doesn&#39;t work with the default TextEditorControl though. --&gt;</span>
 <span class="comment">&lt;!--&lt;Span name=&quot;Program&quot; bold=&quot;false&quot; italic=&quot;true&quot; color=&quot;Red&quot; bgcolor=&quot;#FCFFF0&quot; stopateol=&quot;true&quot;&gt;
 &lt;Begin startofline=&quot;true&quot; singleword=&quot;true&quot;&gt;&lt;/Begin&gt;
 &lt;End&gt; &lt;/End&gt;
 &lt;/Span&gt;--&gt;</span>

 <span class="comment">&lt;!-- Operators --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">KeyWords</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Punctuation</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">#FF0000</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">+</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">-</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">*</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">&amp;amp;lt;</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">&amp;amp;gt;</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">=</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">@</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">KeyWords</span><span class="symbol">&gt;</span>

 <span class="comment">&lt;!-- Standard command.com keywords --&gt;</span>
 <span class="comment">&lt;!-- http://en.wikipedia.org/wiki/COMMAND.COM --&gt;</span>

 <span class="symbol">&lt;</span><span class="name">KeyWords</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">command.com-internalcommands</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">#0000FF</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">break</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">chcp</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">chdir</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">cd</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">cls</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">copy</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">ctty</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">date</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">del</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">erase</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">dir</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">echo</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">exit</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">lfnfor</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">loadhigh</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">lh</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">lock</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">move</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">mkdir</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">md</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">path</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">prompt</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">ren</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">rename</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">rmdir</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">rd</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">set</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">time</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">truename</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">type</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">unlock</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">ver</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">verify</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">vol</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="comment">&lt;!-- http://ss64.com/nt/ --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">color</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">endlocal</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">ftype</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">mklink</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">popd</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">pushd</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">setlocal</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">start</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">title</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">KeyWords</span><span class="symbol">&gt;</span>

 <span class="symbol">&lt;</span><span class="name">KeyWords</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">command.com-commands</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">#0000FF</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">call</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">for</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">goto</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">if</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">in</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">do</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">pause</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">rem</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">shift</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">KeyWords</span><span class="symbol">&gt;</span>

 <span class="comment">&lt;!-- Redirects --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">KeyWords</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">command.com-redirects</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">#0000FF</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">nul</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">con</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">prn</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">aux</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">clock$</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">com0</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">com1</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">com2</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">com3</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">com4</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">com5</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">com6</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">com7</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">com8</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">com9</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">lpt0</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">lpt1</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">lpt2</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">lpt3</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">lpt4</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">lpt5</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">lpt6</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">lpt7</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">lpt8</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">lpt9</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">KeyWords</span><span class="symbol">&gt;</span>

 <span class="symbol">&lt;/</span><span class="name">RuleSet</span><span class="symbol">&gt;</span>

 <span class="symbol">&lt;/</span><span class="name">RuleSets</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">SyntaxDefinition</span><span class="symbol">&gt;</span>
</pre>
</figure>
<h2 id="a-sample-project">A sample project</h2>
<p>I've attached a basic sample project, which shows how to dynamic
load in the definition file. And yes, that is a (stripped down)
version of one of the build scripts used by Spriter. I'm still
an old school guy at heart, and so I use <code>msbuild</code> to compile a
solution, but for anything else I still tend to turn to batch
scripts and custom console apps first rather than MSBuild tasks
or PowerShell cmdlets.</p>
<p>Although the example project loads in the definition from an
external file, I would recommend that you build it into the
<code>TextEditorControl</code> assembly itself to make deployment easier. I
covered this in the <strong>Compiling the definition into the
assembly</strong> section of the <a href="/post/css-syntax-highlighting-in-the-digitalrune-text-editor-control">previous article</a> so I won't
replicate that here.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2014-06-23 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/batch-syntax-highlighting-in-the-digitalrune-text-editor-control .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comColorPicker Controls 1.0.4.0 Updateurn:uuid:54c0656a-d653-421f-bd59-b8bb0a12286c2014-04-13T09:48:03Z2014-04-13T09:48:03Z<p>The <a href="https://github.com/cyotek/Cyotek.Windows.Forms.ColorPicker" rel="external nofollow noopener">ColorPicker Controls</a> have been updated to version
1.0.4.0.</p>
<p>This is a fairly substantial update, with quite a few bug fixes
and enhancements to the code.</p>
<h2 id="whats-next-for-the-library">What's next for the library?</h2>
<p>I feel the code is starting to get a little bloated as the
library is trying to serve two purposes - the primary purpose is
for the selection of colours. However, it also provides the
ability to load and save colour swatches in various formats,
some of which has been the subject of posts on this blog.</p>
<p>Internally, we link to the individual files from the library in
our core product assemblies - all the UI stuff is in
<code>Cyotek.Windows.Forms</code>, and everything else is in
<code>Cyotek.Drawing</code>. I think it would probably be a good idea to
properly split up this library too. If you just want the UI, use
one library, if you want the extended palette serialisation
support, use the other. There is some overlap though so this
will need to be considered a bit first.</p>
<p>Of course splitting the library is a massive breaking change. I
think future versions of our open source libraries will change
to use <a href="http://semver.org/spec/v2.0.0.html" rel="external nofollow noopener">Semantic Versioning</a> so that it will be much clearer
when things are being broken. Of course, it would be preferable
if breaking changes <em>weren't</em> introduced all the time! This is
also why I haven't added a NuGet package yet, when you update a
package you don't expect to have to change your source code too.</p>
<h2 id="changes-and-new-features">Changes and new features</h2>
<ul>
<li>Added new <code>AdobePhotoShopColorSwatchSerializer</code> serializer for
reading and writing Adobe PhotoShop colour swatches (both
version 1 and version 2)</li>
<li>You can now set the <code>Columns</code> property of a <code>ColorGrid</code>
control to <code>0</code>, which will then internally calculate columns
based on the size of the control, the cell size, and the
spacing. A new read-only <code>ActualColumns</code> property has been
added which will allow you to get the real number of columns
if required. The <code>AutoSize</code> behaviour has been changed so that
only the vertical height of the control is adjusted when
<code>Columns</code> is zero</li>
<li><strong>Save Palette</strong> button in the <code>ColorPickerDialog</code> now obtains
the serializer to use based on the selected filter index,
allowing correct saving if multiple serializers use the same
extension.</li>
<li>Added <code>CanReadFrom</code> method to <code>IPaletteSerializer</code>.</li>
<li><code>PaletteSerializer.GetSerializer</code> now makes use of the above
new method to access the relevant serializer rather than just
matching extensions. This means if you have two serializers
that support different .pal formatted files, these can now be
loaded successfully, instead of one loading and one failing.</li>
<li>Added new <code>RawPaletteSerializer</code> which reads and writes
palettes that are simply RGB triplets in byte form</li>
<li>Added new <code>ShowAlphaChannel</code> property to <code>ColorEditor</code> and
<code>ColorPickerDialog</code>. This property allows the alpha channel
editing controls to be hidden, for when working with 8-bit
colours.</li>
<li>The rendering of the selected cell in a <code>ColorGrid</code> control
who's <code>SelectedCellStyle</code> is <code>Zoomed</code> now uses <code>Padding.Left</code>
and <code>Padding.Top</code> to determine the size of the zoom box,
avoiding massive boxes the larger the <code>CellSize</code> gets.</li>
<li>Added a new standard 256 colour palette. You can use this in
the <code>ColorGrid</code> by setting the <code>Palette</code> property to
<code>ColorPalette.Standard256</code> or obtain the array of colours by
calling <code>ColorPalettes.StandardPalette</code></li>
<li><code>ColorGrid</code> and <code>RgbaColorSlider</code> controls now only create
transparency brushes when required. A new virtual method
<code>SupportsTransparentBackColor</code> allows inheritors to create
their own brushes if required.</li>
<li>Added <code>EditingColor</code> event to <code>ColorGrid</code>, allowing the edit
colour action to be cancelled, or replaced with a custom
editor</li>
<li>Added <code>CurrentCell</code> property and <code>GetCellOffset</code> methods to
the <code>ColorGrid</code>.</li>
<li><code>ColorCollection</code> now implements <code>IEquatable</code></li>
<li>Added more tests</li>
<li>Added new <code>Navigate</code> method to <code>ColorGrid</code> for easier moving
within the cells of the grid</li>
</ul>
<h2 id="bug-fixes">Bug Fixes</h2>
<ul>
<li>The <code>ColorGrid</code> control now tries to be smarter with painting,
and only paints cells that intersect with the clip rectangle.
In addition, where possible only individual cells are
invalidated rather than the entire control.</li>
<li>Corrected invalid error messages from the <strong>Save Palette</strong>
button in the <code>ColorPickerDialog</code>.</li>
<li><strong>Load Palette</strong> and <strong>Save Palette</strong> buttons in the
<code>ColorPickerDialog</code> now check the <code>CanRead</code> and <code>CanWrite</code>
properties of the serializer.</li>
<li>Double clicking with any button other than the left in
<code>ColorGrid</code> control no longer attempts to initiate colour
editing</li>
<li>Setting the <code>Color</code> property of the <code>ColorGrid</code> control to
<code>Color.Empty</code> no longer treats the value as a valid colour</li>
<li>The <code>ColorGrid</code> control no longer defines custom colour
regions when the <code>ShowCustomColors</code> property was <code>false</code>. This
manifested in hover and selection effects working if you moved
your mouse over the bottom of a resized grid.</li>
<li>Clicking &quot;white space&quot; areas of a <code>ColorWheel</code> control will no
longer incorrectly set the colour to the closest matching
point on the wheel itself. However, starting to select a
colour within the wheel and then moving outside the bounds
will continue to select the closest match as usual.</li>
<li>Fixed a crash that occurred when creating controls that
inherited from <code>ColorGrid</code> or <code>RgbaColorSlider</code></li>
<li>When the <code>AutoAddColors</code> and <code>ShowCustomColors</code> properties are
<code>false</code>, unmatched colours will no longer be silently added to
the <code>ColorGrid</code> custom palette unexpectedly. This also
resolves various crashes after the colour regions fix above
was applied.</li>
<li>The <code>ColorWheel</code> control now makes use of
<code>ButtonRenderer.DrawParentBackground</code> to draw itself, to avoid
ugly blocks of solid colours when hosted in containers such as
the <code>TabControl</code></li>
<li>The <code>ColorEditorManager</code> control's <code>ColorChanged</code> event has
now been marked as the default event, so when you double click
the component in the designer, a code window now correctly
opens.</li>
<li>If the underlying entry in a <code>ColorCollection</code> bound to a
<code>ColorGrid</code> control was modified, and this particular entry
was the selected colour, the <code>ColorGrid</code> would not keep its
<code>Color</code> property in sync and would clear the selected index.</li>
<li>Attempting to set the <code>Columns</code> property to less than zero now
throws an <code>ArgumentOutOfRange</code> exception rather than setting
it, then crashing later on</li>
<li>Double clicking a colour in the grid of the
<code>ColorPickerDialog</code> no longer opens another copy of the
<code>ColorPickerDialog</code></li>
<li>Fixed problems in the <code>ColorGrid</code> with keyboard navigation and
initial focus if no valid colour index was set.</li>
<li>The <code>ColorCollection.Find</code> method now correctly works when
adding named colours (e.g. <code>Color.CornflowerBlue</code>) to the
collection, but searching by ARGB value (e.g.
<code>Color.FromArgb(100, 149, 237)</code>)</li>
<li>Fixed an issue where if the internal dictionary lookup in
<code>ColorCollection</code> class had been created and the collection
was then updated, in some cases the lookup wasn't correctly
modified.</li>
</ul>
<h2 id="update-history">Update History</h2>
<ul>
<li>2014-04-13 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/colorpicker-controls-1-0-4-0-update .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comAdding drag handles to an ImageBox to allow resizing of selection regionsurn:uuid:f4104c69-7c2f-4db2-9206-246115c2af482014-02-13T20:12:19Z2014-02-13T20:12:19Z<p>The <code>ImageBox</code> control is already a versatile little control and
I use it for all sorts of tasks. One of the features I recently
wanted was to allow users to be able to select a source region,
then adjust this as needed. The control already allows you to
draw a selection region, but if you need to adjust that ...
well, you can't. You can only draw a new region.</p>
<p>This article describes how to extend the <code>ImageBox</code> to include
the ability to resize the selection region. A older
demonstration which shows how to drag the selection around has
also been incorporated, in a more tidy fashion than the demo.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/imageboxresize-1e.png" class="gallery" title="The control in action - and yes, you can resize even when zoomed in or out" ><img src="https://images.cyotek.com/image/thumbnail/devblog/imageboxresize-1e.png" alt="The control in action - and yes, you can resize even when zoomed in or out" decoding="async" loading="lazy" /></a><figcaption>The control in action - and yes, you can resize even when zoomed in or out</figcaption></figure>
<blockquote>
<p>Note: The code presented in this article has not been added to
the core <code>ImageBox</code> control. Mostly this is because I don't
want to clutter the control with bloat (something users of the
old <code>PropertiesList</code> control might wish I'd done!) and partly
because I don't want to add changes to the control that I'll
regret down the line - I don't need another mess like the
<a href="https://github.com/cyotek/Cyotek.Windows.Forms.ColorPicker" rel="external nofollow noopener">Color Picker Controls</a> where every update seems to be a
breaking change! It most likely will be added to the core
control after it's been dog-fooded for a while with different
scenarios.</p>
</blockquote>
<h2 id="getting-started">Getting Started</h2>
<p>As I mentioned above, this isn't part of the core control (yet)
and so has been added to a new <code>ImageBoxEx</code> control. Not the
most imaginative of names, but with it's current status of
internal demonstration code, it matters not.</p>
<p>In addition to this new sub-classed control, we also need some
helper classes. First amongst these is a new enum to describe
the drag handle anchors, so we know which edges to resize.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">enum</span> DragHandleAnchor
<span class="symbol">{</span>
 None<span class="symbol">,</span>
 TopLeft<span class="symbol">,</span>
 TopCenter<span class="symbol">,</span>
 TopRight<span class="symbol">,</span>
 MiddleLeft<span class="symbol">,</span>
 MiddleRight<span class="symbol">,</span>
 BottomLeft<span class="symbol">,</span>
 BottomCenter<span class="symbol">,</span>
 BottomRight
<span class="symbol">}</span>
</pre>
</figure>
<p>Next we have the class that describes an individual drag handle
- nothing special here, although I have added <code>Enabled</code> and
<code>Visible</code> properties to allow for more advanced scenarios, such
as locking an edge, or only showing some handles.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">class</span> DragHandle
<span class="symbol">{</span>
 <span class="keyword">public</span> DragHandle<span class="symbol">(</span>DragHandleAnchor anchor<span class="symbol">)</span>
 <span class="symbol">:</span> <span class="keyword">this</span><span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Anchor <span class="symbol">=</span> anchor<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> DragHandle<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Enabled <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Visible <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> DragHandleAnchor Anchor <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">protected</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> Rectangle Bounds <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">bool</span> Enabled <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">bool</span> Visible <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/imageboxresize-1b.png" class="gallery" title="While you probably wouldn't do this, hiding one or two of the drag handles could be useful for some scenarios" ><img src="https://images.cyotek.com/image/thumbnail/devblog/imageboxresize-1b.png" alt="While you probably wouldn't do this, hiding one or two of the drag handles could be useful for some scenarios" decoding="async" loading="lazy" /></a><figcaption>While you probably wouldn't do this, hiding one or two of the drag handles could be useful for some scenarios</figcaption></figure>
<p>The final support class is a collection for our drag handle
objects - we could just use a <code>List&lt;&gt;</code> or some other generic
collection but as a rule it's best not to expose these in a
public API (and this code will be just that eventually) so we'll
create a dedicated read-only collection.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">class</span> DragHandleCollection <span class="symbol">:</span> IEnumerable<span class="symbol">&lt;</span>DragHandle<span class="symbol">&gt;</span>
<span class="symbol">{</span>
 <span class="keyword">private</span> <span class="keyword">readonly</span> IDictionary<span class="symbol">&lt;</span>DragHandleAnchor<span class="symbol">,</span> DragHandle<span class="symbol">&gt;</span> _items<span class="symbol">;</span>

 <span class="keyword">public</span> DragHandleCollection<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _items <span class="symbol">=</span> <span class="keyword">new</span> Dictionary<span class="symbol">&lt;</span>DragHandleAnchor<span class="symbol">,</span> DragHandle<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 _items<span class="symbol">.</span>Add<span class="symbol">(</span>DragHandleAnchor<span class="symbol">.</span>TopLeft<span class="symbol">,</span> <span class="keyword">new</span> DragHandle<span class="symbol">(</span>DragHandleAnchor<span class="symbol">.</span>TopLeft<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 _items<span class="symbol">.</span>Add<span class="symbol">(</span>DragHandleAnchor<span class="symbol">.</span>TopCenter<span class="symbol">,</span> <span class="keyword">new</span> DragHandle<span class="symbol">(</span>DragHandleAnchor<span class="symbol">.</span>TopCenter<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 _items<span class="symbol">.</span>Add<span class="symbol">(</span>DragHandleAnchor<span class="symbol">.</span>TopRight<span class="symbol">,</span> <span class="keyword">new</span> DragHandle<span class="symbol">(</span>DragHandleAnchor<span class="symbol">.</span>TopRight<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 _items<span class="symbol">.</span>Add<span class="symbol">(</span>DragHandleAnchor<span class="symbol">.</span>MiddleLeft<span class="symbol">,</span> <span class="keyword">new</span> DragHandle<span class="symbol">(</span>DragHandleAnchor<span class="symbol">.</span>MiddleLeft<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 _items<span class="symbol">.</span>Add<span class="symbol">(</span>DragHandleAnchor<span class="symbol">.</span>MiddleRight<span class="symbol">,</span> <span class="keyword">new</span> DragHandle<span class="symbol">(</span>DragHandleAnchor<span class="symbol">.</span>MiddleRight<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 _items<span class="symbol">.</span>Add<span class="symbol">(</span>DragHandleAnchor<span class="symbol">.</span>BottomLeft<span class="symbol">,</span> <span class="keyword">new</span> DragHandle<span class="symbol">(</span>DragHandleAnchor<span class="symbol">.</span>BottomLeft<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 _items<span class="symbol">.</span>Add<span class="symbol">(</span>DragHandleAnchor<span class="symbol">.</span>BottomCenter<span class="symbol">,</span> <span class="keyword">new</span> DragHandle<span class="symbol">(</span>DragHandleAnchor<span class="symbol">.</span>BottomCenter<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 _items<span class="symbol">.</span>Add<span class="symbol">(</span>DragHandleAnchor<span class="symbol">.</span>BottomRight<span class="symbol">,</span> <span class="keyword">new</span> DragHandle<span class="symbol">(</span>DragHandleAnchor<span class="symbol">.</span>BottomRight<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">int</span> Count
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _items<span class="symbol">.</span>Count<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> DragHandle <span class="keyword">this</span><span class="symbol">[</span>DragHandleAnchor index<span class="symbol">]</span>
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _items<span class="symbol">[</span>index<span class="symbol">]</span><span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> IEnumerator<span class="symbol">&lt;</span>DragHandle<span class="symbol">&gt;</span> GetEnumerator<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">return</span> _items<span class="symbol">.</span>Values<span class="symbol">.</span>GetEnumerator<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> DragHandleAnchor HitTest<span class="symbol">(</span>Point point<span class="symbol">)</span>
 <span class="symbol">{</span>
 DragHandleAnchor result<span class="symbol">;</span>

 result <span class="symbol">=</span> DragHandleAnchor<span class="symbol">.</span>None<span class="symbol">;</span>

 <span class="keyword">foreach</span> <span class="symbol">(</span>DragHandle handle <span class="keyword">in</span> <span class="keyword">this</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>handle<span class="symbol">.</span>Visible <span class="symbol">&amp;&amp;</span> handle<span class="symbol">.</span>Bounds<span class="symbol">.</span>Contains<span class="symbol">(</span>point<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> handle<span class="symbol">.</span>Anchor<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
 <span class="symbol">}</span>

 IEnumerator IEnumerable<span class="symbol">.</span>GetEnumerator<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>GetEnumerator<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Again, there's not much special about this class. As it is a
custom class it does give us more flexibility, such as
initializing the required drag handles, and providing a
convenient <code>HitTest</code> method so we can check if a given point is
within the bounds of a <code>DragHandle</code>.</p>
<h2 id="positioning-drag-handles-around-the-selection-region">Positioning drag handles around the selection region</h2>
<p>The <code>ImageBox</code> control includes a nice bunch of helper methods,
such as <code>PointToImage</code>, <code>GetOffsetRectangle</code> and more, which are
very useful for adding scalable elements to an <code>ImageBox</code>
instance. Unfortunately, they are all virtually useless for the
drag handle code due to the fact that the handles themselves
must not scale - the positions of course must update and
resizing must be accurate whether at 100% zoom or not, but the
size must not. This means we can't rely on the built in methods
and must manually recalculate the handles whenever the control
changes.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> PositionDragHandles<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>DragHandles <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>IsEmpty<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span>DragHandle handle <span class="keyword">in</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandles<span class="symbol">)</span>
 <span class="symbol">{</span>
 handle<span class="symbol">.</span>Bounds <span class="symbol">=</span> Rectangle<span class="symbol">.</span>Empty<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> left<span class="symbol">;</span>
 <span class="keyword">int</span> top<span class="symbol">;</span>
 <span class="keyword">int</span> right<span class="symbol">;</span>
 <span class="keyword">int</span> bottom<span class="symbol">;</span>
 <span class="keyword">int</span> halfWidth<span class="symbol">;</span>
 <span class="keyword">int</span> halfHeight<span class="symbol">;</span>
 <span class="keyword">int</span> halfDragHandleSize<span class="symbol">;</span>
 Rectangle viewport<span class="symbol">;</span>
 <span class="keyword">int</span> offsetX<span class="symbol">;</span>
 <span class="keyword">int</span> offsetY<span class="symbol">;</span>

 viewport <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetImageViewPort<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 offsetX <span class="symbol">=</span> viewport<span class="symbol">.</span>Left <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Left <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>AutoScrollPosition<span class="symbol">.</span>X<span class="symbol">;</span>
 offsetY <span class="symbol">=</span> viewport<span class="symbol">.</span>Top <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Top <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>AutoScrollPosition<span class="symbol">.</span>Y<span class="symbol">;</span>
 halfDragHandleSize <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize <span class="symbol">/</span> <span class="number">2</span><span class="symbol">;</span>
 left <span class="symbol">=</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Left <span class="symbol">*</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span> <span class="symbol">+</span> offsetX<span class="symbol">)</span><span class="symbol">;</span>
 top <span class="symbol">=</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Top <span class="symbol">*</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span> <span class="symbol">+</span> offsetY<span class="symbol">)</span><span class="symbol">;</span>
 right <span class="symbol">=</span> left <span class="symbol">+</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Width <span class="symbol">*</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span><span class="symbol">;</span>
 bottom <span class="symbol">=</span> top <span class="symbol">+</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Height <span class="symbol">*</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span><span class="symbol">;</span>
 halfWidth <span class="symbol">=</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Width <span class="symbol">*</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span> <span class="symbol">/</span> <span class="number">2</span><span class="symbol">;</span>
 halfHeight <span class="symbol">=</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Height <span class="symbol">*</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span> <span class="symbol">/</span> <span class="number">2</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>DragHandles<span class="symbol">[</span>DragHandleAnchor<span class="symbol">.</span>TopLeft<span class="symbol">]</span><span class="symbol">.</span>Bounds <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>left <span class="symbol">-</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">,</span> top <span class="symbol">-</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>DragHandles<span class="symbol">[</span>DragHandleAnchor<span class="symbol">.</span>TopCenter<span class="symbol">]</span><span class="symbol">.</span>Bounds <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>left <span class="symbol">+</span> halfWidth <span class="symbol">-</span> halfDragHandleSize<span class="symbol">,</span> top <span class="symbol">-</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>DragHandles<span class="symbol">[</span>DragHandleAnchor<span class="symbol">.</span>TopRight<span class="symbol">]</span><span class="symbol">.</span>Bounds <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>right<span class="symbol">,</span> top <span class="symbol">-</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>DragHandles<span class="symbol">[</span>DragHandleAnchor<span class="symbol">.</span>MiddleLeft<span class="symbol">]</span><span class="symbol">.</span>Bounds <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>left <span class="symbol">-</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">,</span> top <span class="symbol">+</span> halfHeight <span class="symbol">-</span> halfDragHandleSize<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>DragHandles<span class="symbol">[</span>DragHandleAnchor<span class="symbol">.</span>MiddleRight<span class="symbol">]</span><span class="symbol">.</span>Bounds <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>right<span class="symbol">,</span> top <span class="symbol">+</span> halfHeight <span class="symbol">-</span> halfDragHandleSize<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>DragHandles<span class="symbol">[</span>DragHandleAnchor<span class="symbol">.</span>BottomLeft<span class="symbol">]</span><span class="symbol">.</span>Bounds <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>left <span class="symbol">-</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">,</span> bottom<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>DragHandles<span class="symbol">[</span>DragHandleAnchor<span class="symbol">.</span>BottomCenter<span class="symbol">]</span><span class="symbol">.</span>Bounds <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>left <span class="symbol">+</span> halfWidth <span class="symbol">-</span> halfDragHandleSize<span class="symbol">,</span> bottom<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>DragHandles<span class="symbol">[</span>DragHandleAnchor<span class="symbol">.</span>BottomRight<span class="symbol">]</span><span class="symbol">.</span>Bounds <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>right<span class="symbol">,</span> bottom<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandleSize<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The code is fairly straightforward, but we need to call it from
a few places, so we have a bunch of overrides similar to the
below.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnScroll<span class="symbol">(</span>ScrollEventArgs se<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnScroll<span class="symbol">(</span>se<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>PositionDragHandles<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>We call <code>PositionDragHandles</code> from the constructor, and the
<code>Scroll</code>, <code>SelectionRegionChanged</code>, <code>ZoomChanged</code> and <code>Resize</code>
events.</p>
<h2 id="painting-the-drag-handles">Painting the drag handles</h2>
<p>Painting the handles is simple enough - after normal painting
has occurred, we draw our handles on top.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnPaint<span class="symbol">(</span>PaintEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnPaint<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>AllowPainting <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>IsEmpty<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span>DragHandle handle <span class="keyword">in</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandles<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>handle<span class="symbol">.</span>Visible<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>DrawDragHandle<span class="symbol">(</span>e<span class="symbol">.</span>Graphics<span class="symbol">,</span> handle<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> DrawDragHandle<span class="symbol">(</span>Graphics graphics<span class="symbol">,</span> DragHandle handle<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> left<span class="symbol">;</span>
 <span class="keyword">int</span> top<span class="symbol">;</span>
 <span class="keyword">int</span> width<span class="symbol">;</span>
 <span class="keyword">int</span> height<span class="symbol">;</span>
 Pen outerPen<span class="symbol">;</span>
 Brush innerBrush<span class="symbol">;</span>

 left <span class="symbol">=</span> handle<span class="symbol">.</span>Bounds<span class="symbol">.</span>Left<span class="symbol">;</span>
 top <span class="symbol">=</span> handle<span class="symbol">.</span>Bounds<span class="symbol">.</span>Top<span class="symbol">;</span>
 width <span class="symbol">=</span> handle<span class="symbol">.</span>Bounds<span class="symbol">.</span>Width<span class="symbol">;</span>
 height <span class="symbol">=</span> handle<span class="symbol">.</span>Bounds<span class="symbol">.</span>Height<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>handle<span class="symbol">.</span>Enabled<span class="symbol">)</span>
 <span class="symbol">{</span>
 outerPen <span class="symbol">=</span> SystemPens<span class="symbol">.</span>WindowFrame<span class="symbol">;</span>
 innerBrush <span class="symbol">=</span> SystemBrushes<span class="symbol">.</span>Window<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 outerPen <span class="symbol">=</span> SystemPens<span class="symbol">.</span>ControlDark<span class="symbol">;</span>
 innerBrush <span class="symbol">=</span> SystemBrushes<span class="symbol">.</span>Control<span class="symbol">;</span>
 <span class="symbol">}</span>

 graphics<span class="symbol">.</span>FillRectangle<span class="symbol">(</span>innerBrush<span class="symbol">,</span> left <span class="symbol">+</span> <span class="number">1</span><span class="symbol">,</span> top <span class="symbol">+</span> <span class="number">1</span><span class="symbol">,</span> width <span class="symbol">-</span> <span class="number">2</span><span class="symbol">,</span> height <span class="symbol">-</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>
 graphics<span class="symbol">.</span>DrawLine<span class="symbol">(</span>outerPen<span class="symbol">,</span> left <span class="symbol">+</span> <span class="number">1</span><span class="symbol">,</span> top<span class="symbol">,</span> left <span class="symbol">+</span> width <span class="symbol">-</span> <span class="number">2</span><span class="symbol">,</span> top<span class="symbol">)</span><span class="symbol">;</span>
 graphics<span class="symbol">.</span>DrawLine<span class="symbol">(</span>outerPen<span class="symbol">,</span> left<span class="symbol">,</span> top <span class="symbol">+</span> <span class="number">1</span><span class="symbol">,</span> left<span class="symbol">,</span> top <span class="symbol">+</span> height <span class="symbol">-</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>
 graphics<span class="symbol">.</span>DrawLine<span class="symbol">(</span>outerPen<span class="symbol">,</span> left <span class="symbol">+</span> <span class="number">1</span><span class="symbol">,</span> top <span class="symbol">+</span> height <span class="symbol">-</span> <span class="number">1</span><span class="symbol">,</span> left <span class="symbol">+</span> width <span class="symbol">-</span> <span class="number">2</span><span class="symbol">,</span> top <span class="symbol">+</span> height <span class="symbol">-</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 graphics<span class="symbol">.</span>DrawLine<span class="symbol">(</span>outerPen<span class="symbol">,</span> left <span class="symbol">+</span> width <span class="symbol">-</span> <span class="number">1</span><span class="symbol">,</span> top <span class="symbol">+</span> <span class="number">1</span><span class="symbol">,</span> left <span class="symbol">+</span> width <span class="symbol">-</span> <span class="number">1</span><span class="symbol">,</span> top <span class="symbol">+</span> height <span class="symbol">-</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/imageboxresize-1a.png" class="gallery" title="Disabled drag handles are painted using different colors" ><img src="https://images.cyotek.com/image/thumbnail/devblog/imageboxresize-1a.png" alt="Disabled drag handles are painted using different colors" decoding="async" loading="lazy" /></a><figcaption>Disabled drag handles are painted using different colors</figcaption></figure><h2 id="updating-the-cursor">Updating the cursor</h2>
<p>As the mouse travels across the control, we need to adjust the
cursor accordingly - either to change it to one of the four
resize cursors if the mouse is over an enabled handle, or to the
drag cursor if it's within the bounds of the selection region.
Of course, we also need to reset it if none of these conditions
are true.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> SetCursor<span class="symbol">(</span>Point point<span class="symbol">)</span>
<span class="symbol">{</span>
 Cursor cursor<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsSelecting<span class="symbol">)</span>
 <span class="symbol">{</span>
 cursor <span class="symbol">=</span> Cursors<span class="symbol">.</span>Default<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 DragHandleAnchor handleAnchor<span class="symbol">;</span>

 handleAnchor <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>IsResizing <span class="symbol">?</span> <span class="keyword">this</span><span class="symbol">.</span>ResizeAnchor <span class="symbol">:</span> <span class="keyword">this</span><span class="symbol">.</span>HitTest<span class="symbol">(</span>point<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>handleAnchor <span class="symbol">!=</span> DragHandleAnchor<span class="symbol">.</span>None <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandles<span class="symbol">[</span>handleAnchor<span class="symbol">]</span><span class="symbol">.</span>Enabled<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">switch</span> <span class="symbol">(</span>handleAnchor<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> DragHandleAnchor<span class="symbol">.</span>TopLeft<span class="symbol">:</span>
 <span class="keyword">case</span> DragHandleAnchor<span class="symbol">.</span>BottomRight<span class="symbol">:</span>
 cursor <span class="symbol">=</span> Cursors<span class="symbol">.</span>SizeNWSE<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> DragHandleAnchor<span class="symbol">.</span>TopCenter<span class="symbol">:</span>
 <span class="keyword">case</span> DragHandleAnchor<span class="symbol">.</span>BottomCenter<span class="symbol">:</span>
 cursor <span class="symbol">=</span> Cursors<span class="symbol">.</span>SizeNS<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> DragHandleAnchor<span class="symbol">.</span>TopRight<span class="symbol">:</span>
 <span class="keyword">case</span> DragHandleAnchor<span class="symbol">.</span>BottomLeft<span class="symbol">:</span>
 cursor <span class="symbol">=</span> Cursors<span class="symbol">.</span>SizeNESW<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> DragHandleAnchor<span class="symbol">.</span>MiddleLeft<span class="symbol">:</span>
 <span class="keyword">case</span> DragHandleAnchor<span class="symbol">.</span>MiddleRight<span class="symbol">:</span>
 cursor <span class="symbol">=</span> Cursors<span class="symbol">.</span>SizeWE<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">default</span><span class="symbol">:</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentOutOfRangeException<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsMoving <span class="symbol">||</span> <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Contains<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>PointToImage<span class="symbol">(</span>point<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 cursor <span class="symbol">=</span> Cursors<span class="symbol">.</span>SizeAll<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 cursor <span class="symbol">=</span> Cursors<span class="symbol">.</span>Default<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">this</span><span class="symbol">.</span>Cursor <span class="symbol">=</span> cursor<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="initializing-a-move-or-a-drag">Initializing a move or a drag</h2>
<p>When the user first presses the left mouse button, check to see
if the cursor is within the bounds of the selection region, or
any visible drag handle. If so, we record the location of the
cursor, and it's offset to the upper left corner of the
selection region.</p>
<p>The original cursor location will be used as the origin, so once
the mouse starts moving, we use this to determine if a move
should occur, or a resize, or nothing.</p>
<p>The offset is used purely for moving, so that we reposition the
selection relative to the cursor position - otherwise it would
snap to the cursor which would look pretty awful.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseDown<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 Point imagePoint<span class="symbol">;</span>

 imagePoint <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>PointToImage<span class="symbol">(</span>e<span class="symbol">.</span>Location<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Button <span class="symbol">==</span> MouseButtons<span class="symbol">.</span>Left <span class="symbol">&amp;&amp;</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Contains<span class="symbol">(</span>imagePoint<span class="symbol">)</span> <span class="symbol">||</span> <span class="keyword">this</span><span class="symbol">.</span>HitTest<span class="symbol">(</span>e<span class="symbol">.</span>Location<span class="symbol">)</span> <span class="symbol">!=</span> DragHandleAnchor<span class="symbol">.</span>None<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>DragOrigin <span class="symbol">=</span> e<span class="symbol">.</span>Location<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>DragOriginOffset <span class="symbol">=</span> <span class="keyword">new</span> Point<span class="symbol">(</span>imagePoint<span class="symbol">.</span>X <span class="symbol">-</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>X<span class="symbol">,</span> imagePoint<span class="symbol">.</span>Y <span class="symbol">-</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Y<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>DragOriginOffset <span class="symbol">=</span> Point<span class="symbol">.</span>Empty<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>DragOrigin <span class="symbol">=</span> Point<span class="symbol">.</span>Empty<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnMouseDown<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Even if the user immediately moves the mouse, we don't want to
trigger a move or a resize - the mouse may have just twitched.
Instead, we wait until it moves beyond an area centred around
the drag origin - once it has, then we trigger the action.</p>
<p>This drag rectangle is determined via the
<code>SystemInformation.DragSize</code> (<a href="http://msdn.microsoft.com/query/dev12.query?appId=Dev12IDEF1&amp;l=EN-US&amp;k=k(System.Windows.Forms.SystemInformation.DragSize)%3bk(TargetFrameworkMoniker-.NETFramework%2cVersion%3dv2.0)%3bk(DevLang-csharp)&amp;rd=true" rel="external nofollow noopener">MSDN</a>) property.</p>
<p>During a mouse move, as well as triggering a move or resize, we
also need to <em>process</em> any in-progress action, as well as update
the cursor as described in the previous section.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">bool</span> IsOutsideDragZone<span class="symbol">(</span>Point location<span class="symbol">)</span>
<span class="symbol">{</span>
 Rectangle dragZone<span class="symbol">;</span>
 <span class="keyword">int</span> dragWidth<span class="symbol">;</span>
 <span class="keyword">int</span> dragHeight<span class="symbol">;</span>

 dragWidth <span class="symbol">=</span> SystemInformation<span class="symbol">.</span>DragSize<span class="symbol">.</span>Width<span class="symbol">;</span>
 dragHeight <span class="symbol">=</span> SystemInformation<span class="symbol">.</span>DragSize<span class="symbol">.</span>Height<span class="symbol">;</span>
 dragZone <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>DragOrigin<span class="symbol">.</span>X <span class="symbol">-</span> <span class="symbol">(</span>dragWidth <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>DragOrigin<span class="symbol">.</span>Y <span class="symbol">-</span> <span class="symbol">(</span>dragHeight <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">,</span> dragWidth<span class="symbol">,</span> dragHeight<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> <span class="symbol">!</span>dragZone<span class="symbol">.</span>Contains<span class="symbol">(</span>location<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseMove<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// start either a move or a resize operation</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>IsSelecting <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>IsMoving <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>IsResizing <span class="symbol">&amp;&amp;</span> e<span class="symbol">.</span>Button <span class="symbol">==</span> MouseButtons<span class="symbol">.</span>Left <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>DragOrigin<span class="symbol">.</span>IsEmpty <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>IsOutsideDragZone<span class="symbol">(</span>e<span class="symbol">.</span>Location<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 DragHandleAnchor anchor<span class="symbol">;</span>

 anchor <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>HitTest<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>DragOrigin<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>anchor <span class="symbol">==</span> DragHandleAnchor<span class="symbol">.</span>None<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// move</span>
 <span class="keyword">this</span><span class="symbol">.</span>StartMove<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>DragHandles<span class="symbol">[</span>anchor<span class="symbol">]</span><span class="symbol">.</span>Enabled <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>DragHandles<span class="symbol">[</span>anchor<span class="symbol">]</span><span class="symbol">.</span>Visible<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// resize</span>
 <span class="keyword">this</span><span class="symbol">.</span>StartResize<span class="symbol">(</span>anchor<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="comment">// set the cursor</span>
 <span class="keyword">this</span><span class="symbol">.</span>SetCursor<span class="symbol">(</span>e<span class="symbol">.</span>Location<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// perform operations</span>
 <span class="keyword">this</span><span class="symbol">.</span>ProcessSelectionMove<span class="symbol">(</span>e<span class="symbol">.</span>Location<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>ProcessSelectionResize<span class="symbol">(</span>e<span class="symbol">.</span>Location<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnMouseMove<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Although I'm not going to include the code here as this article
is already very code heavy, the <code>StartMove</code> and <code>StartResize</code>
methods simply set some internal flags describing the control
state, and store a copy of the <code>SelectionRegion</code> property - I'll
explain why towards the end of the article. They also raise
events, both to allow the actions to be cancelled, or to allow
the application to update the user interface in some fashion.</p>
<h2 id="performing-the-move">Performing the move</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/imageboxresize-1c.gif" class="gallery" title="Moving the selection around" ><img src="https://images.cyotek.com/image/thumbnail/devblog/imageboxresize-1c.gif" alt="Moving the selection around" decoding="async" loading="lazy" /></a><figcaption>Moving the selection around</figcaption></figure>
<p>Performing the move is simple - we calculate the new position of
the selection region according to the cursor position, and
including the offset from the original drag for a smooth move.</p>
<p>We also check to ensure that the full bounds of the selection
region fit within the controls client area, preventing the user
from dragging out outside the bounds of the underlying
image/virtual size.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> ProcessSelectionMove<span class="symbol">(</span>Point cursorPosition<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsMoving<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> x<span class="symbol">;</span>
 <span class="keyword">int</span> y<span class="symbol">;</span>
 Point imagePoint<span class="symbol">;</span>

 imagePoint <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>PointToImage<span class="symbol">(</span>cursorPosition<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>

 x <span class="symbol">=</span> Math<span class="symbol">.</span>Max<span class="symbol">(</span><span class="number">0</span><span class="symbol">,</span> imagePoint<span class="symbol">.</span>X <span class="symbol">-</span> <span class="keyword">this</span><span class="symbol">.</span>DragOriginOffset<span class="symbol">.</span>X<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>x <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Width <span class="symbol">&gt;=</span> <span class="keyword">this</span><span class="symbol">.</span>ViewSize<span class="symbol">.</span>Width<span class="symbol">)</span>
 <span class="symbol">{</span>
 x <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ViewSize<span class="symbol">.</span>Width <span class="symbol">-</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Width<span class="symbol">;</span>
 <span class="symbol">}</span>

 y <span class="symbol">=</span> Math<span class="symbol">.</span>Max<span class="symbol">(</span><span class="number">0</span><span class="symbol">,</span> imagePoint<span class="symbol">.</span>Y <span class="symbol">-</span> <span class="keyword">this</span><span class="symbol">.</span>DragOriginOffset<span class="symbol">.</span>Y<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>y <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Height <span class="symbol">&gt;=</span> <span class="keyword">this</span><span class="symbol">.</span>ViewSize<span class="symbol">.</span>Height<span class="symbol">)</span>
 <span class="symbol">{</span>
 y <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ViewSize<span class="symbol">.</span>Height <span class="symbol">-</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Height<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion <span class="symbol">=</span> <span class="keyword">new</span> RectangleF<span class="symbol">(</span>x<span class="symbol">,</span> y<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Width<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Height<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="performing-the-resize">Performing the resize</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/imageboxresize-1d.gif" class="gallery" title="Resizing the selection" ><img src="https://images.cyotek.com/image/thumbnail/devblog/imageboxresize-1d.gif" alt="Resizing the selection" decoding="async" loading="lazy" /></a><figcaption>Resizing the selection</figcaption></figure>
<p>The resize code is also reasonably straight forward. We decide
which edges of the selection region we're going to adjust based
on the drag handle. Next, we get the position of the cursor
within the underlying view - snapped to fit within the bounds,
so that you can't size the region outside the view.</p>
<p>The we just update the edges based on this calculation. However,
we also ensure that the selection region is above a minimum
size. Apart from the fact that if the drag handles overlap it's
going to be impossible to size properly, you probably want to
force some minimum size constraints.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> ProcessSelectionResize<span class="symbol">(</span>Point cursorPosition<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsResizing<span class="symbol">)</span>
 <span class="symbol">{</span>
 Point imagePosition<span class="symbol">;</span>
 <span class="keyword">float</span> left<span class="symbol">;</span>
 <span class="keyword">float</span> top<span class="symbol">;</span>
 <span class="keyword">float</span> right<span class="symbol">;</span>
 <span class="keyword">float</span> bottom<span class="symbol">;</span>
 <span class="keyword">bool</span> resizingTopEdge<span class="symbol">;</span>
 <span class="keyword">bool</span> resizingBottomEdge<span class="symbol">;</span>
 <span class="keyword">bool</span> resizingLeftEdge<span class="symbol">;</span>
 <span class="keyword">bool</span> resizingRightEdge<span class="symbol">;</span>

 imagePosition <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>PointToImage<span class="symbol">(</span>cursorPosition<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// get the current selection</span>
 left <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Left<span class="symbol">;</span>
 top <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Top<span class="symbol">;</span>
 right <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Right<span class="symbol">;</span>
 bottom <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Bottom<span class="symbol">;</span>

 <span class="comment">// decide which edges we&#39;re resizing</span>
 resizingTopEdge <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ResizeAnchor <span class="symbol">&gt;=</span> DragHandleAnchor<span class="symbol">.</span>TopLeft <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>ResizeAnchor <span class="symbol">&lt;=</span> DragHandleAnchor<span class="symbol">.</span>TopRight<span class="symbol">;</span>
 resizingBottomEdge <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ResizeAnchor <span class="symbol">&gt;=</span> DragHandleAnchor<span class="symbol">.</span>BottomLeft <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>ResizeAnchor <span class="symbol">&lt;=</span> DragHandleAnchor<span class="symbol">.</span>BottomRight<span class="symbol">;</span>
 resizingLeftEdge <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ResizeAnchor <span class="symbol">==</span> DragHandleAnchor<span class="symbol">.</span>TopLeft <span class="symbol">||</span> <span class="keyword">this</span><span class="symbol">.</span>ResizeAnchor <span class="symbol">==</span> DragHandleAnchor<span class="symbol">.</span>MiddleLeft <span class="symbol">||</span> <span class="keyword">this</span><span class="symbol">.</span>ResizeAnchor <span class="symbol">==</span> DragHandleAnchor<span class="symbol">.</span>BottomLeft<span class="symbol">;</span>
 resizingRightEdge <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ResizeAnchor <span class="symbol">==</span> DragHandleAnchor<span class="symbol">.</span>TopRight <span class="symbol">||</span> <span class="keyword">this</span><span class="symbol">.</span>ResizeAnchor <span class="symbol">==</span> DragHandleAnchor<span class="symbol">.</span>MiddleRight <span class="symbol">||</span> <span class="keyword">this</span><span class="symbol">.</span>ResizeAnchor <span class="symbol">==</span> DragHandleAnchor<span class="symbol">.</span>BottomRight<span class="symbol">;</span>

 <span class="comment">// and resize!</span>
 <span class="keyword">if</span> <span class="symbol">(</span>resizingTopEdge<span class="symbol">)</span>
 <span class="symbol">{</span>
 top <span class="symbol">=</span> imagePosition<span class="symbol">.</span>Y<span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>bottom <span class="symbol">-</span> top <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>MinimumSelectionSize<span class="symbol">.</span>Height<span class="symbol">)</span>
 <span class="symbol">{</span>
 top <span class="symbol">=</span> bottom <span class="symbol">-</span> <span class="keyword">this</span><span class="symbol">.</span>MinimumSelectionSize<span class="symbol">.</span>Height<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>resizingBottomEdge<span class="symbol">)</span>
 <span class="symbol">{</span>
 bottom <span class="symbol">=</span> imagePosition<span class="symbol">.</span>Y<span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>bottom <span class="symbol">-</span> top <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>MinimumSelectionSize<span class="symbol">.</span>Height<span class="symbol">)</span>
 <span class="symbol">{</span>
 bottom <span class="symbol">=</span> top <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>MinimumSelectionSize<span class="symbol">.</span>Height<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>resizingLeftEdge<span class="symbol">)</span>
 <span class="symbol">{</span>
 left <span class="symbol">=</span> imagePosition<span class="symbol">.</span>X<span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>right <span class="symbol">-</span> left <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>MinimumSelectionSize<span class="symbol">.</span>Width<span class="symbol">)</span>
 <span class="symbol">{</span>
 left <span class="symbol">=</span> right <span class="symbol">-</span> <span class="keyword">this</span><span class="symbol">.</span>MinimumSelectionSize<span class="symbol">.</span>Width<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>resizingRightEdge<span class="symbol">)</span>
 <span class="symbol">{</span>
 right <span class="symbol">=</span> imagePosition<span class="symbol">.</span>X<span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>right <span class="symbol">-</span> left <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>MinimumSelectionSize<span class="symbol">.</span>Width<span class="symbol">)</span>
 <span class="symbol">{</span>
 right <span class="symbol">=</span> left <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>MinimumSelectionSize<span class="symbol">.</span>Width<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion <span class="symbol">=</span> <span class="keyword">new</span> RectangleF<span class="symbol">(</span>left<span class="symbol">,</span> top<span class="symbol">,</span> right <span class="symbol">-</span> left<span class="symbol">,</span> bottom <span class="symbol">-</span> top<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="finalizing-the-moveresize-operations">Finalizing the move/resize operations</h2>
<p>So far, we've used the <code>MouseDown</code> and <code>MouseMove</code> events to
control the initializing and processing of the actions. Now,
we've use the <code>MouseUp</code> event to finish things off - to reset
flags that describe the control state, and to raise events.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseUp<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsMoving<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>CompleteMove<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsResizing<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>CompleteResize<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnMouseUp<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="cancelling-a-move-or-resize-operation">Cancelling a move or resize operation</h2>
<p>Assuming the user has started moving the region or resizes it,
and then changes their mind. How to cancel? The easiest way is
to press the <code>Escape</code> key - and so that's what we'll implement.</p>
<p>We can do this by overriding <code>ProcessDialogKey</code>, checking for
<code>Escape</code> and then resetting the control state, and restoring the
<code>SelectionRegion</code> property using the copy we started at the
start of the operation.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">bool</span> ProcessDialogKey<span class="symbol">(</span>Keys keyData<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>keyData <span class="symbol">==</span> Keys<span class="symbol">.</span>Escape <span class="symbol">&amp;&amp;</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsResizing <span class="symbol">||</span> <span class="keyword">this</span><span class="symbol">.</span>IsMoving<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsResizing<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>CancelResize<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>CancelMove<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 result <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="keyword">base</span><span class="symbol">.</span>ProcessDialogKey<span class="symbol">(</span>keyData<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="wrapping-up">Wrapping up</h2>
<p>That covers most of the important code for making these
techniques work, although it's incomplete, so please download
the latest version for the full source. And I hope you find this
addition to the <code>ImageBox</code> component useful!</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2014-02-13 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/adding-drag-handles-to-an-imagebox-to-allow-resizing-of-selection-regions .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comWriting Photoshop Color Swatch (aco) files using C#urn:uuid:185a2dfb-dc4d-479e-b09c-1289092e44142014-01-28T18:25:09Z2014-01-28T18:25:09Z<p>The <a href="/post/reading-photoshop-color-swatch-aco-files-using-csharp">previous article</a> in this mini-series described how to
read files in Abode's Colour File format as used by applications
such as Photoshop and other drawing programs.</p>
<p>In this second article, I'll describe how to write such files.</p>
<div class="article-gallery">
<a href="https://images.cyotek.com/image/devblog/savephotoshopcolorswatch1.png" class="gallery" title="RGB - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/savephotoshopcolorswatch1.png" alt="RGB" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/savephotoshopcolorswatch2.png" class="gallery" title="HSL - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/savephotoshopcolorswatch2.png" alt="HSL" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/savephotoshopcolorswatch3.png" class="gallery" title="Grayscale - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/savephotoshopcolorswatch3.png" alt="Grayscale" decoding="async" loading="lazy" /></a></div>
<h2 id="getting-started">Getting started</h2>
<p>I'm not going to go over the structure again, so if you haven't
already done so, please read the previous article <a href="/post/reading-photoshop-color-swatch-aco-files-using-csharp">Reading
Photoshop Color Swatch (aco) files using C#</a> for full details
on the file structure and how to read it.</p>
<h2 id="writing-big-endian-values">Writing big-endian values</h2>
<p>All the data in an <code>aco</code> file is stored in <a href="http://en.wikipedia.org/wiki/Endianness" rel="external nofollow noopener">big-endian</a>
format and therefore needs to be reversed on Windows systems
before writing it back into the file.</p>
<p>We can use the following two methods to write a <code>short</code> or a
<code>int</code> respectively into a stream as a series of bytes. Of
course, if you just want functions to convert these into bytes
you could use similar code, just remove the bit-shift.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> WriteInt<span class="number">16</span><span class="symbol">(</span>Stream stream<span class="symbol">,</span> <span class="keyword">short</span> value<span class="symbol">)</span>
<span class="symbol">{</span>
 stream<span class="symbol">.</span>WriteByte<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span>value <span class="symbol">&gt;&gt;</span> <span class="number">8</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteByte<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span>value <span class="symbol">&gt;&gt;</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> WriteInt<span class="number">32</span><span class="symbol">(</span>Stream stream<span class="symbol">,</span> <span class="keyword">int</span> value<span class="symbol">)</span>
<span class="symbol">{</span>
 stream<span class="symbol">.</span>WriteByte<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span><span class="symbol">(</span>value <span class="symbol">&amp;</span> <span class="number">0xFF000000</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">24</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteByte<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span><span class="symbol">(</span>value <span class="symbol">&amp;</span> <span class="number">0x00FF0000</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">16</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteByte<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span><span class="symbol">(</span>value <span class="symbol">&amp;</span> <span class="number">0x0000FF00</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">8</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteByte<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span><span class="symbol">(</span><span class="symbol">(</span>value <span class="symbol">&amp;</span> <span class="number">0x000000FF</span><span class="symbol">)</span> <span class="symbol">&gt;&gt;</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>As with the equivalent read functions, the <code>&gt;&gt; 0</code> shift is
unnecessary but it does clarify the code.</p>
<p>We also need to store colour swatch names, so again we'll make
use of the <code>Encoding.BigEndianUnicode</code> property to convert a
string into a series of bytes to write out.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> WriteString<span class="symbol">(</span>Stream stream<span class="symbol">,</span> <span class="keyword">string</span> value<span class="symbol">)</span>
<span class="symbol">{</span>
 stream<span class="symbol">.</span>Write<span class="symbol">(</span>Encoding<span class="symbol">.</span>BigEndianUnicode<span class="symbol">.</span>GetBytes<span class="symbol">(</span>value<span class="symbol">)</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> value<span class="symbol">.</span>Length <span class="symbol">*</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="writing-the-file">Writing the file</h2>
<p>When writing the file, I'm going to follow the specification's
suggestion of writing a version 1 palette (for backwards
compatibility), followed by a version 2 palette (for
applications that support swatch names).</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> <span class="symbol">(</span>Stream stream <span class="symbol">=</span> File<span class="symbol">.</span>Create<span class="symbol">(</span>fileName<span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>WritePalette<span class="symbol">(</span>stream<span class="symbol">,</span> palette<span class="symbol">,</span> FileVersion<span class="symbol">.</span>Version<span class="number">1</span><span class="symbol">,</span> ColorSpace<span class="symbol">.</span>Rgb<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>WritePalette<span class="symbol">(</span>stream<span class="symbol">,</span> palette<span class="symbol">,</span> FileVersion<span class="symbol">.</span>Version<span class="number">2</span><span class="symbol">,</span> ColorSpace<span class="symbol">.</span>Rgb<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The core save routine follows. First, we write the version of
format and then the number of colours in the palette.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> WritePalette<span class="symbol">(</span>Stream stream<span class="symbol">,</span> ICollection<span class="symbol">&lt;</span>Color<span class="symbol">&gt;</span> palette<span class="symbol">,</span> FileVersion version<span class="symbol">,</span> ColorSpace colorSpace<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> swatchIndex<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>WriteInt<span class="number">16</span><span class="symbol">(</span>stream<span class="symbol">,</span> <span class="symbol">(</span><span class="keyword">short</span><span class="symbol">)</span>version<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteInt<span class="number">16</span><span class="symbol">(</span>stream<span class="symbol">,</span> <span class="symbol">(</span><span class="keyword">short</span><span class="symbol">)</span>palette<span class="symbol">.</span>Count<span class="symbol">)</span><span class="symbol">;</span>

 swatchIndex <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
</pre>
</figure>
<p>With that done, we loop through each colour, calculate the four
values that comprise the colour data and then write that.</p>
<p>If it's a version 2 file, we also write the swatch name. As
these basic examples are just using the <code>Color</code> class, there's
no real flexibility in names, so we cheat - if it's a &quot;named&quot;
colour, then we use the <code>Color.Name</code> property. Otherwise, we
generate a <code>Swatch &lt;index&gt;</code> name.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">foreach</span> <span class="symbol">(</span>Color color <span class="keyword">in</span> palette<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">short</span> value<span class="number">1</span><span class="symbol">;</span>
 <span class="keyword">short</span> value<span class="number">2</span><span class="symbol">;</span>
 <span class="keyword">short</span> value<span class="number">3</span><span class="symbol">;</span>
 <span class="keyword">short</span> value<span class="number">4</span><span class="symbol">;</span>

 swatchIndex<span class="symbol">++</span><span class="symbol">;</span>

 <span class="keyword">switch</span> <span class="symbol">(</span>colorSpace<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// Calculate color space values here!</span>
 <span class="keyword">default</span><span class="symbol">:</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidOperationException<span class="symbol">(</span><span class="string">&quot;Color space not supported.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">this</span><span class="symbol">.</span>WriteInt<span class="number">16</span><span class="symbol">(</span>stream<span class="symbol">,</span> <span class="symbol">(</span><span class="keyword">short</span><span class="symbol">)</span>colorSpace<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteInt<span class="number">16</span><span class="symbol">(</span>stream<span class="symbol">,</span> value<span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteInt<span class="number">16</span><span class="symbol">(</span>stream<span class="symbol">,</span> value<span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteInt<span class="number">16</span><span class="symbol">(</span>stream<span class="symbol">,</span> value<span class="number">3</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteInt<span class="number">16</span><span class="symbol">(</span>stream<span class="symbol">,</span> value<span class="number">4</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>version <span class="symbol">==</span> FileVersion<span class="symbol">.</span>Version<span class="number">2</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> name<span class="symbol">;</span>

 name <span class="symbol">=</span> color<span class="symbol">.</span>IsNamedColor <span class="symbol">?</span> color<span class="symbol">.</span>Name <span class="symbol">:</span> <span class="keyword">string</span><span class="symbol">.</span>Format<span class="symbol">(</span><span class="string">&quot;Swatch {0}&quot;</span><span class="symbol">,</span> swatchIndex<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>WriteInt<span class="number">32</span><span class="symbol">(</span>stream<span class="symbol">,</span> name<span class="symbol">.</span>Length<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteString<span class="symbol">(</span>stream<span class="symbol">,</span> name<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="converting-colour-spaces">Converting colour spaces</h2>
<p>As previously mentioned, the <a href="http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1055819" rel="external nofollow noopener">specification</a> states that each
colour is comprised of four values. Even if a particular colour
space doesn't use all four (for example <code>Grayscale</code> just uses
one, you still need to write the other values, typically as
zero's.</p>
<p>Although it's a slight duplication, I'll include the description
table for colour spaces to allow easy reference of the value
types.</p>
<table>
<thead>
<tr>
<th style="text-align: right;">Id</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: right;">0</td>
<td>RGB. The first three values in the colour data are red, green, and blue. They are full unsigned 16-bit values as in Apple's RGBColordata structure. Pure red = 65535, 0, 0.</td>
</tr>
<tr>
<td style="text-align: right;">1</td>
<td>HSB. The first three values in the colour data are hue, saturation, and brightness. They are full unsigned 16-bit values as in Apple's HSVColordata structure. Pure red = 0,65535, 65535.</td>
</tr>
<tr>
<td style="text-align: right;">2</td>
<td>CMYK. The four values in the colour data are cyan, magenta, yellow, and black. They are full unsigned 16-bit values. For example, pure cyan = 0,65535,65535,65535.</td>
</tr>
<tr>
<td style="text-align: right;">7</td>
<td>Lab. The first three values in the colour data are lightness, a chrominance, and b chrominance. Lightness is a 16-bit value from 0...10000. Chrominance components are each 16-bit values from -12800...12700. Gray values are represented by chrominance components of 0. Pure white = 10000,0,0.</td>
</tr>
<tr>
<td style="text-align: right;">8</td>
<td>Grayscale. The first value in the colour data is the gray value, from 0...10000.</td>
</tr>
</tbody>
</table>
<p>While supporting <code>CMYK</code> colours are beyond the scope of this
article as they require colour profiles, and I haven't the
foggiest on the <code>Lab</code> space, we can easily support <code>RGB</code>, <code>HSL</code>
and <code>Grayscale</code> spaces.</p>
<p>RGB is the simplest as .NET colours are already in this format.
The only thing we have to do is multiple each channel by 256 as
the specification uses the range 0-65535 rather than the typical
0-255.</p>
<p>Notice <code>value4</code> is simply initialized to zero as this space only
needs 3 of the 4 values.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">case</span> ColorSpace<span class="symbol">.</span>Rgb<span class="symbol">:</span>
 value<span class="number">1</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">short</span><span class="symbol">)</span><span class="symbol">(</span>color<span class="symbol">.</span>R <span class="symbol">*</span> <span class="number">256</span><span class="symbol">)</span><span class="symbol">;</span>
 value<span class="number">2</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">short</span><span class="symbol">)</span><span class="symbol">(</span>color<span class="symbol">.</span>G <span class="symbol">*</span> <span class="number">256</span><span class="symbol">)</span><span class="symbol">;</span>
 value<span class="number">3</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">short</span><span class="symbol">)</span><span class="symbol">(</span>color<span class="symbol">.</span>B <span class="symbol">*</span> <span class="number">256</span><span class="symbol">)</span><span class="symbol">;</span>
 value<span class="number">4</span> <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
</pre>
</figure>
<p>We can also support HSL without too much trouble as the <code>Color</code>
class already includes methods for extracting these values.
Again, we need to do a little fiddling to change the numbers
into the range used by the specification.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">case</span> ColorSpace<span class="symbol">.</span>Hsb<span class="symbol">:</span>
 value<span class="number">1</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">short</span><span class="symbol">)</span><span class="symbol">(</span>color<span class="symbol">.</span>GetHue<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">*</span> <span class="number">182.04</span><span class="symbol">)</span><span class="symbol">;</span>
 value<span class="number">2</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">short</span><span class="symbol">)</span><span class="symbol">(</span>color<span class="symbol">.</span>GetSaturation<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">*</span> <span class="number">655.35</span><span class="symbol">)</span><span class="symbol">;</span>
 value<span class="number">3</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">short</span><span class="symbol">)</span><span class="symbol">(</span>color<span class="symbol">.</span>GetBrightness<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">*</span> <span class="number">655.35</span><span class="symbol">)</span><span class="symbol">;</span>
 value<span class="number">4</span> <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
</pre>
</figure>
<p>The last format we can easily support is grayscale. If the
source colour is already grey (i.e. the red, green and blue
channels are all the same value), then we use that, otherwise
we'll average the 3 channels and use that as the value.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">case</span> ColorSpace<span class="symbol">.</span>Grayscale<span class="symbol">:</span>
 <span class="keyword">if</span> <span class="symbol">(</span>color<span class="symbol">.</span>R <span class="symbol">==</span> color<span class="symbol">.</span>G <span class="symbol">&amp;&amp;</span> color<span class="symbol">.</span>R <span class="symbol">==</span> color<span class="symbol">.</span>B<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// already grayscale</span>
 value<span class="number">1</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">short</span><span class="symbol">)</span><span class="symbol">(</span>color<span class="symbol">.</span>R <span class="symbol">*</span> <span class="number">39.0625</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="comment">// color is not grayscale, convert</span>
 value<span class="number">1</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">short</span><span class="symbol">)</span><span class="symbol">(</span><span class="symbol">(</span><span class="symbol">(</span>color<span class="symbol">.</span>R <span class="symbol">+</span> color<span class="symbol">.</span>G <span class="symbol">+</span> color<span class="symbol">.</span>B<span class="symbol">)</span> <span class="symbol">/</span> <span class="number">3.0</span><span class="symbol">)</span> <span class="symbol">*</span> <span class="number">39.0625</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 value<span class="number">2</span> <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 value<span class="number">3</span> <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 value<span class="number">4</span> <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="demo-application">Demo Application</h2>
<p>The usual sample application is available from the links at the
end of this article. The sample generates a random 256 colour
palette, then writes this to a temporary file using the
specified colour space. It then reads it back in, and displays
both palettes side by side for comparison.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2009-08-10 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/writing-photoshop-color-swatch-aco-files-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comReading Photoshop Color Swatch (aco) files using C#urn:uuid:a613a036-56f1-4668-bbe2-c782192200a92014-01-22T21:27:55Z2014-01-22T21:27:55Z<p>In a <a href="/post/loading-the-color-palette-from-a-bbm-lbm-image-file-using-csharp">previous article</a> I described how to read the colour
map from a DeluxePaint LBM/BBM file. In the next pair of
articles, I'm going to describe how to load and save colour
swatch files used by Photoshop (those with the <code>.aco</code>
extension).</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/loadphotoshopcolorswatch.png" class="gallery" title="A sample application showing loading of colour swatches from Photoshop colour swatch files" ><img src="https://images.cyotek.com/image/thumbnail/devblog/loadphotoshopcolorswatch.png" alt="A sample application showing loading of colour swatches from Photoshop colour swatch files" decoding="async" loading="lazy" /></a><figcaption>A sample application showing loading of colour swatches from Photoshop colour swatch files</figcaption></figure><h2 id="caveat-emptor">Caveat Emptor</h2>
<p>As usual, I'll start with a warning. I have a very limited set
of sample files to test with, so it may be that there's an error
in this code which means it can't handle all files. Certainly it
can't handle all colour spaces (more on that later). However,
I've tested it on a number of files download from the internet
without problems.</p>
<h2 id="structure-of-a-photoshop-colour-swatch-file">Structure of a Photoshop colour swatch file</h2>
<p>The structure of the <code>aco</code> file is straightforward, helped by
Adobe themselves publishing the <a href="http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1055819" rel="external nofollow noopener">specification</a> which is
something to appreciate. This article was created using the
October 2013 edition of this specification.</p>
<p>According to the specification, there's two versions of the
format both of which are are fairly similar. The specification
also implies that applications which support version 2 should
write a version 1 palette first, which would admirably solve
backwards compatibility problems. In practice this doesn't seem
to be the case, as some of the files I tested only had version 2
palettes in them.</p>
<p>The structure is simple. There's a 2-byte version code, followed
by 2-bytes describing the number of colours. Then, for each
colour, there are 10 further bytes, 2 each describing the colour
space and then four values to describe the colour. Version two
palettes also then follow this with a four byte integer
describing the length of the name, then the bytes which make up
said name.</p>
<table class="table table-condensed table-bordered">
 <thead>
 <tr>
 <th>Length
 </th>
 <th>Description
 </th>
 </tr>
 </thead>
 <tbody>
 <tr>
 <td>2</td>
 <td>Version</td>
 </tr>
 <tr>
 <td>2</td>
 <td>Number of colours</td>
 </tr>
 <tr>
 <td rowspan="2">count * 10 <em>(+ 4 + variable (version 2 only))</em>
 </td>
 <td>
 <p> Colour data</p>
 <table class="table table-condensed table-bordered">
 <thead>
 <tr>
 <th>Length
 </th>
 <th>Description
 </th>
 </tr>
 </thead>
 <tbody>
 <tr>
 <td>2</td>
 <td>Colour space</td>
 </tr>
 <tr>
 <td>2</td>
 <td>Colour data value 1</td>
 </tr>
 <tr>
 <td>2</td>
 <td>Colour data value 2</td>
 </tr>
 <tr>
 <td>2</td>
 <td>Colour data value 3</td>
 </tr>
 <tr>
 <td>2</td>
 <td>Colour data value 4</td>
 </tr>
 </tbody>
 </table>
 </td>
 </tr>
 <tr>
 <td>
 <p><em>Version 2 only</em></p>
 <table class="table table-condensed table-bordered">
 <thead>
 <tr>
 <th>Length
 </th>
 <th>Description
 </th>
 </tr>
 </thead>
 <tbody>
 <tr>
 <td>4</td>
 <td>Length of name string in characters</td>
 </tr>
 <tr>
 <td>length * 2</td>
 <td>Unicode code characters, two bytes per character</td>
 </tr>
 </tbody>
 </table>
 </td>
 </tr>
 </tbody>
</table>
<p>All the data in an <code>aco</code> file is stored in <a href="http://en.wikipedia.org/wiki/Endianness" rel="external nofollow noopener">big-endian</a>
format and therefore needs to be reversed on Windows systems.</p>
<p>Most colour spaces only use three of the four available values,
but regardless of how many are actually used, all must be
specified.</p>
<h2 id="colour-spaces">Colour Spaces</h2>
<p>I mentioned above that each colour has a description of what
colour space it belongs to. The specification defines the
following colour spaces:</p>
<table>
<thead>
<tr>
<th style="text-align: right;">Id</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: right;">0</td>
<td>RGB. The first three values in the colour data are red, green, and blue. They are full unsigned 16-bit values as in Apple's RGBColordata structure. Pure red = 65535, 0, 0.</td>
</tr>
<tr>
<td style="text-align: right;">1</td>
<td>HSB. The first three values in the colour data are hue, saturation, and brightness. They are full unsigned 16-bit values as in Apple's HSVColordata structure. Pure red = 0,65535, 65535.</td>
</tr>
<tr>
<td style="text-align: right;">2</td>
<td>CMYK. The four values in the colour data are cyan, magenta, yellow, and black. They are full unsigned 16-bit values. For example, pure cyan = 0,65535,65535,65535.</td>
</tr>
<tr>
<td style="text-align: right;">7</td>
<td>Lab. The first three values in the colour data are lightness, a chrominance, and b chrominance. Lightness is a 16-bit value from 0...10000. Chrominance components are each 16-bit values from -12800...12700. Gray values are represented by chrominance components of 0. Pure white = 10000,0,0.</td>
</tr>
<tr>
<td style="text-align: right;">8</td>
<td>Grayscale. The first value in the colour data is the gray value, from 0...10000.</td>
</tr>
</tbody>
</table>
<p>To avoid complicating matters, this article will concentrate on
<code>RGB</code> and <code>Grayscale</code> colour spaces, although I'll include the
basics of <code>HSV</code> too for if you have a conversion class kicking
around.</p>
<h2 id="reading-shortint-data-types-from-bytes">Reading short/int data types from bytes</h2>
<p>As I mentioned above, the values in this file format are all
big-endian. As Windows uses little-endian, we need to do some
bit shifting when we read each byte comprising either a <code>short</code>
(<code>Int16</code>) or an <code>int</code> (<code>Int32</code>), using the following helpers:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="selector-tag">///</span> <span class="selector-tag">&lt;summary&gt;</span>
<span class="selector-tag">///</span><span class="comment"> Reads a 16bit unsigned integer in big-endian format.</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;/summary&gt;</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;param name=&quot;stream&quot;&gt;</span><span class="comment">The stream to read the data from.&lt;/param&gt;</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;returns&gt;</span><span class="comment">The unsigned 16bit integer cast to an &lt;c&gt;Int32&lt;/c&gt;.&lt;/returns&gt;</span>
<span class="keyword">private</span> <span class="keyword">int</span> ReadInt<span class="number">16</span><span class="symbol">(</span>Stream stream<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> <span class="symbol">(</span>stream<span class="symbol">.</span>ReadByte<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">&lt;&lt;</span> <span class="number">8</span><span class="symbol">)</span> <span class="symbol">|</span> <span class="symbol">(</span>stream<span class="symbol">.</span>ReadByte<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">&lt;&lt;</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="selector-tag">///</span> <span class="selector-tag">&lt;summary&gt;</span>
<span class="selector-tag">///</span><span class="comment"> Reads a 32bit unsigned integer in big-endian format.</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;/summary&gt;</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;param name=&quot;stream&quot;&gt;</span><span class="comment">The stream to read the data from.&lt;/param&gt;</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;returns&gt;</span><span class="comment">The unsigned 32bit integer cast to an &lt;c&gt;Int32&lt;/c&gt;.&lt;/returns&gt;</span>
<span class="keyword">private</span> <span class="keyword">int</span> ReadInt<span class="number">32</span><span class="symbol">(</span>Stream stream<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span>stream<span class="symbol">.</span>ReadByte<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">&lt;&lt;</span> <span class="number">24</span><span class="symbol">)</span> <span class="symbol">|</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span>stream<span class="symbol">.</span>ReadByte<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">&lt;&lt;</span> <span class="number">16</span><span class="symbol">)</span> <span class="symbol">|</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span>stream<span class="symbol">.</span>ReadByte<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">&lt;&lt;</span> <span class="number">8</span><span class="symbol">)</span> <span class="symbol">|</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span>stream<span class="symbol">.</span>ReadByte<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">&lt;&lt;</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>The <code>&lt;&lt; 0</code> bit-shift in the above methods is technically
unnecessary and can be removed. However, I find it makes the
intent of the code clearer.</p>
</blockquote>
<h2 id="reading-strings">Reading strings</h2>
<p>For version 2 files, we need to read a string, which is
comprised of two bytes per character. Fortunately for us, the
.NET Framework includes a <code>BigEndianUnicode</code> (<a href="http://msdn.microsoft.com/en-us/library/vstudio/system.text.encoding.bigendianunicode(v=vs.90).aspx" rel="external nofollow noopener">MSDN</a>) class
that we can use to convert a byte array to a string. As this
class does the endian conversion for us, we don't need to do
anything special when reading the bytes.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="selector-tag">///</span> <span class="selector-tag">&lt;summary&gt;</span>
<span class="selector-tag">///</span><span class="comment"> Reads a unicode string of the specified length.</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;/summary&gt;</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;param name=&quot;stream&quot;&gt;</span><span class="comment">The stream to read the data from.&lt;/param&gt;</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;param name=&quot;length&quot;&gt;</span><span class="comment">The number of characters in the string.&lt;/param&gt;</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;returns&gt;</span><span class="comment">The string read from the stream.&lt;/returns&gt;</span>
<span class="keyword">private</span> <span class="keyword">string</span> ReadString<span class="symbol">(</span>Stream stream<span class="symbol">,</span> <span class="keyword">int</span> length<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">]</span> buffer<span class="symbol">;</span>

 buffer <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">byte</span><span class="symbol">[</span>length <span class="symbol">*</span> <span class="number">2</span><span class="symbol">]</span><span class="symbol">;</span>

 stream<span class="symbol">.</span>Read<span class="symbol">(</span>buffer<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> buffer<span class="symbol">.</span>Length<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> Encoding<span class="symbol">.</span>BigEndianUnicode<span class="symbol">.</span>GetString<span class="symbol">(</span>buffer<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="reading-the-file">Reading the file</h2>
<p>With the preliminaries done with, lets read the file!</p>
<p>We start off by reading the file version so we know how to
process the rest of the file, or at least the first part of it.
If we don't have a version 1 or version 2 file, then we simply
abort.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> <span class="symbol">(</span>Stream stream <span class="symbol">=</span> File<span class="symbol">.</span>OpenRead<span class="symbol">(</span>fileName<span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 FileVersion version<span class="symbol">;</span>

 <span class="comment">// read the version, which occupies two bytes</span>
 version <span class="symbol">=</span> <span class="symbol">(</span>FileVersion<span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>ReadInt<span class="number">16</span><span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>version <span class="symbol">!=</span> FileVersion<span class="symbol">.</span>Version<span class="number">1</span> <span class="symbol">&amp;&amp;</span> version <span class="symbol">!=</span> FileVersion<span class="symbol">.</span>Version<span class="number">2</span><span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="string">&quot;Invalid version information.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 colorPalette <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ReadSwatches<span class="symbol">(</span>stream<span class="symbol">,</span> version<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>version <span class="symbol">==</span> FileVersion<span class="symbol">.</span>Version<span class="number">1</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 version <span class="symbol">=</span> <span class="symbol">(</span>FileVersion<span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>ReadInt<span class="number">16</span><span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>version <span class="symbol">==</span> FileVersion<span class="symbol">.</span>Version<span class="number">2</span><span class="symbol">)</span>
 colorPalette <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ReadSwatches<span class="symbol">(</span>stream<span class="symbol">,</span> version<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>In the above example, if a file has <em>both</em> versions, then I read
them both (assuming the file contains version 1 followed by
version 2). However, there's no point in doing this if you
aren't going to do anything with the swatch name. For example,
this demonstration program converts all the values into the
standard .NET <code>Color</code> structure - which doesn't allow you to
set the <code>Name</code> property. In this scenario, clearly it's a waste
of time reading the version 2 data if you've just read the data
from version 1. However, if you are storing the data in an
object that supports the name, then it's probably a good idea to
discard the previously read data and re-read the version 2 data.</p>
<h2 id="reading-colour-data">Reading colour data</h2>
<p>As the two documented file formats are almost identical, we can
use the same code to handle reading the data, and then perform a
little bit extra for the newer file format. The core of the code
which reads the colour data looks like this.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="comment">// read the number of colors, which also occupies two bytes</span>
colorCount <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ReadInt<span class="number">16</span><span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> colorCount<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
<span class="symbol">{</span>
 ColorSpace colorSpace<span class="symbol">;</span>
 <span class="keyword">int</span> value<span class="number">1</span><span class="symbol">;</span>
 <span class="keyword">int</span> value<span class="number">2</span><span class="symbol">;</span>
 <span class="keyword">int</span> value<span class="number">3</span><span class="symbol">;</span>
 <span class="keyword">int</span> value<span class="number">4</span><span class="symbol">;</span>

 <span class="comment">// again, two bytes for the color space</span>
 colorSpace <span class="symbol">=</span> <span class="symbol">(</span>ColorSpace<span class="symbol">)</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ReadInt<span class="number">16</span><span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// then the four values which comprise each color</span>
 value<span class="number">1</span> <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ReadInt<span class="number">16</span><span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">;</span>
 value<span class="number">2</span> <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ReadInt<span class="number">16</span><span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">;</span>
 value<span class="number">3</span> <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ReadInt<span class="number">16</span><span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">;</span>
 value<span class="number">4</span> <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ReadInt<span class="number">16</span><span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// and finally, the name of the swatch (version2 only)</span>
 <span class="keyword">if</span> <span class="symbol">(</span>version <span class="symbol">==</span> FileVersion<span class="symbol">.</span>Version<span class="number">2</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> length<span class="symbol">;</span>
 <span class="keyword">string</span> name<span class="symbol">;</span>

 length <span class="symbol">=</span> ReadInt<span class="number">32</span><span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">;</span>
 name <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ReadString<span class="symbol">(</span>stream<span class="symbol">,</span> length<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="translating-the-colour-spaces">Translating the colour spaces</h2>
<p>Once we've read the colour space and the four values of the
colour data, we need to process it.</p>
<p>The first space, RGB, is simple enough. The Adobe format is
using the range 0-65535, so we just need to convert that to the
standard 0-255 range:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">switch</span> <span class="symbol">(</span>colorSpace<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">case</span> ColorSpace<span class="symbol">.</span>Rgb<span class="symbol">:</span>
 <span class="keyword">int</span> red<span class="symbol">;</span>
 <span class="keyword">int</span> green<span class="symbol">;</span>
 <span class="keyword">int</span> blue<span class="symbol">;</span>

 red <span class="symbol">=</span> value<span class="number">1</span> <span class="symbol">/</span> <span class="number">256</span><span class="symbol">;</span> <span class="comment">// 0-255</span>
 green <span class="symbol">=</span> value<span class="number">2</span> <span class="symbol">/</span> <span class="number">256</span><span class="symbol">;</span> <span class="comment">// 0-255</span>
 blue <span class="symbol">=</span> value<span class="number">3</span> <span class="symbol">/</span> <span class="number">256</span><span class="symbol">;</span> <span class="comment">// 0-255</span>

 results<span class="symbol">.</span>Add<span class="symbol">(</span>Color<span class="symbol">.</span>FromArgb<span class="symbol">(</span>red<span class="symbol">,</span> green<span class="symbol">,</span> blue<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
</pre>
</figure>
<p>Next is HSL. How you process that depends on the class you are
using, and the range of values it accepts.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">case</span> ColorSpace<span class="symbol">.</span>Hsb<span class="symbol">:</span>
 <span class="keyword">double</span> hue<span class="symbol">;</span>
 <span class="keyword">double</span> saturation<span class="symbol">;</span>
 <span class="keyword">double</span> brightness<span class="symbol">;</span>

 hue <span class="symbol">=</span> value<span class="number">1</span> <span class="symbol">/</span> <span class="number">182.04</span><span class="symbol">;</span> <span class="comment">// 0-359</span>
 saturation <span class="symbol">=</span> value<span class="number">2</span> <span class="symbol">/</span> <span class="number">655.35</span><span class="symbol">;</span> <span class="comment">// 0-1</span>
 brightness <span class="symbol">=</span> value<span class="number">3</span> <span class="symbol">/</span> <span class="number">655.35</span><span class="symbol">;</span> <span class="comment">// 0-1</span>

 results<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> HslColor<span class="symbol">(</span>hue<span class="symbol">,</span> saturation<span class="symbol">,</span> brightness<span class="symbol">)</span><span class="symbol">.</span>ToRgbColor<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
</pre>
</figure>
<p>The last colour space we can easily support is gray scale.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">case</span> AdobePhotoshopColorSwatchColorSpace<span class="symbol">.</span>Grayscale<span class="symbol">:</span>

 <span class="keyword">int</span> gray<span class="symbol">;</span>

 <span class="comment">// Grayscale.</span>
 <span class="comment">// The first value in the color data is the gray value, from 0...10000.</span>
 gray <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span>value<span class="number">1</span> <span class="symbol">/</span> <span class="number">39.0625</span><span class="symbol">)</span><span class="symbol">;</span>

 results<span class="symbol">.</span>Add<span class="symbol">(</span>Color<span class="symbol">.</span>FromArgb<span class="symbol">(</span>gray<span class="symbol">,</span> gray<span class="symbol">,</span> gray<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
</pre>
</figure>
<p>Files using the Lab or CMYK spaces will throw an exception as
these are beyond the scope of this example.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">default</span><span class="symbol">:</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>Format<span class="symbol">(</span><span class="string">&quot;Color space &#39;{0}&#39; not supported.&quot;</span><span class="symbol">,</span> colorSpace<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Although none of the sample files I tested mixed colour spaces,
they were either all RGB, all Lab or all CMYK, the specification
suggests that it's at least possible. In this case, throwing an
exception might not be the right idea as it could be possible to
load other colours. Therefore it may be a better idea to just
ignore such errors to allow any valid data to be read.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>As with <a href="/post/loading-the-color-palette-from-a-bbm-lbm-image-file-using-csharp">reading LBM colour maps</a>, reading the Photoshop
colour swatches was also quite an easy process.</p>
<p>You can download a fully working sample from the link below, and
my next article will reverse the process to allow you to write
your own <code>aco</code> files.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2014-01-22 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/reading-photoshop-color-swatch-aco-files-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comLoading the color palette from a BBM/LBM image file using C#urn:uuid:71168b3e-e6fa-4939-aff9-d8ad469c95e72014-01-11T15:26:50Z2014-01-11T15:26:50Z<p>I took a break from arguing with our GIF decoder to take a quick
look at the BBM format as I have a few files in that format
containing colour palettes I wished to extract. When I looked
into this, I found a BBM file is essentially an LBM file without
any image data, so I set to work at writing a new palette
serializer for reading and writing the palette files. This
article describes how to read the palettes from BBM and LBM
files.</p>
<blockquote>
<p>Note: I <em>only</em> cover loading of color palette data in this
article. The image data I don't even look at - this article
does not represent a full LBM decoder.</p>
</blockquote>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/lbmpaletteloader.png" class="gallery" title="A sample application showing loading of palettes from BBM/LBM files" ><img src="https://images.cyotek.com/image/thumbnail/devblog/lbmpaletteloader.png" alt="A sample application showing loading of palettes from BBM/LBM files" decoding="async" loading="lazy" /></a><figcaption>A sample application showing loading of palettes from BBM/LBM files</figcaption></figure><h2 id="caveat-emptor">Caveat Emptor</h2>
<p>The sample code presented in this article took all of an hour to
write and has been tested on a pretty small selection of images.
It only handles 8bit LBM files (possibly lower depths too, but I
have not tested this). And, who knows, I might be
misinterpreting the specification or missing a chunk of vital
information.</p>
<h2 id="overview-of-bbmlbm-files">Overview of BBM/LBM Files</h2>
<p>Information is sketchy so I may well be wrong in particulars,
but a BBM file essentially seems to be a LBM without a full
image - in the files I've experimented with, there are header
chunks describing a bitmap, but no real data. A LBM file is of
course a full graphic, most popular (I think) by DeluxePaint on
both the Amiga and MS DOS. As I said though, this information is
my understanding and might be totally wrong. Luckily enough, or
our purposes it doesn't matter. However, to keep things simple,
for the rest of this article I'm going to refer to LBM, but you
can consider this interchangeable with BBM.</p>
<p>The LBM format is more formally known as <em>ILBM - IFF Interleaved
Bitmap</em>. It is built upon <em>&quot;EA IFF 85&quot; Standard for Interchange
Format Files</em>. Both formats were devised by Electronic Arts as a
standard means of sharing data between systems, their example of
writing a theme song with a Macintosh score editor and
incorporating it into an Amiga game summing it up neatly.</p>
<p>More information on these formats can be found in specification
documents <a href="http://home.comcast.net/%7Eerniew/lwsdk/docs/filefmts/eaiff85.html" rel="external nofollow noopener">here</a> and <a href="http://home.comcast.net/%7Eerniew/lwsdk/docs/filefmts/ilbm.html" rel="external nofollow noopener">here</a>.</p>
<h2 id="reading-an-lbm-file">Reading an LBM file</h2>
<p>An IFF file is comprised of chunks of data. Each chunk is
prefixed with a four character ID, the size of the data in the
chunk, and then the chunk data itself.</p>
<p>There is one oddity in that if the size of the chunk is odd, an
extra padding byte is added to the chunk data to make it even.
This padding byte is not included in the size field, so if it's
odd you must make sure you read (and discard) the padding byte.</p>
<p>Oh yes, and there's one other important detail. I don't know if
this is specific to all IFF format files, or just LBM, but
integers and longs are in <a href="http://en.wikipedia.org/wiki/Endianness#Big-endian" rel="external nofollow noopener">big-endian</a> format, so we need to
convert these when we read them.</p>
<h2 id="the-cmap-chunk">The CMAP chunk</h2>
<p>The only section of the LBM file we are interested in is the
<code>CMAP</code> chunk, which describes an 8bit colour palette. According
to the specification however, it's optional so it's entirely
possible that not all LBM files contain a <code>CMAP</code>. Also, only
8bit (or lower?) LBM files will contain a <code>CMAP</code>, as they only
support RGB channels. 24bit or 32bit images won't have one as
there's no scope for storing alpha channels.</p>
<p>The data section of a <code>CMAP</code> chunk is as simple as it gets - one
set of 3 bytes for each colour describing the red, green and
blue channels. The size attribute is the number of colours * 3.</p>
<h2 id="other-chunks">Other Chunks</h2>
<p>Although I'm not reading other chunks, I still have to pay
attention to some of them.</p>
<p>Firstly, the <code>FORM</code> chunk describes an IFF document. So, if the
file we read doesn't start with this, it's not a valid IFF file
and we shouldn't continue reading.</p>
<p>The second chunk we at least want to identify is the chunk that
states if this is an actual image. The specification states that
this should be <code>ILBM</code>, but the sample images I've worked with
use a different header which is <code>PBM</code> for <em>Planar BitMap</em>. Note
there's a trailing space on this ID as the specification states
ID's are four ASCII characters long. As in both cases the <code>CMAP</code>
section is the same, I look for either of these.</p>
<p>Anything else will be discarded.</p>
<h2 id="reading-the-file">Reading the file</h2>
<p>After opening the file, the first thing we do is read the first
four bytes and convert these to an ASCII string. If the string
reads <code>FORM</code>, we know we have an IFF document and continue
reading. Otherwise, we throw an <code>InvalidDataException</code>
exception.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> <span class="symbol">(</span>FileStream stream <span class="symbol">=</span> File<span class="symbol">.</span>OpenRead<span class="symbol">(</span>fileName<span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">]</span> buffer<span class="symbol">;</span>
 <span class="keyword">string</span> header<span class="symbol">;</span>

 <span class="comment">// read the FORM header that identifies the document as an IFF file</span>
 buffer <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">byte</span><span class="symbol">[</span><span class="number">4</span><span class="symbol">]</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>Read<span class="symbol">(</span>buffer<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> buffer<span class="symbol">.</span>Length<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>Encoding<span class="symbol">.</span>ASCII<span class="symbol">.</span>GetString<span class="symbol">(</span>buffer<span class="symbol">)</span> <span class="symbol">!=</span> <span class="string">&quot;FORM&quot;</span><span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="string">&quot;Form header not found.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>Next we read the size of the data contained within the <code>FORM</code>
chunk. As we aren't checking for nested chunks nor reading all
the data, we can safely ignore this.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="comment">// the next value is the size of all the data in the FORM chunk</span>
 <span class="comment">// We don&#39;t actually need this value, but we have to read it</span>
 <span class="comment">// regardless to advance the stream</span>
 <span class="keyword">this</span><span class="symbol">.</span>ReadInt<span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>Time for another sanity check, this time to verify we are
reading an image, be it Planar (<code>PBM </code>) or Interleaved (<code>ILBM</code>).</p>
<p>For some reason this chunk doesn't include a size, so we don't
attempt to read any more bytes as the next byte is the start of
a new chunk.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="comment">// read either the PBM or ILBM header that identifies this document as an image file</span>
 stream<span class="symbol">.</span>Read<span class="symbol">(</span>buffer<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> buffer<span class="symbol">.</span>Length<span class="symbol">)</span><span class="symbol">;</span>
 header <span class="symbol">=</span> Encoding<span class="symbol">.</span>ASCII<span class="symbol">.</span>GetString<span class="symbol">(</span>buffer<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>header <span class="symbol">!=</span> <span class="string">&quot;PBM &quot;</span> <span class="symbol">&amp;&amp;</span> header <span class="symbol">!=</span> <span class="string">&quot;ILBM&quot;</span><span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidDataException<span class="symbol">(</span><span class="string">&quot;Bitmap header not found.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>The reset of the routine is going to load one chunk of data from
the file at a time, and either discard it or process it.</p>
<p>First, we read 4 bytes that will be the ID of the chunk. We also
need the size of the chunk, regardless of whether we use it or
not, so we'll read that too.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">while</span> <span class="symbol">(</span>stream<span class="symbol">.</span>Read<span class="symbol">(</span>buffer<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> buffer<span class="symbol">.</span>Length<span class="symbol">)</span> <span class="symbol">==</span> buffer<span class="symbol">.</span>Length<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> chunkLength<span class="symbol">;</span>

 chunkLength <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ReadInt<span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>As we are only interested in <code>CMAP</code> chunks, if the pending chunk
has any other type of ID, we skip the remainder of the chunk, as
identified by <code>chunkLength</code> read earlier. If we can, we just
move the current position in the stream ahead, but if we can't
(can't think why not!) then we just read and discard bytes until
done.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">if</span> <span class="symbol">(</span>Encoding<span class="symbol">.</span>ASCII<span class="symbol">.</span>GetString<span class="symbol">(</span>buffer<span class="symbol">)</span> <span class="symbol">!=</span> <span class="string">&quot;CMAP&quot;</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// some other LBM chunk, skip it</span>
 <span class="keyword">if</span> <span class="symbol">(</span>stream<span class="symbol">.</span>CanSeek<span class="symbol">)</span>
 stream<span class="symbol">.</span>Seek<span class="symbol">(</span>chunkLength<span class="symbol">,</span> SeekOrigin<span class="symbol">.</span>Current<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> chunkLength<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 stream<span class="symbol">.</span>ReadByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>Aha! We finally found the <code>CMAP</code> chunk. Now it's just a
straightforward reading of colours. <code>chunkLength</code> is the number
of colours / 3 (as each colour is represented by 3 bytes), so a
simple loop to read each triplet and add them to our results
collection is all we need.</p>
<p>Then, we exit out of the <code>while</code> loop - no pointing reading the
entire file now that we have what we wanted.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="comment">// color map chunk!</span>
 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> chunkLength <span class="symbol">/</span> <span class="number">3</span><span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> r<span class="symbol">;</span>
 <span class="keyword">int</span> g<span class="symbol">;</span>
 <span class="keyword">int</span> b<span class="symbol">;</span>

 r <span class="symbol">=</span> stream<span class="symbol">.</span>ReadByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 g <span class="symbol">=</span> stream<span class="symbol">.</span>ReadByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 b <span class="symbol">=</span> stream<span class="symbol">.</span>ReadByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 colorPalette<span class="symbol">.</span>Add<span class="symbol">(</span>Color<span class="symbol">.</span>FromArgb<span class="symbol">(</span>r<span class="symbol">,</span> g<span class="symbol">,</span> b<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="comment">// all done so stop reading the rest of the file</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>If we are still in the loop however, then we need to check our
<code>chunkLength</code> value. If it's odd, we read and discard the
padding byte - otherwise you'll be out of alignment and won't
hit any more chunk headers, except by accident.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="comment">// chunks always contain an even number of bytes even if the recorded length is odd</span>
 <span class="comment">// if the length is odd, then there&#39;s a padding byte in the file - just read and discard</span>
 <span class="keyword">if</span> <span class="symbol">(</span>chunkLength <span class="symbol">%</span> <span class="number">2</span> <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">)</span>
 stream<span class="symbol">.</span>ReadByte<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> colorPalette<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="converting-big-endian-to-little-endian">Converting big-endian to little-endian</h2>
<p>At the start of the article I mentioned that the numeric data
types in an LBM image are stored as big-endian. On the Windows
platform, we use little-endian. So when we try to read the chunk
length from the file... well, it's just not going to work.</p>
<p>As bit shifting still jellies my brain, I took to Stack Overflow
which provided me with a <a href="https://stackoverflow.com/a/14401341/148962" rel="external nofollow noopener">function</a> for converting four bytes
of big-endian data into a little-endian integer.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">int</span> ReadInt<span class="symbol">(</span>Stream stream<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">]</span> buffer<span class="symbol">;</span>

 <span class="comment">// big endian conversion: http://stackoverflow.com/a/14401341/148962</span>

 buffer <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">byte</span><span class="symbol">[</span><span class="number">4</span><span class="symbol">]</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>Read<span class="symbol">(</span>buffer<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> buffer<span class="symbol">.</span>Length<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> <span class="symbol">(</span>buffer<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span> <span class="symbol">&lt;&lt;</span> <span class="number">24</span><span class="symbol">)</span> <span class="symbol">|</span> <span class="symbol">(</span>buffer<span class="symbol">[</span><span class="number">1</span><span class="symbol">]</span> <span class="symbol">&lt;&lt;</span> <span class="number">16</span><span class="symbol">)</span> <span class="symbol">|</span> <span class="symbol">(</span>buffer<span class="symbol">[</span><span class="number">2</span><span class="symbol">]</span> <span class="symbol">&lt;&lt;</span> <span class="number">8</span><span class="symbol">)</span> <span class="symbol">|</span> buffer<span class="symbol">[</span><span class="number">3</span><span class="symbol">]</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>So in our sample, we read our 4 bytes, shift their bits around
and return the result.</p>
<h2 id="that-was-easy">That was easy</h2>
<p>That was fairly straightforward! Well, if I ignore the endian
conversion. And I'm sure if I decided to read the <code>BODY</code> chunk
and actually start decoding the image itself I'd be tearing out
my hair, but the bit I actually wanted could hardly have been
easier.</p>
<p>As usual, a fully working sample is attached to this post.</p>
<h2 id="further-thoughts">Further Thoughts</h2>
<p>As I wrap up this post it occurred to me I forgot to add
anything in for if it's a valid LBM image, but doesn't contain a
<code>CMAP</code> section. Although the clue would be in the empty list
that's returned.</p>
<p>I also didn't even begin to look at writing a BBM file... this
will probably be the next thing I take a look at. Unless I get
distracted by Microsoft's (old?) palette format which I
discovered is also a variant of IFF and should be just as easy
to read.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2014-01-11 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/loading-the-color-palette-from-a-bbm-lbm-image-file-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comHow to be notified when your application is activated and deactivatedurn:uuid:108e9c03-80dc-4e9c-9c2e-0ea57ccf9cb62013-12-29T13:19:24Z2013-12-29T13:19:24Z<p>I recently had a requirement where a user was able to perform an
action externally to my application, and my application then had
to detect this for processing.</p>
<p>I could of course just had a poller running away in the
background to check, but as the requirement also needed user
input, why not just wait until the user switched back to my
application, then check and deal with accordingly?</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/appactivation.png" class="gallery" title="A simple demonstration of application activation and deactivation" ><img src="https://images.cyotek.com/image/thumbnail/devblog/appactivation.png" alt="A simple demonstration of application activation and deactivation" decoding="async" loading="lazy" /></a><figcaption>A simple demonstration of application activation and deactivation</figcaption></figure><h2 id="the-activated-and-deactivate-events">The Activated and Deactivate events</h2>
<p>Your standard <code>Form</code> object has an <code>Activated</code> event and a
<code>Deactivate</code> event that (very logically!) are fired when your
form is activated and deactivated.</p>
<p>Brilliant! Well, it would be - if your application is a single
form application that doesn't show any window <em>ever</em>. Including
message boxes. Still, you could do it that way, if your
processing was absolute and always done there and then. In my
case, I had to give the user a choice - if they said no, then I
would simply ask them again later (ie the next time the
application was activated). But if I stuck with the <code>Activated</code>
event it would mean they would get prompted after displaying
another dialog in my application... probably not the sort of
behaviour you want to exhibit.</p>
<h2 id="the-wm_activateapp-message">The WM_ACTIVATEAPP message</h2>
<p>So how does Windows do it? It does it via the use of the
<code>WM_ACTIVATEAPP</code> (<a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms632614(v=vs.85).aspx" rel="external nofollow noopener">MSDN reference</a>). Windows sends this
message when a window belonging to a different application than
the active window is about to be activated. It's also good
enough enough to send it to the window about to be deactivated
too, so this one message can cover both activation and
deactivation.</p>
<h2 id="overriding-wndproc">Overriding WndProc</h2>
<p>Even after all these years of being a C# developer, I still
marvel at just how easy this stuff is. In my VB6 days, I could
(and did) do exactly the same thing, but it was difficult to
implement and fun to debug.</p>
<p>To handle Windows Messages in C# we just need to override the
<code>WndProc</code> method on your <code>Form</code>. Then we can check for the
<code>WM_ACTIVATEAPP</code> message arriving and handle it accordingly.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">const</span> <span class="keyword">int</span> WM_ACTIVATEAPP <span class="symbol">=</span> <span class="number">0x1C</span><span class="symbol">;</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> WndProc<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>Msg <span class="symbol">==</span> WM_ACTIVATEAPP<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>WParam <span class="symbol">!=</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// the application is getting activated</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="comment">// the application is getting deactivated</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>WndProc<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>According to the MSDN docs, <code>lParam</code> contains the thread
identifier, however we can safely ignore this. <code>wParam</code> (the
<code>WParam</code> property on the <code>Message</code> struct) is the important bit.
It's a simple true or false value (true for activation, false
otherwise) and so we just need to check it isn't zero and we're
good to go.</p>
<h2 id="add-some-events">Add some events</h2>
<p>You could just stick that override in your applications form and
use it like that, but as always we should think a little bit
about the future and promote some code re-use! In this case, I'm
going to create a pair of events and add them (along with the
override) to a base class, so next time I want this
functionality the events are sitting there waiting.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">class</span> BaseApplicationForm <span class="symbol">:</span> Form
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">event</span> EventHandler ApplicationActivated<span class="symbol">;</span>

 <span class="keyword">public</span> <span class="keyword">event</span> EventHandler ApplicationDeactivated<span class="symbol">;</span>

 <span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> OnApplicationActivated<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 EventHandler handler<span class="symbol">;</span>

 handler <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ApplicationActivated<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>handler <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 handler<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">,</span> e<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> OnApplicationDeactivated<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 EventHandler handler<span class="symbol">;</span>

 handler <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ApplicationDeactivated<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>handler <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 handler<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">,</span> e<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> WndProc<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>Msg <span class="symbol">==</span> NativeMethods<span class="symbol">.</span>WM_ACTIVATEAPP<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>WParam <span class="symbol">!=</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnApplicationActivated<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnApplicationDeactivated<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>WndProc<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="a-simple-demonstration">A simple demonstration</h2>
<p>You can download the demonstration code from the end of this
post, but here's a simple example of the code in use:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> MainForm <span class="symbol">:</span> BaseApplicationForm
<span class="symbol">{</span>
 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnActivated<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnActivated<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>LogEvent<span class="symbol">(</span><span class="string">&quot;Activated&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnDeactivate<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnDeactivate<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>LogEvent<span class="symbol">(</span><span class="string">&quot;Deactivate&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnApplicationActivated<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnApplicationActivated<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>LogEvent<span class="symbol">(</span><span class="string">&quot;ApplicationActivated&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnApplicationDeactivated<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnApplicationDeactivated<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>LogEvent<span class="symbol">(</span><span class="string">&quot;ApplicationDeactivated&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">void</span> LogEvent<span class="symbol">(</span><span class="keyword">string</span> text<span class="symbol">)</span>
 <span class="symbol">{</span>
 logTextBox<span class="symbol">.</span>AppendText<span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>Format<span class="symbol">(</span><span class="string">&quot;{0}\t{1}\n\n&quot;</span><span class="symbol">,</span> DateTime<span class="symbol">.</span>UtcNow<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="string">&quot;hh:mm:ss&quot;</span><span class="symbol">)</span><span class="symbol">,</span> text<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The <code>Activated</code>, <code>Deactivate</code>, <code>ApplicationActivated</code> and
<code>ApplicationDeactivated</code> events are all being used (well, their
overrides are) to log information. If you run the example, you
will see that <code>Activated</code> and <code>Deactivate</code> are called whenever
the form itself is processed, ie from opening a dialog or
displaying a message box, whilst the <code>ApplicationActivated</code> and
<code>ApplicationDeactivated</code> events are only called when I switch to
another application.</p>
<h2 id="closing-thoughts">Closing Thoughts</h2>
<p>In the above example, the <code>ApplicationActivated</code> and
<code>ApplicationDeactivated</code> events are raised as expected -
including if you already have a modal dialog open in your
application. Just something to keep in mind if you use this sort
of functionality to prompt the user for an action. If your
events are showing their own modal dialog, that's fine, but if
they are trying to do something else, such as set focus to a
control, or open a floating window - then you're likely to run
into problems.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2013-12-29 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/how-to-be-notified-when-your-application-is-activated-and-deactivated .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comExtending the LabelEdit functionality of a TreeView to include validationurn:uuid:9f45ec5f-59c3-4fff-8fca-511e794277912013-10-28T09:14:14Z2013-10-28T09:14:14Z<p>In my <a href="/post/specifying-custom-text-when-using-the-labeledit-functionality-of-a-treeview">last post</a> I described how to extend the default label
edit functionality of a <code>TreeView</code> control to be somewhat more
flexible, by allowing you to specify custom text other than
blindly using the text of the <code>TreeNode</code> being edited.</p>
<p>This post will extend the original code to include custom
validation. For example, you may wish to restrict the available
characters, or check to see if the value entered doesn't match
an existing value.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/treeview-labeledit-validation.gif" class="gallery" title="An example project showing the use of validation in node editing, and preserving the entered text between errors" ><img src="https://images.cyotek.com/image/thumbnail/devblog/treeview-labeledit-validation.gif" alt="An example project showing the use of validation in node editing, and preserving the entered text between errors" decoding="async" loading="lazy" /></a><figcaption>An example project showing the use of validation in node editing, and preserving the entered text between errors</figcaption></figure><h2 id="getting-started">Getting started</h2>
<p>The code in this article assumes you have a base class that
includes the enhancements from the <a href="/post/specifying-custom-text-when-using-the-labeledit-functionality-of-a-treeview">previous post</a>, or you
can download the complete example from the link at the end of
the article.</p>
<p>Firstly we need to add a new event that will present the
proposed change and allow the implementer to validate it. As
this is event won't allow the label to be modified, we can use
the original <code>NodeLabelEditEventArgs</code> class rather than the
custom class we created in the previous post.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Behavior&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">event</span> EventHandler<span class="symbol">&lt;</span>NodeLabelEditEventArgs<span class="symbol">&gt;</span> ValidateLabelEdit<span class="symbol">;</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> OnValidateLabelEdit<span class="symbol">(</span>NodeLabelEditEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 EventHandler<span class="symbol">&lt;</span>NodeLabelEditEventArgs<span class="symbol">&gt;</span> handler<span class="symbol">;</span>

 handler <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ValidateLabelEdit<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>handler <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 handler<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">,</span> e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>We also need a backing variable to store the current text string
in the event of a validation error in order to correctly
re-initialize the edit field.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">string</span> _postEditText<span class="symbol">;</span>
</pre>
</figure>
<h2 id="raising-the-validation-event">Raising the validation event</h2>
<p>In our extended <code>TreeView</code> component, we had overridden
<code>OnAfterLabelEdit</code> in order to obtain the new display text after
a successful edit. We're going to modify this override slightly
in order to handle validation.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnAfterLabelEdit<span class="symbol">(</span>NodeLabelEditEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Label <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span> <span class="comment">// if the user cancelled the edit this event is still raised, just with a null label</span>
 <span class="symbol">{</span>
 NodeLabelEditEventArgs validateEventArgs<span class="symbol">;</span>

 e<span class="symbol">.</span>CancelEdit <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span> <span class="comment">// cancel the built in operation so we can substitute our own</span>

 validateEventArgs <span class="symbol">=</span> <span class="keyword">new</span> NodeLabelEditEventArgs<span class="symbol">(</span>e<span class="symbol">.</span>Node<span class="symbol">,</span> e<span class="symbol">.</span>Label<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>OnValidateLabelEdit<span class="symbol">(</span>validateEventArgs<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// validate the users input</span>

 <span class="keyword">if</span> <span class="symbol">(</span>validateEventArgs<span class="symbol">.</span>CancelEdit<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// if the users input was invalid, enter edit mode again using the previously entered text to give them the chance to correct it</span>
 _postEditText <span class="symbol">=</span> e<span class="symbol">.</span>Label<span class="symbol">;</span>
 e<span class="symbol">.</span>Node<span class="symbol">.</span>BeginEdit<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="comment">// -- snip --</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnAfterLabelEdit<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Here, we automatically cancel the default handling of the label
edit, as regardless of whether validation passes or not, we'll
be updating node text manually.</p>
<p>First we raise our <code>ValidateLabelEdit</code> event, passing in the
<code>TreeNode</code> to be edited, and the proposed label text. If the
<code>CancelEdit</code> property of the passed <code>NodeLabelEditEventArgs</code> is
set to <code>true</code>, then validation has failed.</p>
<p>If validation does fail, we update the <code>_postEditText</code> variable
we defined earlier with the current label text, then
automatically switch the control back into label editing mode.</p>
<h2 id="changing-how-label-edits-are-initialized">Changing how label edits are initialized</h2>
<p>There's just one thing left to change. As with <code>OnAfterLabelEdit</code>, we had also overridden <code>OnBeforeLabelEdit</code> in order to modify the text displayed in the edit field. We'll need to modify this to provide the current label value if a validation error occurs, otherwise the text will reset to whatever the original value was before editing started. Of course, in the event of a validation error you want he user to be able to retry with the modified value to allow correction of the error. To do this, we'll modify the block of code that obtained the text to display to use the new <code>_postEditText</code> variable and to skip raising the <code>RequestEditText</code> event if its set. We'll also reset the <code>_postEditText</code> to <code>null</code> so that the next time an edit is started, it reverts to the original behaviour. Unless it's another validation error for the current edit operation of course!</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnBeforeLabelEdit<span class="symbol">(</span>NodeLabelEditEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 NodeRequestTextEventArgs editTextArgs<span class="symbol">;</span>

 <span class="comment">// get the text to apply to the label</span>
 editTextArgs <span class="symbol">=</span> <span class="keyword">new</span> NodeRequestTextEventArgs<span class="symbol">(</span>e<span class="symbol">.</span>Node<span class="symbol">,</span> _postEditText <span class="symbol">??</span> e<span class="symbol">.</span>Node<span class="symbol">.</span>Text<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_postEditText <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnRequestEditText<span class="symbol">(</span>editTextArgs<span class="symbol">)</span><span class="symbol">;</span>
 _postEditText <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>

 <span class="comment">// -- snip --</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnBeforeLabelEdit<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>And that is it. Extremely simple, but very useful if you need to
validate this sort of input!</p>
<h2 id="sample-application">Sample application</h2>
<p>The sample project available with this article demonstrates
validation, as shown in the following snippet.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> subclassedTreeView_ValidateLabelEdit<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> NodeLabelEditEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 TreeNode<span class="symbol">[</span><span class="symbol">]</span> matchingNodes<span class="symbol">;</span>

 <span class="comment">// Check to make sure the value the user enters isn&#39;t used by any other node than the current.</span>
 <span class="comment">// This code assumes that all names in the tree are unique, regardless of level</span>
 matchingNodes <span class="symbol">=</span> subclassedTreeView<span class="symbol">.</span>Nodes<span class="symbol">.</span>Find<span class="symbol">(</span>e<span class="symbol">.</span>Label<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>matchingNodes<span class="symbol">.</span>Length <span class="symbol">!=</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> matchingNodes<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span> <span class="symbol">!=</span> e<span class="symbol">.</span>Node<span class="symbol">)</span>
 <span class="symbol">{</span>
 MessageBox<span class="symbol">.</span>Show<span class="symbol">(</span><span class="string">&quot;You must enter a unique value.&quot;</span><span class="symbol">,</span> <span class="string">&quot;Validation Error&quot;</span><span class="symbol">,</span> MessageBoxButtons<span class="symbol">.</span>OK<span class="symbol">,</span> MessageBoxIcon<span class="symbol">.</span>Exclamation<span class="symbol">)</span><span class="symbol">;</span>
 e<span class="symbol">.</span>CancelEdit <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="further-improvements">Further Improvements</h2>
<p>As can be seen from the simple animation at the start of the
article, the edit field is hidden and the original node text
displayed, validation occurs, then editing restarts in the event
of an error. This means, if you display a message box for
example, the original tree state is displayed. It also means
that the cursor and selection state of the edit field is lost.
Ideally, it would be preferable to do validation without causing
the edit field to vanish first, but that would require some more
p-invoke, and probably isn't necessary for most cases - this
method keeps the users entered text which is the important bit.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2013-10-28 - First published</li>
<li>2020-11-21 - Updated formatting, corrected misspellings</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/extending-the-labeledit-functionality-of-a-treeview-to-include-validation .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comSpecifying custom text when using the LabelEdit functionality of a TreeViewurn:uuid:b7a03afe-5f8b-4cdf-a09b-58eb19ca575e2013-10-28T09:09:46Z2013-10-28T09:09:46Z<p>Recently I was updating a support tool that displays documents
in raw form and allows editing of them. This tool is centred
around a <code>TreeView</code>, and the <code>Text</code> property of each <code>TreeNode</code>
is a concatenation of a name and one or more values.</p>
<p>The problem with this approach is if you wish to allow the nodes
to be edited using the built in functionality you're in trouble
as by default you can't actually influence the text that appears
in the in-line editor. In other applications of a similar nature
I used owner-drawn trees as I was using different styles for the
name and the value. In this case, I just wanted the standard
look.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/treeview-labeledit.gif" class="gallery" title="An example project showing the different techniques described in this article" ><img src="https://images.cyotek.com/image/thumbnail/devblog/treeview-labeledit.gif" alt="An example project showing the different techniques described in this article" decoding="async" loading="lazy" /></a><figcaption>An example project showing the different techniques described in this article</figcaption></figure><h2 id="how-you-would-expect-it-to-work">How you would expect it to work</h2>
<p>Ideally, you'd expect that by hooking into the <code>BeforeLabelEdit</code>
event (or overriding <code>OnBeforeLabelEdit</code>) then you could
manipulate the <code>NodeLabelEditEventArgs.Label</code> property. Except
this property is read only.</p>
<p>Scratch that then. What about setting the <code>TreeNode.Text</code>
property to something else in this event, then resetting it
afterwards? Nope, doesn't work either.</p>
<p>Therefore, using just the managed code of the <code>TreeView</code> it's
not possible to do what we want. Lets get slightly outside the
black box with a little Win32 API. We'll get the handle of the
<code>edit</code> control the <code>TreeView</code> is using and directly set it's
text.</p>
<h2 id="getting-the-handle-to-the-edit-control">Getting the handle to the EDIT control</h2>
<p>In order to manipulate the edit control, we first need to get a
handle to it. We can do this succinctly by overriding
<code>OnBeforeLabelEdit</code> (or hooking the <code>BeforeLabelEdit</code> event
although the former is preferable) and using the
<code>TVM_GETEDITCONTROL</code> message.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;USER32&quot;</span><span class="symbol">,</span> EntryPoint <span class="symbol">=</span> <span class="string">&quot;SendMessage&quot;</span><span class="symbol">,</span> CharSet <span class="symbol">=</span> CharSet<span class="symbol">.</span>Auto<span class="symbol">,</span> SetLastError <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">extern</span> IntPtr SendMessage<span class="symbol">(</span>IntPtr hWnd<span class="symbol">,</span> <span class="keyword">int</span> msg<span class="symbol">,</span> IntPtr wParam<span class="symbol">,</span> IntPtr lParam<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">public</span> <span class="keyword">const</span> <span class="keyword">int</span> TVM_GETEDITCONTROL <span class="symbol">=</span> <span class="number">0x110F</span><span class="symbol">;</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnBeforeLabelEdit<span class="symbol">(</span>NodeLabelEditEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 IntPtr editHandle<span class="symbol">;</span>

 editHandle <span class="symbol">=</span> SendMessage<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> TVM_GETEDITCONTROL<span class="symbol">,</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">,</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>editHandle <span class="symbol">!=</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// we have a handle, lets use it!</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnBeforeLabelEdit<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="setting-the-text-of-the-edit-control">Setting the text of the EDIT control</h2>
<p>Now that we have a handle, we can painlessly use <code>WM_SETTEXT</code> to
change the text of the edit control</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;USER32&quot;</span><span class="symbol">,</span> EntryPoint <span class="symbol">=</span> <span class="string">&quot;SendMessage&quot;</span><span class="symbol">,</span> CharSet <span class="symbol">=</span> CharSet<span class="symbol">.</span>Auto<span class="symbol">,</span> SetLastError <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">extern</span> IntPtr SendMessage<span class="symbol">(</span>IntPtr hWnd<span class="symbol">,</span> <span class="keyword">int</span> msg<span class="symbol">,</span> IntPtr wParam<span class="symbol">,</span> <span class="keyword">string</span> lParam<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">public</span> <span class="keyword">const</span> <span class="keyword">int</span> WM_SETTEXT <span class="symbol">=</span> <span class="number">0xC</span><span class="symbol">;</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnBeforeLabelEdit<span class="symbol">(</span>NodeLabelEditEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// -- snip --</span>

 <span class="keyword">if</span> <span class="symbol">(</span>editHandle <span class="symbol">!=</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span>
 SendMessage<span class="symbol">(</span>editHandle<span class="symbol">,</span> WM_SETTEXT<span class="symbol">,</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">,</span> <span class="string">&quot;Magic String&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// -- snip --</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="ok-but-what-about-specifying-the-real-text">OK, but what about specifying the real text?</h2>
<p>If you were hooking into the <code>BeforeLabelEdit</code> event, then you
can just have your own logic in that event to determine the text
to apply. If however you're overriding <code>OnBeforeEdit</code> in order
to make a nice reusable component, you need another way of
allowing implementers to specify the value. For this, I added a
new event to the control.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Behavior&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">event</span> EventHandler<span class="symbol">&lt;</span>NodeRequestTextEventArgs<span class="symbol">&gt;</span> RequestEditText<span class="symbol">;</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> OnRequestEditText<span class="symbol">(</span>NodeRequestTextEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 EventHandler<span class="symbol">&lt;</span>NodeRequestTextEventArgs<span class="symbol">&gt;</span> handler<span class="symbol">;</span>

 handler <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>RequestEditText<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>handler <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 handler<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">,</span> e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The <code>NodeRequestTextEventArgs</code> class is essentially a clone of
<code>NodeLabelEditEventArgs</code> except with a writeable <code>Label</code>
property. I also decided to allow you to cancel the node edit
from this event, so that implementers don't have to hook both
events unless necessary.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">class</span> NodeRequestTextEventArgs <span class="symbol">:</span> CancelEventArgs
<span class="symbol">{</span>
 <span class="keyword">public</span> NodeRequestTextEventArgs<span class="symbol">(</span>TreeNode node<span class="symbol">,</span> <span class="keyword">string</span> label<span class="symbol">)</span>
 <span class="symbol">:</span> <span class="keyword">this</span><span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Node <span class="symbol">=</span> node<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Label <span class="symbol">=</span> label<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> NodeRequestTextEventArgs<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span> <span class="symbol">}</span>

 <span class="keyword">public</span> TreeNode Node <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">protected</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">string</span> Label <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Our final version now looks like this:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnBeforeLabelEdit<span class="symbol">(</span>NodeLabelEditEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 NodeRequestEditTextEventArgs editTextArgs<span class="symbol">;</span>

 <span class="comment">// get the text to apply to the label</span>
 editTextArgs <span class="symbol">=</span> <span class="keyword">new</span> NodeRequestTextEventArgs<span class="symbol">(</span>e<span class="symbol">.</span>Node<span class="symbol">,</span> e<span class="symbol">.</span>Node<span class="symbol">.</span>Text<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnRequestEditText<span class="symbol">(</span>editTextArgs<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// cancel the edit if required</span>
 <span class="keyword">if</span> <span class="symbol">(</span>editTextArgs<span class="symbol">.</span>Cancel<span class="symbol">)</span>
 e<span class="symbol">.</span>CancelEdit <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>

 <span class="comment">// apply the text to the EDIT control</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>e<span class="symbol">.</span>CancelEdit<span class="symbol">)</span>
 <span class="symbol">{</span>
 IntPtr editHandle<span class="symbol">;</span>

 editHandle <span class="symbol">=</span> SendMessage<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> TVM_GETEDITCONTROL<span class="symbol">,</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">,</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// Get the handle of the EDIT control</span>
 <span class="keyword">if</span> <span class="symbol">(</span>editHandle <span class="symbol">!=</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span>
 SendMessage<span class="symbol">(</span>editHandle<span class="symbol">,</span> WM_SETTEXT<span class="symbol">,</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">,</span> editTextArgs<span class="symbol">.</span>Label<span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// And apply the text. Simples.</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnBeforeLabelEdit<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>And an sample usage scenario from the demo application:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> subclassedTreeView_RequestEditText<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> NodeRequestEditTextEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 e<span class="symbol">.</span>Label <span class="symbol">=</span> e<span class="symbol">.</span>Node<span class="symbol">.</span>Name<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>In this example, we are setting the edit text to be the value of
the <code>TreeNode</code>'s <code>Name</code> property, regardless of whatever its
<code>Text</code> is.</p>
<h2 id="updating-the-text-post-edit">Updating the text post-edit</h2>
<p>After the conclusion of the label editing, the node's text will
be set to the new label, and therefore we need to tinker that
logic to allow the implementer to specify the new value text.</p>
<p>You could just hook the <code>AfterLabelEdit</code> event and have your
custom logic in there (remembering to cancel the default edit),
as shown here:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> notifyTreeView_AfterLabelEdit<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> NodeLabelEditEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Label <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 e<span class="symbol">.</span>CancelEdit <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>

 e<span class="symbol">.</span>Node<span class="symbol">.</span>Name <span class="symbol">=</span> e<span class="symbol">.</span>Label<span class="symbol">;</span>
 e<span class="symbol">.</span>Node<span class="symbol">.</span>Text <span class="symbol">=</span> <span class="keyword">string</span><span class="symbol">.</span>Format<span class="symbol">(</span><span class="string">&quot;{0}: {1}&quot;</span><span class="symbol">,</span> e<span class="symbol">.</span>Label<span class="symbol">,</span> e<span class="symbol">.</span>Node<span class="symbol">.</span>Tag<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>However, I didn't want to be having to do this type of code each
time I implemented this sort of behaviour in an application.
Rather than get fancy with subclassed <code>TreeNode</code> classes, I
choose to add a sister event for <code>RequestEditText</code>, named
<code>RequestDisplayText</code> and then handle this automatically. This is
the only aspect of this article that feels &quot;smelly&quot; to me -
ideally it would be nice if the control could handle this for
you without having to ask for more information. But, this should
do for the time being.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnAfterLabelEdit<span class="symbol">(</span>NodeLabelEditEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Label <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span> <span class="comment">// if the user cancelled the edit this event is still raised, just with a null label</span>
 <span class="symbol">{</span>
 NodeRequestTextEventArgs displayTextArgs<span class="symbol">;</span>

 displayTextArgs <span class="symbol">=</span> <span class="keyword">new</span> NodeRequestTextEventArgs<span class="symbol">(</span>e<span class="symbol">.</span>Node<span class="symbol">,</span> e<span class="symbol">.</span>Label<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnRequestDisplayText<span class="symbol">(</span>displayTextArgs<span class="symbol">)</span><span class="symbol">;</span>

 e<span class="symbol">.</span>CancelEdit <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span> <span class="comment">// cancel the built in operation so we can substitute our own</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>displayTextArgs<span class="symbol">.</span>Cancel<span class="symbol">)</span>
 e<span class="symbol">.</span>Node<span class="symbol">.</span>Text <span class="symbol">=</span> displayTextArgs<span class="symbol">.</span>Label<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnAfterLabelEdit<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>And an example of use:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> subclassedTreeView_RequestDisplayText<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> NodeRequestTextEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 e<span class="symbol">.</span>Label <span class="symbol">=</span> <span class="keyword">string</span><span class="symbol">.</span>Format<span class="symbol">(</span><span class="string">&quot;{0}: {1}&quot;</span><span class="symbol">,</span> e<span class="symbol">.</span>Label<span class="symbol">,</span> e<span class="symbol">.</span>Node<span class="symbol">.</span>Tag<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The demonstration shows both of these approaches - the
<code>TreeViewEx</code> control favours the <code>RequestDisplayText</code> event, and
the <code>TreeViewExNotify</code> control leaves it entirely to the
implementer to deal with.</p>
<h2 id="closing-notes">Closing notes</h2>
<p>And that's it. I've seen some implementations of this sort of
functionality in various places across the internet, and some of
them are pretty awful, having to override all sorts of methods,
store and restore various states. The above solution is pretty
simple and works regardless of if you are calling
<code>TreeNode.BeginEdit</code> or using the &quot;click and hover&quot; approach on
a node.</p>
<p>In addition, it's trivially easy to expand this to support
validation as well, I'll cover that in the next article.</p>
<h2 id="bonus-chatter">Bonus Chatter</h2>
<p>I originally tried two different approaches to modifying the
value, both of these involved capturing the <code>TVN_BEGINLABELEDIT</code>
notification. The first approach used a <code>NativeWindow</code> bound to
the <code>TreeView</code> control's parent watching for the <code>WM_NOTIFY</code>
message. The second approach did the same thing, but used MFC's
<a href="http://msdn.microsoft.com/en-us/library/eeah46xd.aspx" rel="external nofollow noopener">Message Reflection</a> via <code>WM_REFLECT</code> to intercept the
notification message on the tree view itself. Both of these
solutions worked, and yet were still overkill as overriding
<code>OnBeforeLabelEdit</code> is sufficient.</p>
<p>Although I'm not going to describe that approach here as it'll
just clutter the article, I did include an implementation of the
<code>WM_REFLECT</code> solution in the demonstration project as I think it
is a neat technique and potentially useful for other
notifications.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2013-10-28 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/specifying-custom-text-when-using-the-labeledit-functionality-of-a-treeview .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comVisual Studio Extension for adding multiple projects to a solutionurn:uuid:3a05e08b-a0d1-40a4-b7f1-709d69070f612014-10-14T16:34:52Z2013-10-12T13:27:52Z<h2 id="background">Background</h2>
<p>My solutions have lots of references to other projects, either
common libraries or unit testing libraries. Neither of these
scenarios lend well to manual binary references or NuGet
packages, so I have lots of source code projects loaded for each
solution.</p>
<p>When creating a new solution (or retro fitting an existing
solution to use new libraries), I end up using <strong>File | Add |
Existing project</strong> <em>a lot</em>. As I was curious about how
extensions in Visual Studio worked, I decided to write a very
simple one to ease the grunt work of adding multiple common
projects.</p>
<p>The last time I wrote an extension (or addin as they were called
back then :)) for an IDE was vbCodeShield for Visual Basic 6 and
that was years ago. I was incredibly surprised to find that
writing an extension for Visual Studio is pretty much the same
as it was (if not worse) - a horrible mass of unintuitive COM
interfaces and clunky code. Whatever happened to the managed
dream?</p>
<p>On the plus side, they are much easier to debug than I remember
VB6 addins being. Or at least, they would be if Resharper didn't
raise continuous exceptions while running in Visual Studio's
Experimental Instance.</p>
<h2 id="almost-ranting">Almost Ranting</h2>
<p>Still, I created the extension without too much pain, although I
have to say it's some of the code I'm least proud of, and I'm
certainly not going to walk through the code on this blog.</p>
<p>I gather you're supposed to use WPF to write extensions, but
well... I wasn't going to learn WPF and the DTE at the same
time. I actually tried (the aborted attempt is still in the
source tree) to use a WPF dialog as recommended by the MSDN
docs, but after finding simple things like checked list boxes
(or even just list views with checkboxes) seemed to have a
learning curve equal to the Moon landing, I went back to Windows
Forms and had it knocked up in no time.</p>
<p>The code is messy, isn't using WPF, doesn't have a great deal of
exception handling, and is full of artefacts from the wizard
generation. But, it does seem to work.</p>
<h2 id="using-the-extension">Using the extension</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/cap-1a.png" class="gallery" title="Accessing the extension" ><img src="https://images.cyotek.com/image/devblog/cap-1a.png" alt="Accessing the extension" decoding="async" loading="lazy" /></a><figcaption>Accessing the extension</figcaption></figure>
<p>To use the extension, open the <strong>Tools</strong> menu and choose <strong>Add
Projects</strong>. This will open a lovely unthemed Windows Forms
dialog containing an empty list of projects.</p>
<h3 id="adding-a-single-project-to-the-mru">Adding a single project to the MRU</h3>
<p>To add a single project to the list, click the <strong>Add File</strong>
button then select the project you want to include.</p>
<h3 id="adding-multiple-projects-to-the-mru">Adding multiple projects to the MRU</h3>
<p>To add multiple projects to the list, click the <strong>Add Folder</strong>
button, then select a folder. After you've selected a folder,
all projects in this folder and its subfolders will be added to
the list.</p>
<h3 id="removing-projects-from-the-mru">Removing projects from the MRU</h3>
<p>You can remove projects from the list, just select them and
press the <strong>Delete</strong> key or the <strong>Remove</strong> button.</p>
<h3 id="adding-projects-to-your-solution">Adding projects to your solution</h3>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/cap-1b.png" class="gallery" title="Using the extension" ><img src="https://images.cyotek.com/image/thumbnail/devblog/cap-1b.png" alt="Using the extension" decoding="async" loading="lazy" /></a><figcaption>Using the extension</figcaption></figure>
<p>Just tick each project you want to add to your solution, then
click the <strong>OK</strong> button. It will then try and add each selected
project to your solution, skipping any that are already present.</p>
<h3 id="configuration-settings">Configuration Settings</h3>
<p>The settings for the extension are saved into an XML file located at <code>%AppData%\Cyotek\VisualStudioExtensions\AddProjects\config.xml</code>.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;?</span><span class="name">xml</span> <span class="name">version</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1.0</span><span class="symbol">&quot;</span><span class="symbol">?&gt;</span>
<span class="symbol">&lt;</span><span class="name">ExtensionSettings</span> <span class="name">xmlns:xsi</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://www.w3.org/2001/XMLSchema-instance</span><span class="symbol">&quot;</span> <span class="name">xmlns:xsd</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://www.w3.org/2001/XMLSchema</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Filter</span><span class="symbol">&gt;</span>C# Projects (*.csproj)|*.csproj|All Files (*.*)|*.*<span class="symbol">&lt;/</span><span class="name">Filter</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Projects</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>C:\Checkout\cyotek\source\Libraries\Cyotek.ApplicationServices\Cyotek.ApplicationServices.csproj<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>C:\Checkout\cyotek\source\Libraries\Cyotek.ApplicationServices.Commands\Cyotek.ApplicationServices.Commands.csproj<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>

 <span class="comment">&lt;!-- SNIP --&gt;</span>

 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>C:\Checkout\cyotek\source\Libraries\Cyotek.Windows.Runtime.Support\Cyotek.Windows.Runtime.Support.csproj<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">string</span><span class="symbol">&gt;</span>C:\Checkout\cyotek\source\Libraries\Cyotek.Windows.Runtime.Testing\Cyotek.Windows.Runtime.Testing.csproj<span class="symbol">&lt;/</span><span class="name">string</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Projects</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">ExtensionSettings</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>The <code>Filter</code> element lets you specify the filter for the <strong>Add
File</strong> dialog, and is also used by <strong>Add Folder</strong> to search for
appropriate files - if you write in Visual Basic rather than C#
you'll probably want to change the filter to be <code>vbproj</code> rather
than <code>csproj</code>!</p>
<p>The <code>Projects</code> element stores the MRU list of projects.</p>
<h2 id="closing">Closing</h2>
<p>That's pretty much it - it's a very simple extension, but
potentially a starting point for something more interesting.</p>
<h2 id="downloading">Downloading</h2>
<p>The best place to get the extension is from the extension page
on the <a href="http://visualstudiogallery.msdn.microsoft.com/dc3adb4b-3b94-4ca0-97fd-3c817bd14a77" rel="external nofollow noopener">Microsoft Visual Studio Gallery</a>. This also ensures
you get notifications when the extension is updated. (<em>Don't
forget to post a review!</em>)</p>
<p>You can also grab the source directly from our <a href="https://github.com/cyotek/Cyotek.AddProjects" rel="external nofollow noopener">GitHub page</a>.</p>
<p>Legacy links available below are no longer maintained.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2013-10-12 - First published</li>
<li>2024-10-14 - Updated to include Visual Studio Gallery and</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/visual-studio-extension-for-adding-multiple-projects-to-a-solution .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comGetting the hWnd of the edit component within a ComboBox controlurn:uuid:ab103b9b-0e7d-4d13-8f7f-1621542657f92013-09-29T15:03:10Z2013-09-29T15:03:10Z<p>I'm currently in the process of dusting off the bugs from a tool
written back in 2011. One of the bugs involves an editable
<code>ComboBox</code> control paired with a separate <code>Button</code> control. When
the button is clicked a popup menu is shown, and when an item in
this menu is clicked, the text of the <code>ComboBox</code> is updated.</p>
<p>The problem with this scenario is by default, as soon as the
<code>ComboBox</code> loses focus, the <code>SelectionStart</code>, <code>SelectionLength</code>
and <code>SelectedText</code> properties are reset, thus preventing my
little menu from replacing the selected text. While this article
doesn't solve this problem (I'll save that for the next
article!), it does describe how you can get the <code>hWnd</code>, or
window handle (in .NET terms the <code>Handle</code>) of the edit
component, thus opening the door for a little Win32 API fun.</p>
<p>There are various approaches to doing this - you could use
<code>FindWindow</code> and search for a child window with a class of
<code>edit</code>, or use <code>SendMessage</code> with the <code>CB_GETCOMBOBOXINFO</code>
message. Or, easiest of all, we can use the <code>GetComboBoxInfo</code>
function.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/getcomboboxinfo1a.png" class="gallery" title="A simple demonstration application" ><img src="https://images.cyotek.com/image/thumbnail/devblog/getcomboboxinfo1a.png" alt="A simple demonstration application" decoding="async" loading="lazy" /></a><figcaption>A simple demonstration application</figcaption></figure><h2 id="interop-declarations">Interop Declarations</h2>
<p>Using this API is very simple, as there's one method and two
simple structs.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">bool</span> GetComboBoxInfo<span class="symbol">(</span>IntPtr hWnd<span class="symbol">,</span> <span class="keyword">ref</span> COMBOBOXINFO pcbi<span class="symbol">)</span><span class="symbol">;</span>

<span class="symbol">[</span>StructLayout<span class="symbol">(</span>LayoutKind<span class="symbol">.</span>Sequential<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">struct</span> COMBOBOXINFO
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">int</span> cbSize<span class="symbol">;</span>
 <span class="keyword">public</span> RECT rcItem<span class="symbol">;</span>
 <span class="keyword">public</span> RECT rcButton<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> stateButton<span class="symbol">;</span>
 <span class="keyword">public</span> IntPtr hwndCombo<span class="symbol">;</span>
 <span class="keyword">public</span> IntPtr hwndEdit<span class="symbol">;</span>
 <span class="keyword">public</span> IntPtr hwndList<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="symbol">[</span>StructLayout<span class="symbol">(</span>LayoutKind<span class="symbol">.</span>Sequential<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">struct</span> RECT
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">int</span> left<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> top<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> right<span class="symbol">;</span>
 <span class="keyword">public</span> <span class="keyword">int</span> bottom<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="calling-the-api">Calling the API</h2>
<p>There is one (well, many, but lets start simple!) caveat which
you may fall foul of if you are new to Win32 API programming -
often the contents of structs need to be initialized with their
size first. But how to you know how big a structure is? Lucky
for you, you don't need to calculate it manually - the
<code>Marshal.SizeOf</code> function will handle this for you.</p>
<p>If you forget to set the size, then the structure simply won't
be populated.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
NativeMethods<span class="symbol">.</span>COMBOBOXINFO info<span class="symbol">;</span>

info <span class="symbol">=</span> <span class="keyword">new</span> NativeMethods<span class="symbol">.</span>COMBOBOXINFO<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
info<span class="symbol">.</span>cbSize <span class="symbol">=</span> Marshal<span class="symbol">.</span>SizeOf<span class="symbol">(</span>info<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">if</span> <span class="symbol">(</span>NativeMethods<span class="symbol">.</span>GetComboBoxInfo<span class="symbol">(</span>control<span class="symbol">.</span>Handle<span class="symbol">,</span> <span class="keyword">ref</span> info<span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// the info structure is now populated, go wild!</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="getting-the-edit-control-handle">Getting the edit control handle</h2>
<p>With the above in place, then getting the handle of the edit
component is very straightforward.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> IntPtr GetEditHandle<span class="symbol">(</span>ComboBox control<span class="symbol">)</span>
<span class="symbol">{</span>
 NativeMethods<span class="symbol">.</span>COMBOBOXINFO info<span class="symbol">;</span>

 info <span class="symbol">=</span> <span class="keyword">new</span> NativeMethods<span class="symbol">.</span>COMBOBOXINFO<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 info<span class="symbol">.</span>cbSize <span class="symbol">=</span> Marshal<span class="symbol">.</span>SizeOf<span class="symbol">(</span>info<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> NativeMethods<span class="symbol">.</span>GetComboBoxInfo<span class="symbol">(</span>control<span class="symbol">.</span>Handle<span class="symbol">,</span> <span class="keyword">ref</span> info<span class="symbol">)</span> <span class="symbol">?</span> info<span class="symbol">.</span>hwndEdit <span class="symbol">:</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Bear in mind that the <code>edit</code> control is a Win32 control - not a
managed .NET control. In order to do anything with this
therefore, you need to use additional Win32 API methods, or
perhaps bind it to a <code>NativeWindow</code> class for easy subclassing.
I'll briefly cover some of this in a future article.</p>
<h2 id="other-goodies">Other goodies</h2>
<p>The <code>COMBOBOXINFO</code> structure has other information, such as the
handle of the <code>list</code> control, which you see if you set the
<code>DropDownStyle</code> property of the <code>ComboBox</code> to <code>Simple</code> and the
state of the dropdown button. You can view the <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb775798(v=vs.85).aspx" rel="external nofollow noopener">MSDN
Documentation</a> to learn more about the structure.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2013-09-29 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/getting-the-hwnd-of-the-edit-component-within-a-combobox-control .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comDownloading new and changed Azure storage blobs at scheduled intervalsurn:uuid:b92f0280-4adf-4491-b150-7546d83e1fe02013-09-08T16:20:16Z2013-09-08T16:20:16Z<h2 id="background">Background</h2>
<p>I recently finished moving our Innovasys Luminitix (Link Removed) server from Amazon's
EC2 into a hybrid of an Azure Web Role and Azure Blob Storage,
and a local machine. It's been running for a couple of weeks now
without a spot of bother - I really like Azure.</p>
<p>However, one thing I did need was a way of pulling down the
files in blob storage so the local loader service could process
them. I ended up writing a rough and ready scheduling app to
download any new files (and as an afterthought, modified files
or optionally uploading of local files).</p>
<h2 id="tldr">tl;dr</h2>
<p>Grab the sample from the link at the end of the post!</p>
<h2 id="about-the-application">About the application</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/azuredownload-1d.png" class="gallery" title="An example of the application doing its thing" ><img src="https://images.cyotek.com/image/devblog/azuredownload-1d.png" alt="An example of the application doing its thing" decoding="async" loading="lazy" /></a><figcaption>An example of the application doing its thing</figcaption></figure>
<p>The sample project attached to this post is a small application
which sits in the <a href="/post/creating-long-running-windows-forms-applications-without-a-start-up-form">system tray without a default user
interface</a>. At regular intervals the application connects to
the configured Azure storage account sand pulls down any new or
modified files, and also optionally uploads files as well.</p>
<p>I wrote this app in a hurry and so I neglected to add incidental
things such as logging. If a job fails, it'll display a balloon
tip with the error message... but you'll have to catch it fast!</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/azuredownload-1c.png" class="gallery" title="An example of a failed task" ><img src="https://images.cyotek.com/image/devblog/azuredownload-1c.png" alt="An example of a failed task" decoding="async" loading="lazy" /></a><figcaption>An example of a failed task</figcaption></figure>
<blockquote>
<p>Note: This sample makes use of NuGet packages. If you have
version 2.7 or later installed, or enable Package Restore,
this should automatically download the missing packages as I'm
not including several MB of binary files in a source code
download!</p>
</blockquote>
<p>I'm not going to describe the source as using the Azure storage
API is pretty much as easy as it gets. If you find this example
project useful, then I'm glad it helped!</p>
<h2 id="configuration">Configuration</h2>
<p>Double clicking the system tray icon allows you to configure the
storage accounts to look it. I've tested it downloading from two
accounts, and downloading + uploading to a further account, and
so far it's been running smoothly, but it's rough code, so while
it works for me, it might not for you.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/azuredownload-1b.png" class="gallery" title="Application settings" ><img src="https://images.cyotek.com/image/devblog/azuredownload-1b.png" alt="Application settings" decoding="async" loading="lazy" /></a><figcaption>Application settings</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/azuredownload-1a.png" class="gallery" title="Download task properties" ><img src="https://images.cyotek.com/image/devblog/azuredownload-1a.png" alt="Download task properties" decoding="async" loading="lazy" /></a><figcaption>Download task properties</figcaption></figure><h2 id="why-use-a-custom-scheduler-instead-of-using-windows">Why use a custom scheduler instead of using Windows?</h2>
<p>The Windows Task Scheduler is a powerful beast (certainly in
latter versions of Windows) and includes many bells and
whistles. You could certainly use this to run a download task
every few minutes if required.</p>
<p>The main reason I went with using a custom scheduler is because
I wanted the system tray icon to let me know what's happening.
And if the program exits as soon as it's finished, then I'll not
be able to see any errors without having to manually check logs.
Which sort of defeats the purpose of using automation.</p>
<p>If you are happy with the stability of the program (or perhaps
add the logging that I neglected or some other form of
notifications) then you could just change the program to remove
the scheduling aspects, and then run it every so often via
Windows and save yourself some resources.</p>
<h2 id="a-note-on-the-task-scheduler">A note on the task scheduler</h2>
<p>I haven't included the source for the task scheduler, as it's
not entirely my own work - the task scheduler I normally use is
too tightly integrated with one of our products for me to easily
pull it out, and so I chopped together one based partly on this,
and partly one some random source I found on the internet. I'll
update this download with the full scheduler source once I've
properly separated our custom one and remove the plagiarized
one.</p>
<h2 id="limitations">Limitations</h2>
<p>As I mentioned, I built this in a hurry, so it doesn't have a
great deal of polish. Amongst the more notable omissions are</p>
<ul>
<li>No logging support. It won't tell you what it has downloaded
or uploaded</li>
<li>Sub folder support. It doesn't do sub folders, either local or
remote.</li>
<li>Uploaded files don't keep the local file's modification time
stamp. That's actually a real annoyance for me, but
unfortunately those properties are read-only for the remote
blobs.</li>
</ul>
<p>Assuming I extend this application to address these (and any
other) limitations, I'll update the source download below.</p>
<h2 id="license">License</h2>
<p>The source code associated with this article is licensed under
the MIT License. See <code>license.txt</code> in the download for more
details.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2013-09-08 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/downloading-new-and-changed-azure-storage-blobs-at-scheduled-intervals .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating long running Windows Forms applications without a start-up formurn:uuid:1cfe3223-0f99-4870-a7d5-7e915e627eb22013-08-26T18:59:30Z2013-08-26T18:59:30Z<p>Sometimes you may wish to create an application that sits
running in the background but doesn't actually display an
initial user interface. However, the user can interact with the
application and so therefore its not appropriate to be a
service. Often such applications are accessible from a system
tray icon. Another viable requirement might be for multiple top
level windows, for example recent versions of Microsoft Word,
where each document has its own application window.</p>
<p>By default however, a normal Windows Form application displays a
single start-up form which definitely isn't desirable when you
want to have a hidden UI, especially as hiding this form isn't
as straightforward as you might expect. Fortunately however, the
framework provides us with the <code>ApplicationContext</code> class that
we can use to create a different approach to managing the
application.</p>
<h2 id="getting-started">Getting Started</h2>
<p>If you look in <code>Program.Main</code>, you'll see code similar to the
following:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Application<span class="symbol">.</span>EnableVisualStyles<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
Application<span class="symbol">.</span>SetCompatibleTextRenderingDefault<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>
Application<span class="symbol">.</span>Run<span class="symbol">(</span><span class="keyword">new</span> MainForm<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>The <code>Application.Run</code> statement is the critical aspect, and it
operates by creating a new instance of your start-up form which,
when closed, causes the application to end. The <code>Run</code> method
also allows you to pass in a custom <code>ApplicationContext</code> class
instead which can be used for more flexibility. In order to exit
the application when using this class, you can either call the
static <code>Application.ExitThread</code> or the <code>ExitThread</code> method of
the <code>ApplicationContext</code> class. In additional,
<code>Application.Exit</code> seems to work just as well.</p>
<p>To start with, we're going to create a basic class that inherits
from <code>ApplicationContent</code> and provides system tray icon and
context menu support. To that end, we'll create a class named
<code>TrayIconApplicationContext</code>.</p>
<p>The first thing we need to do is hook into the <code>ApplicationExit</code>
event. We'll use this for clean-up purposes no matter if the
application is shut down via our new class, or other code
calling <code>ExitThread</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> TrayIconApplicationContext <span class="symbol">:</span> ApplicationContext
<span class="symbol">{</span>
 <span class="keyword">protected</span> TrayIconApplicationContext<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Application<span class="symbol">.</span>ApplicationExit <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>ApplicationExitHandler<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> OnApplicationExit<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>

 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">void</span> ApplicationExitHandler<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnApplicationExit<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>When the event handler is triggered, it calls the
<code>OnApplicationExit</code> virtual method. This makes it easier for
inheritors of this class to provide their own clean up behaviour
without hooking into events. It seems a shame there isn't an
existing method to override in the first place without the the
initial hooking of events, but it's a minor thing.</p>
<h2 id="adding-the-tray-icon">Adding the tray icon</h2>
<p>Now that we have the basic infrastructure in place, we can add
our tray icon. To do this we'll create an instance of the
<code>NotifyIcon</code> component, accessible via an protected property.
We'll also automatically hook into the <code>Click</code> and <code>DoubleClick</code>
events of the icon and provide virtual methods for inheritors.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">readonly</span> NotifyIcon _notifyIcon<span class="symbol">;</span>

<span class="keyword">protected</span> TrayIconApplicationContext<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 Application<span class="symbol">.</span>ApplicationExit <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>ApplicationExitHandler<span class="symbol">;</span>

 _notifyIcon <span class="symbol">=</span> <span class="keyword">new</span> NotifyIcon
 <span class="symbol">{</span>
 Text <span class="symbol">=</span> Application<span class="symbol">.</span>ProductName<span class="symbol">,</span>
 Visible <span class="symbol">=</span> <span class="keyword">true</span>
 <span class="symbol">}</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>TrayIcon<span class="symbol">.</span>MouseDoubleClick <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>TrayIconDoubleClickHandler<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>TrayIcon<span class="symbol">.</span>MouseClick <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>TrayIconClickHandler<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> NotifyIcon TrayIcon
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _notifyIcon<span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
 
<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> OnTrayIconClick<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span> <span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> OnTrayIconDoubleClick<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span> <span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> TrayIconClickHandler<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnTrayIconClick<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> TrayIconDoubleClickHandler<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnTrayIconDoubleClick<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>We'll also update <code>OnApplicationExit</code> to clear up the icon:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">if</span> <span class="symbol">(</span>_notifyIcon <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
<span class="symbol">{</span>
 _notifyIcon<span class="symbol">.</span>Visible <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 _notifyIcon<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>Even though we are setting the icon's <code>Visible</code> property to
<code>true</code>, nothing will happen as you need to assign the <code>Icon</code>
property first.</p>
</blockquote>
<h2 id="adding-a-context-menu">Adding a context menu</h2>
<p>Having a tray icon is very nice, but if the only interaction
possible with our application is double clicking the icon, it's
a bit of a limited application! We'll solve this by adding a
<code>ContextMenuStrip</code> to the class, which will be bound to the
icon. Inheritors can then populate the menu according to their
requirements.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">readonly</span> ContextMenuStrip _contextMenu<span class="symbol">;</span>

<span class="keyword">protected</span> TrayIconApplicationContext<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 _contextMenu <span class="symbol">=</span> <span class="keyword">new</span> ContextMenuStrip<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 Application<span class="symbol">.</span>ApplicationExit <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>ApplicationExitHandler<span class="symbol">;</span>

 _notifyIcon <span class="symbol">=</span> <span class="keyword">new</span> NotifyIcon
 <span class="symbol">{</span>
 ContextMenuStrip <span class="symbol">=</span> _contextMenu<span class="symbol">,</span>
 Text <span class="symbol">=</span> Application<span class="symbol">.</span>ProductName<span class="symbol">,</span>
 Visible <span class="symbol">=</span> <span class="keyword">true</span>
 <span class="symbol">}</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>TrayIcon<span class="symbol">.</span>DoubleClick <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>TrayIconDoubleClickHandler<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>TrayIcon<span class="symbol">.</span>Click <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>TrayIconClickHandler<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> ContextMenuStrip ContextMenu
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _contextMenu<span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Again, we'll update the exit handler to dispose of the menu:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">if</span> <span class="symbol">(</span>_contextMenu <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 _contextMenu<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="creating-the-application">Creating the application</h2>
<p>With our reusable application context class ready, we can now
create a custom application specific version. Of course, you
don't have to do this, you could just make the changes directly
to the original class, but it's better to promote resuse where
you can.</p>
<p>For example, a basic application which had a settings dialog and
an about dialog could look something like this:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">class</span> CustomApplicationContext <span class="symbol">:</span> TrayIconApplicationContext
<span class="symbol">{</span>
 <span class="keyword">public</span> ApplicationContext<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>TrayIcon<span class="symbol">.</span>Icon <span class="symbol">=</span> Resources<span class="symbol">.</span>SmallIcon<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>ContextMenu<span class="symbol">.</span>Items<span class="symbol">.</span>Add<span class="symbol">(</span><span class="string">&quot;&amp;Settings...&quot;</span><span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>SettingsContextMenuClickHandler<span class="symbol">)</span><span class="symbol">.</span>Font <span class="symbol">=</span> <span class="keyword">new</span> Font<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ContextMenu<span class="symbol">.</span>Font<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Bold<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>ContextMenu<span class="symbol">.</span>Items<span class="symbol">.</span>Add<span class="symbol">(</span><span class="string">&quot;-&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>ContextMenu<span class="symbol">.</span>Items<span class="symbol">.</span>Add<span class="symbol">(</span><span class="string">&quot;&amp;About...&quot;</span><span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>AboutContextMenuClickHandler<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>ContextMenu<span class="symbol">.</span>Items<span class="symbol">.</span>Add<span class="symbol">(</span><span class="string">&quot;-&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>ContextMenu<span class="symbol">.</span>Items<span class="symbol">.</span>Add<span class="symbol">(</span><span class="string">&quot;E&amp;xit&quot;</span><span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ExitContextMenuClickHandler<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnTrayIconDoubleClick<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ShowSettings<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnTrayIconDoubleClick<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">void</span> AboutContextMenuClickHandler<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> EventArgs eventArgs<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>Form dialog <span class="symbol">=</span> <span class="keyword">new</span> AboutDialog<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 dialog<span class="symbol">.</span>ShowDialog<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">void</span> ExitContextMenuClickHandler<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> EventArgs eventArgs<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ExitThread<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">void</span> SettingsContextMenuClickHandler<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> EventArgs eventArgs<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ShowSettings<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">void</span> ShowSettings<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>Form dialog <span class="symbol">=</span> <span class="keyword">new</span> SettingsDialog<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 dialog<span class="symbol">.</span>ShowDialog<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>This sample creates a context menu with 3 items; two dialogs and
a way to exit the program. Double clicking the icon also
displays a dialog. Convention usually suggests that for the
context menu, you display the primary item in bold - so in this
example the bold item opens the settings dialog, matching the
double click action.</p>
<p>Finally, we need to modify the entry point of our application to
use the new class.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>STAThread<span class="symbol">]</span>
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> Main<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 Application<span class="symbol">.</span>EnableVisualStyles<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 Application<span class="symbol">.</span>SetCompatibleTextRenderingDefault<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>
 Application<span class="symbol">.</span>Run<span class="symbol">(</span><span class="keyword">new</span> CustomApplicationContext<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>And that's how simple it is to set up an application with no
start up form!</p>
<h2 id="notes">Notes</h2>
<p>While writing a real utility program that made use of this
technique for an application accessed via the system I had the
following observations:</p>
<ul>
<li>Dialogs opened from this class are not modal. You can see this
by double clicking the tray icon several times - if you call
<code>ShowDialog</code>, they aren't modal and you can can therefore open
multiple dialogs by accessing the menu again etc. It's
probably better to have instance variables for such forms, and
then create them on first use, and activate the existing
instance on subsequent calls. The full source code download
available below shows examples of this.</li>
<li>Mixing the <code>MouseClick</code> and <code>MouseDoubleClick</code> events to show
windows doesn't really work as shown in the example project.
Perhaps this can be worked around by using the <code>MouseUp</code> event
instead and the<code>SystemInformation.DoubleClickSize</code> /
<code>SystemInformation.DoubleClickTime</code> properties but that's
beyond the scope of this article.</li>
<li>As there is no top level main window to appear in the taskbar,
you should probably ensure any window that can be opened
directly from the tray icon has its <code>Icon</code>, <code>ShowIcon</code> and
<code>ShowInTaskbar</code> properties set.</li>
<li>Opened dialogs were frequently displayed behind existing
windows of other applications. I didn't observe this while
debugging the project, but only when running the program
outside the IDE. The simplest way I found to work around this
issue was to call <code>this.Activate()</code> from the <code>Shown</code> event</li>
</ul>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnShown<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnShown<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Activate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>As usual an example project is available from the link below
containing a demonstration of this technique.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2013-08-26 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-long-running-windows-forms-applications-without-a-start-up-form .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comImageBox 1.1.4.0 updateurn:uuid:bce482b7-b906-44bb-8152-7b0d8ace32832013-08-10T20:30:53Z2013-08-10T20:30:53Z<p>Today we've released a new updated version of the <code>ImageBox</code>
control, with a nice collection of enhancements and a few bug
fixes.</p>
<p>Full change log for this update:</p>
<h2 id="changes-and-new-features">Changes and new features</h2>
<ul>
<li>Added <a href="https://www.nuget.org/packages/CyotekImageBox/" rel="external nofollow noopener">NuGet</a> package</li>
<li>Added a license file to hopefully cut down on questions about
usage. The <code>ImageBox</code> control is licensed under the MIT
license, allowing you free reign to use it in your projects,
commercial or otherwise. See <code>imagebox-license.txt</code> for the
full text.</li>
<li>Added a new <code>SizeMode</code> property. This allows you to switch
between <code>Normal</code>, <code>Fit</code> and <code>Stretch</code> modes. Stretch is a new
mode for the <code>ImageBox</code>, and acts similar to existing <code>Fit</code>
functionality except the aspect ratio is not preserved.</li>
<li>The <code>SizeToFit</code> property has been marked as deprecated and
should no longer be used. The <code>SizeMode</code> property has a <code>Fit</code>
value that should be used instead. Setting the <code>SizeToFit</code>
property will now manipulate <code>SizeMode</code> instead.</li>
<li>Added a new <code>CenterPoint</code> property. This property returns the
pixel at the center of the current image viewport.</li>
<li>Added a bunch of missing XML comments documentation.</li>
<li>Added new overloads for most methods that accepted a source
<code>Rectangle</code>, <code>Point</code> or <code>Size</code> to also accept <code>float</code> and
<code>int</code> arguments.</li>
<li>Added a new <code>Zoomed</code> event that uses new
<code>ImageBoxZoomEventArgs</code> arguments. This new event allows you
to tell if the zoom was in or out, how it was raised, and
current and previous zoom values. Not hugely thrilled with how
aspects of this change has been internally implemented, so
implementation methods are private rather than virtual so I
can change them without affecting the signature.</li>
<li>Added new <code>CenterToImage</code> method which resets the viewport to
be centered of the image, in the same way as zooming via the
keyboard used to work.</li>
<li>Added support for animated GIF's, thanks to a contribution
from <a href="https://github.com/teamalpha5441" rel="external nofollow noopener">Eggy</a>. Note animations
only play at runtime, not design time.</li>
<li>The <code>Text</code> and <code>Font</code> properties are now available and, if
set, will be displayed in the control. You can use the
<code>ForeColor</code>, <code>TextBackColor</code>, <code>TextAlign</code>, <code>TextDisplayMode</code>
and <code>ScaleText</code> properties to determine how the text will be
rendered.</li>
<li>A new <code>DrawLabel</code> method that performs text drawing is
available for use by custom implementations or virtual modes.</li>
</ul>
<h2 id="demonstration-changes">Demonstration Changes</h2>
<ul>
<li>Added a new <em>Scaled Adornments</em> demonstration, showing how
easy it is to add custom drawing that is scaled and positioned
appropriately.</li>
<li>Added a new <em>Switch Image During Zoom</em> demonstration, a demo
with an unwieldy name that shows how to switch out a low
resolution image with a higher detailed one as you zoom into
an <code>ImageBox</code>.</li>
<li>Added new <em>Text</em> and <em>Size Mode</em> demonstrations.</li>
</ul>
<h2 id="bug-fixes">Bug Fixes</h2>
<ul>
<li><p>Zooming in and out with the keyboard now keeps the view
centered to the same pixel that was centered prior to the zoom</p>
</li>
<li><p>Zooming in and out with the keyboard is now correctly disabled
if the <code>AllowZoom</code> property is <code>False</code>, or the <code>SizeMode</code>
property is a value other than <code>Normal</code>. This means keyboard
behaviour now matches mouse behaviour.</p>
</li>
<li><p>If the mouse wheel was rapidly spun (thus having a multiple of
the base delta), the <code>Zoom</code> property was only adjusted once</p>
</li>
<li><p>Setting the <code>GridScale</code> property to <code>None</code> rendered the
default <code>Small</code> grid. Using a scale of <code>None</code> now correctly
just fills the grid area with a solid brush from the
<code>GridColor</code> property.</p>
</li>
<li><p>The <code>MouseWheel</code> event is now available</p>
</li>
<li><p>Layout changes no longer occur if the <code>AllowPainting</code> property
is <code>false</code> through use of the <code>BeginUpdate</code> method.</p>
</li>
<li><p>Fixed various documentation errors</p>
</li>
</ul>
<h2 id="downloads">Downloads</h2>
<p>As usual, either grab the source from <a href="https://github.com/cyotek/Cyotek.Windows.Forms.ImageBox" rel="external nofollow noopener">GitHub</a>, from the link
below, or make use of the <a href="https://www.nuget.org/packages/CyotekImageBox/" rel="external nofollow noopener">NuGet</a> package!</p>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/imagebox-1-1-4-0-update .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comUsing alternate descriptions for enumeration membersurn:uuid:d0efae35-6587-4d52-a621-78ec84205f7c2013-07-28T19:43:47Z2013-07-28T19:43:47Z<p>The last two articles (<a href="/post/creating-a-custom-typeconverter-part-1">here</a> and <a href="/post/creating-a-custom-typeconverter-part-2">here</a>) described
creating a custom type converter for converting units of
measurement.</p>
<p>However, what happens when you want to display or convert
to/from alternative representations? For example, consider the
enum below.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">enum</span> Unit
<span class="symbol">{</span>
 cm<span class="symbol">,</span>
 mm<span class="symbol">,</span>
 pt<span class="symbol">,</span>
 px
<span class="symbol">}</span>
</pre>
</figure>
<p>Apart from the fact such an enum more than likely doesn't match
any coding standards you use, what happens when you want to
include percentages in the mix? Not many languages are going to
let you use % as a symbol name!</p>
<p>So we rewrite the enum to make more sense, in which case you
might have this:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">enum</span> Unit
<span class="symbol">{</span>
 Centimetre<span class="symbol">,</span>
 Millimetre<span class="symbol">,</span>
 Point<span class="symbol">,</span>
 Pixel<span class="symbol">,</span>
 Percent
<span class="symbol">}</span>
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/typeconv3-a.png" class="gallery" title="Using the actual enum member names doesn't seem like a good idea does it?" ><img src="https://images.cyotek.com/image/thumbnail/devblog/typeconv3-a.png" alt="Using the actual enum member names doesn't seem like a good idea does it?" decoding="async" loading="lazy" /></a><figcaption>Using the actual enum member names doesn't seem like a good idea does it?</figcaption></figure>
<p>Great! Except... your users want to see cm, px, % etc. Now what?</p>
<h2 id="the-manual-way">The manual way</h2>
<p>Well, you could create a function which takes a unit, and
manually checks the values and returns an appropriate value, for
example:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">string</span> GetUnitSuffix<span class="symbol">(</span>Unit unit<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>

 <span class="keyword">switch</span> <span class="symbol">(</span>unit<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> Unit<span class="symbol">.</span>Centimetre<span class="symbol">:</span>
 result <span class="symbol">=</span> <span class="string">&quot;cm&quot;</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">...</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>While this would certainly work, it means you have to duplicate
this code for every enum you wish to have alternate descriptions
for. Not to mention, should you add a new member to the enum,
you have to remember to update this function. More than likely,
you also want a sister version of this function which accepts
the string version, and returns the enum value.</p>
<h2 id="the-automatic-way">The automatic way</h2>
<p>A better way would be to tag each enum member with an
appropriate description, then you can use reflection to scan
your enum members and perform automatic to and from conversions.</p>
<p>In this example, I'm going to use the <code>DescriptionAttribute</code>
from the <code>System.ComponentModel</code> namespace, although depending
on what you're trying to do, a custom attribute may be better -
that's not exactly what this attribute was intended for!</p>
<p>First, decorate your enum with the attribute.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">enum</span> Unit
<span class="symbol">{</span>
 <span class="symbol">[</span>Description<span class="symbol">(</span><span class="string">&quot;cm&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
 Centimetre<span class="symbol">,</span>

 <span class="symbol">[</span>Description<span class="symbol">(</span><span class="string">&quot;mm&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
 Millimetre<span class="symbol">,</span>

 <span class="symbol">[</span>Description<span class="symbol">(</span><span class="string">&quot;pt&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
 Point<span class="symbol">,</span>

 <span class="symbol">[</span>Description<span class="symbol">(</span><span class="string">&quot;px&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
 Pixel<span class="symbol">,</span>

 <span class="symbol">[</span>Description<span class="symbol">(</span><span class="string">&quot;%&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
 Percent
<span class="symbol">}</span>
</pre>
</figure>
<p>Next add a couple of functions that will perform the conversion
of your enum to and from a string. With this in place you can
add new members, and, as long as you add your attribute to
them, the functions will automatically handle the new values.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">string</span> GetDescription<span class="symbol">(</span><span class="keyword">this</span> Unit value<span class="symbol">)</span>
<span class="symbol">{</span>
 FieldInfo field<span class="symbol">;</span>
 DescriptionAttribute attribute<span class="symbol">;</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>

 field <span class="symbol">=</span> value<span class="symbol">.</span>GetType<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">.</span>GetField<span class="symbol">(</span>value<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 attribute <span class="symbol">=</span> <span class="symbol">(</span>DescriptionAttribute<span class="symbol">)</span>Attribute<span class="symbol">.</span>GetCustomAttribute<span class="symbol">(</span>field<span class="symbol">,</span> <span class="keyword">typeof</span><span class="symbol">(</span>DescriptionAttribute<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 result <span class="symbol">=</span> attribute <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">?</span> attribute<span class="symbol">.</span>Description <span class="symbol">:</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">;</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">static</span> Unit GetValue<span class="symbol">(</span><span class="keyword">string</span> value<span class="symbol">)</span>
<span class="symbol">{</span>
 Unit result<span class="symbol">;</span>

 result <span class="symbol">=</span> Unit<span class="symbol">.</span>None<span class="symbol">;</span>

 <span class="keyword">foreach</span> <span class="symbol">(</span>Unit id <span class="keyword">in</span> Enum<span class="symbol">.</span>GetValues<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>Unit<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 FieldInfo field<span class="symbol">;</span>
 DescriptionAttribute attribute<span class="symbol">;</span>

 field <span class="symbol">=</span> id<span class="symbol">.</span>GetType<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">.</span>GetField<span class="symbol">(</span>id<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 attribute <span class="symbol">=</span> Attribute<span class="symbol">.</span>GetCustomAttribute<span class="symbol">(</span>field<span class="symbol">,</span> <span class="keyword">typeof</span><span class="symbol">(</span>DescriptionAttribute<span class="symbol">)</span><span class="symbol">)</span> <span class="keyword">as</span> DescriptionAttribute<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>attribute <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> attribute<span class="symbol">.</span>Description <span class="symbol">==</span> value<span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> id<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>I choose to make the version that accepts the enum member as an
input parameter an extension method, that way I can call it like
this:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">string</span> unitSuffix<span class="symbol">;</span>

unitSuffix <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Unit<span class="symbol">.</span>GetDescription<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>However, as the sister method accepts a string parameter, it
doesn't make sense to make this an extension, unless you want it
to appear on every single string variable you declare! So I just
revert back to the usual static calling convention.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Unit unit<span class="symbol">;</span>

unit <span class="symbol">=</span> EnumExtensions<span class="symbol">.</span>GetValue<span class="symbol">(</span>stringValue<span class="symbol">.</span>Substring<span class="symbol">(</span>nonDigitIndex<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/typeconv3-b.png" class="gallery" title="Much better - the user sees the short form version, but the code uses the full name" ><img src="https://images.cyotek.com/image/thumbnail/devblog/typeconv3-b.png" alt="Much better - the user sees the short form version, but the code uses the full name" decoding="async" loading="lazy" /></a><figcaption>Much better - the user sees the short form version, but the code uses the full name</figcaption></figure><h2 id="using-generics">Using Generics</h2>
<p>While there's nothing wrong with the above methods, they could
still be improved upon. As it stands now, the methods are fixed
to a specific enum, so we can change them to use generics
instead, then they'll work for all enums.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">string</span> GetDescription<span class="symbol">&lt;</span>T<span class="symbol">&gt;</span><span class="symbol">(</span><span class="keyword">this</span> T value<span class="symbol">)</span>
 <span class="keyword">where</span> T <span class="symbol">:</span> <span class="keyword">struct</span>
<span class="symbol">{</span>
 FieldInfo field<span class="symbol">;</span>
 DescriptionAttribute attribute<span class="symbol">;</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>

 field <span class="symbol">=</span> value<span class="symbol">.</span>GetType<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">.</span>GetField<span class="symbol">(</span>value<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 attribute <span class="symbol">=</span> <span class="symbol">(</span>DescriptionAttribute<span class="symbol">)</span>Attribute<span class="symbol">.</span>GetCustomAttribute<span class="symbol">(</span>field<span class="symbol">,</span> <span class="keyword">typeof</span><span class="symbol">(</span>DescriptionAttribute<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 result <span class="symbol">=</span> attribute <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">?</span> attribute<span class="symbol">.</span>Description <span class="symbol">:</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">;</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">static</span> T GetValue<span class="symbol">&lt;</span>T<span class="symbol">&gt;</span><span class="symbol">(</span><span class="keyword">string</span> value<span class="symbol">,</span> T defaultValue<span class="symbol">)</span>
<span class="symbol">{</span>
 T result<span class="symbol">;</span>

 result <span class="symbol">=</span> defaultValue<span class="symbol">;</span>

 <span class="keyword">foreach</span> <span class="symbol">(</span>T id <span class="keyword">in</span> Enum<span class="symbol">.</span>GetValues<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>T<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 FieldInfo field<span class="symbol">;</span>
 DescriptionAttribute attribute<span class="symbol">;</span>

 field <span class="symbol">=</span> id<span class="symbol">.</span>GetType<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">.</span>GetField<span class="symbol">(</span>id<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 attribute <span class="symbol">=</span> Attribute<span class="symbol">.</span>GetCustomAttribute<span class="symbol">(</span>field<span class="symbol">,</span> <span class="keyword">typeof</span><span class="symbol">(</span>DescriptionAttribute<span class="symbol">)</span><span class="symbol">)</span> <span class="keyword">as</span> DescriptionAttribute<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>attribute <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> attribute<span class="symbol">.</span>Description <span class="symbol">==</span> value<span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> id<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="final-points">Final points</h2>
<p>Using reflection does have an overhead. If you expect to be
calling these methods a lot, you may wish to extend them yet
further in order to support caching the results in a dictionary
or other mechanism of your choice. That way, the first time a
new member is requested you perform the reflection lookup, and
thereafter just read the cache. I haven't done any benchmarking,
but it's probably safe to say a dictionary lookup (remember to
use <code>TryGetValue</code>!) is going to be a lot faster than a
reflection scan.</p>
<p>An example showing how the custom type converter from the
previous two articles updated to use the above technique is
available from the link below.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2013-07-28 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/using-alternate-descriptions-for-enumeration-members .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a custom TypeConverter part 2 - Instance descriptors, expandable properties and standard valuesurn:uuid:b472fc9e-34bf-4d49-909b-a433ecf71eac2013-07-28T19:43:09Z2013-07-28T19:43:09Z<p>In the <a href="/post/creating-a-custom-typeconverter-part-1">first part</a> of this article series, I described how
to create a simple type converter for converting an object to
and from a string. This follow up article expands upon that
sample, to include more concise design time code generation,
expandable property support and finally custom lists of values.</p>
<blockquote>
<p>The examples in this article assume you are working from the
original sample project from <a href="/post/creating-a-custom-typeconverter-part-1">part
1</a>.</p>
</blockquote>
<h2 id="designer-code">Designer Code</h2>
<p>When you place a <code>Control</code> or <code>Component</code> onto a design time
surface such as a <code>Form</code>, the IDE will automatically generate
any code required to initialize the object.</p>
<p>Modify the <code>SampleClass</code> class to inherit from <code>Component</code> then
drop an instance onto the form and set the first property. Save
the form, then open the designer file. You should see code
something like this:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> InitializeComponent<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 CustomTypeConverter<span class="number">2</span><span class="symbol">.</span>Length length<span class="number">1</span> <span class="symbol">=</span> <span class="keyword">new</span> CustomTypeConverter<span class="number">2</span><span class="symbol">.</span>Length<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// ... SNIP ...</span>

 <span class="comment">//</span>
 <span class="comment">// sample</span>
 <span class="comment">//</span>
 length<span class="number">1</span><span class="symbol">.</span>Unit <span class="symbol">=</span> CustomTypeConverter<span class="number">2</span><span class="symbol">.</span>Unit<span class="symbol">.</span>px<span class="symbol">;</span>
 length<span class="number">1</span><span class="symbol">.</span>Value <span class="symbol">=</span> <span class="number">32</span>F<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>sample<span class="symbol">.</span>Length<span class="number">1</span> <span class="symbol">=</span> length<span class="number">1</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>sample<span class="symbol">.</span>Length<span class="number">2</span> <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>sample<span class="symbol">.</span>Length<span class="number">3</span> <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>

 <span class="comment">// ... SNIP ...</span>

<span class="symbol">}</span>
</pre>
</figure>
<p>The designer has generated the source code required to populate
the object by specifying each property individually. However,
what happens if you wanted to set both properties at once or
perhaps perform some other initialization code? We can use our
type converter to solve this one.</p>
<blockquote>
<p>Although slightly outside the bounds of this article, it's
probably worth mentioning nonetheless. In the snippet above,
you can see the <code>Length2</code> and <code>Length3</code> properties are
explicitly assigned <code>null</code>, even though that is already the
default value of these properties. If you're creating public
facing library components it's always a good idea to apply the
<code>DefaultValue</code> attribute to properties. It makes for cleaner
code (if the value is the default value, no code will be
generated) and allows other components to perform custom
processing if required. For example, the <code>PropertyGrid</code> shows
default properties in normal style, and non-default ones in
bold.</p>
</blockquote>
<h2 id="updating-the-length-class">Updating the Length class</h2>
<p>Before we can adjust our type converter to support code
generation, we need to extend our <code>Length</code> class by adding a new
constructor.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> Length<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span> <span class="symbol">}</span>

<span class="keyword">public</span> Length<span class="symbol">(</span><span class="keyword">float</span> value<span class="symbol">,</span> Unit unit<span class="symbol">)</span>
 <span class="symbol">:</span> <span class="keyword">this</span><span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Value <span class="symbol">=</span> value<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Unit <span class="symbol">=</span> unit<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>I've added one constructor which will set both <code>Value</code> and
<code>Unit</code> properties of the class. Due to the addition of a
constructor with parameters, I now need to explicitly define a
parameterless constructor as an implicit one will no longer be
generated and I still want to be able to do <code>new Length()</code>.</p>
<p>With these modifications in place, we can now dive into the type
converter modifications.</p>
<h2 id="canconvertto">CanConvertTo</h2>
<p>The first thing we need to do is update our type converter to
state that it supports the <code>InstanceDescriptor</code> class which is
the mechanism the IDE will use for the custom code generation.
We can do this by overriding a new method, <code>CanConvertTo</code>.</p>
<p>Update the <code>LengthConverter</code> class from the previous article to
include the following:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">bool</span> CanConvertTo<span class="symbol">(</span>ITypeDescriptorContext context<span class="symbol">,</span> Type destinationType<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> destinationType <span class="symbol">==</span> <span class="keyword">typeof</span><span class="symbol">(</span>InstanceDescriptor<span class="symbol">)</span> <span class="symbol">||</span> <span class="keyword">base</span><span class="symbol">.</span>CanConvertTo<span class="symbol">(</span>context<span class="symbol">,</span> destinationType<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>This new overloads will inform the caller that we now support
the <code>InstanceDescriptor</code> type, in addition to whatever the base
<code>TypeConverter</code> can handle.</p>
<h2 id="extending-convertto">Extending ConvertTo</h2>
<p>We briefly covered the <code>ConvertTo</code> override in the previous
article in order to display our <code>Length</code> object as a string. Now
that we have overridden <code>CanConvertTo</code> to state that we can
handle additional types, we need to update this method as well.</p>
<p>The <code>InstanceDescriptor</code> class contains information needed to
regenerate an object, and is comprised of two primary pieces of
information.</p>
<ul>
<li>A <code>MemberInfo</code> object which describes a method in the class.
This can either be a constructor (which we'll use in our
example), or something static that will return a new object -
for example, <code>Color.FromArgb</code>.</li>
<li>An <code>ICollection</code> containing any of the arguments required to
pass into the source member.</li>
</ul>
<p>Lets update <code>ConvertTo</code> to include the extract support.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">object</span> ConvertTo<span class="symbol">(</span>ITypeDescriptorContext context<span class="symbol">,</span> CultureInfo culture<span class="symbol">,</span> <span class="keyword">object</span> value<span class="symbol">,</span> Type destinationType<span class="symbol">)</span>
<span class="symbol">{</span>
 Length length<span class="symbol">;</span>
 <span class="keyword">object</span> result<span class="symbol">;</span>

 result <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
 length <span class="symbol">=</span> value <span class="keyword">as</span> Length<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>length <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>destinationType <span class="symbol">==</span> <span class="keyword">typeof</span><span class="symbol">(</span><span class="keyword">string</span><span class="symbol">)</span><span class="symbol">)</span>
 result <span class="symbol">=</span> length<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>destinationType <span class="symbol">==</span> <span class="keyword">typeof</span><span class="symbol">(</span>InstanceDescriptor<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 ConstructorInfo constructorInfo<span class="symbol">;</span>

 constructorInfo <span class="symbol">=</span> <span class="keyword">typeof</span><span class="symbol">(</span>Length<span class="symbol">)</span><span class="symbol">.</span>GetConstructor<span class="symbol">(</span><span class="keyword">new</span><span class="symbol">[</span><span class="symbol">]</span> <span class="symbol">{</span> <span class="keyword">typeof</span><span class="symbol">(</span><span class="keyword">float</span><span class="symbol">)</span><span class="symbol">,</span> <span class="keyword">typeof</span><span class="symbol">(</span>Unit<span class="symbol">)</span> <span class="symbol">}</span><span class="symbol">)</span><span class="symbol">;</span>
 result <span class="symbol">=</span> <span class="keyword">new</span> InstanceDescriptor<span class="symbol">(</span>constructorInfo<span class="symbol">,</span> <span class="keyword">new</span> <span class="keyword">object</span><span class="symbol">[</span><span class="symbol">]</span> <span class="symbol">{</span> length<span class="symbol">.</span>Value<span class="symbol">,</span> length<span class="symbol">.</span>Unit <span class="symbol">}</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result <span class="symbol">??</span> <span class="keyword">base</span><span class="symbol">.</span>ConvertTo<span class="symbol">(</span>context<span class="symbol">,</span> culture<span class="symbol">,</span> value<span class="symbol">,</span> destinationType<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>We still do our null check to ensure we have a valid value to
convert, but now we check to see if the type is either <code>string</code>
or <code>InstanceDescriptor</code> and process accordingly.</p>
<p>For instance descriptors, we use Reflection in order to get the
constructor which takes two parameters, and then we create an
<code>InstanceDescriptor</code> object from that. Easy enough!</p>
<p>Now when we modify our <code>SampleClass</code> component in the designer,
source code is generated similar to the below. (With the caveat
of the warning in the next section)</p>
<p>Note that I'd also modified the properties on the <code>SampleClass</code>
to include <code>[DefaultValue(typeof(Length), &quot;&quot;)]</code> for default
value support.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> InitializeComponent<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>

 <span class="comment">// ... SNIP ...</span>

 <span class="comment">//</span>
 <span class="comment">// sample</span>
 <span class="comment">//</span>
 <span class="keyword">this</span><span class="symbol">.</span>sample<span class="symbol">.</span>Length<span class="number">1</span> <span class="symbol">=</span> <span class="keyword">new</span> CustomTypeConverter<span class="number">2</span><span class="symbol">.</span>Length<span class="symbol">(</span><span class="number">16</span>F<span class="symbol">,</span> CustomTypeConverter<span class="number">2</span><span class="symbol">.</span>Unit<span class="symbol">.</span>px<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// ... SNIP ...</span>
 
<span class="symbol">}</span>
</pre>
</figure>
<p>Much cleaner!</p>
<h2 id="a-warning-on-visual-studio">A warning on Visual Studio</h2>
<p>While writing this article, Visual Studio frequently took a huff
and refused to generate the design time code. I assume it is due
to Visual Studio caching the assembly containing the
<code>TypeConverter</code>, or it is another manifestation of not being
able to unload managed assemblies without destroying the
application domain. Whatever the reason, I found it quickly to
be a source of frustration requiring frequent restarts of the
IDE in order to pick up changed code.</p>
<p>As an experiment, I did a test where the <code>Length</code> and
<code>LengthConverter</code> classes were in another assembly referenced in
binary form. In this mode, I didn't have a single problem.</p>
<p>Finally, whereas basic conversions are easy to debug, the
<code>InstanceDescriptor</code> conversion is much less so.</p>
<p>Something to bear in mind.</p>
<h2 id="expandable-properties">Expandable properties</h2>
<p>Returning to the <code>ExpandableObjectConverter</code> and property
expansion, that is trivially easy to add to your custom
converter by overriding the <code>GetPropertiesSupported</code> and
<code>GetProperties</code> methods.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">bool</span> GetPropertiesSupported<span class="symbol">(</span>ITypeDescriptorContext context<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">true</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">override</span> PropertyDescriptorCollection GetProperties<span class="symbol">(</span>ITypeDescriptorContext context<span class="symbol">,</span> <span class="keyword">object</span> value<span class="symbol">,</span> Attribute<span class="symbol">[</span><span class="symbol">]</span> attributes<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> TypeDescriptor<span class="symbol">.</span>GetProperties<span class="symbol">(</span>value<span class="symbol">,</span> attributes<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>First, by overriding <code>GetPropertiesSupported</code> we tell the caller
that we support individual property editing. Then we can
override <code>GetProperties</code> to return the actual properties to
display.</p>
<p>In the above example, we return all available properties, which
is probably normal behaviour. Let us assume the <code>Length</code> class
has a property on it which we didn't want to see. We could
return a different collection with that property filtered out:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> PropertyDescriptorCollection GetProperties<span class="symbol">(</span>ITypeDescriptorContext context<span class="symbol">,</span> <span class="keyword">object</span> value<span class="symbol">,</span> Attribute<span class="symbol">[</span><span class="symbol">]</span> attributes<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">//return TypeDescriptor.GetProperties(value, attributes);</span>
 <span class="keyword">return</span> <span class="keyword">new</span> PropertyDescriptorCollection<span class="symbol">(</span>TypeDescriptor<span class="symbol">.</span>GetProperties<span class="symbol">(</span>value<span class="symbol">,</span> attributes<span class="symbol">)</span><span class="symbol">.</span>Cast<span class="symbol">&lt;</span>PropertyDescriptor<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">.</span>Where<span class="symbol">(</span>p <span class="symbol">=&gt;</span> p<span class="symbol">.</span>Name <span class="symbol">!=</span> <span class="string">&quot;BadProperty&quot;</span><span class="symbol">)</span><span class="symbol">.</span>ToArray<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>An awkward example, but it does demonstrate the feature.</p>
<blockquote>
<p>The property grid honours the <code>Browsable</code> attribute - this is a much better way of controlling visibility of properties than the above!</p>
</blockquote>
<h2 id="custom-values">Custom Values</h2>
<p>The final example I want to demonstrate is custom values.
Although you might assume that you'd have to create a custom
<code>UITypeEditor</code>, if you just want a basic drop down list, you can
do this directly from your type converter by overriding
<code>GetStandardValuesSupported</code> and <code>GetStandardValues</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">bool</span> GetStandardValuesSupported<span class="symbol">(</span>ITypeDescriptorContext context<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">true</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">override</span> TypeConverter<span class="symbol">.</span>StandardValuesCollection GetStandardValues<span class="symbol">(</span>ITypeDescriptorContext context<span class="symbol">)</span>
<span class="symbol">{</span>
 List<span class="symbol">&lt;</span>Length<span class="symbol">&gt;</span> values<span class="symbol">;</span>

 values <span class="symbol">=</span> <span class="keyword">new</span> List<span class="symbol">&lt;</span>Length<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 values<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> Length<span class="symbol">(</span><span class="number">16</span><span class="symbol">,</span> Unit<span class="symbol">.</span>px<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 values<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> Length<span class="symbol">(</span><span class="number">32</span><span class="symbol">,</span> Unit<span class="symbol">.</span>px<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 values<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> Length<span class="symbol">(</span><span class="number">64</span><span class="symbol">,</span> Unit<span class="symbol">.</span>px<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 values<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> Length<span class="symbol">(</span><span class="number">128</span><span class="symbol">,</span> Unit<span class="symbol">.</span>px<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> <span class="keyword">new</span> StandardValuesCollection<span class="symbol">(</span>values<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>First you need to override <code>GetStandardValuesSupported</code> in order
to specify that we do support such values. Then in
<code>GetStandardValues</code> we simply return the objects we want to see.
In this example, I've generate 4 lengths which I return. When
you run the program, you can see and select these values. Of
course, you need to make sure that the values you return can be
handled by the <code>ConvertFrom</code> method!</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/typeconv2-a.png" class="gallery" title="A simple example of adding some standard values" ><img src="https://images.cyotek.com/image/thumbnail/devblog/typeconv2-a.png" alt="A simple example of adding some standard values" decoding="async" loading="lazy" /></a><figcaption>A simple example of adding some standard values</figcaption></figure><h2 id="summing-up">Summing up</h2>
<p>Adding even an advanced type converter is still a easy task, and
is something that can help enrich editing functionality.</p>
<p>You can download the complete example from the link below.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2013-07-28 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-a-custom-typeconverter-part-2 .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a custom TypeConverter part 1 - getting startedurn:uuid:ed01e541-6988-477b-92c1-08aae89649fa2013-07-28T19:42:30Z2013-07-28T19:42:30Z<p>It is common practice to create a class that describes
something, a person, a product - some entity or other. Your
application may provide a sublime UI for editing these objects,
or rely on something more basic such as a <code>PropertyGrid</code>.
However, if you use this approach, you may find that some of
your properties can't be edited. Yet examples abound of
non-simple editing via the grid, such as colours, enumerations
and image selection to name a few.</p>
<p>By making use of the <code>TypeConverter</code> and <code>UITypeEditor</code> classes,
you can quite easily provide the ability to create richer
editing support for your objects. This first article in this
series will detail how to use <code>TypeConverter</code> allowing complex
objects to be edited as though they were simple strings.</p>
<h2 id="the-scenario">The Scenario</h2>
<p>As with most of my articles, I'm starting with a real world
example and a solid required. I need to store units of
measurement, so for this I have a simple class that has a pair
of properties describing a given measurement.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">class</span> Length
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">string</span> ToString<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> value<span class="symbol">;</span>
 <span class="keyword">string</span> unit<span class="symbol">;</span>

 value <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Value<span class="symbol">.</span>ToString<span class="symbol">(</span>CultureInfo<span class="symbol">.</span>InvariantCulture<span class="symbol">)</span><span class="symbol">;</span>
 unit <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Unit<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> <span class="keyword">string</span><span class="symbol">.</span>Concat<span class="symbol">(</span>value<span class="symbol">,</span> unit<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> Unit Unit <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">float</span> Value <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>A fairly standard class that simply has two properties along
with a default (implicit) constructor. I'm also overriding
<code>ToString</code>, as it's useful both for debugging purposes and for
having something other than <code>CustomTypeConverter1.Length</code>
displayed in the <code>PropertyGrid</code>.</p>
<p>And for the purposes of this demonstration, I have created a
sample class which has three length properties.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">class</span> SampleClass
<span class="symbol">{</span>
 <span class="keyword">public</span> Length Length<span class="number">1</span> <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> Length Length<span class="number">2</span> <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> Length Length<span class="number">3</span> <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Just for completeness sake, here's the <code>Unit</code> enum.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">enum</span> Unit
<span class="symbol">{</span>
 None<span class="symbol">,</span>
 cm<span class="symbol">,</span>
 mm<span class="symbol">,</span>
 pt<span class="symbol">,</span>
 px
<span class="symbol">}</span>
</pre>
</figure>
<p>Isn't that an ugly enum? For this example, it will suffice, but
there is another article which describes an <a href="/post/using-alternate-descriptions-for-enumeration-members">alternative
approach</a>.</p>
<h2 id="first-steps">First Steps</h2>
<p>I've set up a sample project which binds an instance of our
<code>SampleClass</code> to a <code>PropertyGrid</code>, with the <code>Length1</code> property
pre-set to <strong>32px</strong>. When you run this project, you are left
with a very unsatisfactory editing experience as you can't edit
anything.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/typeconv1-a.png" class="gallery" title="Default editing functionality... or lack thereof" ><img src="https://images.cyotek.com/image/thumbnail/devblog/typeconv1-a.png" alt="Default editing functionality... or lack thereof" decoding="async" loading="lazy" /></a><figcaption>Default editing functionality... or lack thereof</figcaption></figure>
<p>So, what can we do about this?</p>
<h2 id="the-typeconverterattribute-class">The TypeConverterAttribute Class</h2>
<p>The <code>TypeConverterAttribute</code> allows you to associate your class
with a type that can handle conversion of instances of your
type to and from other objects. You can only have one occurrence
of this attribute per type. As with a lot of these types of
attributes, you can provide the conversion type one of two ways:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>TypeConverter<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>LengthConverter<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">]</span>
</pre>
</figure>
<p>Here, we pass in a type object, meaning the type has to be
directly referenced by your project and distributed as a
dependency.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>TypeConverter<span class="symbol">(</span><span class="string">&quot;CustomTypeConverter1.LengthConverter, CustomTypeConverter1&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
</pre>
</figure>
<p>Another alternative is to use a direct string, as shown above.
This string is the fully qualified type name, meaning it could
be located in a differently assembly, but one that isn't
referenced directly or flagged as a dependency.</p>
<p>Which one you use depends on your needs, but bear in mind no
compile time checking can be done of the string version, so if
you get the name wrong, you won't find out until you are unable
to edit the type!</p>
<h2 id="the-expandableobjectconverter">The ExpandableObjectConverter</h2>
<p>This class is built into the .NET Framework and will provide a
minimum of functionality at minimum cost.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>TypeConverter<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>ExpandableObjectConverter<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">class</span> Length
<span class="symbol">{</span>
</pre>
</figure>
<p>If we change the declaration of our <code>Length</code> class to be the
above and run our sample, we get this:</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/typeconv1-b.png" class="gallery" title="A little better, as long as you don't mind editing each property individually" ><img src="https://images.cyotek.com/image/thumbnail/devblog/typeconv1-b.png" alt="A little better, as long as you don't mind editing each property individually" decoding="async" loading="lazy" /></a><figcaption>A little better, as long as you don't mind editing each property individually</figcaption></figure>
<p>The first property can now be expanded, and each property of the
<code>Length</code> class can be individually set. However, there are two
immediate problems with this approach:</p>
<ul>
<li>Properties can only be edited one at a time, you can't combine
values via the root property.</li>
<li>Properties with a null value (the second and third properties
in the example screenshot) cannot be instantiated.</li>
</ul>
<p>Again, depending on your requirements, this might be perfectly
acceptable. In my case, it isn't, so on with the custom
converter!</p>
<h2 id="writing-a-custom-converter">Writing a custom converter</h2>
<p>In order to create a custom converter, you need to have a class
which inherits from <code>TypeConverter</code>. At a minimum, you would
override the <code>CanConvertFrom</code> and <code>ConvertFrom</code> methods.</p>
<p>Here's a sample converter for our simple <code>Length</code> class:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">class</span> LengthConverter <span class="symbol">:</span> TypeConverter
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">bool</span> CanConvertFrom<span class="symbol">(</span>ITypeDescriptorContext context<span class="symbol">,</span> Type sourceType<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">return</span> sourceType <span class="symbol">==</span> <span class="keyword">typeof</span><span class="symbol">(</span><span class="keyword">string</span><span class="symbol">)</span> <span class="symbol">||</span> <span class="keyword">base</span><span class="symbol">.</span>CanConvertFrom<span class="symbol">(</span>context<span class="symbol">,</span> sourceType<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">object</span> ConvertFrom<span class="symbol">(</span>ITypeDescriptorContext context<span class="symbol">,</span> CultureInfo culture<span class="symbol">,</span> <span class="keyword">object</span> value<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> stringValue<span class="symbol">;</span>
 <span class="keyword">object</span> result<span class="symbol">;</span>

 result <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
 stringValue <span class="symbol">=</span> value <span class="keyword">as</span> <span class="keyword">string</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>stringValue<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> nonDigitIndex<span class="symbol">;</span>

 nonDigitIndex <span class="symbol">=</span> stringValue<span class="symbol">.</span>IndexOf<span class="symbol">(</span>stringValue<span class="symbol">.</span>FirstOrDefault<span class="symbol">(</span><span class="keyword">char</span><span class="symbol">.</span>IsLetter<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>nonDigitIndex <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="keyword">new</span> Length
 <span class="symbol">{</span>
 Value <span class="symbol">=</span> Convert<span class="symbol">.</span>ToSingle<span class="symbol">(</span>stringValue<span class="symbol">.</span>Substring<span class="symbol">(</span><span class="number">0</span><span class="symbol">,</span> nonDigitIndex<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">,</span>
 Unit <span class="symbol">=</span> <span class="symbol">(</span>Unit<span class="symbol">)</span>Enum<span class="symbol">.</span>Parse<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>Unit<span class="symbol">)</span><span class="symbol">,</span> stringValue<span class="symbol">.</span>Substring<span class="symbol">(</span>nonDigitIndex<span class="symbol">)</span><span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span>
 <span class="symbol">}</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result <span class="symbol">??</span> <span class="keyword">base</span><span class="symbol">.</span>ConvertFrom<span class="symbol">(</span>context<span class="symbol">,</span> culture<span class="symbol">,</span> value<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>So, what is this short class doing?</p>
<p>The first override, <code>CanConvertFrom</code>, is called when .NET wants
to know if it can convert from a given type. Here, I'm saying
&quot;if you are a string, then yes I can convert&quot; (or at least
try!), otherwise it falls back and requests if the base
converter can do the conversion. In most cases that'll probably
be a &quot;no&quot;, but it's probably a good idea to leave it in
regardless.</p>
<p>Now for the interesting method. <code>ConvertFrom</code> does the type
conversion. I'm going to ignore the <em>context</em> parameter for now
as I haven't had a need for it. You can use the <em>culture</em>
parameter as a guide if you need to do any conversions such as
numbers or dates. The key parameter, is <em>value</em> as this contains
the raw data to convert.</p>
<ul>
<li>The first thing this method does is see if <em>value</em> is a
non-null non-empty string. (If you're using .NET 4 or above
you'd probably use the <code>IsNullOrWhitespace</code> method instead).</li>
<li>Next I try and find the index of the first letter character -
the method assumes the input is in the form of
&lt;number&gt;&lt;unit&gt;.</li>
<li>If I find a letter, then I create a new <code>Length</code> object and
use object initialization to set the <code>Value</code> property to be
the first part of the string converted to a float, and
<code>Enum.Parse</code> to set the <code>Unit</code> property using the latter part
of the string. And that explains the horribly named enum. I'll
still show you a better way though!</li>
</ul>
<p>And that is all you need. Well almost, we need to change our
class header:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>TypeConverter<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>LengthConverter<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">class</span> Length
<span class="symbol">{</span>
</pre>
</figure>
<p>Now when we run the sample project, we can directly type in a
value into the different <code>Length</code> based properties and have them
converted to the correct values, including creating new values.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/typeconv1-c.png" class="gallery" title="With the custom type converter, we can now directly enter units of measurement" ><img src="https://images.cyotek.com/image/thumbnail/devblog/typeconv1-c.png" alt="With the custom type converter, we can now directly enter units of measurement" decoding="async" loading="lazy" /></a><figcaption>With the custom type converter, we can now directly enter units of measurement</figcaption></figure>
<blockquote>
<p>Note that this example doesn't cover clearing a value - for
example if you enter an empty string. You could return a new
<code>Length</code> object in this case and then change the <code>ToString</code>
method to return an empty string. Simply returning <code>null</code> from
<code>ConvertFrom</code> doesn't actually work, so at the moment I don't
know the best method for accomplishing a value reset.</p>
</blockquote>
<h2 id="error-handling">Error Handling</h2>
<p>I haven't demonstrated error handling, firstly as this is a bare
bones example, and also due to .NET providing it for you, at
least in the case of the property grid. It will automatically
handle the failure to convert a value. The disadvantage is the
rather unhelpful error message. If you throw an exception
yourself, the exception text you provide is displayed in the
<em>Details</em> section of the dialog, allowing you to specifying a
more succinct message.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/typeconv1-d.png" class="gallery" title="Generic error messages aren't the best - do your users a favour and provide detailed exception text" ><img src="https://images.cyotek.com/image/thumbnail/devblog/typeconv1-d.png" alt="Generic error messages aren't the best - do your users a favour and provide detailed exception text" decoding="async" loading="lazy" /></a><figcaption>Generic error messages aren't the best - do your users a favour and provide detailed exception text</figcaption></figure><h2 id="converting-to-a-different-data-type">Converting to a different data type</h2>
<p>As well as converting a type into our class, we can also use a
type converter to convert our class into another type by
overriding the <code>ConvertTo</code> method.</p>
<p>In this example, the <code>Length</code> class overrides the <code>ToString</code>
method. I would still recommend doing that in additional to this
next tip, but as with everything, it depends on your purpose. In
this case, we can use the <code>ConvertTo</code> method to convert our
<code>Length</code> object into a string.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">object</span> ConvertTo<span class="symbol">(</span>ITypeDescriptorContext context<span class="symbol">,</span> CultureInfo culture<span class="symbol">,</span> <span class="keyword">object</span> value<span class="symbol">,</span> Type destinationType<span class="symbol">)</span>
<span class="symbol">{</span>
 Length length<span class="symbol">;</span>
 <span class="keyword">object</span> result<span class="symbol">;</span>

 result <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
 length <span class="symbol">=</span> value <span class="keyword">as</span> Length<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>length <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> destinationType <span class="symbol">==</span> <span class="keyword">typeof</span><span class="symbol">(</span><span class="keyword">string</span><span class="symbol">)</span><span class="symbol">)</span>
 result <span class="symbol">=</span> length<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> result <span class="symbol">??</span> <span class="keyword">base</span><span class="symbol">.</span>ConvertTo<span class="symbol">(</span>context<span class="symbol">,</span> culture<span class="symbol">,</span> value<span class="symbol">,</span> destinationType<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>As with all the methods you override, if you can't explicitly
handle the passed values, then ask the base class to attempt to
handle it. The above method shows how I check to ensure the
<em>value</em> is a <code>Length</code> object, and then if the <em>destinationType</em>
is a string, I simply return <code>value.ToString()</code>. Whatever is
returned via this method will appear in the <code>PropertyGrid</code>, so
use caution if you decide to return formatted strings - you'll
need to handle them in <code>ConvertFrom</code>.</p>
<p>There is another, more useful, purpose for this override, but
I'll defer that for the <a href="/post/creating-a-custom-typeconverter-part-2">next article</a>.</p>
<h2 id="summing-up">Summing up</h2>
<p>Adding a basic type converter is a very simple thing to do, and
is something that can help enrich editing functionality, or even
debugging (in lieu of an immediate window or scripting support,
Cyotek products have a sample add-in which displays documents in
a <code>PropertyGrid</code> for simple editing and querying). Even if you
only go as far as adding the <code>ExpandableObjectConverter</code>
attribute to a base class, it's more useful than nothing!</p>
<p>You can download the complete example from the link below.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2013-07-28 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-a-custom-typeconverter-part-1 .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comColorPicker Controls Update 1.0.2.0urn:uuid:2cd07bf2-c75a-4edf-a79f-8f501f9ab5f12013-07-14T08:48:08Z2013-07-13T18:06:56Z<p>I've been pretty busy recently pushing out updates to
<a href="https://www.cyotek.com/cyotek-webcopy">WebCopy</a>, a pending update to <a href="https://www.cyotek.com/cyotek-spriter">Spriter</a> and working on a
game project so blog posts have suffered a bit. While I work to
correct that, we've just pushed an update to the ColorPicker
controls.</p>
<h2 id="important">Important</h2>
<p>This update contains breaking changes due to a number of renamed
classes and enum members.</p>
<h2 id="so-whats-new">So what's new?</h2>
<ul>
<li>All the <code>*PaletteReader</code> classes have been replaced with
<code>*PaletteSerializer</code>. As the new names imply, palettes can now
be written as well as read. This is a breaking change and may
require some reworking of any code that used to use the old
readers.</li>
<li>The <code>ColorEditor</code> now supports selecting of named colors as
the hex editor is now a dropdown list. As well as being able
to select named colors from the list, you can now also type
names directly into the hex editor and they will be processed
accordingly.</li>
<li>The <code>ColorPickerDialog</code> now can load and save palette files</li>
<li>Palette support has been reworked to allow the saving of
palettes as well as loading. Unfortunately due to the initial
names of these classes this is a breaking change, albeit a
minor one.</li>
<li>Added a bit more documentation</li>
<li>Corrected some grammatical errors in existing documentation
and headers</li>
<li>Added additional tests to ensure palettes are written
correctly</li>
</ul>
<h2 id="new-from-1.0.1.0">New from 1.0.1.0</h2>
<ul>
<li>GIMP Palette support</li>
<li>Some unit tests to make sure palettes are read correctly</li>
</ul>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/cp-1020-editor.png" class="gallery" title="Named color support" ><img src="https://images.cyotek.com/image/thumbnail/devblog/cp-1020-editor.png" alt="Named color support" decoding="async" loading="lazy" /></a><figcaption>Named color support</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/cp-1020-dialog.png" class="gallery" title="Support for loading and saving palettes" ><img src="https://images.cyotek.com/image/thumbnail/devblog/cp-1020-dialog.png" alt="Support for loading and saving palettes" decoding="async" loading="lazy" /></a><figcaption>Support for loading and saving palettes</figcaption></figure><h2 id="downloads">Downloads</h2>
<p>Grab the update from the link below or from the <a href="https://www.cyotek.com/cyotek-spriter">GitHub</a>
page.</p>
<p>We hope you enjoy these improvements!</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2013-07-13 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/colorpicker-controls-update-1-0-2-0 .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comColorPicker Controls for Windows Formsurn:uuid:25ad5918-b209-45fa-b2a2-5d19e328ef372013-07-13T18:10:06Z2013-04-04T16:09:52Z<p>Back at the start of the new millennium, I had a publishing
agreement with another company to publish our components under
their branding. The first of these components was the
ColorPicker ActiveX control. Roll on 13 years later and that
publishing agreement is long expired, ActiveX is dead, and yet
here I am again writing a color picker control. Except this time
losing money rather than making it. There's probably a life
lesson buried in there somewhere.</p>
<p>All of our current products ask for a color at least once
(mostly buried in an options dialog), and some of the prototype
products we are working on ask for more. Currently, we just wrap
around the <code>System.Drawing.Design.ColorEditor</code> class, which
overtime has identified a few problems:</p>
<ul>
<li>Keyboard support is iffy (this is more to do with how it's
implemented at our end I suspect)</li>
<li>You can't choose alpha channels, or enter custom values</li>
<li>The dependency on <code>System.Design</code> prevents us from targeting
the Client Profile, and indeed will cause a crash if deployed
to a system with only the Client Profile installed</li>
</ul>
<p>The other option briefly considered was just to replace with the
standard color common dialog. But this dialog looks and operates
the same as it did back in Windows 3, and while that is pretty
good from a consistent UI standpoint, it does mean it hasn't
kept up with changing times - such as entering colors as hex
values. I took a look at some other third party libraries but
none of them were flexible enough for what I wanted.</p>
<p>In the end I went with writing my own set of C# based color
picker controls which we're providing here as open source - we
hope you find them useful. As there's quite a lot of code, I'm
not going to describe them line by line as I've done in the
past, but just provide an overview of each component.</p>
<h2 id="colorgrid-control">ColorGrid Control</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/cp-colorgridcontrol.png" class="gallery" title="ColorGrid control demonstration" ><img src="https://images.cyotek.com/image/thumbnail/devblog/cp-colorgridcontrol.png" alt="ColorGrid control demonstration" decoding="async" loading="lazy" /></a><figcaption>ColorGrid control demonstration</figcaption></figure>
<p>This control displays a grid of colors, and supports both a
primary palette, and a custom color palette. Several properties
are available for configuring the appearing of the control, and
there are behaviour options too, such as built in editing of
colors.</p>
<h2 id="colorwheel-control">ColorWheel Control</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/cp-colorwheelcontrol.png" class="gallery" title="ColorWheel control demonstration" ><img src="https://images.cyotek.com/image/thumbnail/devblog/cp-colorwheelcontrol.png" alt="ColorWheel control demonstration" decoding="async" loading="lazy" /></a><figcaption>ColorWheel control demonstration</figcaption></figure>
<p>This control displays a radial wheel of colors and allows
selection from any point in the wheel. Not much in the way of
customisation for this control!</p>
<h2 id="colorslider-controls">ColorSlider Controls</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/cp-colorslidercontrols.png" class="gallery" title="ColorSlider controls demonstration" ><img src="https://images.cyotek.com/image/thumbnail/devblog/cp-colorslidercontrols.png" alt="ColorSlider controls demonstration" decoding="async" loading="lazy" /></a><figcaption>ColorSlider controls demonstration</figcaption></figure>
<p>A bunch of controls (inheriting from a single base) that allow
selection of values via a colourful bar. Similar to the
<code>TrackBar</code> control you have a few options for specifying the
drag handle's position and bar orientation.</p>
<h2 id="coloreditor-control">ColorEditor Control</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/cp-coloreditorcontrol.png" class="gallery" title="ColorEditor control demonstration" ><img src="https://images.cyotek.com/image/thumbnail/devblog/cp-coloreditorcontrol.png" alt="ColorEditor control demonstration" decoding="async" loading="lazy" /></a><figcaption>ColorEditor control demonstration</figcaption></figure>
<p>This control allows you enter/select a color using RGB/HSL or
hex formats.</p>
<h2 id="screencolorpicker-control">ScreenColorPicker Control</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/cp-screencolorpickercontrol.png" class="gallery" title="ScreenColorPicker control demonstration" ><img src="https://images.cyotek.com/image/thumbnail/devblog/cp-screencolorpickercontrol.png" alt="ScreenColorPicker control demonstration" decoding="async" loading="lazy" /></a><figcaption>ScreenColorPicker control demonstration</figcaption></figure>
<p>This control allows the user to pick a color from any pixel
displayed on the screen.</p>
<h2 id="colorpickerdialog-form">ColorPickerDialog Form</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/cp-colorpickerdialog.png" class="gallery" title="ColorPickerDialog demonstration" ><img src="https://images.cyotek.com/image/thumbnail/devblog/cp-colorpickerdialog.png" alt="ColorPickerDialog demonstration" decoding="async" loading="lazy" /></a><figcaption>ColorPickerDialog demonstration</figcaption></figure>
<p>This form puts together the previous controls in a ready to use
dialog.</p>
<h2 id="coloreditormanager">ColorEditorManager</h2>
<p>This is a non-GUI component that you can drop onto a form, and
bind to other controls in this library. When the <code>Color</code>
property of one control changes, it is reflected in the others
without having to lift a finger. Useful if you're creating
composite displays from multiple controls.</p>
<h2 id="color-palettes">Color Palettes</h2>
<p>The <code>ColorGrid</code> control has <code>Colors</code> and <code>CustomColors</code>
properties which return a <code>ColorCollection</code>. These two
properties make it easier as a developer to keep separate a
primary palette whilst having the flexibility of custom colors,
although it does complicate the control's internal logic a bit!
The grid will automatically populate custom colors if you try
and set the control's <code>Color</code> to a value not currently defined.</p>
<p>As well as manually populating <code>ColorCollection</code> instances, you
can also load in external palette files. Paint.NET and the age
old JASC 1.0 formats are currently supported.</p>
<h2 id="keyboard-support">Keyboard Support</h2>
<p>All GUI components, with the exception of the
<code>ScreenColorPicker</code> include full keyboard/focus support. Many
controls support <code>SmallChange</code> and <code>LargeChange</code> properties
which influence how navigation keys are processed. Although in
the case of the <code>ColorWheel</code> it's not really a bonus... but
that's what the <code>ColorEditor</code> control is best suited for!</p>
<h2 id="known-issues">Known Issues</h2>
<ul>
<li>XML documentation comments are incomplete</li>
<li>The <code>ColorEditorManager</code> control currently allows you to bind
to the <code>LightnessColorSlider</code> control, but doesn't fully
support it yet</li>
</ul>
<h2 id="acknowledgements">Acknowledgements</h2>
<ul>
<li>Inspiration (and some code!) was taken from <a href="http://www.codeproject.com/Articles/21965/Color-Picker-with-Color-Wheel-and-Eye-Dropper" rel="external nofollow noopener">Color Picker with
Color Wheel and Eye Dropper</a></li>
<li>The icon used by the demonstration is from the [Crystal
Project Icons<a href="http://www.iconfinder.com/icondetails/17937/128/color_color_scheme_icons_renk_icon" rel="external nofollow noopener">2</a></li>
<li>The eye dropper png graphic is from the <a href="http://www.iconfinder.com/icondetails/84569/32/eyedropper_icon" rel="external nofollow noopener">Momentum Glossy
Icons</a></li>
</ul>
<h2 id="source-code">Source Code</h2>
<p>Download the source code from the link below, or from the
<a href="https://github.com/cyotek/Cyotek.Windows.Forms.ColorPicker/" rel="external nofollow noopener">GitHub</a> page.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2013-04-04 - First published</li>
<li>2020-11-21 - Updated formatting, corrected some spelling
errors</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/colorpicker-controls-for-windows-forms .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comDividing up a rectangle based on pairs of points using C#urn:uuid:ff67a638-dab6-4764-9165-0c2399d4765d2013-02-10T08:37:04Z2013-02-10T08:37:04Z<p>Recently we released the first alpha of our latest product,
<a href="https://cyotek.com/cyotek-slicr">Cyotek Slicr</a>, a tool for slicing up an image. At the heart
of this tool is a series of routines that take a given image and
pairs of input points, from which the image is chopped up
accordingly. This article describes how to break up a rectangle
into smaller parts based on user defined co-ordinates.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/rectangle1.png" class="gallery" title="An example of the programs output" ><img src="https://images.cyotek.com/image/thumbnail/devblog/rectangle1.png" alt="An example of the programs output" decoding="async" loading="lazy" /></a><figcaption>An example of the programs output</figcaption></figure><h2 id="caveat-emptor">Caveat Emptor</h2>
<blockquote>
<p>Before I get started, I just want to point out that the code
I'm about to show you is proof of concept code. It doesn't use
algorithms such as <a href="http://en.wikipedia.org/wiki/Bentley%E2%80%93Ottmann_algorithm" rel="external nofollow noopener">Bentley–Ottmann</a>, it's not very
efficient, and there's probably a hundred ways of doing it
better. However, it works, which is pretty much all I care
about at this moment!</p>
</blockquote>
<h2 id="getting-started">Getting Started</h2>
<p>These are the rules for splitting up a rectangle into component
parts:</p>
<ol>
<li>Lines must be straight, so each pair of co-ordinates will
align on one axis</li>
<li>Co-ordinates must start from either an edge, or the
intersection of another pair. The second co-ordinate must end
in a similar fashion. Any &quot;orphan&quot; co-ordinates which don't
start/end on another edge/join are illegal and should be
ignored</li>
<li>Co-ordinates can start and end at any point in the bounds of
the canvas, as long as they follow the previous rules.</li>
</ol>
<p>In order to achieve this, I ended up with creating a number of
support classes</p>
<ul>
<li><code>Segment</code> - this class starts the starting point of a line,
it's length, and it's orientation. Originally I just started
by storing the two pairs of co-ordinates, but in the end it
was easier with the length and orientation.</li>
<li><code>SegmentPoint</code> - this class stores the details of a single
point. An instance of this class is created for each unique
point created by the start and end of a segment, and any
intersections. It also stores the directions of neighbouring
points.</li>
</ul>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">class</span> Segment
<span class="symbol">{</span>
 <span class="keyword">public</span> Point EndLocation
 <span class="symbol">{</span>
 <span class="keyword">get</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> x<span class="symbol">;</span>
 <span class="keyword">int</span> y<span class="symbol">;</span>

 <span class="keyword">switch</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Orientation<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> SegmentOrientation<span class="symbol">.</span>Vertical<span class="symbol">:</span>
 x <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>X<span class="symbol">;</span>
 y <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>Y <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">default</span><span class="symbol">:</span>
 x <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>X <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">;</span>
 y <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>Y<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> <span class="keyword">new</span> Point<span class="symbol">(</span>x<span class="symbol">,</span> y<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> Point Location <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> SegmentOrientation Orientation <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">int</span> Size <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">internal</span> <span class="keyword">class</span> SegmentPoint
<span class="symbol">{</span>
 <span class="keyword">public</span> SegmentPointConnections Connections <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> Point Location <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">int</span> X <span class="symbol">{</span> <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>X<span class="symbol">;</span> <span class="symbol">}</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">int</span> Y <span class="symbol">{</span> <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>Y<span class="symbol">;</span> <span class="symbol">}</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>With these helper classes, I can now construct the code to
process them and extract the rectangles.</p>
<h2 id="calculating-points">Calculating Points</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/rectangle2.png" class="gallery" title="In this example, a rectangle has been created by segments crossing each other. We need to detect the intersections to find these types of rectangles" ><img src="https://images.cyotek.com/image/thumbnail/devblog/rectangle2.png" alt="In this example, a rectangle has been created by segments crossing each other. We need to detect the intersections to find these types of rectangles" decoding="async" loading="lazy" /></a><figcaption>In this example, a rectangle has been created by segments crossing each other. We need to detect the intersections to find these types of rectangles</figcaption></figure>
<p>The image above demonstrates the first problem. The four
segments intersect with each other, causing a rectangle to be
generated untouched by any user defined point. However, if we
are going to find that rectangle, we need to find any new point
generated by multiple segments intersecting.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> CalculatePoints<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 List<span class="symbol">&lt;</span>Segment<span class="symbol">&gt;</span> segments<span class="symbol">;</span>

 segments <span class="symbol">=</span> <span class="keyword">new</span> List<span class="symbol">&lt;</span>Segment<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Points <span class="symbol">=</span> <span class="keyword">new</span> Dictionary<span class="symbol">&lt;</span>Point<span class="symbol">,</span> SegmentPoint<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// add segments representing the edges</span>
 segments<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> Segment <span class="symbol">{</span> Location <span class="symbol">=</span> Point<span class="symbol">.</span>Empty<span class="symbol">,</span> Size <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">.</span>Width<span class="symbol">,</span> Orientation <span class="symbol">=</span> SegmentOrientation<span class="symbol">.</span>Horizontal <span class="symbol">}</span><span class="symbol">)</span><span class="symbol">;</span>
 segments<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> Segment <span class="symbol">{</span> Location <span class="symbol">=</span> <span class="keyword">new</span> Point<span class="symbol">(</span><span class="number">0</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">.</span>Height<span class="symbol">)</span><span class="symbol">,</span> Size <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">.</span>Width<span class="symbol">,</span> Orientation <span class="symbol">=</span> SegmentOrientation<span class="symbol">.</span>Horizontal <span class="symbol">}</span><span class="symbol">)</span><span class="symbol">;</span>
 segments<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> Segment <span class="symbol">{</span> Location <span class="symbol">=</span> Point<span class="symbol">.</span>Empty<span class="symbol">,</span> Size <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">.</span>Height<span class="symbol">,</span> Orientation <span class="symbol">=</span> SegmentOrientation<span class="symbol">.</span>Vertical <span class="symbol">}</span><span class="symbol">)</span><span class="symbol">;</span>
 segments<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> Segment <span class="symbol">{</span> Location <span class="symbol">=</span> <span class="keyword">new</span> Point<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">.</span>Width<span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">,</span> Size <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">.</span>Height<span class="symbol">,</span> Orientation <span class="symbol">=</span> SegmentOrientation<span class="symbol">.</span>Vertical <span class="symbol">}</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// add the rest of the segments</span>
 segments<span class="symbol">.</span>AddRange<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Segments<span class="symbol">)</span><span class="symbol">;</span>

 segments<span class="symbol">.</span>Sort<span class="symbol">(</span><span class="symbol">(</span>a<span class="symbol">,</span> b<span class="symbol">)</span> <span class="symbol">=&gt;</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> result <span class="symbol">=</span> a<span class="symbol">.</span>Location<span class="symbol">.</span>X<span class="symbol">.</span>CompareTo<span class="symbol">(</span>b<span class="symbol">.</span>Location<span class="symbol">.</span>X<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>result <span class="symbol">==</span> <span class="number">0</span><span class="symbol">)</span>
 result <span class="symbol">=</span> a<span class="symbol">.</span>Location<span class="symbol">.</span>Y<span class="symbol">.</span>CompareTo<span class="symbol">(</span>b<span class="symbol">.</span>Location<span class="symbol">.</span>Y<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">return</span> result<span class="symbol">;</span>
 <span class="symbol">}</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">foreach</span> <span class="symbol">(</span>Segment segment <span class="keyword">in</span> segments<span class="symbol">)</span>
 <span class="symbol">{</span>
 Segment currentSegment<span class="symbol">;</span>

 <span class="comment">// add the segment points</span>
 <span class="keyword">this</span><span class="symbol">.</span>UpdatePoint<span class="symbol">(</span>segment<span class="symbol">.</span>Location<span class="symbol">,</span> segment<span class="symbol">.</span>Orientation <span class="symbol">==</span> SegmentOrientation<span class="symbol">.</span>Horizontal <span class="symbol">?</span> SegmentPointConnections<span class="symbol">.</span>Left <span class="symbol">:</span> SegmentPointConnections<span class="symbol">.</span>Top<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>UpdatePoint<span class="symbol">(</span>segment<span class="symbol">.</span>EndLocation<span class="symbol">,</span> segment<span class="symbol">.</span>Orientation <span class="symbol">==</span> SegmentOrientation<span class="symbol">.</span>Horizontal <span class="symbol">?</span> SegmentPointConnections<span class="symbol">.</span>Right <span class="symbol">:</span> SegmentPointConnections<span class="symbol">.</span>Bottom<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// calculate any intersecting points</span>
 currentSegment <span class="symbol">=</span> segment<span class="symbol">;</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span>Segment otherSegment <span class="keyword">in</span> segments<span class="symbol">.</span>Where<span class="symbol">(</span>s <span class="symbol">=&gt;</span> s <span class="symbol">!=</span> currentSegment<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Point intersection<span class="symbol">;</span>

 intersection <span class="symbol">=</span> Intersection<span class="symbol">.</span>FindLineIntersection<span class="symbol">(</span>segment<span class="symbol">.</span>Location<span class="symbol">,</span> segment<span class="symbol">.</span>EndLocation<span class="symbol">,</span> otherSegment<span class="symbol">.</span>Location<span class="symbol">,</span> otherSegment<span class="symbol">.</span>EndLocation<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>intersection<span class="symbol">.</span>IsEmpty<span class="symbol">)</span>
 <span class="symbol">{</span>
 SegmentPointConnections flags<span class="symbol">;</span>

 flags <span class="symbol">=</span> SegmentPointConnections<span class="symbol">.</span>None<span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>intersection <span class="symbol">!=</span> segment<span class="symbol">.</span>Location <span class="symbol">&amp;&amp;</span> intersection <span class="symbol">!=</span> segment<span class="symbol">.</span>EndLocation<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>segment<span class="symbol">.</span>Orientation <span class="symbol">==</span> SegmentOrientation<span class="symbol">.</span>Horizontal<span class="symbol">)</span>
 flags <span class="symbol">|=</span> <span class="symbol">(</span> SegmentPointConnections<span class="symbol">.</span>Left <span class="symbol">|</span> SegmentPointConnections<span class="symbol">.</span>Right<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 flags <span class="symbol">|=</span> <span class="symbol">(</span>SegmentPointConnections<span class="symbol">.</span>Top <span class="symbol">|</span> SegmentPointConnections<span class="symbol">.</span>Bottom<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>intersection <span class="symbol">!=</span> otherSegment<span class="symbol">.</span>Location <span class="symbol">&amp;&amp;</span> intersection <span class="symbol">!=</span> otherSegment<span class="symbol">.</span>EndLocation<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>otherSegment<span class="symbol">.</span>Orientation <span class="symbol">==</span> SegmentOrientation<span class="symbol">.</span>Horizontal<span class="symbol">)</span>
 flags <span class="symbol">|=</span> <span class="symbol">(</span>SegmentPointConnections<span class="symbol">.</span>Left <span class="symbol">|</span> SegmentPointConnections<span class="symbol">.</span>Right<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 flags <span class="symbol">|=</span> <span class="symbol">(</span>SegmentPointConnections<span class="symbol">.</span>Top <span class="symbol">|</span> SegmentPointConnections<span class="symbol">.</span>Bottom<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>flags <span class="symbol">!=</span> SegmentPointConnections<span class="symbol">.</span>None<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>UpdatePoint<span class="symbol">(</span>intersection<span class="symbol">,</span> flags<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Breaking the code down, we do the following:</p>
<ul>
<li>Create an additional four segments representing the boundaries
of the canvas</li>
<li>Sort the segments by their starting locations</li>
<li>Cycle each segment and</li>
<li>Create a point for the starting co-ordinate</li>
<li>Create a point for the ending co-ordinate</li>
<li>Cycle each other segment and see if it intersects with the
current segment. If it does, create a new point at the
intersection</li>
</ul>
<p>In any case above where I state to create a point, the code will
either create a point if one doesn't already exist, otherwise it
will update the connections of the existing point.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> UpdatePoint<span class="symbol">(</span>Point location<span class="symbol">,</span> SegmentPointConnections connections<span class="symbol">)</span>
<span class="symbol">{</span>
 SegmentPoint point<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>Points<span class="symbol">.</span>TryGetValue<span class="symbol">(</span>location<span class="symbol">,</span> <span class="keyword">out</span> point<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 point <span class="symbol">=</span> <span class="keyword">new</span> SegmentPoint <span class="symbol">{</span> Location <span class="symbol">=</span> location<span class="symbol">,</span> Connections <span class="symbol">=</span> connections <span class="symbol">}</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Points<span class="symbol">.</span>Add<span class="symbol">(</span>point<span class="symbol">.</span>Location<span class="symbol">,</span> point<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>point<span class="symbol">.</span>Connections<span class="symbol">.</span>HasFlag<span class="symbol">(</span>connections<span class="symbol">)</span><span class="symbol">)</span>
 point<span class="symbol">.</span>Connections <span class="symbol">|=</span> connections<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The <code>CalculatePoints</code> method above is very inefficient, but it
does the job. Once this routine has run, we'll have an array of
co-ordinates and the directions of linked points.</p>
<h2 id="calculating-rectangles">Calculating Rectangles</h2>
<p>Now that we have all points, both user defined, and
intersections, we can use this to generate the actual
rectangles.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> CalculateRectangles<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 SegmentPoint<span class="symbol">[</span><span class="symbol">]</span> horizontalPoints<span class="symbol">;</span>
 SegmentPoint<span class="symbol">[</span><span class="symbol">]</span> verticalPoints<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Rectangles <span class="symbol">=</span> <span class="keyword">new</span> HashSet<span class="symbol">&lt;</span>Rectangle<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 horizontalPoints <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Points<span class="symbol">.</span>Values<span class="symbol">.</span>OrderBy<span class="symbol">(</span>p <span class="symbol">=&gt;</span> p<span class="symbol">.</span>X<span class="symbol">)</span><span class="symbol">.</span>ToArray<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 verticalPoints <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Points<span class="symbol">.</span>Values<span class="symbol">.</span>OrderBy<span class="symbol">(</span>p <span class="symbol">=&gt;</span> p<span class="symbol">.</span>Y<span class="symbol">)</span><span class="symbol">.</span>ToArray<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">foreach</span> <span class="symbol">(</span>SegmentPoint topLeft <span class="keyword">in</span> <span class="keyword">this</span><span class="symbol">.</span>Points<span class="symbol">.</span>Values<span class="symbol">.</span>Where<span class="symbol">(</span>p <span class="symbol">=&gt;</span> p<span class="symbol">.</span>Connections<span class="symbol">.</span>HasFlag<span class="symbol">(</span>SegmentPointConnections<span class="symbol">.</span>Left <span class="symbol">|</span> SegmentPointConnections<span class="symbol">.</span>Top<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 SegmentPoint topRight<span class="symbol">;</span>
 SegmentPoint bottomLeft<span class="symbol">;</span>

 topRight <span class="symbol">=</span> horizontalPoints<span class="symbol">.</span>FirstOrDefault<span class="symbol">(</span>p <span class="symbol">=&gt;</span> p<span class="symbol">.</span>X <span class="symbol">&gt;</span> topLeft<span class="symbol">.</span>X <span class="symbol">&amp;&amp;</span> p<span class="symbol">.</span>Y <span class="symbol">==</span> topLeft<span class="symbol">.</span>Y <span class="symbol">&amp;&amp;</span> p<span class="symbol">.</span>Connections<span class="symbol">.</span>HasFlag<span class="symbol">(</span>SegmentPointConnections<span class="symbol">.</span>Right <span class="symbol">|</span> SegmentPointConnections<span class="symbol">.</span>Top<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 bottomLeft <span class="symbol">=</span> verticalPoints<span class="symbol">.</span>FirstOrDefault<span class="symbol">(</span>p <span class="symbol">=&gt;</span> p<span class="symbol">.</span>X <span class="symbol">==</span> topLeft<span class="symbol">.</span>X <span class="symbol">&amp;&amp;</span> p<span class="symbol">.</span>Y <span class="symbol">&gt;</span> topLeft<span class="symbol">.</span>Y <span class="symbol">&amp;&amp;</span> p<span class="symbol">.</span>Connections<span class="symbol">.</span>HasFlag<span class="symbol">(</span>SegmentPointConnections<span class="symbol">.</span>Left <span class="symbol">|</span> SegmentPointConnections<span class="symbol">.</span>Bottom<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>topRight <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> bottomLeft <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 SegmentPoint bottomRight<span class="symbol">;</span>

 bottomRight <span class="symbol">=</span> horizontalPoints<span class="symbol">.</span>FirstOrDefault<span class="symbol">(</span>p <span class="symbol">=&gt;</span> p<span class="symbol">.</span>X <span class="symbol">==</span> topRight<span class="symbol">.</span>X <span class="symbol">&amp;&amp;</span> p<span class="symbol">.</span>Y <span class="symbol">==</span> bottomLeft<span class="symbol">.</span>Y <span class="symbol">&amp;&amp;</span> p<span class="symbol">.</span>Connections<span class="symbol">.</span>HasFlag<span class="symbol">(</span>SegmentPointConnections<span class="symbol">.</span>Right <span class="symbol">|</span> SegmentPointConnections<span class="symbol">.</span>Bottom<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>bottomRight <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Rectangle rectangle<span class="symbol">;</span>

 rectangle <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>topLeft<span class="symbol">.</span>X<span class="symbol">,</span> topLeft<span class="symbol">.</span>Y<span class="symbol">,</span> bottomRight<span class="symbol">.</span>X <span class="symbol">-</span> topLeft<span class="symbol">.</span>X<span class="symbol">,</span> bottomRight<span class="symbol">.</span>Y <span class="symbol">-</span> topLeft<span class="symbol">.</span>Y<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Rectangles<span class="symbol">.</span>Add<span class="symbol">(</span>rectangle<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>In this method, we loop through all our defined points that have
connections in the upper left corner.</p>
<p>For each matching point, we then try and find points with the
following criteria</p>
<ul>
<li>has the same Y co-ordinate and a right or top connection. This
gives us the upper right corner.</li>
<li>has the same X co-ordinate and a left or bottom connection.
This gives us the lower left corner.</li>
<li>if we have both the above corners, we then try and find a
point that has the same X co-ordinate as the upper right
corner, the same Y co-ordinate as the lower left corner, and
right or bottom connections. This gives us the last corner,
lower right</li>
<li>if we have all four corners, and the rectangle. The use of a
<code>HashSet</code> ensures the same rectangle isn't added twice.</li>
</ul>
<p>And that's all there is to it. With these two routines, I can
now break up a rectangle into many smaller pieces just by
defining pairs of points.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/rectangle3.png" class="gallery" title="Another example of the programs output" ><img src="https://images.cyotek.com/image/thumbnail/devblog/rectangle3.png" alt="Another example of the programs output" decoding="async" loading="lazy" /></a><figcaption>Another example of the programs output</figcaption></figure><h2 id="closing-remarks">Closing Remarks</h2>
<p>There are a few things that I'm aware of that the code doesn't
do</p>
<ul>
<li>As mentioned (several times!) none of this code is
particularly efficient. The more segments you add, the slower
it will get. Gareth Rees posted a nice <a href="https://stackoverflow.com/a/13923149/148962" rel="external nofollow noopener">diagram</a> of what I
should be doing, and indeed his pointers help me get this code
working originally</li>
<li>It doesn't handle overlapping segments very well. It will
rerun the point generation for these, adding to the overall
time</li>
<li>Ordering of the output rectangles isn't always what you
expect, it jumps around a bit</li>
<li>The end coordinate must be equal to or greater than the start
(using the sample, providing a negative segment size would
trigger this bug)</li>
</ul>
<p>As always the source code for this sample can be downloaded from
the link below.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2013-02-10 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/dividing-up-a-rectangle-based-on-pairs-of-points-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a custom ErrorProvider component for use with Windows Forms applicationsurn:uuid:8e214269-da98-48b9-89a5-2100bd073e792013-01-01T17:56:51Z2013-01-01T17:56:51Z<p>In recent code, I've been trying to avoid displaying validation
errors as message boxes, but display something in-line. The .NET
Framework provides an <code>ErrorProvider</code> component which does just
this. One of the disadvantages of this control is that it
displays an icon indicating error state - which means you need a
chunk of white space somewhere around your control, which may
not always be very desirable.</p>
<p>This article describes how to create a custom error provider
component that uses background colours and tool tips to indicate
error state.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/custom-error-provider.png" class="gallery" title="A simple application demonstrating the custom provider" ><img src="https://images.cyotek.com/image/thumbnail/devblog/custom-error-provider.png" alt="A simple application demonstrating the custom provider" decoding="async" loading="lazy" /></a><figcaption>A simple application demonstrating the custom provider</figcaption></figure>
<blockquote>
<p>Note: I don't use data binding, so the provider implementation
I demonstrate below currently has no support for this.</p>
</blockquote>
<h2 id="getting-started">Getting Started</h2>
<p>Create a new <code>Component</code> class and implement the
<code>IExtenderProvider</code> interface. This interface is used to add
custom properties to other controls - it has a single method
<code>CanExtend</code> that must return true for a given source object if
it can extend itself to said object.</p>
<p>In this example, we'll offer our properties to any control.
However, you can always customize this to work only with certain
control types such as <code>TextBoxBase</code>, <code>ListBoxControl</code> etc.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">bool</span> IExtenderProvider<span class="symbol">.</span>CanExtend<span class="symbol">(</span><span class="keyword">object</span> extendee<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> extendee <span class="keyword">is</span> Control<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="implementing-custom-properties">Implementing Custom Properties</h2>
<p>Unlike how properties are normally defined, you need to create
get and set methods for each property you wish to expose. In our
case, we'll be offering <code>Error</code> and <code>ErrorBackColor</code> properties.
Using <code>Error</code> as an example, the methods would be <code>GetError</code> and
<code>SetError</code>. Both methods need to have a parameter for the source
object, and the set also needs a parameter for the property
value.</p>
<blockquote>
<p>Note: I named this property <code>Error</code> so I could drop in replace
the new component for the .NET Framework one without changing
any code bar the control declaration. If you don't plan on
doing this, you may wish to name it <code>ErrorText</code> or something
more descriptive!</p>
</blockquote>
<p>In this example, we'll store all our properties in dictionaries,
keyed on the source control. If you want to be more efficient,
rather than using multiple dictionaries you could use one tied
to a backing class/structure but we'll keep this example nice
and simple.</p>
<p>Below is the implementation for getting the value.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Appearance&quot;</span><span class="symbol">)</span><span class="symbol">,</span> DefaultValue<span class="symbol">(</span><span class="string">&quot;&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">string</span> GetError<span class="symbol">(</span>Control control<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>

 <span class="keyword">if</span><span class="symbol">(</span>control <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException<span class="symbol">(</span><span class="string">&quot;control&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span><span class="symbol">(</span><span class="symbol">!</span>_errorTexts<span class="symbol">.</span>TryGetValue<span class="symbol">(</span>control<span class="symbol">,</span> <span class="keyword">out</span> result<span class="symbol">)</span><span class="symbol">)</span>
 result <span class="symbol">=</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">;</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Getting the value is straightforward, we attempt to get a custom
value from our backing dictionary, if one does not exist then we
return a default value.</p>
<p>It's also a good idea to decorate your get methods with
<code>Category</code> and <code>DefaultValue</code> attributes. The <code>Category</code>
attribute allows you to place the property in the <code>PropertyGrid</code>
(otherwise it will end up in the <em>Misc</em> group), while the
<code>DefaultValue</code> attribute does two things. Firstly, in designers
such as the <code>PropertyGrid</code>, default values appear in a normal
type face whilst custom values appear in bold. Secondly, it
avoids cluttering up auto generated code files with assignment
statements. If the default value is an empty string, and the
property is set to that value, no serialization code will be
generated. (Which is also helpful if you decide to change
default values, such as the default error colour later on)</p>
<p>Next, we have our set method code.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">void</span> SetError<span class="symbol">(</span>Control control<span class="symbol">,</span> <span class="keyword">string</span> value<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span><span class="symbol">(</span>control <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException<span class="symbol">(</span><span class="string">&quot;control&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span><span class="symbol">(</span>value <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 value <span class="symbol">=</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">;</span>

 <span class="keyword">if</span><span class="symbol">(</span><span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>value<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _errorTexts<span class="symbol">[</span>control<span class="symbol">]</span> <span class="symbol">=</span> value<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>ShowError<span class="symbol">(</span>control<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="keyword">this</span><span class="symbol">.</span>ClearError<span class="symbol">(</span>control<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>As we want &quot;unset&quot; values to be the empty string, we have a
quick null check in place to convert nulls to empty strings. If
a non-empty string is passed in, we update the source control to
be in it's &quot;error&quot; state. If it's blank, then we clear the
error.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> ShowError<span class="symbol">(</span>Control control<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span><span class="symbol">(</span>control <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException<span class="symbol">(</span><span class="string">&quot;control&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span><span class="symbol">(</span><span class="symbol">!</span>_originalColors<span class="symbol">.</span>ContainsKey<span class="symbol">(</span>control<span class="symbol">)</span><span class="symbol">)</span>
 _originalColors<span class="symbol">.</span>Add<span class="symbol">(</span>control<span class="symbol">,</span> control<span class="symbol">.</span>BackColor<span class="symbol">)</span><span class="symbol">;</span>

 control<span class="symbol">.</span>BackColor <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetErrorBackColor<span class="symbol">(</span>control<span class="symbol">)</span><span class="symbol">;</span>
 _toolTip<span class="symbol">.</span>SetToolTip<span class="symbol">(</span>control<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>GetError<span class="symbol">(</span>control<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>_erroredControls<span class="symbol">.</span>Contains<span class="symbol">(</span>control<span class="symbol">)</span><span class="symbol">)</span>
 _erroredControls<span class="symbol">.</span>Add<span class="symbol">(</span>control<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Above you can see the code to display an error. First we store
the original background colour of the control if we haven't
previously saved it, and then apply the error colour. And
because users still need to know what the actual error is, we
add a tool tip with the error text. Finally, we store the
control in an internal list - we'll use that later on.</p>
<p>Clearing the error state is more or less the reverse. First we
try and set the background colour back it what it's original
value, and we remove the tool tip.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">void</span> ClearError<span class="symbol">(</span>Control control<span class="symbol">)</span>
<span class="symbol">{</span>
 Color originalColor<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_originalColors<span class="symbol">.</span>TryGetValue<span class="symbol">(</span>control<span class="symbol">,</span> <span class="keyword">out</span> originalColor<span class="symbol">)</span><span class="symbol">)</span>
 control<span class="symbol">.</span>BackColor <span class="symbol">=</span> originalColor<span class="symbol">;</span>

 _errorTexts<span class="symbol">.</span>Remove<span class="symbol">(</span>control<span class="symbol">)</span><span class="symbol">;</span>
 _toolTip<span class="symbol">.</span>SetToolTip<span class="symbol">(</span>control<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>
 _erroredControls<span class="symbol">.</span>Remove<span class="symbol">(</span>control<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="checking-if-errors-are-present">Checking if errors are present</h2>
<p>Personally speaking, I don't like the built in <code>Validating</code>
event as it prevents focus from shifting until you resolve the
error. That is a pretty horrible user experience in my view
which is why my validation runs from change events. But then,
how do you know if validation errors are present when submitting
data? You could keep track of this separately, but we might as
well get our component to do this.</p>
<p>When an error is shown, we store that control in a list, and
then remove it from the list when the error is cleared. So we
can add a very simple property to the control to check if errors
are present:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">bool</span> HasErrors
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _erroredControls<span class="symbol">.</span>Count <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>At present the error list isn't exposed, but that would be easy
enough to do if required.</p>
<h2 id="designer-support">Designer Support</h2>
<p>If you now drop this component onto a form and try and use it,
you'll find nothing happens. In order to get your new properties
to appear on other controls, you need to add some attributes to
the component.</p>
<p>For each new property you are exposing, you have to add a
<code>ProviderProperty</code> declaration to the top of the class
containing the name of the property, and the type of the objects
that can get the new properties.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>ProvideProperty<span class="symbol">(</span><span class="string">&quot;ErrorBackColor&quot;</span><span class="symbol">,</span> <span class="keyword">typeof</span><span class="symbol">(</span>Control<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">,</span> ProvideProperty<span class="symbol">(</span><span class="string">&quot;Error&quot;</span><span class="symbol">,</span> <span class="keyword">typeof</span><span class="symbol">(</span>Control<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> ErrorProvider <span class="symbol">:</span> Component<span class="symbol">,</span> IExtenderProvider
<span class="symbol">{</span>
 <span class="symbol">...</span>
</pre>
</figure>
<p>With these attributes in place (and assuming you have correctly
created <code>&lt;PropertyName&gt;Get</code> and <code>&lt;PropertyName&gt;Set</code> methods,
your new component should now start adding properties to other
controls in the designer.</p>
<h2 id="example-usage">Example Usage</h2>
<p>In this component validation is done from event handlers - you
can either use the built in <code>Control.Validating</code> event, or use
the most appropriate change event of your source control. For
example, the demo project uses the following code to validate
integer inputs:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> integerTextBox_TextChanged<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 Control control<span class="symbol">;</span>
 <span class="keyword">string</span> errorText<span class="symbol">;</span>
 <span class="keyword">int</span> value<span class="symbol">;</span>

 control <span class="symbol">=</span> <span class="symbol">(</span>Control<span class="symbol">)</span>sender<span class="symbol">;</span>
 errorText <span class="symbol">=</span> <span class="symbol">!</span><span class="keyword">int</span><span class="symbol">.</span>TryParse<span class="symbol">(</span>control<span class="symbol">.</span>Text<span class="symbol">,</span> <span class="keyword">out</span> value<span class="symbol">)</span> <span class="symbol">?</span> <span class="string">&quot;Please enter a valid integer&quot;</span> <span class="symbol">:</span> <span class="keyword">null</span><span class="symbol">;</span>

 errorProvider<span class="symbol">.</span>SetError<span class="symbol">(</span>control<span class="symbol">,</span> errorText<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> okButton_Click<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>errorProvider<span class="symbol">.</span>HasErrors<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// submit the new data</span>

 <span class="keyword">this</span><span class="symbol">.</span>DialogResult <span class="symbol">=</span> DialogResult<span class="symbol">.</span>OK<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Close<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="keyword">this</span><span class="symbol">.</span>DialogResult <span class="symbol">=</span> DialogResult<span class="symbol">.</span>None<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The only thing you need to remember is to clear errors as well
as display them!</p>
<h2 id="limitations">Limitations</h2>
<p>As mentioned at the start of the article, the sample class
doesn't support data binding.</p>
<p>Also, while you can happily set custom error background colours
at design time, it probably won't work so well if you try and
set the error text at design time. Not sure if the original
<code>ErrorProvider</code> supports this either, but it hasn't been
specifically coded for in this sample as my requirements are to
use it via change events of the controls. For this reason, when
clearing an error (or all errors), the text dictionary is always
updated, but the background colour dictionaries are left alone.</p>
<h2 id="final-words">Final words</h2>
<p>As usual, this code should be tested before being used in a
production application - while we are currently using this in
almost-live code, it hasn't been thoroughly tested and may
contain bugs or omissions.</p>
<p>The sample project below includes the full source for this
example class, and a basic demonstration project.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2013-01-01 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-a-custom-errorprovider-component-for-use-with-windows-forms-applications .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comImageBox and TabList update's - virtual mode, pixel grid, bug fixes and more!urn:uuid:090ccddd-473e-47f7-ab27-b623ab52a7372013-08-10T20:32:56Z2012-12-31T19:53:27Z<p>Our last post before the new year and some new material is an
update to the <code>ImageBox</code> (now at version 1.1.2.0) and <code>TabList</code>
(at version 1.0.0.2) controls. You can grab the updated source
from the links at the end of the post, or from the <a href="https://github.com/cyotek" rel="external nofollow noopener">GitHub</a>
page.</p>
<h2 id="imagebox">ImageBox</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/imgbox-6a.png" class="gallery" title="Virtual mode allows you to use the ImageBox control without a backing image" ><img src="https://images.cyotek.com/image/thumbnail/devblog/imgbox-6a.png" alt="Virtual mode allows you to use the ImageBox control without a backing image" decoding="async" loading="lazy" /></a><figcaption>Virtual mode allows you to use the ImageBox control without a backing image</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/imgbox-6b.png" class="gallery" title="The pixel grid allows you to show a grid when zoomed in" ><img src="https://images.cyotek.com/image/thumbnail/devblog/imgbox-6b.png" alt="The pixel grid allows you to show a grid when zoomed in" decoding="async" loading="lazy" /></a><figcaption>The pixel grid allows you to show a grid when zoomed in</figcaption></figure><h3 id="changes-and-new-features">Changes and new features</h3>
<ul>
<li>Added <code>IsPointInImage</code> method. This function returns if a
given point is within the image viewport, and is useful for
combining with <code>PointToImage</code>.</li>
<li>Added <code>ImageBorderColor</code> property, allowing you to customize
the color of the image border</li>
<li>Added a new <code>ImageBoxBorderStyle</code>, <code>FixedSingleGlowShadow</code>.
This style allows for a more smoother outer glow shadow
instead of the existing clunky drop shadow.</li>
<li>Added <code>ShowPixelGrid</code> and <code>PixelGridColor</code> properties. When
set, a dotted grid is displayed around pixels when zooming in
on an image.</li>
<li>Added new overload to <code>PointToImage</code> which allows you to
specify if the function should map the given point to the
nearest available edge(s) if the point is outside the image
boundaries</li>
<li>Added <code>AllowDoubleClick</code> property. When set, the normal double
click events and overrides work as expected.</li>
<li>Added <code>VirtualMode</code> and <code>VirtualSize</code> properties. These new
properties allow you to use all functionality of the ImageBox
control without having to set the <code>Image</code> property. You can
also use the new <code>VirtualDraw</code> event to provide custom drawing
without having to override existing drawing functionality.</li>
<li>Additional documentation added via XML comments</li>
</ul>
<h3 id="fixed">Fixed</h3>
<ul>
<li>If the <code>GridDisplayMode</code> property is set to <code>Image</code> an
explicit image border is no longer drawn, instead the
<code>ImageBorder</code> property is correctly honoured.</li>
<li>Fixes a problem where half the pixels of the first row/column
were lost when zooming. Thanks to Rotem for the fix.</li>
<li>The <code>GetImageViewport</code> method now correctly returns a width
and height that accounts for control size, padding and zoom
levels.</li>
<li>Fixed incorrect attributes on <code>AutoSize</code> property</li>
<li>Fixed the image viewport sometimes being the incorrect size
when zoomed in. Thanks to WMJ for the fix.</li>
<li>Fixes &quot;see also&quot; documentation errors for events</li>
</ul>
<h2 id="tablist">TabList</h2>
<h3 id="changes-and-new-features-1">Changes and new features</h3>
<ul>
<li>Added <code>ShowTabList</code> property. When set to <code>False</code>, the list of
tabs is no longer displayed, and navigation can only occur via
code.</li>
<li>Added <code>AllowTabSelection</code> property. When set to <code>False</code>, the
control can no longer gain focus, mouse hover effects are not
displayed, and navigation can only occur via code. This allows
you to disable navigation whilst still having the tabs
visible.</li>
</ul>
<h3 id="fixed-1">Fixed</h3>
<ul>
<li>Fixed the <code>HoverIndex</code> property always defaulting to zero.</li>
</ul>
<p>Happy New Year all!</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2012-12-31 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/imagebox-and-tablist-updates-virtual-mode-pixel-grid-bug-fixes-and-more .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comManually writing the byte order mark (BOM) for an encoding into a streamurn:uuid:910dd2cc-0ffc-480d-9930-468e436557db2012-12-11T20:13:02Z2012-12-11T20:13:02Z<p>I recently discovered a problem with our <a href="https://cyotek.com/cyotek-webcopy">WebCopy</a> and
<a href="https://cyotek.com/cyotek-sitemap-creator">Cyotek Sitemap Creator</a> products to do with &quot;corruption&quot; of
plain text documents, where non-ANSI characters appeared
incorrectly. It didn't take long to realize that these programs
were saving text content as ANSI files. Which I found curious as
the Crawler library they use detects response encoding and uses
this to save the files.</p>
<p>Or does it? Consider the code below:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">string</span> fileName<span class="symbol">;</span>
<span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">]</span> data<span class="symbol">;</span>
Encoding encoding<span class="symbol">;</span>

fileName <span class="symbol">=</span> Path<span class="symbol">.</span>GetTempFileName<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
data <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">byte</span><span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span><span class="symbol">;</span> <span class="comment">// assume you have a populated byte array!</span>
encoding <span class="symbol">=</span> Encoding<span class="symbol">.</span>UTF<span class="number">8</span><span class="symbol">;</span>

<span class="keyword">using</span> <span class="symbol">(</span>FileStream stream <span class="symbol">=</span> <span class="keyword">new</span> FileStream<span class="symbol">(</span>fileName<span class="symbol">,</span> FileMode<span class="symbol">.</span>Create<span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>BinaryWriter writer <span class="symbol">=</span> <span class="keyword">new</span> BinaryWriter<span class="symbol">(</span>stream<span class="symbol">,</span> encoding<span class="symbol">)</span><span class="symbol">)</span>
 writer<span class="symbol">.</span>Write<span class="symbol">(</span>data<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Looking at this, you might be tempted to assume (as I did) that
this code would save the content in the given encoding. When I
tried opening one of the files generated by similar code to the
above in Notepad++, I found they were encoded as ANSI files.
Switching the encoding to UTF-8 immediately displayed the files
correctly without the &quot;corruption&quot;. So it seems the byte order
mark (BOM) isn't actually written by the BinaryWriter - I think
it only uses the given encoding for converting strings to a byte
array. All this time I assumed files were being saved as UTF-8
(or whatever the response encoding was) and properly supported
Unicode, and all this time I was wrong.</p>
<p>So how do you manually write a BOM into a document? The oddly
named <code>GetPreamble</code> function available from the <code>Encoding</code> class
is what you need - this returns the bytes that comprise the BOM,
and you can then write this directly to your stream:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">string</span> fileName<span class="symbol">;</span>
<span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">]</span> data<span class="symbol">;</span>
Encoding encoding<span class="symbol">;</span>

fileName <span class="symbol">=</span> Path<span class="symbol">.</span>GetTempFileName<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
data <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">byte</span><span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span><span class="symbol">;</span> <span class="comment">// assume you have a populated byte array!</span>
encoding <span class="symbol">=</span> Encoding<span class="symbol">.</span>UTF<span class="number">8</span><span class="symbol">;</span>

<span class="keyword">using</span> <span class="symbol">(</span>FileStream stream <span class="symbol">=</span> <span class="keyword">new</span> FileStream<span class="symbol">(</span>fileName<span class="symbol">,</span> FileMode<span class="symbol">.</span>Create<span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>BinaryWriter writer <span class="symbol">=</span> <span class="keyword">new</span> BinaryWriter<span class="symbol">(</span>stream<span class="symbol">,</span> encoding<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 writer<span class="symbol">.</span>Write<span class="symbol">(</span>encoding<span class="symbol">.</span>GetPreamble<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 writer<span class="symbol">.</span>Write<span class="symbol">(</span>data<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>Note that you only need to write a BOM if your document is
actually supposed to be a text file - if it is &quot;normal&quot; binary
data (such as an image or a gzip stream) then you definitely
do not want to write a BOM, or you truly will have a corrupt
file.</p>
</blockquote>
<p>Now the files produced by WebCopy and Sitemap Creator are
encoded correctly and I can be happily with yet another bug
squashed, unhappy at yet another reminder of why I need to write
a proper set of automated tests for the libraries I use, but
happy again that I had another (albeit brief) tip to post on
this blog.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2012-12-11 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/manually-writing-the-byte-order-mark-bom-for-an-encoding-into-a-stream .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comExtracting email addresses from Outlookurn:uuid:9351bcc1-f7d6-41ab-902c-d48cf9730ead2012-09-26T18:24:44Z2012-09-26T18:24:44Z<p>The cyotek.com receives an awful lot of spam and a lot of this
is sent to email addresses that don't exist. However, as we
currently have catch all's enabled, it means we receive it
regardless. This is compounded by the fact that I tend to create
a unique email address for each website or service I interact
with. And it's impossible to remember them all!</p>
<p>As a first step to deleting the catch alls, I wanted to see how
many unique @cyotek.com addresses were in use. The simplest way
of picking up these would be scanning PST files - we have email
going back to 2002 in these files, and there's the odd backup
elsewhere going back even further. Last time I used OLE
Automation with Outlook was back in the days of VB6 and I recall
well getting plagued with permission dialogs each time I dreamed
of trying to access the API. Still, I thought I'd take a look.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/outlook.png" class="gallery" title="A console application merrily extracting my personal email addresses from my Outlook store" ><img src="https://images.cyotek.com/image/thumbnail/devblog/outlook.png" alt="A console application merrily extracting my personal email addresses from my Outlook store" decoding="async" loading="lazy" /></a><figcaption>A console application merrily extracting my personal email addresses from my Outlook store</figcaption></figure><h2 id="setting-up">Setting up</h2>
<blockquote>
<p>Note: I tested this project on an Outlook profile which has
loaded a primary PST, an archive PST, and a Gmail account. I
haven't tested this with any other type of account (for
example Exchange) or with accounts using non-SMTP email
addresses. Caveat emptor!</p>
</blockquote>
<p>The first thing to do is add a reference to the Outlook COM
objects. I have VS2010 and VS2012 installed on this machine, and
one of them has installed a bunch of prepared Office Interop
DLL's into the GAC. Handy, I won't have to create my own! Adding
a reference to the <strong>Microsoft Outlook 14.0 Object Library</strong>
added three references,
<strong>Microsoft.Office.Interop.Outlook.dll</strong>, <strong>Office.dll</strong> and
<strong>stdole</strong> to my project.</p>
<blockquote>
<p>Note: Depending on your version of VS / .NET Framework, the
references may have a property named <strong>Embed Interop Types</strong>
which defaults to <code>true</code>. When left at this, you may have
problems debugging as you won't be able to access the objects
properly through the Immediate window, instead getting an
error similar to</p>
<blockquote>
<p>&quot;Member 'To' on embedded interop type
'Microsoft.Office.Interop.Outlook.MailItem' cannot be
evaluated while debugging since it is never referenced in the
program. Consider casting the source object to type 'dynamic'
first or building with the 'Embed Interop Types' property set
to false when debugging&quot;</p>
</blockquote>
<p>Probably a good idea to set this to <strong>false</strong> before debugging
your code!</p>
</blockquote>
<h2 id="connecting-to-outlook">Connecting to Outlook</h2>
<blockquote>
<p>All the code below assumes that you have a <code>using Microsoft.Office.Interop.Outlook;</code> statement at the top of
your code file.</p>
</blockquote>
<p>Connecting to Outlook is easy enough, just create a new instance
of the Application interface. We'll use as a root for everything
else.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Application application<span class="symbol">;</span>

application <span class="symbol">=</span> <span class="keyword">new</span> Application<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<blockquote>
<p>Remember I mentioned permission dialogs? Older versions of
Outlook used to prompt for permissions. Outlook 2010 just
seems to quietly get on with things. The only thing I've
noticed is that if you try and create a new <code>Application</code> when
Outlook isn't currently running, it will be silently started
and the system tray icon will have a slightly different icon
and a tooltip informing that some other program is using
Outlook. Much nicer than previous behaviours!</p>
</blockquote>
<h2 id="getting-account-folders">Getting Account Folders</h2>
<p>The <code>Session</code> property of the <code>Application</code> interface returns a
<code>NameSpace</code> that details your Outlook setup, and allows access
to accounts, profile details etc. However, for this project, the
only thing I care about is the <code>Folders</code> property which returns
a collection of <code>MAPIFolder</code> objects. In my case, it was the
three top level folders for my profile - I was somewhat
surprised that the Gmail account was loaded actually.</p>
<p>Now that we have a folder, we can scan it by enumerating the
<code>Items</code> property. As Outlook folders can contain items of
various types, you need to check the item type - I'm looking for
<code>MailItem</code> objects in order to extract those addresses.</p>
<h2 id="pulling-out-email-addresses">Pulling out email addresses</h2>
<p>Each <code>MailItem</code> has <code>Sender</code>, <code>To</code> and <code>Recipients</code> properties.
<code>To</code> seems to be just a string version of <code>Recipients</code> and so
shall be completely ignored - why bother parsing it manually
when <code>Recipients</code> already does it for you. The <code>Sender</code> property
returns an <code>AddressEntry</code>, and each item in the <code>Recipients</code>
collection (a <code>Recipient</code>) offers an <code>AddressEntry</code> property. So
we're all set!</p>
<p>The following code snippet is from the example project, and
basically shows how I scan a source <code>MAPIFolder</code> looking for
<code>MailItem</code> objects.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> ScanFolder<span class="symbol">(</span>MAPIFolder folder<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>CurrentFolderIndex<span class="symbol">++</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnFolderScanning<span class="symbol">(</span><span class="keyword">new</span> MAPIFolderEventArgs<span class="symbol">(</span>folder<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>FolderCount<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>CurrentFolderIndex<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// items</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span><span class="keyword">object</span> item <span class="keyword">in</span> folder<span class="symbol">.</span>Items<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>item <span class="keyword">is</span> MailItem<span class="symbol">)</span>
 <span class="symbol">{</span>
 MailItem email<span class="symbol">;</span>

 email <span class="symbol">=</span> <span class="symbol">(</span>MailItem<span class="symbol">)</span>item<span class="symbol">;</span>

 <span class="comment">// add the sender of the email</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Options<span class="symbol">.</span>HasFlag<span class="symbol">(</span>Options<span class="symbol">.</span>Sender<span class="symbol">)</span><span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>ProcessAddress<span class="symbol">(</span>email<span class="symbol">.</span>Sender<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// add the recipies of the email</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Options<span class="symbol">.</span>HasFlag<span class="symbol">(</span>Options<span class="symbol">.</span>Recipient<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span>Recipient recipient <span class="keyword">in</span> email<span class="symbol">.</span>Recipients<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>ProcessAddress<span class="symbol">(</span>recipient<span class="symbol">.</span>AddressEntry<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="comment">// sub folders</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Options<span class="symbol">.</span>HasFlag<span class="symbol">(</span>Options<span class="symbol">.</span>SubFolders<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span>MAPIFolder childFolder <span class="keyword">in</span> folder<span class="symbol">.</span>Folders<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>ScanFolder<span class="symbol">(</span>childFolder<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>When I find an <code>AddressEntry</code> to process, I call the following
functions:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> ProcessAddress<span class="symbol">(</span>AddressEntry addressEntry<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>addressEntry <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> <span class="symbol">(</span>addressEntry<span class="symbol">.</span>AddressEntryUserType <span class="symbol">==</span> OlAddressEntryUserType<span class="symbol">.</span>olSmtpAddressEntry <span class="symbol">||</span> addressEntry<span class="symbol">.</span>AddressEntryUserType <span class="symbol">==</span> OlAddressEntryUserType<span class="symbol">.</span>olOutlookContactAddressEntry<span class="symbol">)</span><span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>ProcessAddress<span class="symbol">(</span>addressEntry<span class="symbol">.</span>Address<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>addressEntry <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 Debug<span class="symbol">.</span>Print<span class="symbol">(</span><span class="string">&quot;Unknown address type: {0} ({1})&quot;</span><span class="symbol">,</span> addressEntry<span class="symbol">.</span>AddressEntryUserType<span class="symbol">,</span> addressEntry<span class="symbol">.</span>Address<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> ProcessAddress<span class="symbol">(</span><span class="keyword">string</span> emailAddress<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> domainStartPosition<span class="symbol">;</span>

 domainStartPosition <span class="symbol">=</span> emailAddress<span class="symbol">.</span>IndexOf<span class="symbol">(</span><span class="string">&quot;@&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>emailAddress<span class="symbol">)</span> <span class="symbol">&amp;&amp;</span> domainStartPosition <span class="symbol">!=</span> <span class="symbol">-</span><span class="number">1</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">bool</span> canAdd<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Options<span class="symbol">.</span>HasFlag<span class="symbol">(</span>Options<span class="symbol">.</span>FilterByDomain<span class="symbol">)</span><span class="symbol">)</span>
 canAdd <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>IncludedDomains<span class="symbol">.</span>Contains<span class="symbol">(</span>emailAddress<span class="symbol">.</span>Substring<span class="symbol">(</span>domainStartPosition <span class="symbol">+</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 canAdd <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>canAdd<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>EmailAddresses<span class="symbol">.</span>Add<span class="symbol">(</span>emailAddress<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Although I'm scanning my entire PST, I don't want every single
email address in there - I ran it once and it brought back just
over 5000 addresses. What I want, is addresses tied to the
domains I own, so I added some filtering for this. With this
filtering enabled it returned a more manageable 497 unique
addresses. Although I'm not creating 497 aliases on the email
server!</p>
<h3 id="wrapping-up">Wrapping up</h3>
<p>This is a lot easier than what I was expecting, and in fact this
is probably the smoothest piece of COM interop I've done with
.NET yet. No strange errors, no forced to compile in 32bit mode,
It Just Works.</p>
<p>You can find the example project in the link below.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2012-09-26 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/extracting-email-addresses-from-outlook .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comImageBox update, version 1.1.0.0urn:uuid:24d152df-a604-4f8a-a7eb-7b149ac194be2012-08-30T21:53:50Z2012-08-30T21:53:50Z<p>The <code>ImageBox</code> control has had quite a big update, you can
download the source from the link below, or from our <a href="https://github.com/cyotek" rel="external nofollow noopener">GitHub</a>
page.</p>
<p>Listed below are the changes made during this update, we hope
you enjoy them!</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/imgbox-1-1-0-0.png" class="gallery" title="The updated ImageBox demonstration application" ><img src="https://images.cyotek.com/image/thumbnail/devblog/imgbox-1-1-0-0.png" alt="The updated ImageBox demonstration application" decoding="async" loading="lazy" /></a><figcaption>The updated ImageBox demonstration application</figcaption></figure><h2 id="changes-and-new-features">Changes and new features</h2>
<ul>
<li>Zooming with the mouse is now smoother, and the control
attempts to keep the area under the mouse before the zoom in
the same area after the zoon.</li>
<li>Added a <code>ZoomLevels</code> property which allows you to configure
the different zoom levels supported by the control. Now
instead of the control trying to guess the next zoom level, it
cycles appropriately through the defined levels. <em>Currently
ZoomLevels (apart from the default series) can only be set at
runtime.</em></li>
<li>The <code>ZoomIncrement</code> property has been removed due to the
introduction of the new zoom levels.</li>
<li>New <code>CenterAt</code> and <code>ScrollTo</code> methods allow you to scroll to a
given location in the source image.</li>
<li>Split shortcut handling into two methods
<code>ProcessScrollingShortcuts</code> for handling arrow keys and
<code>ProcessImageShortcuts</code> for handling pretty much anything
else.</li>
<li>Added <code>EnableShortcuts</code> property, allowing the built in
keyboard support to be disabled. When this property is true,
<code>ProcessImageShortcuts</code> is not called, allowing the control to
still be scrolled via the keyboard, but not zoomed etc.</li>
<li>Zooming can now be performed by the -/+ keys (<code>OemMinus</code> and
<code>Oemplus</code>).</li>
<li>When zooming (except via mouse action), if the <code>AutoCenter</code>
property is set, the control will always center the image even
when scrollbars are present.</li>
<li>Nestable <code>BeginUpdate</code> and <code>EndUpdate</code> methods allow you to
disable and enable painting of the control, for example when
changing multiple properties at once.</li>
<li>Added a new <code>GetSelectedImage</code> method which creates a new
<code>Bitmap</code> based on the current selection.</li>
<li>Added new <code>FitRectangle</code> method which takes a given rectangle
and ensure it fits within the image boundaries</li>
<li>The <code>AllowClickZoom</code> property now defaults to <code>false</code>.</li>
<li>The <code>PointToImage</code> function no longer adds +1 to the result of
the function.</li>
<li>Added a new <code>ZoomToRegion</code> method. This will caculate and
appropriate zoom level and scrollbar positions to fit a given
rectangle.</li>
<li>Added new <code>SelectionMode.Zoom</code>. When this mode is selected,
drawing a region will automatically zoom and position the
control to fit the region, after which the region is
automatically cleared.</li>
</ul>
<h2 id="bug-fixes">Bug fixes</h2>
<ul>
<li>Panning no longer tries to activate if no scrollbars are
visible</li>
<li>A new base class, <code>VirtualScrollableControl</code> is now used
instead of <code>ScrollableControl</code>. This removes completely the
flicker issues present in previous versions of the control.</li>
<li>The BorderStyle property has been moved to the <code>ScrollControl</code>
class, so that borders now correctly surround the control
(including scrollbars) rather than just the client area.</li>
<li>If the <code>AllowZoomClick</code> property is <code>true</code>, the control no
longer magically zooms after panning or selecting a region.
Code previously in the <code>OnMouseClick</code> override is now in
<code>OnMouseUp</code>.</li>
<li>If both <code>AutoPan</code> and a valid <code>SelectionMode</code> are set, only
selections are processed, instead of the control tying to do
both. As a result of this fix, setting the <code>SelectionMode</code>
property no longer resets <code>AutoPan</code></li>
<li>With the introduction of the <code>VirtualScrollableControl</code>, the
<code>MouseWheel</code> event is now raised as expected.</li>
</ul>
<h2 id="known-issues">Known issues</h2>
<ul>
<li>The <code>ScrollProperties</code> class hasn't been fully integrated with
the <code>ScrollControl</code>, setting properties on this class won't
update the owner control.</li>
</ul>
<h2 id="update-history">Update History</h2>
<ul>
<li>2012-08-30 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/imagebox-update-version-1-1-0-0 .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comZooming to fit a region in a ScrollableControlurn:uuid:ed92ad10-5c49-4670-abe5-28c36be518bd2012-08-30T18:56:27Z2012-08-30T18:56:27Z<p>I suspect the titles of these last pair of articles are a touch
misleading as they talk about extended zoom operations without
actually describing a zoom process (as this is already part of
<a href="/tag/imagebox">other ImageBox articles</a>. Unfortunately I can't really think
of better titles and the theory is generic enough to be applied
to any type of zooming, not just the <code>ImageBox</code>.</p>
<p>My <a href="/post/zooming-into-a-fixed-point-on-a-scrollablecontrol">previous article</a> touched on zooming in a
<code>ScrollableControl</code> while keeping the content correctly aligned
to a fixed point, usually the mouse position prior to the zoom.
This article expands on that with another new feature in the
upcoming <code>ImageBox</code> update, zooming to a given region. You've
probably seen this behaviour in other paint programs, where you
select a Zoom tool, draw a rectangle, and the document is
automatically zoomed to fit.</p>
<p>Again, this is actually quite simple to achieve and this blog
post is pretty much just a reference to me of how I did this so
I don't forget next time I want something similar! We'll start
off by quickly describing a method that I forgot to include in
the previous post - <code>CenterAt</code>. This simple method centers the
view port on the given document location. This use useful for
when you want to use <code>ScrollTo</code> without providing a relative
offset... or for centering the display on a selection rectangle.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">virtual</span> <span class="keyword">void</span> CenterAt<span class="symbol">(</span>Point imageLocation<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ScrollTo<span class="symbol">(</span>imageLocation<span class="symbol">,</span> <span class="keyword">new</span> Point<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ClientSize<span class="symbol">.</span>Width <span class="symbol">/</span> <span class="number">2</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ClientSize<span class="symbol">.</span>Height <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Very straightforward, it simply calls <code>ScrollTo</code>, using the
center of the control as the offset. Now for the actual
<code>ZoomToRegion</code> method:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">virtual</span> <span class="keyword">void</span> ZoomToRegion<span class="symbol">(</span>RectangleF rectangle<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">double</span> ratioX<span class="symbol">;</span>
 <span class="keyword">double</span> ratioY<span class="symbol">;</span>
 <span class="keyword">int</span> cx<span class="symbol">;</span>
 <span class="keyword">int</span> cy<span class="symbol">;</span>

 ratioX <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ClientSize<span class="symbol">.</span>Width <span class="symbol">/</span> rectangle<span class="symbol">.</span>Width<span class="symbol">;</span>
 ratioY <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ClientSize<span class="symbol">.</span>Height <span class="symbol">/</span> rectangle<span class="symbol">.</span>Height<span class="symbol">;</span>
 cx <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span>rectangle<span class="symbol">.</span>X <span class="symbol">+</span> <span class="symbol">(</span>rectangle<span class="symbol">.</span>Width <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 cy <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span>rectangle<span class="symbol">.</span>Y <span class="symbol">+</span> <span class="symbol">(</span>rectangle<span class="symbol">.</span>Height <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span>Math<span class="symbol">.</span>Min<span class="symbol">(</span>ratioX<span class="symbol">,</span> ratioY<span class="symbol">)</span> <span class="symbol">*</span> <span class="number">100</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>CenterAt<span class="symbol">(</span><span class="keyword">new</span> Point<span class="symbol">(</span>cx<span class="symbol">,</span> cy<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>This accepts a <code>RectangleF</code> structure (you could use a
<code>Rectangle</code>, but then if you attempt to draw selection regions
on a zoomed out document, rounding from <code>float</code> to <code>int</code> would
render your selections useless), and it then calculates a new
zoom factor and offset.</p>
<ol>
<li>First, the ration of the width and height of the region
against the width and height of the view port is calculated</li>
<li>We use the smallest ratio (to ensure if that everything you
selected appears when the zoom is applied) to calculate the
new zom level</li>
<li>After this, we define the center of the rectangle</li>
<li>With all the calculations done, we set the zoom level of the
control</li>
<li>And finally, we call our new <code>CenterAt</code> method to center the
view port on the center of the source region</li>
</ol>
<p>In the actual <code>ImageBox</code> control, a new <code>SelectionMode</code> has been
added - <code>Zoom</code>. As the name somewhat logically suggests, when
this mode is active, after the user draws a selection rectangle,
the control then zooms to match the rectangle they have drawn.
This updated mode is called from the <code>OnSelected</code> method, as
follows:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>SelectionRegion <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Width <span class="symbol">&gt;</span> ImageBox<span class="symbol">.</span>SelectionDeadZone <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Height <span class="symbol">&gt;</span> ImageBox<span class="symbol">.</span>SelectionDeadZone<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ZoomToRegion<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion <span class="symbol">=</span> RectangleF<span class="symbol">.</span>Empty<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The mysterious <code>ImageBox.SelectionDeadZone</code> field is a constant
currently set to 5, and basically ensures the user selects a
valid rectangle first - when I was testing the first iteration
of this code, having the mouse wobble as you clicked the control
was enough to generate a 1x1 rectangle, definitely not a good
user experience!</p>
<p>The only disadvantage is this functionality only lends itself to
zooming it, and not out.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2012-08-30 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/zooming-to-fit-a-region-in-a-scrollablecontrol .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comZooming into a fixed point on a ScrollableControlurn:uuid:07765221-f6e5-4a07-a261-15a3cc899e372012-08-29T17:56:14Z2012-08-29T17:44:27Z<p>If I'd built subtitle support into the CMS that powers this
website, then surely the subtitle would have been &quot;<em>or how I
fixed that annoying zoom bug in the ImageBox control</em>&quot;. And with
that digression out of the way, onto the article, a nice and
short one for a change!</p>
<blockquote>
<p>I should probably point out that this article doesn't describe
how to actually do any zooming (as that is dependant on what
it is you are actually doing a zoom upon), but rather how to
keep the viewport focused on a given point after zooming. To
learn about zooming, please see <a href="/tag/imagebox">previous
articles</a> that describe the ImageBox
control in detail.</p>
</blockquote>
<p>Users of the <code>ImageBox</code> control are probably aware of the zoom
bug, where each time you use the mouse wheel to zoom in or out,
the final image position is slightly offset, as shown by this
short animation:</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/imgbox-before.gif" class="gallery" title="Mouse wheel zoom - subtly broken" ><img src="https://images.cyotek.com/image/thumbnail/devblog/imgbox-before.gif" alt="Mouse wheel zoom - subtly broken" decoding="async" loading="lazy" /></a><figcaption>Mouse wheel zoom - subtly broken</figcaption></figure>
<p>Fixing this bug is actually quite simple and I'm actually
embarrassed at how long it took to fix and how I missed the
solution for so long. The key to resolving this issue is finding
out the document position under the mouse (by which I mean the
position in the entire scroll area, not just the visible
viewport) <strong>before</strong> applying the zoom, and then recalculating
this position with the new zoom level, offset by the mouse
position in the client control.</p>
<p>As it's probably easier just to show you the code rather than
try and describe it, it results in this small function:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">virtual</span> <span class="keyword">void</span> ScrollTo<span class="symbol">(</span>Point imageLocation<span class="symbol">,</span> Point relativeDisplayPoint<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> x<span class="symbol">;</span>
 <span class="keyword">int</span> y<span class="symbol">;</span>

 x <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span>imageLocation<span class="symbol">.</span>X <span class="symbol">*</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span> <span class="symbol">-</span> relativeDisplayPoint<span class="symbol">.</span>X<span class="symbol">;</span>
 y <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span>imageLocation<span class="symbol">.</span>Y <span class="symbol">*</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span> <span class="symbol">-</span> relativeDisplayPoint<span class="symbol">.</span>Y<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>AutoScrollPosition <span class="symbol">=</span> <span class="keyword">new</span> Point<span class="symbol">(</span>x<span class="symbol">,</span> y<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>To use it, you add code similar to the following where you
process mouse clicks, or mouse wheel, however you control
zooming with the mouse:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Point cursorPosition<span class="symbol">;</span>
Point currentPixel<span class="symbol">;</span>
<span class="keyword">int</span> currentZoom<span class="symbol">;</span>

<span class="comment">// TODO: Obtain cursor position from MouseEventArgs etc.</span>

currentPixel <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>PointToImage<span class="symbol">(</span>cursorPosition<span class="symbol">)</span><span class="symbol">;</span>
currentZoom <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Zoom<span class="symbol">;</span>

<span class="comment">// TODO: Perform zoom here</span>

<span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">!=</span> currentZoom<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>ScrollTo<span class="symbol">(</span>currentPixel<span class="symbol">,</span> cursorPosition<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>So how does this work?</p>
<ol>
<li>Get the mouse cursor position, relative to the control</li>
<li>Convert that position into the position of your virtual
document - for the <code>ImageBox</code> control we use the
<code>PointToImage</code> method</li>
<li>Perform your zoom and recalculate the document scroll size
etc.</li>
<li>Call the <code>ScrollTo</code> method, passing in the document position
and mouse cursor position</li>
</ol>
<p>And now you end up with something similar to this:</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/imgbox-after.gif" class="gallery" title="Mouse wheel zoom - works a lot smoother now" ><img src="https://images.cyotek.com/image/thumbnail/devblog/imgbox-after.gif" alt="Mouse wheel zoom - works a lot smoother now" decoding="async" loading="lazy" /></a><figcaption>Mouse wheel zoom - works a lot smoother now</figcaption></figure>
<p>There is one case where this does not work as expected - when
you scroll in or out sufficiently to remove the scrollbars, or
when moving from no-scrollbars to scrollbars. However, I think
is fine given it works so well the rest of the time!</p>
<h2 id="thats-fine-but-wheres-the-imagebox-update">That's fine, but where's the ImageBox update?</h2>
<p>Thanks to a generous donation from a visitor to the site, I
recently sat down to work on the <code>ImageBox</code> control and resolve
some of the issues - like the scrolling above. The next update
has quite a lot of new functionality (better keyboard support,
configurable zoom levels, flicker free scrolling and a handful
of bug fixes to name a few of the changes) and will be posted
presently. While the build is being finalized however, the above
code will work fine in current builds of the <code>ImageBox</code>, if you
adjust for the pixel offset the current <code>PointToImage</code>
implementation uses.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2012-08-29 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/zooming-into-a-fixed-point-on-a-scrollablecontrol .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a multi-paged container control with design time supporturn:uuid:d569d6ce-22aa-418f-9f61-d564fe58eacd2012-08-20T16:54:07Z2012-08-19T18:02:02Z<p>This article describes adding design time support for a
<code>TabControl</code>-like component which renders the same way the
Project Properties in Visual Studio 2012.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/tablist.png" class="gallery" title="The TabList demonstration application" ><img src="https://images.cyotek.com/image/thumbnail/devblog/tablist.png" alt="The TabList demonstration application" decoding="async" loading="lazy" /></a><figcaption>The TabList demonstration application</figcaption></figure>
<blockquote>
<p>This is the first time I've tried to make more advanced use of
component designers so there are going to be areas that I'm
not aware of or have not implemented correctly. The component
seems to be working fine, but it's entirely possible that bugs
exist, which could cause problems. Caveat emptor!</p>
</blockquote>
<h2 id="overview-of-the-control">Overview of the control</h2>
<p>For this article, I'm not going to delve into how the control
itself was put together as I want to focus on the design time
support, so I'm just going to provide a quick overview.</p>
<ul>
<li><code>TabList</code> - the main control</li>
<li><code>TabListPage</code> - these are hosted by the <code>TabList</code> to provided
multi-paged support</li>
<li><code>TabListControlCollection</code> - a custom <code>ControlCollection</code> that
handles <code>TabListPages</code>, and prevents adding other controls
directly onto the <code>TabList</code></li>
<li><code>TabListPageCollection</code> - a strongly typed wrapper for
<code>TabListPage</code> objects</li>
</ul>
<p>The basics of these four classes are all based on the
<code>TabControl</code>. If you know how to use that, then you know how to
use the <code>TabList</code> control, some property names have changed but
otherwise it's pretty similar.</p>
<p>For rendering support, we use these classes:</p>
<ul>
<li><code>ITabListPageRenderer</code> - interface to be implemented by
rendering classes</li>
<li><code>TabListPageRenderer</code> - base class to inherit for render
support, and also provides a default renderer property</li>
<li><code>TabListPageState</code> - flags which describe the state of a
<code>TabListPage</code></li>
<li><code>DefaultTabListPageRenderer</code> - simple renderer which draws a
header in a Visual Studio 2012-esque style.</li>
</ul>
<p>And finally, we have the two designers which this article will
concentrate on:</p>
<ul>
<li><code>TabListDesigner</code> - designer class for the <code>TabList</code> control</li>
<li><code>TabListPageDesigner</code> - designer class for the <code>TabListPage</code>
control</li>
</ul>
<h2 id="implementing-the-tablistdesigner">Implementing the TabListDesigner</h2>
<p>As the <code>TabList</code> control is a container control, we can't use
the base <code>ControlDesigner</code>. Instead, we'll use
<code>ParentControlDesigner</code> which has a bunch of extra functionality
we need.</p>
<h3 id="initializing-a-new-control">Initializing a new control</h3>
<p>Normally, I initialize a component via the constructor of the
control. This is fine when you're initializing properties to
default values, but what about adding child items? Consider for
example the <code>TabControl</code>. When add one of these to a form, it
generates two hosted pages. If you remove these, they don't come
back. If you've ever looked at the designer generated code for a
control, you'll see it will <em>add</em> items to a collection, but
doesn't <em>clear</em> the collection first so creating items via the
initialization method of a component would be problematic.</p>
<p>Fortunately for us, the designer has two methods you can
override. <code>InitializeNewComponent</code> is called when you create a
new instance of the designed type. <code>InitializeExistingComponent</code>
can be used to modify an existing component. There's also a
third override, <code>InitializeNonDefault</code> although I'm not sure
when this is called.</p>
<p>For our purposes, overriding the <code>InitializeNewComponent</code> method
is enough:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">void</span> InitializeNewComponent<span class="symbol">(</span>IDictionary defaultValues<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>InitializeNewComponent<span class="symbol">(</span>defaultValues<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// add two default pages to each new control and reset the selected index</span>
 <span class="keyword">this</span><span class="symbol">.</span>AddTabListPage<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>AddTabListPage<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>TabListControl<span class="symbol">.</span>SelectedIndex <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Now, whenever you add a <code>TabList</code> control onto a designer
surface such as a <code>Form</code>, it'll get two shiny new
<code>TabListPages</code>.</p>
<h3 id="hooking-up-events">Hooking up events</h3>
<p>For our designer, we need to know when certain actions occur so
we can act accordingly - for example, to disable the Remove verb
if there's nothing to remove. We'll set these up by overriding
the <code>Initialize</code> method.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">void</span> Initialize<span class="symbol">(</span>IComponent component<span class="symbol">)</span>
<span class="symbol">{</span>
 TabList control<span class="symbol">;</span>
 ISelectionService selectionService<span class="symbol">;</span>
 IComponentChangeService changeService<span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>Initialize<span class="symbol">(</span>component<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// attach an event so we can be notified when the selected components in the host change</span>
 selectionService <span class="symbol">=</span> <span class="symbol">(</span>ISelectionService<span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>GetService<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>ISelectionService<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>selectionService <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 selectionService<span class="symbol">.</span>SelectionChanged <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>OnSelectionChanged<span class="symbol">;</span>

 <span class="comment">// attach an event to notify us of when a component has been modified</span>
 changeService <span class="symbol">=</span> <span class="symbol">(</span>IComponentChangeService<span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>GetService<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>IComponentChangeService<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>changeService <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 changeService<span class="symbol">.</span>ComponentChanged <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>OnComponentChanged<span class="symbol">;</span>

 <span class="comment">// attach an event so we can tell when the SelectedIndex of the TabList control changes</span>
 control <span class="symbol">=</span> component <span class="keyword">as</span> TabList<span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>control <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 control<span class="symbol">.</span>SelectedIndexChanged <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>OnSelectedIndexChanged<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h4 id="onselectionchanged">OnSelectionChanged</h4>
<p>The first event we attached as
<code>ISelectionService.SelectionChanged</code>. This event is raised when
the selected components change. We'll use this event to
automatically activate a given <code>TabListPage</code> if a control hosted
upon it is selected.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> OnSelectionChanged<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 ISelectionService service<span class="symbol">;</span>

 service <span class="symbol">=</span> <span class="symbol">(</span>ISelectionService<span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>GetService<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>ISelectionService<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>service <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 TabList control<span class="symbol">;</span>

 control <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>TabListControl<span class="symbol">;</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span><span class="keyword">object</span> component <span class="keyword">in</span> service<span class="symbol">.</span>GetSelectedComponents<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 TabListPage ownedPage<span class="symbol">;</span>

 <span class="comment">// check to see if one of the selected controls is hosted on a TabListPage. If it is</span>
 <span class="comment">// activate the page. This means, if for example, you select a control via the</span>
 <span class="comment">// IDE&#39;s properties window, the relavent TabListPage will be activated</span>

 ownedPage <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetComponentOwner<span class="symbol">(</span>component<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>ownedPage <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> ownedPage<span class="symbol">.</span>Parent <span class="symbol">==</span> control<span class="symbol">)</span>
 <span class="symbol">{</span>
 control<span class="symbol">.</span>SelectedPage <span class="symbol">=</span> ownedPage<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h4 id="oncomponentchanged">OnComponentChanged</h4>
<p>The second event <code>IComponentChangeService.ComponentChanged</code> is
raised when the <code>RaiseComponentChanged</code> method is called. We'll
describe how this method works a bit further on, but for now, we
use the event to determine if there are any tab pages in the
control - if there are, the remove command is enabled, otherwise
it's disabled. (We'll also describe the verbs further down too!)</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> OnComponentChanged<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> ComponentChangedEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// disable the Remove command if we dont&#39; have anything we can actually remove</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_removeVerb <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 _removeVerb<span class="symbol">.</span>Enabled <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>TabListControl<span class="symbol">.</span>TabListPageCount <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h4 id="onselectedindexchanged">OnSelectedIndexChanged</h4>
<p>The final event, <code>TabList.SelectedIndexChanged</code> is on the
<code>TabList</code> control itself. We use this event to select the
<code>TabList</code> component for designing due to how component selection
seems to work when you mix runtime and design time
functionality.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> OnSelectedIndexChanged<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 ISelectionService service<span class="symbol">;</span>

 service <span class="symbol">=</span> <span class="symbol">(</span>ISelectionService<span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>GetService<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>ISelectionService<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>service <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// set the TabList control as the selected object. We need to do this as if the control is selected as a result</span>
 <span class="comment">// of GetHitTest returning true, normal designer actions don&#39;t seem to take place</span>
 <span class="comment">// Alternatively, we could select the selected TabListPage instead but might as well stick with the standard behaviour</span>
 service<span class="symbol">.</span>SetSelectedComponents<span class="symbol">(</span><span class="keyword">new</span> <span class="keyword">object</span><span class="symbol">[</span><span class="symbol">]</span> <span class="symbol">{</span> <span class="keyword">this</span><span class="symbol">.</span>Control <span class="symbol">}</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="verbs">Verbs</h3>
<p>I mentioned verbs above, but just what are they? Well, they are
commands you attach to the context and tasks menu of controls.
To do this, override the <code>Verbs</code> property of your designer and
create a verbs collection.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> DesignerVerbCollection Verbs
<span class="symbol">{</span>
 <span class="keyword">get</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_verbs <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _verbs <span class="symbol">=</span> <span class="keyword">new</span> DesignerVerbCollection<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 _addVerb <span class="symbol">=</span> <span class="keyword">new</span> DesignerVerb<span class="symbol">(</span><span class="string">&quot;Add TabListPage&quot;</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>AddVerbHandler<span class="symbol">)</span> <span class="symbol">{</span> Description <span class="symbol">=</span> <span class="string">&quot;Add a new TabListPage to the parent control.&quot;</span> <span class="symbol">}</span><span class="symbol">;</span>
 _removeVerb <span class="symbol">=</span> <span class="keyword">new</span> DesignerVerb<span class="symbol">(</span><span class="string">&quot;Remove TabListPage&quot;</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>RemoveVerbHandler<span class="symbol">)</span> <span class="symbol">{</span> Description <span class="symbol">=</span> <span class="string">&quot;Remove the currently selected TabListPage from the parent control.&quot;</span> <span class="symbol">}</span><span class="symbol">;</span>

 _verbs<span class="symbol">.</span>Add<span class="symbol">(</span>_addVerb<span class="symbol">)</span><span class="symbol">;</span>
 _verbs<span class="symbol">.</span>Add<span class="symbol">(</span>_removeVerb<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> _verbs<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Each verb binds to an event handler. For our purposes the events
are simple and just pass through into other methods.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> AddVerbHandler<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>AddTabListPage<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> RemoveVerbHandler<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>RemoveSelectedTabListPage<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>I suppose you could just use an anonymous delegate instead.</p>
<h3 id="modifying-a-component-with-undo-support">Modifying a component with undo support</h3>
<p>If you are making multiple changes a control, and one of these
goes wrong, the IDE won't automatically undo the changes for you
and you will need to handle this yourself. Fortunately, the IDE
does provide the facility via designer transactions. In
additional to providing a single undo for a number of
operations, using transactions can also be good for performance
as UI updates are delayed until the transaction is complete.</p>
<p>The code below is called by the <strong>Add</strong> verb and adds a new
<code>TabListPage</code> to the control.</p>
<p>These are the basic steps for making changes:</p>
<ul>
<li>Create a transaction via <code>IDesignerHost.CreateTransaction</code></li>
<li>Notify the designer of impending changes via the
<code>RaiseComponentChanging</code> method</li>
<li>Make the change</li>
<li>Notify the designer that the change has been made via the
<code>RaiseComponentChanged</code> method. This will raise the
<code>IComponentChangeService.ComponentChanged</code> event mentioned
above.</li>
<li>Either <code>Commit</code> or <code>Cancel</code> the transaction</li>
</ul>
<p>In this case, despite wrapping the transaction in a <code>using</code>
statement, I've got got an explicit <code>try</code> <code>catch</code> block to
cancel the transaction in the event of an error. I'm not sure if
this is strictly necessary however.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> AddTabListPage<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 TabList control<span class="symbol">;</span>
 IDesignerHost host<span class="symbol">;</span>

 control <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>TabListControl<span class="symbol">;</span>
 host <span class="symbol">=</span> <span class="symbol">(</span>IDesignerHost<span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>GetService<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>IDesignerHost<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>host <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>DesignerTransaction transaction <span class="symbol">=</span> host<span class="symbol">.</span>CreateTransaction<span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>Format<span class="symbol">(</span><span class="string">&quot;Add TabListPage to &#39;{0}&#39;&quot;</span><span class="symbol">,</span> control<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">try</span>
 <span class="symbol">{</span>
 TabListPage page<span class="symbol">;</span>
 MemberDescriptor controlsProperty<span class="symbol">;</span>

 page <span class="symbol">=</span> <span class="symbol">(</span>TabListPage<span class="symbol">)</span>host<span class="symbol">.</span>CreateComponent<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>TabListPage<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 controlsProperty <span class="symbol">=</span> TypeDescriptor<span class="symbol">.</span>GetProperties<span class="symbol">(</span>control<span class="symbol">)</span><span class="symbol">[</span><span class="string">&quot;Controls&quot;</span><span class="symbol">]</span><span class="symbol">;</span>

 <span class="comment">// tell the designer we&#39;re about to start making changes</span>
 <span class="keyword">this</span><span class="symbol">.</span>RaiseComponentChanging<span class="symbol">(</span>controlsProperty<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// set the text to match the name</span>
 page<span class="symbol">.</span>Text <span class="symbol">=</span> page<span class="symbol">.</span>Name<span class="symbol">;</span>

 <span class="comment">// add the new control to the parent, and set it to be the active page</span>
 control<span class="symbol">.</span>Controls<span class="symbol">.</span>Add<span class="symbol">(</span>page<span class="symbol">)</span><span class="symbol">;</span>
 control<span class="symbol">.</span>SelectedIndex <span class="symbol">=</span> control<span class="symbol">.</span>TabListPageCount <span class="symbol">-</span> <span class="number">1</span><span class="symbol">;</span>

 <span class="comment">// inform the designer we&#39;re finished making changes</span>
 <span class="keyword">this</span><span class="symbol">.</span>RaiseComponentChanged<span class="symbol">(</span>controlsProperty<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// commit the transaction</span>
 transaction<span class="symbol">.</span>Commit<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span>
 <span class="symbol">{</span>
 transaction<span class="symbol">.</span>Cancel<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">throw</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The handler for the remove verb does pretty much the same thing,
except we use <code>IDesignerHost.DestroyComponent</code> to remove the
selected <code>TabListPage</code> control.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> RemoveSelectedTabListPage<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 TabList control<span class="symbol">;</span>

 control <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>TabListControl<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>control <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> control<span class="symbol">.</span>TabListPageCount <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 IDesignerHost host<span class="symbol">;</span>

 host <span class="symbol">=</span> <span class="symbol">(</span>IDesignerHost<span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>GetService<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>IDesignerHost<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>host <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>DesignerTransaction transaction <span class="symbol">=</span> host<span class="symbol">.</span>CreateTransaction<span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>Format<span class="symbol">(</span><span class="string">&quot;Remove TabListPage from &#39;{0}&#39;&quot;</span><span class="symbol">,</span> control<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">try</span>
 <span class="symbol">{</span>
 MemberDescriptor controlsProperty<span class="symbol">;</span>

 controlsProperty <span class="symbol">=</span> TypeDescriptor<span class="symbol">.</span>GetProperties<span class="symbol">(</span>control<span class="symbol">)</span><span class="symbol">[</span><span class="string">&quot;Controls&quot;</span><span class="symbol">]</span><span class="symbol">;</span>

 <span class="comment">// inform the designer we&#39;re about to make changes</span>
 <span class="keyword">this</span><span class="symbol">.</span>RaiseComponentChanging<span class="symbol">(</span>controlsProperty<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// remove the tab page</span>
 host<span class="symbol">.</span>DestroyComponent<span class="symbol">(</span>control<span class="symbol">.</span>SelectedPage<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// tell the designer we&#39;re finished making changes</span>
 <span class="keyword">this</span><span class="symbol">.</span>RaiseComponentChanged<span class="symbol">(</span>controlsProperty<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// commit the transaction</span>
 transaction<span class="symbol">.</span>Commit<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span>
 <span class="symbol">{</span>
 transaction<span class="symbol">.</span>Cancel<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">throw</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="adding-controls-to-the-selected-tablistpage">Adding controls to the selected TabListPage</h3>
<p>If the <code>TabList</code> control is selected and you try to drag a
control on it, you'll get an error stating that only
<code>TabListPage</code> controls can be hosted. By overriding the
<code>CreateToolCore</code> method, we can intercept the control creation,
and forward it onto the current <code>TabListPage</code> via the
<code>InvokeCreateTool</code> method.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> IComponent<span class="symbol">[</span><span class="symbol">]</span> CreateToolCore<span class="symbol">(</span>ToolboxItem tool<span class="symbol">,</span> <span class="keyword">int</span> x<span class="symbol">,</span> <span class="keyword">int</span> y<span class="symbol">,</span> <span class="keyword">int</span> width<span class="symbol">,</span> <span class="keyword">int</span> height<span class="symbol">,</span> <span class="keyword">bool</span> hasLocation<span class="symbol">,</span> <span class="keyword">bool</span> hasSize<span class="symbol">)</span>
<span class="symbol">{</span>
 TabList control<span class="symbol">;</span>
 IDesignerHost host<span class="symbol">;</span>

 control <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>TabListControl<span class="symbol">;</span>

 <span class="comment">// prevent controls from being created directly on the TabList</span>
 <span class="keyword">if</span> <span class="symbol">(</span>control<span class="symbol">.</span>SelectedPage <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentException<span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>Format<span class="symbol">(</span><span class="string">&quot;Cannot add control &#39;{0}&#39;, no page is selected.&quot;</span><span class="symbol">,</span> tool<span class="symbol">.</span>DisplayName<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 host <span class="symbol">=</span> <span class="symbol">(</span>IDesignerHost<span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>GetService<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>IDesignerHost<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>host <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 ParentControlDesigner childDesigner<span class="symbol">;</span>

 childDesigner <span class="symbol">=</span> <span class="symbol">(</span>ParentControlDesigner<span class="symbol">)</span>host<span class="symbol">.</span>GetDesigner<span class="symbol">(</span>control<span class="symbol">.</span>SelectedPage<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// add controls onto the TabListPage control instead of the TabList</span>
 ParentControlDesigner<span class="symbol">.</span>InvokeCreateTool<span class="symbol">(</span>childDesigner<span class="symbol">,</span> tool<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> <span class="keyword">null</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Returning <code>null</code> via <code>CreateToolCore</code> prevents the control from
being created on the <code>TabList</code>. The reminder of the logic
forwards the call onto the selected <code>TabListPage</code>, if one is
available.</p>
<h3 id="allowing-tablistpage-selection-at-design-time">Allowing TabListPage selection at design-time</h3>
<p>As you'll have noticed, most controls can't be used at design
time - when you click a control it just selects it. This default
behaviour is a serious problem for our component as if you can't
active other pages, how can you add controls to them?
Fortunately, this is extremely easy to implement as the designer
provides a <code>GetHitTest</code> method which you can override. If you
return <code>true</code> from this method, then mouse clicks will be
processed by the underlying control instead of the designer.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">bool</span> GetHitTest<span class="symbol">(</span>Point point<span class="symbol">)</span>
<span class="symbol">{</span>
 TabList control<span class="symbol">;</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>
 Point location<span class="symbol">;</span>

 <span class="comment">// return true if the mouse is located over a TabListPage header</span>
 <span class="comment">// this allows you to switch pages at design time with the mouse</span>
 <span class="comment">// rather than just selecting the control as it would otherwise</span>

 control <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>TabListControl<span class="symbol">;</span>
 location <span class="symbol">=</span> control<span class="symbol">.</span>PointToClient<span class="symbol">(</span>point<span class="symbol">)</span><span class="symbol">;</span>
 result <span class="symbol">=</span> control<span class="symbol">.</span>HitTest<span class="symbol">(</span>location<span class="symbol">)</span> <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">;</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>In the above code, we translate the provided mouse co-ordinates
into the client co-ordinates, then test to see if they are on
the header of a <code>TabListPage</code>. If they are, we return <code>true</code> and
the call will then be forward onto the <code>TabList</code> control which
will then selected that page.</p>
<p>There is one side effect of this behaviour. As we have
essentially intercepted the mouse call, that means the <code>TabList</code>
control isn't selected. This behaviour is inconsistent with
standard behaviour and this is why when the designer was
initialized we hooked into the <code>SelectedIndexChanged</code> event of
the <code>TabList</code> control. With this hooked, as soon as the
<code>SelectedIndex</code> property is changed we can manually select the
<code>TabList</code> control. Of course, if you'd rather, you could change
that code to select the active <code>TabListPage</code> instead, but again
that's inconsistent with standard behaviour.</p>
<p>Unfortunately there's also another side effect I discovered -
the context menu no longer works if you right click on an area
where you allow mouse clicks to pass through. Again, this is
fairly straightforward to work around by overriding <code>WndProc</code>
and intercepting the <code>WM_CONTEXTMENU</code> message.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> WndProc<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">switch</span> <span class="symbol">(</span>m<span class="symbol">.</span>Msg<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> <span class="number">0x7b</span><span class="symbol">:</span> <span class="comment">// WM_CONTEXTMENU</span>
 Point position<span class="symbol">;</span>

 <span class="comment">// For some reason the context menu is no longer displayed when right clicking the control</span>
 <span class="comment">// By hooking into the WM_CONTEXTMENU context message we can display the menu ourselves</span>

 position <span class="symbol">=</span> Cursor<span class="symbol">.</span>Position<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>OnContextMenu<span class="symbol">(</span>position<span class="symbol">.</span>X<span class="symbol">,</span> position<span class="symbol">.</span>Y<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">default</span><span class="symbol">:</span>
 <span class="keyword">base</span><span class="symbol">.</span>WndProc<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>Note: Normally I wouldn't use &quot;magic numbers&quot; as I have here.
But at the same time, I don't want to define WM_CONTEXTMENU in
this class - for my internal projects, I link to an assembly
I've created which contains all the Win32 API functionality
that I use. Linking that to this not possible for this example
and I don't want to create a <code>Native</code> class for a just a
single member. So this time I'll cheat and leave an inline
magic number.</p>
</blockquote>
<p>The final side effect I've found is double clicking to open the
default event handler doesn't work either.</p>
<h3 id="design-time-control-paining">Design time control paining</h3>
<p>The final section of the <code>TabListDesigner</code> class I want to
discuss is design time painting. Normally, in the <code>OnPaint</code>
overriding of my control, I would have a block similar to the
below.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnPaint<span class="symbol">(</span>PaintEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnPaint<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>DesignMode<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// Design time painting here</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>While there's nothing wrong with this approach, if you are using
a designer than you have another option, which saves you having
to do design time checks each time your contain is painted at
runtime. The designer has an <code>OnPaintAdornments</code> method, just
override this to perform your design time drawing.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnPaintAdornments<span class="symbol">(</span>PaintEventArgs pe<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnPaintAdornments<span class="symbol">(</span>pe<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// outline the control at design time as we don&#39;t have any borders</span>
 ControlPaint<span class="symbol">.</span>DrawFocusRectangle<span class="symbol">(</span>pe<span class="symbol">.</span>Graphics<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Control<span class="symbol">.</span>ClientRectangle<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>As the <code>TabList</code> doesn't have a border property, I draw a dotted
line around the control using <code>ControlPaint.DrawFocusRectangle</code>.</p>
<h2 id="implementing-the-tablistpage-designer">Implementing the TabListPage designer</h2>
<p>Although the <code>TabListPage</code> control is basically a <code>Panel</code>
control with a bunch of properties and events hidden, it still
needs a designer to override some functionality. For the
<code>TabListPageDesigner</code> class, we'll inherit from
<code>ScrollableControlDesigner</code>.</p>
<h3 id="removing-sizing-and-moving-handles">Removing sizing and moving handles</h3>
<p>As the <code>TabList</code> control takes care of sizing its child
<code>TabListPage</code> controls, we don't really want the user to be able
to resize or move them at design time. By overriding the
<code>SelectionRules</code> property, you can define exactly which handles
are displayed. As I don't want the control to be moved or sized,
I get rid of everything via the <code>Locked</code> flag.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> SelectionRules SelectionRules
<span class="symbol">{</span> <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> SelectionRules<span class="symbol">.</span>Locked<span class="symbol">;</span> <span class="symbol">}</span> <span class="symbol">}</span>
</pre>
</figure>
<h3 id="preventing-the-component-from-being-re-parented">Preventing the component from being re-parented</h3>
<p>The <code>CanBeParentedTo</code> method is used to determine if a component
can be hosted by another control. I'm overriding this to make
sure that they can only be parented on another <code>TabList</code>
control. Although, as I've disabled the dragging of
<code>TabListPage</code> controls with selection rules above, you can't
drag them to reparent anyway.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">bool</span> CanBeParentedTo<span class="symbol">(</span>IDesigner parentDesigner<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> parentDesigner <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> parentDesigner<span class="symbol">.</span>Component <span class="keyword">is</span> TabList<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="known-issues">Known Issues</h2>
<ul>
<li>As described above, if you double click one of the
<code>TabListPage</code> headers nothing happens. Normally, you'd expect
a code window to be opened at the default event handler for
the control. While it should be possible to trap the
<code>WM_LBUTTONDBLCLK</code> message, I don't know how to open a code
window, or create a default event handler is one is missing.</li>
<li>Another issue I spotted is that I can't Cut (or Copy) a
<code>TagListPage</code> from one <code>TabList</code> control to another. Not sure
why yet, but I'll update the source on GitHub when I fix it.</li>
</ul>
<h2 id="the-source">The source</h2>
<p>Get the source code from the link below. I've also uploaded it
to <a href="https://github.com/cyotek/Cyotek.Windows.Forms.TabList" rel="external nofollow noopener">GitHub</a>, feel free to fork and make pull requests to make
this component even better!</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2012-08-19 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-a-multi-paged-container-control-with-design-time-support .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comDisplaying the contents of a PDF file in an ASP.NET application using GhostScripturn:uuid:808da458-9ecb-459e-8797-0c9f80a1be6e2012-07-10T19:06:46Z2012-07-10T19:06:46Z<p>After receiving quite a few requests on making the <a href="/post/convert-a-pdf-into-a-series-of-images-using-csharp-and-ghostscript">PDF image
conversion</a> work in a web application, I wanted to see how
hard it would be to do. Not hard at all as it turns out, I had a
nice working sample running with a bare 5 minutes of work.</p>
<p>The sample available for download below is a basic ASP.NET
application, comprised of a single page with an <code>IHttpHandler</code>
for displaying the image. In order to make this sample as easy
as possible, it uses pure server side controls and code, nothing
client side.</p>
<h2 id="getting-started">Getting Started</h2>
<p>In order to run this sample, you'll need the
<a href="https://www.cyotek.com/downloads/view/Cyotek.GhostScript.zip/"><code>Cyotek.GhostScript</code></a> and
<a href="https://www.cyotek.com/downloads/view/Cyotek.GhostScript.PdfConversion.zip/"><code>Cyotek.GhostScript.PdfConversion.zip</code></a> components described
in a <a href="/post/convert-a-pdf-into-a-series-of-images-using-csharp-and-ghostscript">previous article</a>.</p>
<p>You'll also need to download <a href="http://www.ghostscript.com/" rel="external nofollow noopener">GhostScript</a>. As with my other
articles on the subject, please make sure you check their
license terms - they seem very keen that people don't use the
GPL version or distribute GhostScript without a commercial
license.</p>
<h2 id="locating-gsdll32.dll">Locating gsdll32.dll</h2>
<p>In order for this to work, <code>gsdll32.dll</code> needs to be somewhere
in your applications path. This could be in your <code>system32</code>
directory on 32bit Windows, or <code>SysWOW64</code> on 64bit Windows.</p>
<p>While developing this sample, I also tried having the file in
the bin directory of the website - this also worked fine.
However, as the website was running on my local machine, it's
probably running in Full Trust, and I have no idea if it will
work in Medium Trust or lower.</p>
<h3 id="im-running-64bit-windows">I'm running 64bit Windows</h3>
<p>Congratulations! I have nothing but issues with 32bit web
servers. But I digress. The sample projects I have provided on
this website all use the 32bit version of GhostScript. There is
a 64bit version available, but I haven't downloaded it to test.
Your options should be as follows:</p>
<ul>
<li>Build against the 64bit GhostScript DLL. This may need some
refactoring if their public API has changed. At the very
least, you'll need to change the DLL filename in the native
method calls.</li>
<li>Using IIS7 or higher? Keep using the 32bit version, and set
your worker pool to run in 32bit mode</li>
<li>Using IIS6? Commiserations, I feel your pain. The only option
here, if you stay 32bit, is to have the entire IIS run as
32bit.</li>
</ul>
<p>I have tested on a Windows 7 Professional 64bit machine as
follows:</p>
<ul>
<li>Firstly, using IISExpress which is running as a 32bit process</li>
<li>Secondly, using IIS7 with a custom application pool running in
32bit mode</li>
</ul>
<p>Both of these scenarios worked perfectly well.</p>
<h2 id="creating-the-solution">Creating the solution</h2>
<p>Create a new <strong>ASP.NET Web Forms Site</strong></p>
<blockquote>
<p>Note: Even though this example uses pure WebForms, there's no
reason that this sort of code won't work fine in ASP.NET MVC
or any other .NET framework of your choice.</p>
</blockquote>
<p>Open up <code>Default.aspx</code> and add some controls similar to the
following:</p>
<figure class="lang-html highlight"><figcaption><span>html</span></figcaption><pre class="code">
<span class="htmlServerSideScript">&lt;%</span><span class="literal">@</span> <span class="name">Page</span> <span class="name">Language</span><span class="symbol">=</span><span class="attribute">&quot;C#&quot;</span> <span class="name">AutoEventWireup</span><span class="symbol">=</span><span class="attribute">&quot;true&quot;</span> <span class="name">CodeBehind</span><span class="symbol">=</span><span class="attribute">&quot;Default.aspx.cs&quot;</span> <span class="name">Inherits</span><span class="symbol">=</span><span class="attribute">&quot;GhostScriptWebTest._Default&quot;</span> <span class="htmlServerSideScript">%&gt;</span>

<span class="literal">&lt;!</span><span class="name">DOCTYPE</span> <span class="name">html</span> <span class="name">PUBLIC</span> <span class="attribute">&quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;</span> <span class="attribute">&quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;</span><span class="literal">&gt;</span>

<span class="literal">&lt;</span><span class="name">html</span> <span class="name">xmlns</span><span class="symbol">=</span><span class="attribute">&quot;http://www.w3.org/1999/xhtml&quot;</span><span class="literal">&gt;</span>
<span class="literal">&lt;</span><span class="name">head</span> <span class="name">runat</span><span class="symbol">=</span><span class="attribute">&quot;server&quot;</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">title</span><span class="literal">&gt;</span>PDF Conversion Example<span class="literal">&lt;/</span><span class="name">title</span><span class="literal">&gt;</span>
<span class="literal">&lt;/</span><span class="name">head</span><span class="literal">&gt;</span>
<span class="literal">&lt;</span><span class="name">body</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">form</span> <span class="name">id</span><span class="symbol">=</span><span class="attribute">&quot;form1&quot;</span> <span class="name">runat</span><span class="symbol">=</span><span class="attribute">&quot;server&quot;</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">div</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">p</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">asp</span><span class="literal">:</span><span class="name">LinkButton</span> <span class="name">runat</span><span class="symbol">=</span><span class="attribute">&quot;server&quot;</span> <span class="name">ID</span><span class="symbol">=</span><span class="attribute">&quot;previousLinkButton&quot;</span> <span class="name">Text</span><span class="symbol">=</span><span class="attribute">&quot;Previous&quot;</span> <span class="name">OnClick</span><span class="symbol">=</span><span class="attribute">&quot;previousLinkButton_Click&quot;</span> <span class="literal">/&gt;</span>
 <span class="literal">&lt;</span><span class="name">asp</span><span class="literal">:</span><span class="name">LinkButton</span> <span class="name">runat</span><span class="symbol">=</span><span class="attribute">&quot;server&quot;</span> <span class="name">ID</span><span class="symbol">=</span><span class="attribute">&quot;nextLinkButton&quot;</span> <span class="name">Text</span><span class="symbol">=</span><span class="attribute">&quot;Next&quot;</span> <span class="name">OnClick</span><span class="symbol">=</span><span class="attribute">&quot;nextLinkButton_Click&quot;</span> <span class="literal">/&gt;</span>
 <span class="literal">&lt;/</span><span class="name">p</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">p</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">asp</span><span class="literal">:</span><span class="name">Image</span> <span class="name">runat</span><span class="symbol">=</span><span class="attribute">&quot;server&quot;</span> <span class="name">ID</span><span class="symbol">=</span><span class="attribute">&quot;pdfImage&quot;</span> <span class="name">ImageUrl</span><span class="symbol">=</span><span class="attribute">&quot;~/PdfImage.ashx?fileName=sample.pdf&amp;page=1&quot;</span> <span class="literal">/&gt;</span>
 <span class="literal">&lt;/</span><span class="name">p</span><span class="literal">&gt;</span>
 <span class="literal">&lt;/</span><span class="name">div</span><span class="literal">&gt;</span>
 <span class="literal">&lt;/</span><span class="name">form</span><span class="literal">&gt;</span>
<span class="literal">&lt;/</span><span class="name">body</span><span class="literal">&gt;</span>
<span class="literal">&lt;/</span><span class="name">html</span><span class="literal">&gt;</span>
</pre>
</figure>
<p>The controls should be fairly self explanatory! The main thing
of interest is the <code>pdfImage</code> Image control - this will call a
<em>Generic Handler</em> that I'll describe in the next section. Note
that VS2010 and VS2012 have another option, an <strong>ASP.NET
Handler</strong> - this implements the same <code>IHttpHandler</code> interface
but doesn't have a <code>.ashx</code> file and is registered differently.
If you are using IIS7 or above, you're probably better off using
that.</p>
<p>Note that by default the <code>pdfImage</code> control is pointing to a
sample file named <em>sample.pdf</em> - add any old PDF to the root of
your website and name it sample. Ensure that the <strong>Build
Action</strong> for the PDF is set to <strong>Content</strong>, otherwise it won't
be deployed with your application.</p>
<h3 id="creating-the-image-handler">Creating the image handler</h3>
<p>Tutorials on creating image handlers with <code>IHttpHandler</code> can be
found scattered throughout the net, so I'll not go into how they
work, but just describe the implementation I'm using in this
example. Add a new generic handler to your project, then fill in
the <code>ProcessRequest</code> method as follows. Make sure you add the
two GhostScript API components to your solution and add
references to them to your web application first!</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> System<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Drawing<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Drawing<span class="symbol">.</span>Imaging<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Web<span class="symbol">;</span>
<span class="keyword">using</span> Cyotek<span class="symbol">.</span>GhostScript<span class="symbol">.</span>PdfConversion<span class="symbol">;</span>

<span class="keyword">namespace</span> GhostScriptWebTest
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">class</span> PdfImage <span class="symbol">:</span> IHttpHandler
 <span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">void</span> ProcessRequest<span class="symbol">(</span>HttpContext context<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> fileName<span class="symbol">;</span>
 <span class="keyword">int</span> pageNumber<span class="symbol">;</span>
 Pdf<span class="number">2</span>Image convertor<span class="symbol">;</span>
 Bitmap image<span class="symbol">;</span>

 fileName <span class="symbol">=</span> context<span class="symbol">.</span>Server<span class="symbol">.</span>MapPath<span class="symbol">(</span><span class="string">&quot;~/&quot;</span> <span class="symbol">+</span> context<span class="symbol">.</span>Request<span class="symbol">.</span>QueryString<span class="symbol">[</span><span class="string">&quot;fileName&quot;</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span>
 pageNumber <span class="symbol">=</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span>context<span class="symbol">.</span>Request<span class="symbol">.</span>QueryString<span class="symbol">[</span><span class="string">&quot;page&quot;</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// convert the image</span>
 convertor <span class="symbol">=</span> <span class="keyword">new</span> Pdf<span class="number">2</span>Image<span class="symbol">(</span>fileName<span class="symbol">)</span><span class="symbol">;</span>
 image <span class="symbol">=</span> convertor<span class="symbol">.</span>GetImage<span class="symbol">(</span>pageNumber<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// set the content type</span>
 context<span class="symbol">.</span>Response<span class="symbol">.</span>ContentType <span class="symbol">=</span> <span class="string">&quot;image/png&quot;</span><span class="symbol">;</span>

 <span class="comment">// save the image directly to the response stream</span>
 image<span class="symbol">.</span>Save<span class="symbol">(</span>context<span class="symbol">.</span>Response<span class="symbol">.</span>OutputStream<span class="symbol">,</span> ImageFormat<span class="symbol">.</span>Png<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">bool</span> IsReusable
 <span class="symbol">{</span> <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">true</span><span class="symbol">;</span> <span class="symbol">}</span> <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Again, this is extremely simple code. I extract the query string
of the request to obtain the file name of the PDF document to
convert, and the page to display. I then create an instance of
the <code>Pdf2Image</code> class, and grab an image of the specified page.</p>
<p>Next, you need to set the <code>ContentType</code> of the <code>Response</code> object
so the web browser knows what to do with your content. Finally,
I save the image directly to the response's <code>OutputStream</code>. Make
sure that the format you save the image as matches the content
type you've specified.</p>
<p>With these steps complete, building and running the website
should present you with a pair of hyper links, and the first
page of your PDF file as a static image. [Well, it will if you
add a pair of blank event handlers for those defined for the two
hyperlink buttons anyway]</p>
<h2 id="simple-navigation">Simple navigation</h2>
<p>Now that we can display our PDF, we'll add some basic
navigation. Open up the code behind file for <code>Default.aspx</code> and
fill in the event handlers for the two hyperlink buttons.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> System<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Collections<span class="symbol">.</span>Specialized<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Web<span class="symbol">;</span>
<span class="keyword">using</span> Cyotek<span class="symbol">.</span>GhostScript<span class="symbol">.</span>PdfConversion<span class="symbol">;</span>

<span class="keyword">namespace</span> GhostScriptWebTest
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> _Default <span class="symbol">:</span> System<span class="symbol">.</span>Web<span class="symbol">.</span>UI<span class="symbol">.</span>Page
 <span class="symbol">{</span>
 <span class="keyword">protected</span> <span class="keyword">void</span> previousLinkButton_Click<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>IncrementPage<span class="symbol">(</span><span class="symbol">-</span><span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">void</span> nextLinkButton_Click<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>IncrementPage<span class="symbol">(</span><span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">void</span> IncrementPage<span class="symbol">(</span><span class="keyword">int</span> increment<span class="symbol">)</span>
 <span class="symbol">{</span>
 NameValueCollection queryString<span class="symbol">;</span>
 <span class="keyword">int</span> pageNumber<span class="symbol">;</span>
 <span class="keyword">string</span> pdfFileName<span class="symbol">;</span>
 Pdf<span class="number">2</span>Image converter<span class="symbol">;</span>

 queryString <span class="symbol">=</span> HttpUtility<span class="symbol">.</span>ParseQueryString<span class="symbol">(</span>pdfImage<span class="symbol">.</span>ImageUrl<span class="symbol">.</span>Substring<span class="symbol">(</span>pdfImage<span class="symbol">.</span>ImageUrl<span class="symbol">.</span>IndexOf<span class="symbol">(</span><span class="string">&quot;?&quot;</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 pdfFileName <span class="symbol">=</span> queryString<span class="symbol">[</span><span class="string">&quot;fileName&quot;</span><span class="symbol">]</span><span class="symbol">;</span>
 pageNumber <span class="symbol">=</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span>queryString<span class="symbol">[</span><span class="string">&quot;page&quot;</span><span class="symbol">]</span><span class="symbol">)</span> <span class="symbol">+</span> increment<span class="symbol">;</span>
 converter <span class="symbol">=</span> <span class="keyword">new</span> Pdf<span class="number">2</span>Image<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Server<span class="symbol">.</span>MapPath<span class="symbol">(</span><span class="string">&quot;~/&quot;</span> <span class="symbol">+</span> pdfFileName<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>pageNumber <span class="symbol">&gt;</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> pageNumber <span class="symbol">&lt;=</span> converter<span class="symbol">.</span>PageCount<span class="symbol">)</span>
 pdfImage<span class="symbol">.</span>ImageUrl <span class="symbol">=</span> <span class="keyword">string</span><span class="symbol">.</span>Format<span class="symbol">(</span><span class="string">&quot;~/PdfImage.ashx?fileName={0}&amp;page={1}&quot;</span><span class="symbol">,</span> pdfFileName<span class="symbol">,</span> pageNumber<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>As with the image handler, this code simply extracts the file
name of the PDF file and the current page number. It also
creates a new instance of the <code>Pdf2Image</code> class in order to
obtain the number of pages in the document. If the new page
number is in range, it updates the <code>ImageUrl</code> of the <code>pdfImage</code>
causing the image handler to pull back the next page.</p>
<h2 id="in-conclusion">In Conclusion</h2>
<p>This sample is pretty inefficient and at the very least should
be caching the images. But, it's as simple an example as I can
make. Hopefully someone will find it useful. At the present time
I'm not working with the GhostScript API library so I suspect
this will be the last article on the subject for the time being.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2012-07-10 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/displaying-the-contents-of-a-pdf-file-in-an-asp-net-application-using-ghostscript .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comArcade explosion generatorurn:uuid:c44b7b56-f7ca-4f7d-9c20-82619ff939202012-06-05T09:47:03Z2012-06-03T19:57:59Z<p>Over the past few weeks I've been messing around creating a
unique graphics for our <a href="http://binaryrealms.co.uk/jewel-rush" rel="external nofollow noopener">Jewel Rush</a> game. One of the things
I was experimenting with was explosion animations. Although
tools exist for <a href="http://www.positech.co.uk/content/explosion/explosiongenerator.html" rel="external nofollow noopener">generating explosions</a> the problem with most
of these is that they create large sprites which don't shrink
well, and the output is a bit more realistic than what I was
looking for.</p>
<p>And while I'm competent enough to do application graphics (more
or less!), gaming graphics are a completely different kettle of
fish!</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/aeg1a.png" class="gallery" title="A screenshot from Missile Command" ><img src="https://images.cyotek.com/image/thumbnail/devblog/aeg1a.png" alt="A screenshot from Missile Command" decoding="async" loading="lazy" /></a><figcaption>A screenshot from Missile Command</figcaption></figure>
<p>Above is a screenshot from Missile Command, a classic from
Atari. That's the sort of explosions I wanted to create, so I
wrote a small tool that would create these sort of graphics in a
random (but reproducible) fashion and export them to images for
use in other tools such as <a href="https://cyotek.com/cyotek-spriter">Spriter</a>. As it turned out, the
graphics it produces didn't end up quite that way (I was having
problems with the intersection stuff) but it's usable enough for
the purposes I want.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/aeg1b.png" class="gallery" title="A sample of the default output" ><img src="https://images.cyotek.com/image/thumbnail/devblog/aeg1b.png" alt="A sample of the default output" decoding="async" loading="lazy" /></a><figcaption>A sample of the default output</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/aeg1c.png" class="gallery" title="Another sample using some custom settings" ><img src="https://images.cyotek.com/image/thumbnail/devblog/aeg1c.png" alt="Another sample using some custom settings" decoding="async" loading="lazy" /></a><figcaption>Another sample using some custom settings</figcaption></figure>
<p>The application was thrown together over the weekend so it's
probably not hugely robust and may contain a small army of bugs.
But it works and is possibly an interesting starting point for
other projects. There's some interesting bits of code here and
there, although I'm not writing about the implementation of the
code.</p>
<h2 id="application-features">Application Features</h2>
<ul>
<li>Configuration settings can be saved and reloaded for tweaking
of favoured settings. Uses basic Reflection serialization as
<code>XmlSerializer</code> can't handle colors without having to create
duplicate color properties in string format.</li>
<li>Can export either a complete sprite sheet, or the individual
images</li>
<li>Copy the sprite sheet to the clipboard (although I noticed
that transparent doesn't work if you do, something to look at
later)</li>
<li>Uses the ImageBox (of course!) for displaying previews</li>
<li>The <code>TrackBar</code> control embedded in the <code>ToolStrip</code> is a custom
component inheriting from <code>ToolStripControlHost</code> which can be
reused. And once you understand the principles, it's so easy
to host other controls.</li>
</ul>
<h2 id="graphic-settings">Graphic Settings</h2>
<ul>
<li>Either specify a seed to always recreate the same explosion,
or use a random seed each time. (If you find a seed you like,
clicking the seed number in the status bar will apply it to
your configuration settings).</li>
<li>Specify the number of animation frames that will be generated,
and the size of the frames</li>
<li>Specify the maximum number of explosion booms available at
once. There's also an option to automatically remove and
recreate &quot;expired&quot; blooms.</li>
<li>Choose the colors used to render the blooms</li>
<li>Specify the percentage by which blooms grow (and shrink), and
how many growth states there are. Once a bloom has shrunk to
its minimum size, it is expired and no longer draw.</li>
<li>Anti alias options, useful if you don't want pixel graphics</li>
<li>Border size and growth</li>
<li>Set a random order, in which newly created blooms will be
inserted randomly in the list.</li>
<li>An experimental mask mode which was supposed to enable me to
create those Missile Command style XOR drawing. However, it
doesn't really work and I'll probably have another go at it at
some point.</li>
</ul>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/aeg1d.png" class="gallery" title="An example generated by the project" ><img src="https://images.cyotek.com/image/devblog/aeg1d.png" alt="An example generated by the project" decoding="async" loading="lazy" /></a><figcaption>An example generated by the project</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/aeg1e.png" class="gallery" title="Another example of generated output" ><img src="https://images.cyotek.com/image/devblog/aeg1e.png" alt="Another example of generated output" decoding="async" loading="lazy" /></a><figcaption>Another example of generated output</figcaption></figure><h2 id="room-for-improvement">Room for improvement</h2>
<p>Everything can be improved, one of the ideas I'd had for this
tool was greater control over blooms, allowing you configure
their locations etc with better precision but it wasn't
necessary for the graphic I was creating. As mentioned above,
the masking doesn't work as expected, it would have been nice if
it did. Some better rendering would be a plus too, at the moment
the &quot;explosions&quot; are simple rings of color. Some noise or other
minor particle effects to make them a little less uniform would
probably look interesting.</p>
<h2 id="source-code">Source Code</h2>
<p>Source code, and optionally pre-compiled binaries, are available
from the link below. The code has been compiled against the .NET
3.5 Client Profile. Due to some minor use of Linq and auto
generated properties a small amount of work would be needed to
compile against .NET 2.0. I'm afraid comments are somewhat
lacking as well, I wasn't planning on releasing this publicly
originally.</p>
<p>If anyone creates any interesting graphics or improves upon the
code, we'd <a href="https://cyotek.com/contact">love to hear</a> from you.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2012-06-03 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/arcade-explosion-generator .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating an image viewer in C# Part 5: Selecting part of an imageurn:uuid:e2670884-1a55-450f-9831-417fe9e335852012-11-25T09:20:27Z2012-05-30T17:09:14Z<p>Part 4 of this series (by far the most popular article on
cyotek.com) was supposed to be the end, but recently I was asked
if was possible to select part of an image for saving it to a
file. After implementing the new functionality and lacking ideas
for a new post on other matters, here we are with a new part!</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/imgbox-5a.png" class="gallery" title="The demonstration program showing the selection functionality" ><img src="https://images.cyotek.com/image/thumbnail/devblog/imgbox-5a.png" alt="The demonstration program showing the selection functionality" decoding="async" loading="lazy" /></a><figcaption>The demonstration program showing the selection functionality</figcaption></figure><h2 id="getting-started">Getting Started</h2>
<p>If you aren't already familiar with the <code>ImageBox</code> component,
you may wish to view parts <a href="/post/creating-a-scrollable-and-zoomable-image-viewer-in-csharp-part-1">1</a>, <a href="/post/creating-a-scrollable-and-zoomable-image-viewer-in-csharp-part-2">2</a>, <a href="/post/creating-a-scrollable-and-zoomable-image-viewer-in-csharp-part-3">3</a> and <a href="/post/creating-a-scrollable-and-zoomable-image-viewer-in-csharp-part-4">4</a> for
the original background and specification of the control.</p>
<p>First thing is to add some new properties, along with backing
events. These are:</p>
<ul>
<li><code>SelectionMode</code> - Determines if selection is available within
the control</li>
<li><code>SelectionColor</code> - Primary color for drawing the selection
region</li>
<li><code>SelectionRegion</code> - The currently selected region.</li>
<li><code>LimitSelectionToImage</code> - This property allows you to control
if the selection region can be drawn outside the image
boundaries.</li>
<li><code>IsSelecting</code> - This property returns if a selection operation
is in progress</li>
</ul>
<p>If the <code>SelectionMode</code> property is set, then the <code>AutoPan</code> and
<code>AllowClickZoom</code> properties will both be set to <code>false</code> to avoid
conflicting actions.</p>
<p>We also need a couple of new events not directly tried to
properties.</p>
<ul>
<li><code>Selecting</code> - Occurs when the user starts to draw a selection
region and can be used to cancel the action.</li>
<li><code>Selected</code> - Occurs when the user completes drawing a
selection region</li>
</ul>
<p>These events are called when setting the <code>IsSelecting</code> property:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">,</span> DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">virtual</span> <span class="keyword">bool</span> IsSelecting
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _isSelecting<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">protected</span> <span class="keyword">set</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_isSelecting <span class="symbol">!=</span> value<span class="symbol">)</span>
 <span class="symbol">{</span>
 CancelEventArgs args<span class="symbol">;</span>

 args <span class="symbol">=</span> <span class="keyword">new</span> CancelEventArgs<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>value<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnSelecting<span class="symbol">(</span>args<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnSelected<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>args<span class="symbol">.</span>Cancel<span class="symbol">)</span>
 _isSelecting <span class="symbol">=</span> value<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="drawing-the-selection-highlight">Drawing the selection highlight</h2>
<p>Before adding support for defining the selection region, we'll
add the code to draw it - that way we'll know the code to define
the region works! To do this, we'll modify the existing
<code>OnPaint</code> override, and insert a call to a new method named
<code>DrawSelection</code>:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnPaint<span class="symbol">(</span>PaintEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">/* Snipped existing code for brevity */</span>

 <span class="comment">// draw the selection</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>SelectionRegion <span class="symbol">!=</span> Rectangle<span class="symbol">.</span>Empty<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>DrawSelection<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnPaint<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The <code>DrawSelection</code> method itself is very straightforward. First
it fills the region with a translucent variant of the
<code>SelectionColor</code> property, then draws a solid outline around
this. A clip region is also applied to avoid overwriting the
controls borders.</p>
<p>As with most of the methods and properties in the <code>ImageBox</code>
control, it has been marked as <code>virtual</code> to allow you to
override it and provide your own drawing implementation if
required, without needing to redraw all of the control.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> DrawSelection<span class="symbol">(</span>PaintEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 RectangleF rect<span class="symbol">;</span>

 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>SetClip<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>GetInsideViewPort<span class="symbol">(</span><span class="keyword">true</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 rect <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetOffsetRectangle<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>SelectionRegion<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Brush brush <span class="symbol">=</span> <span class="keyword">new</span> SolidBrush<span class="symbol">(</span>Color<span class="symbol">.</span>FromArgb<span class="symbol">(</span><span class="number">128</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>SelectionColor<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>FillRectangle<span class="symbol">(</span>brush<span class="symbol">,</span> rect<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Pen pen <span class="symbol">=</span> <span class="keyword">new</span> Pen<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>SelectionColor<span class="symbol">)</span><span class="symbol">)</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>DrawRectangle<span class="symbol">(</span>pen<span class="symbol">,</span> rect<span class="symbol">.</span>X<span class="symbol">,</span> rect<span class="symbol">.</span>Y<span class="symbol">,</span> rect<span class="symbol">.</span>Width<span class="symbol">,</span> rect<span class="symbol">.</span>Height<span class="symbol">)</span><span class="symbol">;</span>

 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>ResetClip<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The <code>GetOffsetRectangle</code> method will be described a little
further down this article.</p>
<h2 id="defining-the-selection-region">Defining the selection region</h2>
<p>Currently the selection region can only be defined via the
mouse; there is no keyboard support. To do this, we'll do the
usual overriding of <code>MouseDown</code>, <code>MouseMove</code> and <code>MouseUp</code></p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseDown<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnMouseDown<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">/* Snipped existing code for brevity */</span>

 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Button <span class="symbol">==</span> MouseButtons<span class="symbol">.</span>Left <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>SelectionMode <span class="symbol">!=</span> ImageBoxSelectionMode<span class="symbol">.</span>None<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion <span class="symbol">=</span> Rectangle<span class="symbol">.</span>Empty<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseMove<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnMouseMove<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Button <span class="symbol">==</span> MouseButtons<span class="symbol">.</span>Left<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">/* Snipped existing code for brevity */</span>
 
 <span class="keyword">this</span><span class="symbol">.</span>ProcessSelection<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseUp<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnMouseUp<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsPanning<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>IsPanning <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsSelecting<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>IsSelecting <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p><code>OnMouseDown</code> and <code>OnMouseUp</code> aren't being used for much in this
case, the former is used to clear an existing selection region,
the later to notify that the selection is no longer being
defined. <code>OnMouseMove</code> calls the <code>ProcessSelection</code> method which
is where all the action happens.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> ProcessSelection<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>SelectionMode <span class="symbol">!=</span> ImageBoxSelectionMode<span class="symbol">.</span>None<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>IsSelecting<span class="symbol">)</span>
 <span class="symbol">{</span>
 _startMousePosition <span class="symbol">=</span> e<span class="symbol">.</span>Location<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>IsSelecting <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>First, we check to make sure a valid selection mode is set.
Then, if a selection operation hasn't been initiated, we attempt
to set the <code>IsSelecting</code> property. As noted above, this property
will call the <code>Selecting</code> event allowing the selection to be
cancelled if required by the implementing application.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsSelecting<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">float</span> x<span class="symbol">;</span>
 <span class="keyword">float</span> y<span class="symbol">;</span>
 <span class="keyword">float</span> w<span class="symbol">;</span>
 <span class="keyword">float</span> h<span class="symbol">;</span>
 Point imageOffset<span class="symbol">;</span>

 imageOffset <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetImageViewPort<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">.</span>Location<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>X <span class="symbol">&lt;</span> _startMousePosition<span class="symbol">.</span>X<span class="symbol">)</span>
 <span class="symbol">{</span>
 x <span class="symbol">=</span> e<span class="symbol">.</span>X<span class="symbol">;</span>
 w <span class="symbol">=</span> _startMousePosition<span class="symbol">.</span>X <span class="symbol">-</span> e<span class="symbol">.</span>X<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 x <span class="symbol">=</span> _startMousePosition<span class="symbol">.</span>X<span class="symbol">;</span>
 w <span class="symbol">=</span> e<span class="symbol">.</span>X <span class="symbol">-</span> _startMousePosition<span class="symbol">.</span>X<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Y <span class="symbol">&lt;</span> _startMousePosition<span class="symbol">.</span>Y<span class="symbol">)</span>
 <span class="symbol">{</span>
 y <span class="symbol">=</span> e<span class="symbol">.</span>Y<span class="symbol">;</span>
 h <span class="symbol">=</span> _startMousePosition<span class="symbol">.</span>Y <span class="symbol">-</span> e<span class="symbol">.</span>Y<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 y <span class="symbol">=</span> _startMousePosition<span class="symbol">.</span>Y<span class="symbol">;</span>
 h <span class="symbol">=</span> e<span class="symbol">.</span>Y <span class="symbol">-</span> _startMousePosition<span class="symbol">.</span>Y<span class="symbol">;</span>
 <span class="symbol">}</span>

 x <span class="symbol">=</span> x <span class="symbol">-</span> imageOffset<span class="symbol">.</span>X <span class="symbol">-</span> <span class="keyword">this</span><span class="symbol">.</span>AutoScrollPosition<span class="symbol">.</span>X<span class="symbol">;</span>
 y <span class="symbol">=</span> y <span class="symbol">-</span> imageOffset<span class="symbol">.</span>Y <span class="symbol">-</span> <span class="keyword">this</span><span class="symbol">.</span>AutoScrollPosition<span class="symbol">.</span>Y<span class="symbol">;</span>
</pre>
</figure>
<p>If selection was allowed, we construct the co-ordinates for a
rectangle, automatically switching values around to ensure that
the rectangle will always have a positive width and height.
We'll also offset the co-ordinates if the image has been
scrolled or if it has been centred (or both!).</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 x <span class="symbol">=</span> x <span class="symbol">/</span> <span class="symbol">(</span><span class="keyword">float</span><span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">;</span>
 y <span class="symbol">=</span> y <span class="symbol">/</span> <span class="symbol">(</span><span class="keyword">float</span><span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">;</span>
 w <span class="symbol">=</span> w <span class="symbol">/</span> <span class="symbol">(</span><span class="keyword">float</span><span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">;</span>
 h <span class="symbol">=</span> h <span class="symbol">/</span> <span class="symbol">(</span><span class="keyword">float</span><span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">;</span>
</pre>
</figure>
<p>As this is the <em>zoomable</em> scrolling image control, we also need
to rescale the rectangle according to the current zoom level.
This ensures the <code>SelectionRegion</code> property always returns a
rectangle that describes the selection at 100% zoom.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>LimitSelectionToImage<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>x <span class="symbol">&lt;</span> <span class="number">0</span><span class="symbol">)</span>
 x <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>y <span class="symbol">&lt;</span> <span class="number">0</span><span class="symbol">)</span>
 y <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>x <span class="symbol">+</span> w <span class="symbol">&gt;</span> <span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Width<span class="symbol">)</span>
 w <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Width <span class="symbol">-</span> x<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>y <span class="symbol">+</span> h <span class="symbol">&gt;</span> <span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Height<span class="symbol">)</span>
 h <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Height <span class="symbol">-</span> y<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion <span class="symbol">=</span> <span class="keyword">new</span> RectangleF<span class="symbol">(</span>x<span class="symbol">,</span> y<span class="symbol">,</span> w<span class="symbol">,</span> h<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The final step is to constrain the rectangle to the image size
if the <code>LimitSelectionToImage</code> property is set, before assigning
the final rectangle to the <code>SelectionRegion</code> property.</p>
<p>And that's pretty much all there is to it.</p>
<h2 id="scaling-and-offsetting">Scaling and offsetting</h2>
<p>When using the control in our own products, it's very rarely to
display a single image, but rather to display multiple items, be
it sprites in a sprite sheet or tiles in a map. These
implementations therefore often require the ability to get a
single item, for example to display hover effects. This can be
tricky with a control that scrolls, zooms and centres the image.
Rather than repeat <code>ZoomFactor</code> calculations (and worse
<code>AutoScrollPosition</code>) everywhere, we added a number of helper
methods named <code>GetOffset*</code> and <code>GetScaled*</code>. Calling these with
a &quot;normal&quot; value, will return that value repositioned and
rescaled according to the current state of the control. An
example of this is the <code>DrawSelection</code> method described above
which needs ensure the current selection region is rendered
correctly.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">virtual</span> RectangleF GetScaledRectangle<span class="symbol">(</span>RectangleF source<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">new</span> RectangleF
 <span class="symbol">(</span>
 <span class="symbol">(</span><span class="keyword">float</span><span class="symbol">)</span><span class="symbol">(</span>source<span class="symbol">.</span>Left <span class="symbol">*</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span><span class="symbol">,</span>
 <span class="symbol">(</span><span class="keyword">float</span><span class="symbol">)</span><span class="symbol">(</span>source<span class="symbol">.</span>Top <span class="symbol">*</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span><span class="symbol">,</span>
 <span class="symbol">(</span><span class="keyword">float</span><span class="symbol">)</span><span class="symbol">(</span>source<span class="symbol">.</span>Width <span class="symbol">*</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span><span class="symbol">,</span>
 <span class="symbol">(</span><span class="keyword">float</span><span class="symbol">)</span><span class="symbol">(</span>source<span class="symbol">.</span>Height <span class="symbol">*</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span>
 <span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">virtual</span> RectangleF GetOffsetRectangle<span class="symbol">(</span>RectangleF source<span class="symbol">)</span>
<span class="symbol">{</span>
 RectangleF viewport<span class="symbol">;</span>
 RectangleF scaled<span class="symbol">;</span>
 <span class="keyword">float</span> offsetX<span class="symbol">;</span>
 <span class="keyword">float</span> offsetY<span class="symbol">;</span>

 viewport <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetImageViewPort<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 scaled <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetScaledRectangle<span class="symbol">(</span>source<span class="symbol">)</span><span class="symbol">;</span>
 offsetX <span class="symbol">=</span> viewport<span class="symbol">.</span>Left <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Left <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>AutoScrollPosition<span class="symbol">.</span>X<span class="symbol">;</span>
 offsetY <span class="symbol">=</span> viewport<span class="symbol">.</span>Top <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Top <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>AutoScrollPosition<span class="symbol">.</span>Y<span class="symbol">;</span>

 <span class="keyword">return</span> <span class="keyword">new</span> RectangleF<span class="symbol">(</span><span class="keyword">new</span> PointF<span class="symbol">(</span>scaled<span class="symbol">.</span>Left <span class="symbol">+</span> offsetX<span class="symbol">,</span> scaled<span class="symbol">.</span>Top <span class="symbol">+</span> offsetY<span class="symbol">)</span><span class="symbol">,</span> scaled<span class="symbol">.</span>Size<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Versions of these methods exist for the following structures:</p>
<ul>
<li><code>Point</code></li>
<li><code>PointF</code></li>
<li><code>Size</code></li>
<li><code>SizeF</code></li>
<li><code>Rectangle</code></li>
<li><code>RectangleF</code></li>
</ul>
<p>These methods can come in extremely useful depending on how you
are using the control!</p>
<h2 id="cropping-an-image">Cropping an image</h2>
<p>The demonstration program displays two <code>ImageBox</code> controls, the
first allows you to select part of an image, and the second
displays the cropped selection. I didn't add any sort of crop
functionality to the control itself, but the following snippets
shows how the demonstration program creates the cropped version.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Rectangle rect<span class="symbol">;</span>

<span class="keyword">if</span> <span class="symbol">(</span>_previewImage <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 _previewImage<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

rect <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>imageBox<span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>X<span class="symbol">,</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>imageBox<span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Y<span class="symbol">,</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>imageBox<span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Width<span class="symbol">,</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>imageBox<span class="symbol">.</span>SelectionRegion<span class="symbol">.</span>Height<span class="symbol">)</span><span class="symbol">;</span>

_previewImage <span class="symbol">=</span> <span class="keyword">new</span> Bitmap<span class="symbol">(</span>rect<span class="symbol">.</span>Width<span class="symbol">,</span> rect<span class="symbol">.</span>Height<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">using</span> <span class="symbol">(</span>Graphics g <span class="symbol">=</span> Graphics<span class="symbol">.</span>FromImage<span class="symbol">(</span>_previewImage<span class="symbol">)</span><span class="symbol">)</span>
 g<span class="symbol">.</span>DrawImage<span class="symbol">(</span>imageBox<span class="symbol">.</span>Image<span class="symbol">,</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>Point<span class="symbol">.</span>Empty<span class="symbol">,</span> rect<span class="symbol">.</span>Size<span class="symbol">)</span><span class="symbol">,</span> rect<span class="symbol">,</span> GraphicsUnit<span class="symbol">.</span>Pixel<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

previewImageBox<span class="symbol">.</span>Image <span class="symbol">=</span> _previewImage<span class="symbol">;</span>
</pre>
</figure>
<h2 id="finishing-touches">Finishing touches</h2>
<p>We'll finish off by adding a couple of helper methods that
implementers can call:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">virtual</span> <span class="keyword">void</span> SelectAll<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Image <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> InvalidOperationException<span class="symbol">(</span><span class="string">&quot;No image set&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion <span class="symbol">=</span> <span class="keyword">new</span> RectangleF<span class="symbol">(</span>PointF<span class="symbol">.</span>Empty<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Size<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">virtual</span> <span class="keyword">void</span> SelectNone<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>SelectionRegion <span class="symbol">=</span> RectangleF<span class="symbol">.</span>Empty<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="known-issues">Known issues</h2>
<p>Currently, if you try and draw the selection bigger than the
visible area of the control, it will work, but it will not
scroll the control for you. I also was going to add the ability
to move or modify the selection but ran out of time for this
particular post.</p>
<p>As always, if you have any comments or questions, please contact
us!</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2012-05-30 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-an-image-viewer-in-csharp-part-5-selecting-part-of-an-image .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comConverting 2D arrays to 1D and accessing as either 2D or 1Durn:uuid:e8bd0586-1437-4700-a355-cd4b7dd309212016-01-11T17:26:30Z2012-04-11T09:29:13Z<p>While working on a recent gaming project, I was originally using
2D arrays to store information relating to the different levels
in the game. But when it came to loop through the contents of
these levels, it wasn't as straightforward to do a simple
<code>foreach</code> loop due to the multiple dimensions.</p>
<p>Instead, I changed the code so that the 2D data was stored in a
single dimension array. By using <a href="http://en.wikipedia.org/wiki/Row-major_order" rel="external nofollow noopener">row-major order</a> you can
calculate any position in 2D space and map it into the 1D array.
This then allows you to continue accessing the data using 2D
co-ordinates, but opens up 1D access too.</p>
<h2 id="defining-your-array">Defining your array</h2>
<p>Given the size of your 2D array, the 1D creation code is
trivial:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
T<span class="symbol">[</span><span class="symbol">]</span> items <span class="symbol">=</span> <span class="keyword">new</span> T<span class="symbol">[</span>width <span class="symbol">*</span> height<span class="symbol">]</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="converting-2d-co-ordinates-into-1d-index">Converting 2D co-ordinates into 1D index</h2>
<p>Once your have your array, converting a 2D co-ordinate such as
<code>3, 4</code> into the correct index of your 1D array using row-major
order using the following formula:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
y <span class="symbol">*</span> width <span class="symbol">+</span> x
</pre>
</figure>
<h2 id="converting-1d-index-into-2d-co-ordinates">Converting 1D index into 2D co-ordinates</h2>
<p>The calculation to convert a 1D index into a 2D co-ordinate is
fairly straightforward:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
y <span class="symbol">=</span> index <span class="symbol">/</span> width<span class="symbol">;</span>
x <span class="symbol">=</span> index <span class="symbol">%</span> width<span class="symbol">;</span>
</pre>
</figure>
<h2 id="putting-it-together-the-arraymapt-class">Putting it together - the ArrayMap&lt;T&gt; class</h2>
<p>To avoid constantly having to repeat the calculations, I created
a generic <code>ArrayMap</code> class that I could use to store any data
type in a 1D array, and access the values using either indexes
or co-ordinates, as well as adding enumerable support. The class
is very straightforward to use:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
ArrayMap<span class="symbol">&lt;</span><span class="keyword">int</span><span class="symbol">&gt;</span> grid<span class="symbol">;</span>
Size size<span class="symbol">;</span>
<span class="keyword">int</span> value<span class="symbol">;</span>

size <span class="symbol">=</span> <span class="keyword">new</span> Size<span class="symbol">(</span><span class="number">10</span><span class="symbol">,</span> <span class="number">10</span><span class="symbol">)</span><span class="symbol">;</span>
value <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
grid <span class="symbol">=</span> <span class="keyword">new</span> ArrayMap<span class="symbol">&lt;</span><span class="keyword">int</span><span class="symbol">&gt;</span><span class="symbol">(</span>size<span class="symbol">)</span><span class="symbol">;</span>

<span class="comment">// set values via 2D co-ordinates</span>
<span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> y <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> y <span class="symbol">&lt;</span> size<span class="symbol">.</span>Height<span class="symbol">;</span> y<span class="symbol">++</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> x <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> x <span class="symbol">&lt;</span> size<span class="symbol">.</span>Width<span class="symbol">;</span> x<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 grid<span class="symbol">[</span>x<span class="symbol">,</span> y<span class="symbol">]</span> <span class="symbol">=</span> value<span class="symbol">;</span>
 value<span class="symbol">++</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="comment">// get values via 2D co-ordinates</span>
Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span>grid<span class="symbol">[</span><span class="number">9</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// 9</span>
Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span>grid<span class="symbol">[</span><span class="number">0</span><span class="symbol">,</span> <span class="number">9</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// 90</span>
Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span>grid<span class="symbol">[</span><span class="number">9</span><span class="symbol">,</span> <span class="number">9</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// 99</span>

<span class="comment">// set values via indexes</span>
<span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> grid<span class="symbol">.</span>Count<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 grid<span class="symbol">[</span>i<span class="symbol">]</span> <span class="symbol">=</span> i<span class="symbol">;</span>

<span class="comment">// get values via index</span>
Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span>grid<span class="symbol">[</span><span class="number">9</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// 9</span>
Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span>grid<span class="symbol">[</span><span class="number">90</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// 90</span>
Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span>grid<span class="symbol">[</span><span class="number">99</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// 99</span>

<span class="comment">// enumerate items</span>
<span class="keyword">foreach</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="keyword">in</span> grid<span class="symbol">)</span>
 Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span>i<span class="symbol">)</span><span class="symbol">;</span>

<span class="comment">// get index</span>
Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span>grid<span class="symbol">.</span>GetItemIndex<span class="symbol">(</span><span class="number">9</span><span class="symbol">,</span> <span class="number">9</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// 99</span>

<span class="comment">// get location</span>
Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span>grid<span class="symbol">.</span>GetItemLocation<span class="symbol">(</span><span class="number">99</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// 9,9</span>
</pre>
</figure>
<p>Below is the full source to the class.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> System<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Collections<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Collections<span class="symbol">.</span>Generic<span class="symbol">;</span>

<span class="keyword">namespace</span> BinaryRealms<span class="symbol">.</span>Engine
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">class</span> ArrayMap<span class="symbol">&lt;</span>T<span class="symbol">&gt;</span> <span class="symbol">:</span> IEnumerable<span class="symbol">&lt;</span>T<span class="symbol">&gt;</span>
 <span class="symbol">{</span>
 <span class="keyword">private</span> T<span class="symbol">[</span><span class="symbol">]</span> _items<span class="symbol">;</span>
 <span class="keyword">private</span> Size _size<span class="symbol">;</span>

 <span class="keyword">public</span> ArrayMap<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span> <span class="symbol">}</span>

 <span class="keyword">public</span> ArrayMap<span class="symbol">(</span><span class="keyword">int</span> width<span class="symbol">,</span> <span class="keyword">int</span> height<span class="symbol">)</span>
 <span class="symbol">:</span> <span class="keyword">this</span><span class="symbol">(</span><span class="keyword">new</span> Size<span class="symbol">(</span>width<span class="symbol">,</span> height<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span> <span class="symbol">}</span>

 <span class="keyword">public</span> ArrayMap<span class="symbol">(</span>Size size<span class="symbol">)</span>
 <span class="symbol">:</span> <span class="keyword">this</span><span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Size <span class="symbol">=</span> size<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> IEnumerator<span class="symbol">&lt;</span>T<span class="symbol">&gt;</span> GetEnumerator<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span>T item <span class="keyword">in</span> _items<span class="symbol">)</span>
 <span class="keyword">yield</span> <span class="keyword">return</span> item<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">int</span> GetItemIndex<span class="symbol">(</span><span class="keyword">int</span> x<span class="symbol">,</span> <span class="keyword">int</span> y<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>x <span class="symbol">&lt;</span> <span class="number">0</span> <span class="symbol">||</span> x <span class="symbol">&gt;=</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">.</span>Width<span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> IndexOutOfRangeException<span class="symbol">(</span><span class="string">&quot;X is out of range&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>y <span class="symbol">&lt;</span> <span class="number">0</span> <span class="symbol">||</span> y <span class="symbol">&gt;=</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">.</span>Height<span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> IndexOutOfRangeException<span class="symbol">(</span><span class="string">&quot;Y is out of range&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> y <span class="symbol">*</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">.</span>Width <span class="symbol">+</span> x<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">int</span> GetItemIndex<span class="symbol">(</span>Point point<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>GetItemIndex<span class="symbol">(</span>point<span class="symbol">.</span>X<span class="symbol">,</span> point<span class="symbol">.</span>Y<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> Point GetItemLocation<span class="symbol">(</span><span class="keyword">int</span> index<span class="symbol">)</span>
 <span class="symbol">{</span>
 Point point<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>index <span class="symbol">&lt;</span> <span class="number">0</span> <span class="symbol">||</span> index <span class="symbol">&gt;=</span> _items<span class="symbol">.</span>Length<span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> IndexOutOfRangeException<span class="symbol">(</span><span class="string">&quot;Index is out of range&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 point <span class="symbol">=</span> <span class="keyword">new</span> Point<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 point<span class="symbol">.</span>Y <span class="symbol">=</span> index <span class="symbol">/</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">.</span>Width<span class="symbol">;</span>
 point<span class="symbol">.</span>X <span class="symbol">=</span> index <span class="symbol">%</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">.</span>Width<span class="symbol">;</span>

 <span class="keyword">return</span> point<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">int</span> Count
 <span class="symbol">{</span> <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _items<span class="symbol">.</span>Length<span class="symbol">;</span> <span class="symbol">}</span> <span class="symbol">}</span>

 <span class="keyword">public</span> Size Size
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _size<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 _size <span class="symbol">=</span> value<span class="symbol">;</span>
 _items <span class="symbol">=</span> <span class="keyword">new</span> T<span class="symbol">[</span>_size<span class="symbol">.</span>Width <span class="symbol">*</span> _size<span class="symbol">.</span>Height<span class="symbol">]</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> T <span class="keyword">this</span><span class="symbol">[</span>Point location<span class="symbol">]</span>
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">[</span>location<span class="symbol">.</span>X<span class="symbol">,</span> location<span class="symbol">.</span>Y<span class="symbol">]</span><span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> <span class="keyword">this</span><span class="symbol">[</span>location<span class="symbol">.</span>X<span class="symbol">,</span> location<span class="symbol">.</span>Y<span class="symbol">]</span> <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> T <span class="keyword">this</span><span class="symbol">[</span><span class="keyword">int</span> x<span class="symbol">,</span> <span class="keyword">int</span> y<span class="symbol">]</span>
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">[</span><span class="keyword">this</span><span class="symbol">.</span>GetItemIndex<span class="symbol">(</span>x<span class="symbol">,</span> y<span class="symbol">)</span><span class="symbol">]</span><span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> <span class="keyword">this</span><span class="symbol">[</span><span class="keyword">this</span><span class="symbol">.</span>GetItemIndex<span class="symbol">(</span>x<span class="symbol">,</span> y<span class="symbol">)</span><span class="symbol">]</span> <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> T <span class="keyword">this</span><span class="symbol">[</span><span class="keyword">int</span> index<span class="symbol">]</span>
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _items<span class="symbol">[</span>index<span class="symbol">]</span><span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> _items<span class="symbol">[</span>index<span class="symbol">]</span> <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>

 IEnumerator IEnumerable<span class="symbol">.</span>GetEnumerator<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>GetEnumerator<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Currently I'm using this class without any problems, but if you
spot any errors or think it could do with anything else, please
let me know!</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2012-04-11 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/converting-2d-arrays-to-1d-and-accessing-as-either-2d-or-1d .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comAngelCode bitmap font parsing using C#urn:uuid:889c72c3-3e0d-4890-8752-c58164d633112015-10-06T17:34:21Z2012-01-02T13:54:51Z<blockquote>
<p>Updated 10Jun2015: Code is now available on <a href="https://github.com/cyotek/Cyotek.Drawing.BitmapFont" rel="external nofollow noopener">GitHub</a>, any
updates can be found there</p>
</blockquote>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/bitmapfont1.png" class="gallery" title="The font parser library was used by this OpenGL application that renders text" ><img src="https://images.cyotek.com/image/thumbnail/devblog/bitmapfont1.png" alt="The font parser library was used by this OpenGL application that renders text" decoding="async" loading="lazy" /></a><figcaption>The font parser library was used by this OpenGL application that renders text</figcaption></figure>
<p>While writing some bitmap font processing for an OpenGL project,
I settled on using <a href="http://www.angelcode.com/products/bmfont/" rel="external nofollow noopener">AngelCode's BMFont</a> utility to generate
both the textures and the font definition. However, this means I
then needed to write a parser in order to use this in my OpenGL
solution.</p>
<p>This library is a generic parser for the BMFont format - it
doesn't include any rendering functionality or exotic references
and should be usable in any version of .NET from 2.0 upwards.
BMFont can generate fonts in three formats - binary, text and
XML. The library currently supports text and XML, I may add
binary support at another time; but currently I'm happy using
the text format.</p>
<blockquote>
<p>Note: This library only provides parsing functionality for
loading BMFont data. It is up to you to provide functionality
used to load textures and render characters</p>
</blockquote>
<h2 id="overview-of-the-library">Overview of the library</h2>
<p>There are four main classes used to describe a font:</p>
<ul>
<li><code>BitmapFont</code> - the main class representing the font and its
attributes</li>
<li><code>Character</code> - representing a single character</li>
<li><code>Kerning</code> - represents the kerning between a pair of
characters</li>
<li><code>Page</code> - represents a texture page</li>
</ul>
<p>There is also a support class, <code>Padding</code>, as I didn't want to
reference <code>System.Windows.Forms</code> in order to use its own and
using a <code>Rectangle</code> instead would be confusing. You can replace
with this <code>System.Windows.Forms</code> version if you want.</p>
<p>Finally, the <code>BitmapFontLoader</code> class is a static class that
will handle the loading of your fonts.</p>
<h2 id="loading-a-font">Loading a font</h2>
<p>To load a font, call <code>BitmapFontLoader.LoadFontFromFile</code>. This
will attempt to auto detect the file type and load a font.
Alternatively, if you already know the file type in advance,
then call the variations <code>BitmapFontLoader.LoadFontFromTextFile</code>
or <code>BitmapFontLoader.LoadFontFromXmlFile</code>.</p>
<p>Each of these functions returns a new <code>BitmapFont</code> object on
success.</p>
<h2 id="using-a-font">Using a font</h2>
<p>The <code>BitmapFont</code> class returns all the information specified in
the font file, such as the attributes used to create the font.
Most of these not directly used and are there only for if your
application needs to know how the font was generated (for
example if the textures are packed or not). The main things you
would be interested in are:</p>
<ul>
<li><code>Characters</code> - this property contains all the characters
defined in the font.</li>
<li><code>Kernings</code> - this property contains all kerning definitions.
However, mostly you should use the <code>GetKerning</code> method to get
the kerning for a pair of characters.</li>
<li><code>Pages</code> -this property contains the filenames of the textures
used by the font. You'll need to manually load the relevant
textures.</li>
<li><code>LineHeight</code> - this property returns the line height. When
rending text across multiple lines, use this property for
incrementing the vertical coordinate - don't just use the
largest character height or you'll end up with inconsistent
line heights.</li>
</ul>
<p>The <code>Character</code> class describes a single character. Your
rendering functionality will probably need to use all of the
properties it contains:</p>
<ul>
<li><code>Bounds</code> - the location and size of the character in the
source texture.</li>
<li><code>TexturePage</code> - the index of the page containing the source
texture.</li>
<li><code>Offset</code> - an offset to use when rendering the character so it
lines up correctly with other characters.</li>
<li><code>XAdvance</code> - the value to increment the horizontal coordinate
by. Don't forgot to combine this value with the result of a
call to <code>GetKerning</code>.</li>
</ul>
<h2 id="example-rendering-using-gdi">Example rendering using GDI</h2>
<blockquote>
<p>The sample project which accompanies this article shows a very
basic way of rending using GDI; however this is just for
demonstration purposes and you should probably come up with
something more efficient in a real application!</p>
</blockquote>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/bitmapfont3.png" class="gallery" title="Example rendering using the bitmap font viewer" ><img src="https://images.cyotek.com/image/thumbnail/devblog/bitmapfont3.png" alt="Example rendering using the bitmap font viewer" decoding="async" loading="lazy" /></a><figcaption>Example rendering using the bitmap font viewer</figcaption></figure><figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> DrawCharacter<span class="symbol">(</span>Graphics g<span class="symbol">,</span> Character character<span class="symbol">,</span> <span class="keyword">int</span> x<span class="symbol">,</span> <span class="keyword">int</span> y<span class="symbol">)</span>
<span class="symbol">{</span>
 g<span class="symbol">.</span>DrawImage<span class="symbol">(</span>_textures<span class="symbol">[</span>character<span class="symbol">.</span>TexturePage<span class="symbol">]</span><span class="symbol">,</span> <span class="keyword">new</span> RectangleF<span class="symbol">(</span>x<span class="symbol">,</span> y<span class="symbol">,</span> character<span class="symbol">.</span>Bounds<span class="symbol">.</span>Width<span class="symbol">,</span> character<span class="symbol">.</span>Bounds<span class="symbol">.</span>Height<span class="symbol">)</span><span class="symbol">,</span> character<span class="symbol">.</span>Bounds<span class="symbol">,</span> GraphicsUnit<span class="symbol">.</span>Pixel<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> DrawPreview<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 Size size<span class="symbol">;</span>
 Bitmap image<span class="symbol">;</span>
 <span class="keyword">string</span> normalizedText<span class="symbol">;</span>
 <span class="keyword">int</span> x<span class="symbol">;</span>
 <span class="keyword">int</span> y<span class="symbol">;</span>
 <span class="keyword">char</span> previousCharacter<span class="symbol">;</span>

 previousCharacter <span class="symbol">=</span> <span class="string">&#39; &#39;</span><span class="symbol">;</span>
 normalizedText <span class="symbol">=</span> _font<span class="symbol">.</span>NormalizeLineBreaks<span class="symbol">(</span>previewTextBox<span class="symbol">.</span>Text<span class="symbol">)</span><span class="symbol">;</span>
 size <span class="symbol">=</span> _font<span class="symbol">.</span>MeasureFont<span class="symbol">(</span>normalizedText<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>size<span class="symbol">.</span>Height <span class="symbol">!=</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> size<span class="symbol">.</span>Width <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 image <span class="symbol">=</span> <span class="keyword">new</span> Bitmap<span class="symbol">(</span>size<span class="symbol">.</span>Width<span class="symbol">,</span> size<span class="symbol">.</span>Height<span class="symbol">)</span><span class="symbol">;</span>
 x <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 y <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Graphics g <span class="symbol">=</span> Graphics<span class="symbol">.</span>FromImage<span class="symbol">(</span>image<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span><span class="keyword">char</span> character <span class="keyword">in</span> normalizedText<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">switch</span> <span class="symbol">(</span>character<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> <span class="string">&#39;\n&#39;</span><span class="symbol">:</span>
 x <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 y <span class="symbol">+=</span> _font<span class="symbol">.</span>LineHeight<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">default</span><span class="symbol">:</span>
 Character data<span class="symbol">;</span>
 <span class="keyword">int</span> kerning<span class="symbol">;</span>

 data <span class="symbol">=</span> _font<span class="symbol">[</span>character<span class="symbol">]</span><span class="symbol">;</span>
 kerning <span class="symbol">=</span> _font<span class="symbol">.</span>GetKerning<span class="symbol">(</span>previousCharacter<span class="symbol">,</span> character<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>DrawCharacter<span class="symbol">(</span>g<span class="symbol">,</span> data<span class="symbol">,</span> x <span class="symbol">+</span> data<span class="symbol">.</span>Offset<span class="symbol">.</span>X <span class="symbol">+</span> kerning<span class="symbol">,</span> y <span class="symbol">+</span> data<span class="symbol">.</span>Offset<span class="symbol">.</span>Y<span class="symbol">)</span><span class="symbol">;</span>

 x <span class="symbol">+=</span> data<span class="symbol">.</span>XAdvance <span class="symbol">+</span> kerning<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 previousCharacter <span class="symbol">=</span> character<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 previewImageBox<span class="symbol">.</span>Image <span class="symbol">=</span> image<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="the-bitmap-font-viewer-application">The Bitmap Font Viewer application</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/bitmapfont2.png" class="gallery" title="This sample application loads and previews bitmap fonts" ><img src="https://images.cyotek.com/image/thumbnail/devblog/bitmapfont2.png" alt="This sample application loads and previews bitmap fonts" decoding="async" loading="lazy" /></a><figcaption>This sample application loads and previews bitmap fonts</figcaption></figure>
<p>Also included in the download for this article is a simple
Windows Forms application for viewing a bitmap font.</p>
<blockquote>
<p>Note: All of the fonts I have created and tested were
unpacked. The font viewer does not support packed textures,
and while it will still load the font, it will not draw glyphs
properly as it isn't able to do any of the magic with channels
that the packed texture requires. In addition, as .NET doesn't
support the TGA format by default, neither does this sample
project.</p>
</blockquote>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>Unlike my other articles, I haven't really gone into the source
code or pointed out how it works, however it should all be
simple to understand and use (despite having virtually no
documentation) - please let me know if you think otherwise!</p>
<p>As mentioned above, I'm currently not using packed textures. The
font parser will give you all the information you need regarding
channels for extracting the information, but could probably be
nicer done, such as using enums instead of magic ints - I may
address this in a future update, along side implementing the
binary file format.</p>
<p>Ideally the best way to use this code would be to inherit or
extend the <code>BitmapFont</code> class. Therefore it would probably be
better directly embedding the source code into your application,
change the namespaces to match your own solution, then build
from there.</p>
<p>I haven't tested with many fancy fonts - it's probable that the
<code>MeasureFont</code> method doesn't handle cases of fonts with have a
larger draw area than their basic box size.</p>
<p>Updates to this project will be posted to either CodePlex or
GitHub - this article will be updated once the code is up.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2012-01-02 - First published</li>
<li>2015-06-10 - Added GitHub links</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/angelcode-bitmap-font-parsing-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comDetecting if an application is running as an elevated process, and spawning a new process using elevated permissionsurn:uuid:cf279d2c-631e-4759-b8fc-43682c746c572011-11-27T11:52:13Z2011-11-27T11:39:44Z<p>Recently I was writing some code to allow a program to register
itself to start with Windows for all users. On Windows 7 with
User Account Control (UAC) enabled, trying to write to the
relevant registry key without having elevated permissions throws
an <code>UnauthorizedAccessException</code> exception. If you want to make
these sorts of modifications to a system, the application needs
to be running as an administrator.</p>
<h2 id="checking-if-your-application-is-running-with-elevated-permissions">Checking if your application is running with elevated permissions</h2>
<p>To check if your application is currently running with elevated
permissions, you simply need to see if the current user belongs
to the <code>Administrator</code> user group.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="comment">// Requires &quot;using System.Security.Principal;&quot;</span>

<span class="keyword">public</span> <span class="keyword">bool</span> IsElevated
<span class="symbol">{</span>
 <span class="keyword">get</span>
 <span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">new</span> WindowsPrincipal<span class="symbol">(</span>WindowsIdentity<span class="symbol">.</span>GetCurrent<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">.</span>IsInRole<span class="symbol">(</span>WindowsBuiltInRole<span class="symbol">.</span>Administrator<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">void</span> SomeMethod<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsElevated<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// running as administrator</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="running-an-application-as-an-administrator">Running an application as an administrator</h2>
<p>While it might be possible to elevate your applications process
via the <code>LogonUser</code> API, this requires user names and passwords,
and isn't a trivial task. So we'll ignore this approach in
favour of something much more simplistic and less likely to go
wrong, not to mention not requiring admin passwords.</p>
<p>You're probably already aware that there are various &quot;verbs&quot;
predefined for dealing with specific actions relating to
interaction with a file, such as <code>print</code> and <code>open</code>. While these
verbs are normally configured on file associations in the
Windows Registry, you can also force a process to be run under
the administration account by specifying the <code>runas</code> verb.</p>
<blockquote>
<p>Note: Specifying this verb in Windows XP displays a dialog
allowing a user to be selected. Unfortunately this means that
it's possible for the spawned application to not have the
required permissions either - remember to check that you have
permission to do an action before actually attempting it!</p>
</blockquote>
<p>For my scenario, the core application shouldn't need to run in
elevated mode, so I decided to create a generic stub program
which would accept a number of arguments for if the startup
program should be registered or unregistered, and the title and
location used to perform the action. Then the main application
simply spawned this process in administration mode to apply the
users choice.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
ProcessStartInfo startInfo<span class="symbol">;</span>

startInfo <span class="symbol">=</span> <span class="keyword">new</span> ProcessStartInfo<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
startInfo<span class="symbol">.</span>FileName <span class="symbol">=</span> Path<span class="symbol">.</span>Combine<span class="symbol">(</span>Path<span class="symbol">.</span>GetDirectoryName<span class="symbol">(</span>Application<span class="symbol">.</span>ExecutablePath<span class="symbol">)</span><span class="symbol">,</span> <span class="string">&quot;regstart.exe&quot;</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// replace with your filename</span>
startInfo<span class="symbol">.</span>Arguments <span class="symbol">=</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">;</span> <span class="comment">// if you need to pass any command line arguments to your stub, enter them here</span>
startInfo<span class="symbol">.</span>UseShellExecute <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
startInfo<span class="symbol">.</span>Verb <span class="symbol">=</span> <span class="string">&quot;runas&quot;</span><span class="symbol">;</span>

Process<span class="symbol">.</span>Start<span class="symbol">(</span>startInfo<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<blockquote>
<p>Note: Although I haven't included it in the example above, you
may wish to handle the <code>Win32Exception</code> that can be thrown by
the <code>Process.Start</code> method. If the user cancels the UAC
prompt, this exception will be automatically thrown with the
<code>ERROR_CANCELLED</code> (1223) error code.</p>
</blockquote>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/uac-prompt.png" class="gallery" title="An example of a UAC prompt for an unsigned application" ><img src="https://images.cyotek.com/image/thumbnail/devblog/uac-prompt.png" alt="An example of a UAC prompt for an unsigned application" decoding="async" loading="lazy" /></a><figcaption>An example of a UAC prompt for an unsigned application</figcaption></figure>
<p>With the <code>runas</code> verb specified, the application is now run in
elevated mode, and the operating system asks the user for
permission to continue. Unfortunately, if your application isn't
signed, then you get a scarier version of the prompt, as
displayed above. If your application is signed, then you'll get
something similar to the screenshot below.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/uac-prompt-signed.png" class="gallery" title="An example of a UAC prompt for a properly signed application" ><img src="https://images.cyotek.com/image/thumbnail/devblog/uac-prompt-signed.png" alt="An example of a UAC prompt for a properly signed application" decoding="async" loading="lazy" /></a><figcaption>An example of a UAC prompt for a properly signed application</figcaption></figure><h2 id="update-history">Update History</h2>
<ul>
<li>2011-11-27 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/detecting-if-an-application-is-running-as-an-elevated-process-and-spawning-a-new-process-using-elevated-permissions .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comExtending the ImageBox component to display the contents of a PDF file using C#urn:uuid:6127dd18-25af-4467-ac03-cf9ca52236512011-09-04T16:48:55Z2011-09-04T16:48:55Z<p>In this article, I'll describe how to extend the <a href="/tag/imagebox">ImageBox</a>
control discussed in earlier articles to be able to display PDF
files with the help of the <a href="http://www.ghostscript.com/" rel="external nofollow noopener">GhostScript library</a> and the
conversion library described in the <a href="/post/convert-a-pdf-into-a-series-of-images-using-csharp-and-ghostscript">previous article</a>.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/pdfimagebox.png" class="gallery" title="A sample application demonstrating displaying a PDF file in the ImageBox control" ><img src="https://images.cyotek.com/image/thumbnail/devblog/pdfimagebox.png" alt="A sample application demonstrating displaying a PDF file in the ImageBox control" decoding="async" loading="lazy" /></a><figcaption>A sample application demonstrating displaying a PDF file in the ImageBox control</figcaption></figure><h2 id="getting-started">Getting Started</h2>
<p>You can download the source code used in this article from the
links below, these are:</p>
<ul>
<li><strong>Cyotek.GhostScript</strong> - core library providing GhostScript
integration support</li>
<li><strong>Cyotek.GhostScript.PdfConversion</strong> - support library for
converting a PDF document into images</li>
<li><strong>PdfImageBoxSample</strong> - sample project containing an updated
<code>ImageBox</code> control, and the extended <code>PdfImageBox</code>.</li>
</ul>
<blockquote>
<p>Please note that the native GhostScript DLL is not included in
these downloads, you will need to obtain that from the
<a href="http://www.ghostscript.com/" rel="external nofollow noopener">GhostScript project page</a>.</p>
</blockquote>
<h2 id="extending-the-imagebox">Extending the ImageBox</h2>
<p>To start extending the <code>ImageBox</code>, create a new class and
inherit the <code>ImageBox</code> control. I also decided to override some
of the default properties, so I added a constructor which sets
the new values.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> PdfImageBox<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// override some of the original ImageBox defaults</span>
 <span class="keyword">this</span><span class="symbol">.</span>GridDisplayMode <span class="symbol">=</span> ImageBoxGridDisplayMode<span class="symbol">.</span>None<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>BackColor <span class="symbol">=</span> SystemColors<span class="symbol">.</span>AppWorkspace<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>ImageBorderStyle <span class="symbol">=</span> ImageBoxBorderStyle<span class="symbol">.</span>FixedSingleDropShadow<span class="symbol">;</span>

 <span class="comment">// new pdf conversion settings</span>
 <span class="keyword">this</span><span class="symbol">.</span>Settings <span class="symbol">=</span> <span class="keyword">new</span> Pdf<span class="number">2</span>ImageSettings<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>To ensure correct designer support, override versions of the
properties with new <code>DefaultValue</code> attributes were added. With
this done, it's time to add the new properties that will support
viewing PDF files. The new properties are:</p>
<ul>
<li><code>PdfFileName</code> - the filename of the PDF to view</li>
<li><code>PdfPassword</code> - specifies the password of the PDF file if one
is required to open it (<em>note, I haven't actually tested that
this works!</em>)</li>
<li><code>Settings</code> - uses the <code>Pdf2ImageSettings</code> class <a href="/post/convert-a-pdf-into-a-series-of-images-using-csharp-and-ghostscript">discussed
earlier</a> to control quality settings for the converted
document.</li>
<li><code>PageCache</code> - an internal dictionary which stores a <code>Bitmap</code>
against a page number to cache pages after these have loaded.</li>
</ul>
<p>With the exception of <code>PageCache</code>, each of these properties also
has backing event for change notifications, and as
<code>Pdf2ImageSettings</code> implements <code>INotifyPropertyChanged</code> we'll
also bind an event detect when the individual setting properties
are modified.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Appearance&quot;</span><span class="symbol">)</span><span class="symbol">,</span> DefaultValue<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>Pdf<span class="number">2</span>ImageSettings<span class="symbol">)</span><span class="symbol">,</span> <span class="string">&quot;&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">virtual</span> Pdf<span class="number">2</span>ImageSettings Settings
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _settings<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Settings <span class="symbol">!=</span> value<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_settings <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 _settings<span class="symbol">.</span>PropertyChanged <span class="symbol">-=</span> SettingsPropertyChangedHandler<span class="symbol">;</span>

 _settings <span class="symbol">=</span> value<span class="symbol">;</span>
 _settings<span class="symbol">.</span>PropertyChanged <span class="symbol">+=</span> SettingsPropertyChangedHandler<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>OnSettingsChanged<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> SettingsPropertyChangedHandler<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> PropertyChangedEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnSettingsChanged<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> OnSettingsChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>OpenPDF<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>SettingsChanged <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>SettingsChanged<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">,</span> e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="navigation-support">Navigation support</h2>
<p>Although the <code>PdfImageBox</code> doesn't supply a user interface for
navigating to different pages, we want to make it easy for the
hosting application to provide one. To support this, a new
<code>CurrentPage</code> property will be added for allowing the active
page to retrieved or set, and also a number of readonly
<code>CanMove*</code> properties. These properties allow the host to query
which navigation options are applicable in order to present the
correct UI.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">virtual</span> <span class="keyword">int</span> PageCount
<span class="symbol">{</span> <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _converter <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">?</span> _converter<span class="symbol">.</span>PageCount <span class="symbol">:</span> <span class="number">0</span><span class="symbol">;</span> <span class="symbol">}</span> <span class="symbol">}</span>

<span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Appearance&quot;</span><span class="symbol">)</span><span class="symbol">,</span> DefaultValue<span class="symbol">(</span><span class="number">1</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">int</span> CurrentPage
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _currentPage<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>CurrentPage <span class="symbol">!=</span> value<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>value <span class="symbol">&lt;</span> <span class="number">1</span> <span class="symbol">||</span> value <span class="symbol">&gt;</span> <span class="keyword">this</span><span class="symbol">.</span>PageCount<span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentException<span class="symbol">(</span><span class="string">&quot;Page number is out of bounds&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 _currentPage <span class="symbol">=</span> value<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>OnCurrentPageChanged<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">bool</span> CanMoveFirst
<span class="symbol">{</span> <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>PageCount <span class="symbol">!=</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>CurrentPage <span class="symbol">!=</span> <span class="number">1</span><span class="symbol">;</span> <span class="symbol">}</span> <span class="symbol">}</span>

<span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">bool</span> CanMoveLast
<span class="symbol">{</span> <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>PageCount <span class="symbol">!=</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>CurrentPage <span class="symbol">!=</span> <span class="keyword">this</span><span class="symbol">.</span>PageCount<span class="symbol">;</span> <span class="symbol">}</span> <span class="symbol">}</span>

<span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">bool</span> CanMoveNext
<span class="symbol">{</span> <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>PageCount <span class="symbol">!=</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>CurrentPage <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>PageCount<span class="symbol">;</span> <span class="symbol">}</span> <span class="symbol">}</span>

<span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">bool</span> CanMovePrevious
<span class="symbol">{</span> <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>PageCount <span class="symbol">!=</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>CurrentPage <span class="symbol">&gt;</span> <span class="number">1</span><span class="symbol">;</span> <span class="symbol">}</span> <span class="symbol">}</span>
</pre>
</figure>
<p>Again, to make it easier for the host to connect to the control,
we also add some helper navigation methods.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">void</span> FirstPage<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>CurrentPage <span class="symbol">=</span> <span class="number">1</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">void</span> LastPage<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>CurrentPage <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>PageCount<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">void</span> NextPage<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>CurrentPage<span class="symbol">++</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">void</span> PreviousPage<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>CurrentPage<span class="symbol">--</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Finally, it can sometimes take a few seconds to convert a page
in a PDF file. To allow the host to provide a busy notification,
such as setting the wait cursor or displaying a status bar
message, we'll add a pair of events which will be called before
and after a page is converted.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">event</span> EventHandler LoadingPage<span class="symbol">;</span>

<span class="keyword">public</span> <span class="keyword">event</span> EventHandler LoadedPage<span class="symbol">;</span>
</pre>
</figure>
<h4 id="opening-the-pdf-file">Opening the PDF file</h4>
<p>Each of the property changed handlers in turn call the <code>OpenPDF</code>
method. This method first clears any existing image cache and
then initializes the conversion class based on the current PDF
file name and quality settings. If the specified file is a valid
PDF, the first page is converted, cached, and displayed.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">void</span> OpenPDF<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>CleanUp<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>DesignMode<span class="symbol">)</span>
 <span class="symbol">{</span>
 _converter <span class="symbol">=</span> <span class="keyword">new</span> Pdf<span class="number">2</span>Image<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 PdfFileName <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>PdfFileName<span class="symbol">,</span>
 PdfPassword <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>PdfPassword<span class="symbol">,</span>
 Settings <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Settings
 <span class="symbol">}</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Image <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>PageCache<span class="symbol">=</span> <span class="keyword">new</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">int</span><span class="symbol">,</span> Bitmap<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 _currentPage <span class="symbol">=</span> <span class="number">1</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>PageCount <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _currentPage <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>CurrentPage <span class="symbol">=</span> <span class="number">1</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> CleanUp<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// release bitmaps</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>PageCache <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span>KeyValuePair<span class="symbol">&lt;</span><span class="keyword">int</span><span class="symbol">,</span> Bitmap<span class="symbol">&gt;</span> pair <span class="keyword">in</span> <span class="keyword">this</span><span class="symbol">.</span>PageCache<span class="symbol">)</span>
 pair<span class="symbol">.</span>Value<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>PageCache <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h4 id="displaying-the-image">Displaying the image</h4>
<p>Each time the <code>CurrentPage</code> property is changed, it calls the
<code>SetPageImage</code> method. This method first checks to ensure the
specified page is present in the cache. If it is not, it will
load the page in. Once the page is in the cache, it is then
displayed in the <code>ImageBox</code>, and the user can then pan and zoom
as with any other image.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> SetPageImage<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>DesignMode <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>PageCache <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">lock</span> <span class="symbol">(</span>_lock<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>PageCache<span class="symbol">.</span>ContainsKey<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>CurrentPage<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnLoadingPage<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>PageCache<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>CurrentPage<span class="symbol">,</span> _converter<span class="symbol">.</span>GetImage<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>CurrentPage<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnLoadedPage<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">this</span><span class="symbol">.</span>Image <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>PageCache<span class="symbol">[</span><span class="keyword">this</span><span class="symbol">.</span>CurrentPage<span class="symbol">]</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Note that we operate a lock during the execution of this method,
to ensure that you can't try and load the same page twice.</p>
<p>With this method in place, the control is complete and ready to
be used as a basic PDF viewer. In order to keep the article down
to a reasonable size, I've excluded some of the definitions,
overloads and helper methods; these can all be found in the
sample download below.</p>
<p>The sample project demonstrates all the features described above
and provides an example setting up a user interface for
navigating a PDF document.</p>
<h2 id="future-changes">Future changes</h2>
<p>At the moment, the <code>PdfImageBox</code> control processes on page at a
time and caches the results. This means that navigation through
already viewed pages is fast, but displaying new pages can be
less than ideal. A possible enhancement would be to make the
control multithreaded, and continue to load pages on a
background thread.</p>
<p>Another issue is that as the control is caching the converted
images in memory, it may use a lot of memory in order to display
large PDF files. Not quite sure on the best approach to resolve
this one, either to &quot;expire&quot; older pages, or to keep only a
fixed number in memory. Or even save each page to a temporary
disk file.</p>
<p>Finally, I haven't put in any handling at all for if the
converter fails to convert a given page... I'll add this to a
future update, and hopefully get the code hosted on an SVN
server for interested parties.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2011-09-04 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/extending-the-imagebox-component-to-display-the-contents-of-a-pdf-file-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comConvert a PDF into a series of images using C# and GhostScripturn:uuid:9d2ef296-4c45-4df2-a1b2-24936bcbe8f52012-07-11T17:41:18Z2011-09-04T16:38:15Z<p>An application I was recently working on received PDF files from
a webservice which it then needed to store in a database. I
wanted the ability to display previews of these documents within
the application. While there are a number of solutions for
creating PDF files from C#, options for viewing a PDF within
your application is much more limited, unless you purchase
expensive commercial products, or use COM interop to embed
Acrobat Reader into your application.</p>
<p>This article describes an alternate solution, in which the pages
in a PDF are converted into images using GhostScript, from where
you can then display them in your application.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/pdfimagebox.png" class="gallery" title="A sample application demonstrating displaying a PDF file in the ImageBox control" ><img src="https://images.cyotek.com/image/thumbnail/devblog/pdfimagebox.png" alt="A sample application demonstrating displaying a PDF file in the ImageBox control" decoding="async" loading="lazy" /></a><figcaption>A sample application demonstrating displaying a PDF file in the ImageBox control</figcaption></figure>
<p>In order to avoid huge walls of text, this article has been
split into two parts, the first dealing with the actual
conversion of a PDF, and the <a href="/post/extending-the-imagebox-component-to-display-the-contents-of-a-pdf-file-using-csharp">second</a> demonstrates how to
extend the <a href="/tag/imagebox">ImageBox</a> control to display the images.</p>
<h2 id="caveat-emptor">Caveat emptor</h2>
<p>Before we start, some quick points.</p>
<ul>
<li>The method I'm about to demonstrate converts each page of the
PDF into an image. This means that it is very suitable for
viewing, but interactive elements such as forms, hyperlinks
and even good old text selection are not available.</li>
<li>GhostScript has a number of licenses associated with it but I
can't find any information of the pricing of commercial
licenses.</li>
<li>The GhostScript API Integration library used by this project
isn't complete and I'm not going to go into the bells and
whistles of how it works in this pair of articles - once I've
completed the outstanding functionality I'll create a new
article for it.</li>
</ul>
<h2 id="getting-started">Getting Started</h2>
<p>You can download the two libraries used in this article from the
links below, these are:</p>
<ul>
<li><strong>Cyotek.GhostScript</strong> - core library providing GhostScript
integration support</li>
<li><strong>Cyotek.GhostScript.PdfConversion</strong> - support library for
converting a PDF document into images</li>
</ul>
<blockquote>
<p>Please note that the native GhostScript DLL is not included in
these downloads, you will need to obtain that from the
<a href="http://www.ghostscript.com/" rel="external nofollow noopener">GhostScript project page</a></p>
</blockquote>
<h2 id="using-the-ghostscriptapi-class">Using the GhostScriptAPI class</h2>
<p>As mentioned above, the core GhostScript library isn't complete
yet, so I'll just give a description of the basic functionality
required by the conversion library.</p>
<p>The <code>GhostScriptAPI</code> class handles all communication with
GhostScript. When you create an instance of the class, it
automatically calls <code>gsapi_new_instance</code> in the native
GhostScript DLL. When the class is disposed, it will
automatically release any handles and calls the native
<code>gsapi_exit</code> and <code>gsapi_delete_instance</code> methods.</p>
<p>In order to actually call GhostScript, you call the <code>Execute</code>
method, passing in either a string array of all the arguments to
pass to GhostScript, or a typed dictionary of commands and
values. The <code>GhostScriptCommand</code> enum contains most of the
commands supported by GhostScript, which may be a preferable
approach rather than trying to remember the parameter names
themselves.</p>
<h2 id="defining-conversion-settings">Defining conversion settings</h2>
<p>The <code>Pdf2ImageSettings</code> class allows you to customize various
properties of the output image. The following properties are
available:</p>
<ul>
<li><code>AntiAliasMode</code> - specifies the antialiasing level between
Low, Medium and High. This internally will set the
<code>dTextAlphaBits</code> and <code>dGraphicsAlphaBits</code> GhostScript switches
to appropriate values.</li>
<li><code>Dpi</code> - dots per inch. Internally sets the <code>r</code> switch. This
property is not used if a paper size is set.</li>
<li><code>GridFitMode</code> - controls the text readability mode. Internally
sets the <code>dGridFitTT</code> switch.</li>
<li><code>ImageFormat</code> - specifies the output image format. Internally
sets the <code>sDEVICE</code> switch.</li>
<li><code>PaperSize</code> - specifies a paper size from one of the standard
sizes supported by GhostScript.</li>
<li><code>TrimMode</code> - specifies how the image should be sized. Your
milage may vary if you try and use the paper size option.
Internally sets either the <code>dFIXEDMEDIA</code> and <code>sPAPERSIZE</code> or
the <code>dUseCropBox</code> or the <code>dUseTrimBox</code> switches.</li>
</ul>
<p>Typical settings could look like this:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Pdf<span class="number">2</span>ImageSettings settings<span class="symbol">;</span>

settings <span class="symbol">=</span> <span class="keyword">new</span> Pdf<span class="number">2</span>ImageSettings<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
settings<span class="symbol">.</span>AntiAliasMode <span class="symbol">=</span> AntiAliasMode<span class="symbol">.</span>High<span class="symbol">;</span>
settings<span class="symbol">.</span>Dpi <span class="symbol">=</span> <span class="number">300</span><span class="symbol">;</span>
settings<span class="symbol">.</span>GridFitMode <span class="symbol">=</span> GridFitMode<span class="symbol">.</span>Topological<span class="symbol">;</span>
settings<span class="symbol">.</span>ImageFormat <span class="symbol">=</span> ImageFormat<span class="symbol">.</span>Png<span class="number">24</span><span class="symbol">;</span>
settings<span class="symbol">.</span>TrimMode <span class="symbol">=</span> PdfTrimMode<span class="symbol">.</span>CropBox<span class="symbol">;</span>
</pre>
</figure>
<h2 id="converting-the-pdf">Converting the PDF</h2>
<p>To convert a PDF file into a series of images, use the
<code>Pdf2Image</code> class. The following properties and methods are
offered:</p>
<ul>
<li><code>ConvertPdfPageToImage</code> - converts a given page in the PDF
into an image which is saved to disk</li>
<li><code>GetImage</code> - converts a page in the PDF into an image and
returns the image</li>
<li><code>GetImages</code> - converts a range of pages into the PDF into
images and returns an image array</li>
<li><code>PageCount</code> - returns the number of pages in the source PDF</li>
<li><code>PdfFilename</code> - returns or sets the filename of the PDF
document to convert</li>
<li><code>PdfPassword</code> - returns or sets the password of the PDF
document to convert</li>
<li><code>Settings</code> - returns or sets the settings object described
above</li>
</ul>
<p>A typical example to convert the first image in a PDF document:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Bitmap firstPage <span class="symbol">=</span> <span class="keyword">new</span> Pdf<span class="number">2</span>Image<span class="symbol">(</span><span class="string">&quot;sample.pdf&quot;</span><span class="symbol">)</span><span class="symbol">.</span>GetImage<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="the-inner-workings">The inner workings</h2>
<p>Most of the code in the class is taken up with the
<code>GetConversionArguments</code> method. This method looks at the
various properties of the conversion such as output format,
quality, etc, and returns the appropriate commands to pass to
GhostScript:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> IDictionary<span class="symbol">&lt;</span>GhostScriptCommand<span class="symbol">,</span> <span class="keyword">object</span><span class="symbol">&gt;</span> GetConversionArguments<span class="symbol">(</span><span class="keyword">string</span> pdfFileName<span class="symbol">,</span> <span class="keyword">string</span> outputImageFileName<span class="symbol">,</span> <span class="keyword">int</span> pageNumber<span class="symbol">,</span> <span class="keyword">string</span> password<span class="symbol">,</span> Pdf<span class="number">2</span>ImageSettings settings<span class="symbol">)</span>
<span class="symbol">{</span>
 IDictionary<span class="symbol">&lt;</span>GhostScriptCommand<span class="symbol">,</span> <span class="keyword">object</span><span class="symbol">&gt;</span> arguments<span class="symbol">;</span>

 arguments <span class="symbol">=</span> <span class="keyword">new</span> Dictionary<span class="symbol">&lt;</span>GhostScriptCommand<span class="symbol">,</span> <span class="keyword">object</span><span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// basic GhostScript setup</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>Silent<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>Safer<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>Batch<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>NoPause<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// specify the output</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>Device<span class="symbol">,</span> GhostScriptAPI<span class="symbol">.</span>GetDeviceName<span class="symbol">(</span>settings<span class="symbol">.</span>ImageFormat<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>OutputFile<span class="symbol">,</span> outputImageFileName<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// page numbers</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>FirstPage<span class="symbol">,</span> pageNumber<span class="symbol">)</span><span class="symbol">;</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>LastPage<span class="symbol">,</span> pageNumber<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// graphics options</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>UseCIEColor<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>settings<span class="symbol">.</span>AntiAliasMode <span class="symbol">!=</span> AntiAliasMode<span class="symbol">.</span>None<span class="symbol">)</span>
 <span class="symbol">{</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>TextAlphaBits<span class="symbol">,</span> settings<span class="symbol">.</span>AntiAliasMode<span class="symbol">)</span><span class="symbol">;</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>GraphicsAlphaBits<span class="symbol">,</span> settings<span class="symbol">.</span>AntiAliasMode<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>GridToFitTT<span class="symbol">,</span> settings<span class="symbol">.</span>GridFitMode<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// image size</span>
 <span class="keyword">if</span> <span class="symbol">(</span>settings<span class="symbol">.</span>TrimMode <span class="symbol">!=</span> PdfTrimMode<span class="symbol">.</span>PaperSize<span class="symbol">)</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>Resolution<span class="symbol">,</span> settings<span class="symbol">.</span>Dpi<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">switch</span> <span class="symbol">(</span>settings<span class="symbol">.</span>TrimMode<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> PdfTrimMode<span class="symbol">.</span>PaperSize<span class="symbol">:</span>
 <span class="keyword">if</span> <span class="symbol">(</span>settings<span class="symbol">.</span>PaperSize <span class="symbol">!=</span> PaperSize<span class="symbol">.</span>Default<span class="symbol">)</span>
 <span class="symbol">{</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>FixedMedia<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>PaperSize<span class="symbol">,</span> settings<span class="symbol">.</span>PaperSize<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> PdfTrimMode<span class="symbol">.</span>TrimBox<span class="symbol">:</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>UseTrimBox<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> PdfTrimMode<span class="symbol">.</span>CropBox<span class="symbol">:</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>UseCropBox<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="comment">// pdf password</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>password<span class="symbol">)</span><span class="symbol">)</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>PDFPassword<span class="symbol">,</span> password<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// pdf filename</span>
 arguments<span class="symbol">.</span>Add<span class="symbol">(</span>GhostScriptCommand<span class="symbol">.</span>InputFile<span class="symbol">,</span> pdfFileName<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> arguments<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>As you can see from the method above, the commands are being
returned as a strongly typed dictionary - the <code>GhostScriptAPI</code>
class will convert these into the correct GhostScript commands,
but the enum is much easier to work with from your code! The
following is an example of the typical GhostScript commands to
convert a single page in a PDF document:</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
-q -dSAFER -dBATCH -dNOPAUSE -sDEVICE=png16m -sOutputFile=tmp78BC.tmp -dFirstPage=1 -dLastPage=1 -dUseCIEColor -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -dGridFitTT=2 -r150 -dUseCropBox=true sample.pdf
</pre>
</figure>
<p>The next step is to call GhostScript and convert the PDF which
is done using the <code>ConvertPdfPageToImage</code> method:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">void</span> ConvertPdfPageToImage<span class="symbol">(</span><span class="keyword">string</span> outputFileName<span class="symbol">,</span> <span class="keyword">int</span> pageNumber<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>pageNumber <span class="symbol">&lt;</span> <span class="number">1</span> <span class="symbol">||</span> pageNumber <span class="symbol">&gt;</span> <span class="keyword">this</span><span class="symbol">.</span>PageCount<span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentException<span class="symbol">(</span><span class="string">&quot;Page number is out of bounds&quot;</span><span class="symbol">,</span> <span class="string">&quot;pageNumber&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>GhostScriptAPI api <span class="symbol">=</span> <span class="keyword">new</span> GhostScriptAPI<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 api<span class="symbol">.</span>Execute<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>GetConversionArguments<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>_pdfFileName<span class="symbol">,</span> outputFileName<span class="symbol">,</span> pageNumber<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>PdfPassword<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Settings<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>As you can see, this is a very simple call - create an instance
of the GhostScriptAPI class and then pass in the list of
parameters to execute. The <code>GhostScriptAPI</code> class takes care of
everything else.</p>
<p>Once the file is saved to disk, you can then load it into a
<code>Bitmap</code> or <code>Image</code> object for use in your application. Don't
forget to delete the file when you are finished with it!</p>
<p>Alternatively, the <code>GetImage</code> method will convert the file and
return the bitmap image for you, automatically deleting the
temporary file. This saves you from having to worry about
providing and deleting the output file, but it does mean you are
responsible for disposing of the returned bitmap.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> Bitmap GetImage<span class="symbol">(</span><span class="keyword">int</span> pageNumber<span class="symbol">)</span>
<span class="symbol">{</span>
 Bitmap result<span class="symbol">;</span>
 <span class="keyword">string</span> workFile<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>pageNumber <span class="symbol">&lt;</span> <span class="number">1</span> <span class="symbol">||</span> pageNumber <span class="symbol">&gt;</span> <span class="keyword">this</span><span class="symbol">.</span>PageCount<span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentException<span class="symbol">(</span><span class="string">&quot;Page number is out of bounds&quot;</span><span class="symbol">,</span> <span class="string">&quot;pageNumber&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 workFile <span class="symbol">=</span> Path<span class="symbol">.</span>GetTempFileName<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ConvertPdfPageToImage<span class="symbol">(</span>workFile<span class="symbol">,</span> pageNumber<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">using</span> <span class="symbol">(</span>FileStream stream <span class="symbol">=</span> <span class="keyword">new</span> FileStream<span class="symbol">(</span>workFile<span class="symbol">,</span> FileMode<span class="symbol">.</span>Open<span class="symbol">,</span> FileAccess<span class="symbol">.</span>Read<span class="symbol">)</span><span class="symbol">)</span>
 result <span class="symbol">=</span> <span class="keyword">new</span> Bitmap<span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">finally</span>
 <span class="symbol">{</span>
 File<span class="symbol">.</span>Delete<span class="symbol">(</span>workFile<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>You could also convert a range of pages at once using the
<code>GetImages</code> method:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> Bitmap<span class="symbol">[</span><span class="symbol">]</span> GetImages<span class="symbol">(</span><span class="keyword">int</span> startPage<span class="symbol">,</span> <span class="keyword">int</span> lastPage<span class="symbol">)</span>
<span class="symbol">{</span>
 List<span class="symbol">&lt;</span>Bitmap<span class="symbol">&gt;</span> results<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>startPage <span class="symbol">&lt;</span> <span class="number">1</span> <span class="symbol">||</span> startPage <span class="symbol">&gt;</span> <span class="keyword">this</span><span class="symbol">.</span>PageCount<span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentException<span class="symbol">(</span><span class="string">&quot;Start page number is out of bounds&quot;</span><span class="symbol">,</span> <span class="string">&quot;startPage&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>lastPage <span class="symbol">&lt;</span> <span class="number">1</span> <span class="symbol">||</span> lastPage <span class="symbol">&gt;</span> <span class="keyword">this</span><span class="symbol">.</span>PageCount<span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentException<span class="symbol">(</span><span class="string">&quot;Last page number is out of bounds&quot;</span><span class="symbol">,</span> <span class="string">&quot;lastPage&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>lastPage <span class="symbol">&lt;</span> startPage<span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentException<span class="symbol">(</span><span class="string">&quot;Last page cannot be less than start page&quot;</span><span class="symbol">,</span> <span class="string">&quot;lastPage&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 results <span class="symbol">=</span> <span class="keyword">new</span> List<span class="symbol">&lt;</span>Bitmap<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> startPage<span class="symbol">;</span> i <span class="symbol">&lt;=</span> lastPage<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 results<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>GetImage<span class="symbol">(</span>i<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> results<span class="symbol">.</span>ToArray<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="in-conclusion">In conclusion</h3>
<p>The above methods provide a simple way of providing basic PDF
viewing in your applications. In the <a href="/post/extending-the-imagebox-component-to-display-the-contents-of-a-pdf-file-using-csharp">next part</a>] of this
series, we describe how to extend the <a href="/tag/imagebox">ImageBox</a> component to
support conversion and navigation.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2011-09-04 - First published</li>
<li>2012-07-10 - Added follow up article links</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/convert-a-pdf-into-a-series-of-images-using-csharp-and-ghostscript .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comDetecting if a given font style exists in C#urn:uuid:a8943517-da86-41c6-8809-5b1ca73e29d02011-08-07T13:23:26Z2011-08-07T13:23:26Z<p>In a previous article, <a href="/post/creating-a-wysiwyg-font-combobox-using-csharp">Creating a WYSIWYG font ComboBox using
C#</a>, there is a hacky bit of code which uses a try catch
block to handle processing when a given font style doesn't
exist. This article describes a better way of handling this
requirement without relying on the exception handler.</p>
<p>Originally we used the following code to determine if a font
style exists: (Some of the additional code has been removed for
clarity)</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="comment">// bad code do not use!</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> Font GetFont<span class="symbol">(</span><span class="keyword">string</span> fontFamilyName<span class="symbol">)</span>
<span class="symbol">{</span>
 Font font<span class="symbol">;</span>

 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Regular<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>font <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Bold<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>font <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Italic<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>font <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Bold <span class="symbol">|</span> FontStyle<span class="symbol">.</span>Italic<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> font<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> Font GetFont<span class="symbol">(</span><span class="keyword">string</span> fontFamilyName<span class="symbol">,</span> FontStyle fontStyle<span class="symbol">)</span>
<span class="symbol">{</span>
 Font font<span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 font <span class="symbol">=</span> <span class="keyword">new</span> Font<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>PreviewFontSize<span class="symbol">,</span> fontStyle<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span>
 <span class="symbol">{</span>
 font <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> font<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>This code essentially &quot;tests&quot; each style by attempting to create
a font instance of a given style. If the style doesn't exist, an
exception is thrown and the code moves onto the next style.</p>
<p>A better way is to use the <code>IsStyleAvailable</code> function of the
<code>FontFamily</code> object. You simply create an instance of this
object with the name of the font you wish to query, then call
the method with the style to test. Note that the constructor for
<code>FontFamily</code> will throw an exception if the font you try to
create doesn't exist.</p>
<p>Switching the GetFont method above to use <code>IsStyleAvailable</code>
ends up looking like this:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> Font GetFont<span class="symbol">(</span><span class="keyword">string</span> fontFamilyName<span class="symbol">)</span>
<span class="symbol">{</span>
 Font font<span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>FontFamily family <span class="symbol">=</span> <span class="keyword">new</span> FontFamily<span class="symbol">(</span>fontFamilyName<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>family<span class="symbol">.</span>IsStyleAvailable<span class="symbol">(</span>FontStyle<span class="symbol">.</span>Regular<span class="symbol">)</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Regular<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>family<span class="symbol">.</span>IsStyleAvailable<span class="symbol">(</span>FontStyle<span class="symbol">.</span>Bold<span class="symbol">)</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Bold<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>family<span class="symbol">.</span>IsStyleAvailable<span class="symbol">(</span>FontStyle<span class="symbol">.</span>Italic<span class="symbol">)</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Italic<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>family<span class="symbol">.</span>IsStyleAvailable<span class="symbol">(</span>FontStyle<span class="symbol">.</span>Bold <span class="symbol">|</span> FontStyle<span class="symbol">.</span>Italic<span class="symbol">)</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Bold <span class="symbol">|</span> FontStyle<span class="symbol">.</span>Italic<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 font <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> font<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> Font GetFont<span class="symbol">(</span><span class="keyword">string</span> fontFamilyName<span class="symbol">,</span> FontStyle fontStyle<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">new</span> Font<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>PreviewFontSize<span class="symbol">,</span> fontStyle<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Based on this, a simple method to check if a given font name and
style exists is presented below. As the constructor for
<code>FontFamily</code> throws an <code>ArgumentException</code> if the given font
doesn't exist, we can trap that and return <code>false</code>. Any other
error will be thrown, rather than being silently ignored as in
our earlier solution.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">bool</span> DoesFontExist<span class="symbol">(</span><span class="keyword">string</span> fontFamilyName<span class="symbol">,</span> FontStyle fontStyle<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>FontFamily family <span class="symbol">=</span> <span class="keyword">new</span> FontFamily<span class="symbol">(</span>fontFamilyName<span class="symbol">)</span><span class="symbol">)</span>
 result <span class="symbol">=</span> family<span class="symbol">.</span>IsStyleAvailable<span class="symbol">(</span>fontStyle<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>ArgumentException<span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="update-history">Update History</h2>
<ul>
<li>2011-08-07 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/detecting-if-a-given-font-style-exists-in-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCSS Syntax Highlighting in the DigitalRune Text Editor Controlurn:uuid:364dffb1-7e3a-4d8c-896a-219c539503af2011-07-08T19:21:26Z2011-07-08T19:21:26Z<p>For projects where I need some form of syntax highlighting, I
tend to use the open source <a href="http://www.digitalrune.com/Products/TextEditorControl/Overview.aspx" rel="external nofollow noopener">DigitalRune Text Editor Control</a>
which is a modified version of the text editor used in
<a href="http://sharpdevelop.net/OpenSource/SD/Default.aspx" rel="external nofollow noopener">SharpDevelop</a>. While it has a number of syntax definitions
built in, the one it didn't have was for CSS formatting.</p>
<p>After doing a quick search on the internet and finding pretty
much nothing, I created my own. This article describes that
process, along with how to embed the definition directly in a
custom version of the control, or loading it into the vendor
supplied control.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/css-syntax.png" class="gallery" title="A sample application demonstrating CSS syntax highlighting" ><img src="https://images.cyotek.com/image/thumbnail/devblog/css-syntax.png" alt="A sample application demonstrating CSS syntax highlighting" decoding="async" loading="lazy" /></a><figcaption>A sample application demonstrating CSS syntax highlighting</figcaption></figure><h2 id="creating-the-rule-set">Creating the rule set</h2>
<p>Each definition is an XML document which contains various
sections describing how to syntax highlight a document. An XSD
schema is available, named <em>Mode.xsd</em> and located in the
<em>/Resources</em> directory in the control's source code.</p>
<p>Here's an example of an (almost) empty definition - I've filled
in the definition name and the list of file extensions it will
support:</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;?</span><span class="name">xml</span> <span class="name">version</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1.0</span><span class="symbol">&quot;</span> <span class="name">encoding</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">utf-8</span><span class="symbol">&quot;</span><span class="symbol">?&gt;</span>
<span class="symbol">&lt;</span><span class="name">SyntaxDefinition</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">CSS</span><span class="symbol">&quot;</span> <span class="name">extensions</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">*.css</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">RuleSets</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">RuleSets</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">SyntaxDefinition</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>The <code>RuleSets</code> element contains one of more <code>RuleSet</code> elements
which in turn describe formatting. I'm not sure how the control
decides to process these, but in my example I started with an
unnamed rule which references a named rule, and in turn that
references another - seems to work fine.</p>
<p>There are two key constructs we'll be using for highlighting -
first is <strong>span</strong> highlighting, where an block of text which
starts and ends with given symbols is highlighted. The second is
<strong>keywords</strong>, where distinct words are highlighted. From having
a quick look through the source code to figure out problems,
there appears to be one or two other constructs available, but
I'll ignore these for now.</p>
<p>First, I need to add a rule for comments, which should be quite
straight forward - look for a <code>/*</code> and end with <code>*/</code>:</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">RuleSet</span> <span class="name">ignorecase</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Span</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Comment</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Green</span><span class="symbol">&quot;</span> <span class="name">stopateol</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Begin</span><span class="symbol">&gt;</span>/*<span class="symbol">&lt;/</span><span class="name">Begin</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">End</span><span class="symbol">&gt;</span>*/<span class="symbol">&lt;/</span><span class="name">End</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Span</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">RuleSet</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>The <code>Span</code> tag creates a span highlighting construct. The
<code>Begin</code> and <code>End</code> tags describe the phrase that marks the
beginning and end of the text to match. The <code>stopateol</code>
attribute determines if the line breaks should stop at the end
of a line. The formatting properties should be evident!</p>
<p>Next, I added another span rule to process the highlighting of
the actual CSS rules - so anything between <code>{</code> and <code>}</code>.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">Span</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">CssClass</span><span class="symbol">&quot;</span> <span class="name">rule</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">CssClass</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Black</span><span class="symbol">&quot;</span> <span class="name">stopateol</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Begin</span><span class="symbol">&gt;</span>{<span class="symbol">&lt;/</span><span class="name">Begin</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">End</span><span class="symbol">&gt;</span>}<span class="symbol">&lt;/</span><span class="name">End</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">Span</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>Note this time the <code>rule</code> attribute - this is pointing to a new
ruleset (more on that below). Without this attribute, I found
that I was unable to style keywords and values inside the CSS
rule, as the span above always took precedence. The new ruleset
looks similar to this, although in this example I have stripped
out most of the CSS property names. (The list of which came from
<a href="http://www.w3schools.com/cssref/default.asp" rel="external nofollow noopener">w3schools</a>)</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">RuleSet</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">CssClass</span><span class="symbol">&quot;</span> <span class="name">ignorecase</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Span</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Value</span><span class="symbol">&quot;</span> <span class="name">rule</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">ValueRules</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Blue</span><span class="symbol">&quot;</span> <span class="name">stopateol</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Begin</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Black</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>:<span class="symbol">&lt;/</span><span class="name">Begin</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">End</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Black</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>;<span class="symbol">&lt;/</span><span class="name">End</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Span</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">KeyWords</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">CSSLevel1PropertyNames</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Red</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">background</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">background-attachment</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 (snip)
 <span class="symbol">&lt;/</span><span class="name">KeyWords</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">KeyWords</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">CSSLevel2PropertyNames</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Red</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">border-collapse</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">border-spacing</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 (snip)
 <span class="symbol">&lt;/</span><span class="name">KeyWords</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">KeyWords</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">CSSLevel3PropertyNames</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Red</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">@font-face</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">@keyframes</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 (snip)
 <span class="symbol">&lt;/</span><span class="name">KeyWords</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">RuleSet</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>First is a new span to highlight attribute values (found between
the <code>:</code> and <code>;</code> characters in blue, and then 3 sets of a new
construct - <code>KeyWords</code>. This basically matches a given word and
formats it appropriately. In this example, I have split each of
the 3 major CSS versions into separate sections, on the off
chance you want to reconfigure the file to only support a
subset, for example CSS1 and CSS2. Also note that I haven't
included any vendor prefixes.</p>
<p>One thing to note, in the <code>Value</code> span above, the begin and end
tags have <code>color</code> attributes. This overrides the overall span
color (blue) and colors those individual colors with the
override (black). Again, from checking the scheme it looks like
this can be done for most elements, and supports the <code>color</code>,
<code>bold</code> and <code>italic</code> attributes, plus a <code>bgcolor</code> attribute that
I haven't used yet.</p>
<p>The span in the above ruleset references a final ruleset, as
follows:</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">RuleSet</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">ValueRules</span><span class="symbol">&quot;</span> <span class="name">ignorecase</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Span</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Comment</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Green</span><span class="symbol">&quot;</span> <span class="name">stopateol</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Begin</span><span class="symbol">&gt;</span>/*<span class="symbol">&lt;/</span><span class="name">Begin</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">End</span><span class="symbol">&gt;</span>*/<span class="symbol">&lt;/</span><span class="name">End</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Span</span><span class="symbol">&gt;</span>
 &lt;<span class="symbol">&lt;</span><span class="name">pan</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">String</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">BlueViolet</span><span class="symbol">&quot;</span> <span class="name">stopateol</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Begin</span><span class="symbol">&gt;</span>&quot;<span class="symbol">&lt;/</span><span class="name">Begin</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">End</span><span class="symbol">&gt;</span>&quot;<span class="symbol">&lt;/</span><span class="name">End</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Span</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Span</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Char</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">BlueViolet</span><span class="symbol">&quot;</span> <span class="name">stopateol</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Begin</span><span class="symbol">&gt;</span>&#39;<span class="symbol">&lt;/</span><span class="name">Begin</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">End</span><span class="symbol">&gt;</span>&#39;<span class="symbol">&lt;/</span><span class="name">End</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">Span</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">KeyWords</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Flags</span><span class="symbol">&quot;</span> <span class="name">bold</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span> <span class="name">italic</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">BlueViolet</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Key</span> <span class="name">word</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">!important</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">KeyWords</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">RuleSet</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>This ruleset has 3 spans, and one keyword. I had to duplicate
the comment span from the first ruleset, I couldn't comment
highlighting to work inside <code>{ }</code> blocks otherwise - probably
some subtlety of the definition format that I'm missing. This is
followed by two spans which highlight strings (depending on
whether single or double quoted). Finally, we have a keyword
rule for formatting <code>!important</code>. (Of course, ideally you
wouldn't be using this keyword at all, but you never know!)</p>
<p>Put together, this definition nicely highlights CSS. Except for
one thing - everything outside a comment or style block is
black. And I want it to be something else! Initially I tried
just setting the <code>ForeColor</code> property of the control itself, but
this was blatantly ignored when it drew itself. Fortunately a
scan of the schema gave the answer - you can add an
<code>Environment</code> tag and set up a large bunch of colors. Or one, in
this case.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">Environment</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Default</span> <span class="name">color</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Maroon</span><span class="symbol">&quot;</span> <span class="name">bgcolor</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">White</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
<span class="symbol">&lt;/</span><span class="name">Environment</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>Now save the file somewhere with the <code>.xshd</code> extension - in
keeping with the convention of the existing definitions, I named
it <code>CSS-Mode.xshd</code>.</p>
<h2 id="loading-the-definition-into-the-text-editor-control">Loading the definition into the Text Editor control</h2>
<p>This is where I was a little bit stumped - as I didn't have a
clue how to get the definition in. Fortunately, DigitalRune's
technical support were able to help.</p>
<p>If you are using a custom version of the source code, you can
add the definition directly into the source and have it
available with the compiled assembly. However, if you are using
the vendor supplied assembly, you'll need to include the
definition with your application in order to load it in.</p>
<h2 id="compiling-the-definition-into-the-assembly">Compiling the definition into the assembly</h2>
<p>This is quite straight forward, and easily recommended if you
have a custom version.</p>
<ol>
<li><p>Copy the definition file into the <em>Resources</em> folder of the
control's project</p>
</li>
<li><p>Set the <strong>Build Action</strong> to be <em>Embedded Resource</em></p>
</li>
<li><p>Open <code>SyntaxModes.xml</code> located in the same folder and add a
mode tag which points to your definition, for
example</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">Mode</span> <span class="name">file</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">CSS-Mode.xshd</span><span class="symbol">&quot;</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">CSS</span><span class="symbol">&quot;</span> <span class="name">extensions</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">.css</span><span class="symbol">&quot;</span><span class="symbol">/&gt;</span>
</pre>
</figure>
<p>While I haven't checked to see if it is
enforced, common sense would suggest you ensure the <code>name</code>
and <code>extensions</code> attributes match in both the syntax
definition and the ruleset definition.</p>
</li>
<li><p>Compile the solution.</p>
</li>
</ol>
<p>With that done, your definition is now available for use!</p>
<h2 id="loading-the-definitions-externally">Loading the definitions externally</h2>
<p>You don't need to compile the definitions into the control
assembly, but can load them externally. To do this, you need to
have the definition file and the syntax mode file available for
loading.</p>
<ol>
<li><p>Add a new folder to your project and copy into this folder
your <code>.xshd</code> file and set the <strong>Copy to Output Directory</strong>
property to <em>Copy always</em>.</p>
</li>
<li><p>Create a file named <code>SyntaxMode.xml</code> in the folder, and paste
in the definition below. You'll also need to set the copy to
output directory attribute.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;?</span><span class="name">xml</span> <span class="name">version</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1.0</span><span class="symbol">&quot;</span> <span class="name">encoding</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">utf-8</span><span class="symbol">&quot;</span><span class="symbol">?&gt;</span>
<span class="symbol">&lt;</span><span class="name">SyntaxModes</span> <span class="name">version</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1.0</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Mode</span> <span class="name">file</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">CSS-Mode.xshd</span><span class="symbol">&quot;</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">CSS</span><span class="symbol">&quot;</span> <span class="name">extensions</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">.css</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
<span class="symbol">&lt;/</span><span class="name">SyntaxModes</span><span class="symbol">&gt;</span>
</pre>
</figure>
</li>
<li><p>The following line of code will load the definition file into
the text editor control:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
HighlightingManager<span class="symbol">.</span>Manager<span class="symbol">.</span>AddSyntaxModeFileProvider<span class="symbol">(</span><span class="keyword">new</span> FileSyntaxModeProvider<span class="symbol">(</span>definitionsFolder<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
</li>
</ol>
<h2 id="setting-up-the-text-editor-control">Setting up the Text Editor Control</h2>
<p>To instruct instances of the Text Editor control to use CSS
syntax highlighting, add the following line of code to your
application (replacing <code>CSS</code> with the name of your definition
if you called it something different):</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
textEditorControl<span class="symbol">.</span>Document<span class="symbol">.</span>HighlightingStrategy <span class="symbol">=</span> HighlightingManager<span class="symbol">.</span>Manager<span class="symbol">.</span>FindHighlighter<span class="symbol">(</span><span class="string">&quot;CSS&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="syntax-highlighting-isnt-appearing-what-went-wrong">Syntax highlighting isn't appearing, what went wrong?</h2>
<p>Rather frustratingly, the control doesn't raise an error if a
definition file is invalid, it just silently ignores it and uses
a default highlighting scheme. Use the source code for the
control so you can catch the exceptions being raised by the
<code>HighlightingDefinitionParser</code> class in order to determine any
problems. Remember the definition you create is implicitly
linked to the schema and so must conform to it.</p>
<h2 id="sharpdevelop">SharpDevelop?</h2>
<p>As the DigitalRune control is derived from the original
SharpDevelop editing component, I believe this article and
sample code will work in exactly the same way for the
SharpDevelop control. However, I don't have this installed and
so this remains untested - let me know if it works for you!</p>
<h2 id="sample-application">Sample application</h2>
<p>The download available below includes the CSS definition file
and a sample application which will load in the definition
files. Note that no binaries are included in the archive, you'll
need to add a reference to a copy of the DigitalRune Text Editor
control installed on your own system.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2011-07-08 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/css-syntax-highlighting-in-the-digitalrune-text-editor-control .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comEnabling shell styles for the ListView and TreeView controls in C#urn:uuid:28e73b1b-08b7-4f14-9a1f-a17f42eeccc42011-04-16T17:42:53Z2011-04-16T17:25:02Z<p>For those who remember the Common Controls OCX's featured in
Visual Basic 5 and 6, there was one peculiarity of these. In
Visual Basic 5, the Common Controls were linked directly to
their shell counterparts. As the shell was updated, so did the
look of any VB app using these. However, for Visual Basic 6,
this behaviour was changed and they didn't use the shell for
drawing.</p>
<p>Curiously enough, history repeats itself in a limited way with
Visual Studio .NET. If you use the <code>ListView</code> or <code>TreeView</code>
controls on Windows Vista or higher, you'll find they are
somewhat drawn according to the &quot;classic&quot; Windows style - no
gradients on selection highlights, column separators (ListView)
or alternate +/- glyphs (TreeView).</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/shellcontrols1.png" class="gallery" title="Examples of the default TreeView and ListView controls in Windows 7" ><img src="https://images.cyotek.com/image/thumbnail/devblog/shellcontrols1.png" alt="Examples of the default TreeView and ListView controls in Windows 7" decoding="async" loading="lazy" /></a><figcaption>Examples of the default TreeView and ListView controls in Windows 7</figcaption></figure>
<p>Fortunately however, it is quite simple to enable this with a
single call to the <code>SetWindowTheme</code> API when creating the
control.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;uxtheme.dll&quot;</span><span class="symbol">,</span> CharSet <span class="symbol">=</span> CharSet<span class="symbol">.</span>Unicode<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">extern</span> <span class="keyword">static</span> <span class="keyword">int</span> SetWindowTheme<span class="symbol">(</span>IntPtr hWnd<span class="symbol">,</span> <span class="keyword">string</span> pszSubAppName<span class="symbol">,</span> <span class="keyword">string</span> pszSubIdList<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>In the sample application (available for download from the link
below), we create two new <code>ListView</code> and <code>TreeView</code> classes
which inherit from their <code>System.Windows.Forms</code> counterparts.</p>
<p>In each class, override the <code>OnHandleCreated</code> method, and check
to see what OS is being run - if you try to call
<code>SetWindowTheme</code> on an unsupported OS, you'll get a crash. In
this case, I'm checking for Windows Vista or higher.</p>
<p>If the version is fine, call <code>SetWindowTheme</code> with the handle of
the control, and the name of the shell style - <strong>explorer</strong> in
this case.</p>
<p>It's as simple as that - now when you run the application, the
controls will be drawn using whatever shell styles are in use.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> System<span class="symbol">;</span>

<span class="keyword">namespace</span> ShellControlsExample
<span class="symbol">{</span>
 <span class="keyword">class</span> TreeView <span class="symbol">:</span> System<span class="symbol">.</span>Windows<span class="symbol">.</span>Forms<span class="symbol">.</span>TreeView
 <span class="symbol">{</span>
 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnHandleCreated<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnHandleCreated<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>DesignMode <span class="symbol">&amp;&amp;</span> Environment<span class="symbol">.</span>OSVersion<span class="symbol">.</span>Platform <span class="symbol">==</span> PlatformID<span class="symbol">.</span>Win<span class="number">32</span>NT <span class="symbol">&amp;&amp;</span> Environment<span class="symbol">.</span>OSVersion<span class="symbol">.</span>Version<span class="symbol">.</span>Major <span class="symbol">&gt;=</span> <span class="number">6</span><span class="symbol">)</span>
 NativeMethods<span class="symbol">.</span>SetWindowTheme<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> <span class="string">&quot;explorer&quot;</span><span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>For the <code>TreeView</code> control, I'd also recommend setting the
<code>ShowLines</code> property to <code>false</code> as it will look odd otherwise.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/shellcontrols2.png" class="gallery" title="Examples of the TreeView and ListView controls in Windows 7 after using the SetWindowTheme API" ><img src="https://images.cyotek.com/image/thumbnail/devblog/shellcontrols2.png" alt="Examples of the TreeView and ListView controls in Windows 7 after using the SetWindowTheme API" decoding="async" loading="lazy" /></a><figcaption>Examples of the TreeView and ListView controls in Windows 7 after using the SetWindowTheme API</figcaption></figure><h2 id="update-history">Update History</h2>
<ul>
<li>2011-04-16 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/enabling-shell-styles-for-the-listview-and-treeview-controls-in-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a WYSIWYG font ComboBox using C#urn:uuid:33ca6c15-e6c6-46e9-a7bb-3dfb282339b82011-02-19T20:05:31Z2011-02-19T20:05:31Z<p>This article shows how to use the built in ownerdraw
functionality of a standard Windows Forms ComboBox control to
display a WYSIWYG font list.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/font-combobox.png" class="gallery" title="The FontComboBox control in a sample application" ><img src="https://images.cyotek.com/image/thumbnail/devblog/font-combobox.png" alt="The FontComboBox control in a sample application" decoding="async" loading="lazy" /></a><figcaption>The FontComboBox control in a sample application</figcaption></figure><h2 id="setting-up-the-control">Setting up the control</h2>
<p>To start, we'll create a new class, and inherit this from the
<code>ComboBox</code> control.</p>
<p>We are going to use variable ownerdraw for this sample, as it
gives us a little more flexibility without having to mess around
with the <code>ItemHeight</code> property. We'll add a constructor, and set
the ownerdraw mode here. Also, we'll add a new version of the
<code>DrawMode</code> property, which we'll both hide and disable the value
persistence. We always want the font list to be sorted, so for
now we'll do the same with the <code>Sorted</code> property.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> FontComboBox<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>DrawMode <span class="symbol">=</span> DrawMode<span class="symbol">.</span>OwnerDrawVariable<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Sorted <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">,</span> DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">,</span> EditorBrowsable<span class="symbol">(</span>EditorBrowsableState<span class="symbol">.</span>Never<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">new</span> DrawMode DrawMode
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">base</span><span class="symbol">.</span>DrawMode<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>DrawMode <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">,</span> DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">,</span> EditorBrowsable<span class="symbol">(</span>EditorBrowsableState<span class="symbol">.</span>Never<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">new</span> <span class="keyword">bool</span> Sorted
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">base</span><span class="symbol">.</span>Sorted<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>Sorted <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="caching-font-objects">Caching Font objects</h2>
<p>In order to avoid continuously creating and destroying font
objects, we'll create a internal cache of fonts. When it's time
to draw the control, the cache will be queried - if the
requested font exists, it will be returned, otherwise the font
will be created and added to the cache. This will be done via
the <code>GetFont</code> method below.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> Font GetFont<span class="symbol">(</span><span class="keyword">string</span> fontFamilyName<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">lock</span> <span class="symbol">(</span>_fontCache<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>_fontCache<span class="symbol">.</span>ContainsKey<span class="symbol">(</span>fontFamilyName<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Font font<span class="symbol">;</span>

 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Regular<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>font <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Bold<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>font <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Italic<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>font <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Bold <span class="symbol">|</span> FontStyle<span class="symbol">.</span>Italic<span class="symbol">)</span><span class="symbol">;</span>

 _fontCache<span class="symbol">.</span>Add<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> font<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> _fontCache<span class="symbol">[</span>fontFamilyName<span class="symbol">]</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> Font GetFont<span class="symbol">(</span><span class="keyword">string</span> fontFamilyName<span class="symbol">,</span> FontStyle fontStyle<span class="symbol">)</span>
<span class="symbol">{</span>
 Font font<span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 font <span class="symbol">=</span> <span class="keyword">new</span> Font<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>PreviewFontSize<span class="symbol">,</span> fontStyle<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span>
 <span class="symbol">{</span>
 font <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> font<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>Note: Whilst testing the control, I discovered that some of
the fonts installed on the development system only had bold or
italic styles. The original version of this method, which
always attempts to get the normal style would cause a crash.</p>
</blockquote>
<p>Due to this, I changed the method to try and access the normal
style, and if that failed, to try the other styles. Perhaps
there is a better way of doing this, but I leave that as an
exercise for the future.</p>
<p>As we don't want the font size of the dropdown list to
necessarily match that of the display/edit portion, we'll add a
new property named `PreviewFontSize*.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">event</span> EventHandler PreviewFontSizeChanged<span class="symbol">;</span>

<span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Appearance&quot;</span><span class="symbol">)</span><span class="symbol">,</span> DefaultValue<span class="symbol">(</span><span class="number">12</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">int</span> PreviewFontSize
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _previewFontSize<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 _previewFontSize <span class="symbol">=</span> value<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>OnPreviewFontSizeChanged<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> OnPreviewFontSizeChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>PreviewFontSizeChanged <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 PreviewFontSizeChanged<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">,</span> e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CalculateLayout<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>When certain actions occur, such as this property changing, we
want to calculate the height of items in the dropdown list.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> CalculateLayout<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ClearFontCache<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Font font <span class="symbol">=</span> <span class="keyword">new</span> Font<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Font<span class="symbol">.</span>FontFamily<span class="symbol">,</span> <span class="symbol">(</span><span class="keyword">float</span><span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>PreviewFontSize<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Size textSize<span class="symbol">;</span>

 textSize <span class="symbol">=</span> TextRenderer<span class="symbol">.</span>MeasureText<span class="symbol">(</span><span class="string">&quot;yY&quot;</span><span class="symbol">,</span> font<span class="symbol">)</span><span class="symbol">;</span>
 _itemHeight <span class="symbol">=</span> textSize<span class="symbol">.</span>Height <span class="symbol">+</span> <span class="number">2</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="loading-the-list-of-font-families">Loading the list of font families</h2>
<p>In order to avoid slowing the control down without reason, we'll
delay loading the list of font families until there is a reason,
either when the control's text has changed, or when the control
gets focus.</p>
<p>This will be done by creating a <code>LoadFontFamilies</code> method which
will be called by overriding <code>OnGotFocus</code> and <code>OnTextChanged</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">virtual</span> <span class="keyword">void</span> LoadFontFamilies<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count <span class="symbol">==</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Cursor<span class="symbol">.</span>Current <span class="symbol">=</span> Cursors<span class="symbol">.</span>WaitCursor<span class="symbol">;</span>

 <span class="keyword">foreach</span> <span class="symbol">(</span>FontFamily fontFamily <span class="keyword">in</span> FontFamily<span class="symbol">.</span>Families<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Add<span class="symbol">(</span>fontFamily<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">;</span>

 Cursor<span class="symbol">.</span>Current <span class="symbol">=</span> Cursors<span class="symbol">.</span>Default<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnGotFocus<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>LoadFontFamilies<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnGotFocus<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnTextChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnTextChanged<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count <span class="symbol">==</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> selectedIndex<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>LoadFontFamilies<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 selectedIndex <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>FindStringExact<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Text<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>selectedIndex <span class="symbol">!=</span> <span class="symbol">-</span><span class="number">1</span><span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>SelectedIndex <span class="symbol">=</span> selectedIndex<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="drawing-the-items">Drawing the items</h2>
<p>Drawing an overdraw ComboBox is done by overriding the
<code>OnDrawItem</code> method. However, as we have told the control we are
doing variable sized ownerdraw, we also need to override
<code>OnMeasureItem</code>. This method allows us to define the size for
each item, or in the case of this control to set the height of
each item to match the pixel height calculated for the value of
the <code>PreviewFontSize</code> property.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMeasureItem<span class="symbol">(</span>MeasureItemEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnMeasureItem<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Index <span class="symbol">&gt;</span> <span class="symbol">-</span><span class="number">1</span> <span class="symbol">&amp;&amp;</span> e<span class="symbol">.</span>Index <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count<span class="symbol">)</span>
 <span class="symbol">{</span>
 e<span class="symbol">.</span>ItemHeight <span class="symbol">=</span> _itemHeight<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnDrawItem<span class="symbol">(</span>DrawItemEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnDrawItem<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Index <span class="symbol">&gt;</span> <span class="symbol">-</span><span class="number">1</span> <span class="symbol">&amp;&amp;</span> e<span class="symbol">.</span>Index <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count<span class="symbol">)</span>
 <span class="symbol">{</span>
 e<span class="symbol">.</span>DrawBackground<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">(</span>e<span class="symbol">.</span>State <span class="symbol">&amp;</span> DrawItemState<span class="symbol">.</span>Focus<span class="symbol">)</span> <span class="symbol">==</span> DrawItemState<span class="symbol">.</span>Focus<span class="symbol">)</span>
 e<span class="symbol">.</span>DrawFocusRectangle<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>SolidBrush textBrush <span class="symbol">=</span> <span class="keyword">new</span> SolidBrush<span class="symbol">(</span>e<span class="symbol">.</span>ForeColor<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> fontFamilyName<span class="symbol">;</span>

 fontFamilyName <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">[</span>e<span class="symbol">.</span>Index<span class="symbol">]</span><span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>DrawString<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">)</span><span class="symbol">,</span> textBrush<span class="symbol">,</span> e<span class="symbol">.</span>Bounds<span class="symbol">,</span> _stringFormat<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The actual drawing is very simple - we use the built in drawing
for the background and focus rectangle, and then use the
<code>Graphics</code> object to draw the text using the <code>GetFont</code> method
explained above.</p>
<p>You might notice that the above code is referencing a previously
defined <code>StringFormat</code> object. This is created using the below
method.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> CreateStringFormat<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_stringFormat <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 _stringFormat<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 _stringFormat <span class="symbol">=</span> <span class="keyword">new</span> StringFormat<span class="symbol">(</span>StringFormatFlags<span class="symbol">.</span>NoWrap<span class="symbol">)</span><span class="symbol">;</span>
 _stringFormat<span class="symbol">.</span>Trimming <span class="symbol">=</span> StringTrimming<span class="symbol">.</span>EllipsisCharacter<span class="symbol">;</span>
 _stringFormat<span class="symbol">.</span>HotkeyPrefix <span class="symbol">=</span> HotkeyPrefix<span class="symbol">.</span>None<span class="symbol">;</span>
 _stringFormat<span class="symbol">.</span>Alignment <span class="symbol">=</span> StringAlignment<span class="symbol">.</span>Near<span class="symbol">;</span>
 _stringFormat<span class="symbol">.</span>LineAlignment <span class="symbol">=</span> StringAlignment<span class="symbol">.</span>Center<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsUsingRTL<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">)</span><span class="symbol">)</span>
 _stringFormat<span class="symbol">.</span>FormatFlags <span class="symbol">|=</span> StringFormatFlags<span class="symbol">.</span>DirectionRightToLeft<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">bool</span> IsUsingRTL<span class="symbol">(</span>Control control<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>control<span class="symbol">.</span>RightToLeft <span class="symbol">==</span> RightToLeft<span class="symbol">.</span>Yes<span class="symbol">)</span>
 result <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>control<span class="symbol">.</span>RightToLeft <span class="symbol">==</span> RightToLeft<span class="symbol">.</span>Inherit <span class="symbol">&amp;&amp;</span> control<span class="symbol">.</span>Parent <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 result <span class="symbol">=</span> IsUsingRTL<span class="symbol">(</span>control<span class="symbol">.</span>Parent<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="cleaning-up">Cleaning up</h2>
<p>As we are creating a large number of objects, we need to clean
these up in the controls <code>Dispose</code> method.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> Dispose<span class="symbol">(</span><span class="keyword">bool</span> disposing<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ClearFontCache<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_stringFormat <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 _stringFormat<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>Dispose<span class="symbol">(</span>disposing<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> ClearFontCache<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_fontCache <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span><span class="keyword">string</span> key <span class="keyword">in</span> _fontCache<span class="symbol">.</span>Keys<span class="symbol">)</span>
 _fontCache<span class="symbol">[</span>key<span class="symbol">]</span><span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 _fontCache<span class="symbol">.</span>Clear<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="suggestions-for-improvement">Suggestions for improvement</h3>
<p>The control as it stands is a basic example, and depending on
your application's needs, it could be further expanded. For
example:</p>
<ul>
<li>Currently each instance of the control will use its own font
cache. By making the cache and access methods static, a single
cache could be used by all instances</li>
<li>When you select a font in Word, this is added to a kind of
&quot;recently used&quot; list at the top of Word's own font picker. The
same sort of functionality could be quite easily added to this
control.</li>
<li>Currently the font text is displayed on a single line. If the
control isn't wide enough, the text is trimmed and therefore
it may not be always possible to tell the full name of a font.
Either tooltip support or drawing across multiple lines could
help with this, or by resizing the dropdown component to be
the minimum width required to display all font names without
trimming.</li>
</ul>
<h3 id="full-source">Full source</h3>
<p>The full source of the class is below.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> System<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Collections<span class="symbol">.</span>Generic<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>ComponentModel<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Drawing<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Drawing<span class="symbol">.</span>Text<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Windows<span class="symbol">.</span>Forms<span class="symbol">;</span>

<span class="keyword">namespace</span> Cyotek<span class="symbol">.</span>Windows<span class="symbol">.</span>Forms
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">class</span> FontComboBox <span class="symbol">:</span> ComboBox
 <span class="symbol">{</span>
 <span class="keyword">#region</span>&#160;&#160;Private&#160;Member&#160;Declarations&#160;&#160;

 <span class="keyword">private</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> Font<span class="symbol">&gt;</span> _fontCache<span class="symbol">;</span>
 <span class="keyword">private</span> <span class="keyword">int</span> _itemHeight<span class="symbol">;</span>
 <span class="keyword">private</span> <span class="keyword">int</span> _previewFontSize<span class="symbol">;</span>
 <span class="keyword">private</span> StringFormat _stringFormat<span class="symbol">;</span>

 <span class="keyword">public</span> FontComboBox<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _fontCache <span class="symbol">=</span> <span class="keyword">new</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> Font<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>DrawMode <span class="symbol">=</span> DrawMode<span class="symbol">.</span>OwnerDrawVariable<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Sorted <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>PreviewFontSize <span class="symbol">=</span> <span class="number">12</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CalculateLayout<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>CreateStringFormat<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">event</span> EventHandler PreviewFontSizeChanged<span class="symbol">;</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> Dispose<span class="symbol">(</span><span class="keyword">bool</span> disposing<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ClearFontCache<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_stringFormat <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 _stringFormat<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>Dispose<span class="symbol">(</span>disposing<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnDrawItem<span class="symbol">(</span>DrawItemEventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnDrawItem<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Index <span class="symbol">&gt;</span> <span class="symbol">-</span><span class="number">1</span> <span class="symbol">&amp;&amp;</span> e<span class="symbol">.</span>Index <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count<span class="symbol">)</span>
 <span class="symbol">{</span>
 e<span class="symbol">.</span>DrawBackground<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">(</span>e<span class="symbol">.</span>State <span class="symbol">&amp;</span> DrawItemState<span class="symbol">.</span>Focus<span class="symbol">)</span> <span class="symbol">==</span> DrawItemState<span class="symbol">.</span>Focus<span class="symbol">)</span>
 e<span class="symbol">.</span>DrawFocusRectangle<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>SolidBrush textBrush <span class="symbol">=</span> <span class="keyword">new</span> SolidBrush<span class="symbol">(</span>e<span class="symbol">.</span>ForeColor<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> fontFamilyName<span class="symbol">;</span>

 fontFamilyName <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">[</span>e<span class="symbol">.</span>Index<span class="symbol">]</span><span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>DrawString<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">)</span><span class="symbol">,</span> textBrush<span class="symbol">,</span> e<span class="symbol">.</span>Bounds<span class="symbol">,</span> _stringFormat<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnFontChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnFontChanged<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CalculateLayout<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnGotFocus<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>LoadFontFamilies<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnGotFocus<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMeasureItem<span class="symbol">(</span>MeasureItemEventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnMeasureItem<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Index <span class="symbol">&gt;</span> <span class="symbol">-</span><span class="number">1</span> <span class="symbol">&amp;&amp;</span> e<span class="symbol">.</span>Index <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count<span class="symbol">)</span>
 <span class="symbol">{</span>
 e<span class="symbol">.</span>ItemHeight <span class="symbol">=</span> _itemHeight<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnRightToLeftChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnRightToLeftChanged<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CreateStringFormat<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnTextChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnTextChanged<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count <span class="symbol">==</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> selectedIndex<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>LoadFontFamilies<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 selectedIndex <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>FindStringExact<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Text<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>selectedIndex <span class="symbol">!=</span> <span class="symbol">-</span><span class="number">1</span><span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>SelectedIndex <span class="symbol">=</span> selectedIndex<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">virtual</span> <span class="keyword">void</span> LoadFontFamilies<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count <span class="symbol">==</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Cursor<span class="symbol">.</span>Current <span class="symbol">=</span> Cursors<span class="symbol">.</span>WaitCursor<span class="symbol">;</span>

 <span class="keyword">foreach</span> <span class="symbol">(</span>FontFamily fontFamily <span class="keyword">in</span> FontFamily<span class="symbol">.</span>Families<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Add<span class="symbol">(</span>fontFamily<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">;</span>

 Cursor<span class="symbol">.</span>Current <span class="symbol">=</span> Cursors<span class="symbol">.</span>Default<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">,</span> DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">,</span> EditorBrowsable<span class="symbol">(</span>EditorBrowsableState<span class="symbol">.</span>Never<span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">new</span> DrawMode DrawMode
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">base</span><span class="symbol">.</span>DrawMode<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>DrawMode <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Appearance&quot;</span><span class="symbol">)</span><span class="symbol">,</span> DefaultValue<span class="symbol">(</span><span class="number">12</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">int</span> PreviewFontSize
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _previewFontSize<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 _previewFontSize <span class="symbol">=</span> value<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>OnPreviewFontSizeChanged<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">,</span> DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">,</span> EditorBrowsable<span class="symbol">(</span>EditorBrowsableState<span class="symbol">.</span>Never<span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">new</span> <span class="keyword">bool</span> Sorted
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">base</span><span class="symbol">.</span>Sorted<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>Sorted <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">void</span> CalculateLayout<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ClearFontCache<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Font font <span class="symbol">=</span> <span class="keyword">new</span> Font<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Font<span class="symbol">.</span>FontFamily<span class="symbol">,</span> <span class="symbol">(</span><span class="keyword">float</span><span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>PreviewFontSize<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Size textSize<span class="symbol">;</span>

 textSize <span class="symbol">=</span> TextRenderer<span class="symbol">.</span>MeasureText<span class="symbol">(</span><span class="string">&quot;yY&quot;</span><span class="symbol">,</span> font<span class="symbol">)</span><span class="symbol">;</span>
 _itemHeight <span class="symbol">=</span> textSize<span class="symbol">.</span>Height <span class="symbol">+</span> <span class="number">2</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">bool</span> IsUsingRTL<span class="symbol">(</span>Control control<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>control<span class="symbol">.</span>RightToLeft <span class="symbol">==</span> RightToLeft<span class="symbol">.</span>Yes<span class="symbol">)</span>
 result <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>control<span class="symbol">.</span>RightToLeft <span class="symbol">==</span> RightToLeft<span class="symbol">.</span>Inherit <span class="symbol">&amp;&amp;</span> control<span class="symbol">.</span>Parent <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 result <span class="symbol">=</span> IsUsingRTL<span class="symbol">(</span>control<span class="symbol">.</span>Parent<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> ClearFontCache<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_fontCache <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span><span class="keyword">string</span> key <span class="keyword">in</span> _fontCache<span class="symbol">.</span>Keys<span class="symbol">)</span>
 _fontCache<span class="symbol">[</span>key<span class="symbol">]</span><span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 _fontCache<span class="symbol">.</span>Clear<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> CreateStringFormat<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_stringFormat <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 _stringFormat<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 _stringFormat <span class="symbol">=</span> <span class="keyword">new</span> StringFormat<span class="symbol">(</span>StringFormatFlags<span class="symbol">.</span>NoWrap<span class="symbol">)</span><span class="symbol">;</span>
 _stringFormat<span class="symbol">.</span>Trimming <span class="symbol">=</span> StringTrimming<span class="symbol">.</span>EllipsisCharacter<span class="symbol">;</span>
 _stringFormat<span class="symbol">.</span>HotkeyPrefix <span class="symbol">=</span> HotkeyPrefix<span class="symbol">.</span>None<span class="symbol">;</span>
 _stringFormat<span class="symbol">.</span>Alignment <span class="symbol">=</span> StringAlignment<span class="symbol">.</span>Near<span class="symbol">;</span>
 _stringFormat<span class="symbol">.</span>LineAlignment <span class="symbol">=</span> StringAlignment<span class="symbol">.</span>Center<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsUsingRTL<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">)</span><span class="symbol">)</span>
 _stringFormat<span class="symbol">.</span>FormatFlags <span class="symbol">|=</span> StringFormatFlags<span class="symbol">.</span>DirectionRightToLeft<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">virtual</span> Font GetFont<span class="symbol">(</span><span class="keyword">string</span> fontFamilyName<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">lock</span> <span class="symbol">(</span>_fontCache<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>_fontCache<span class="symbol">.</span>ContainsKey<span class="symbol">(</span>fontFamilyName<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Font font<span class="symbol">;</span>

 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Regular<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>font <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Bold<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>font <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Italic<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>font <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Bold <span class="symbol">|</span> FontStyle<span class="symbol">.</span>Italic<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>font <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="symbol">(</span>Font<span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>Font<span class="symbol">.</span>Clone<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 _fontCache<span class="symbol">.</span>Add<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> font<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> _fontCache<span class="symbol">[</span>fontFamilyName<span class="symbol">]</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">virtual</span> Font GetFont<span class="symbol">(</span><span class="keyword">string</span> fontFamilyName<span class="symbol">,</span> FontStyle fontStyle<span class="symbol">)</span>
 <span class="symbol">{</span>
 Font font<span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 font <span class="symbol">=</span> <span class="keyword">new</span> Font<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>PreviewFontSize<span class="symbol">,</span> fontStyle<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span>
 <span class="symbol">{</span>
 font <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> font<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> OnPreviewFontSizeChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>PreviewFontSizeChanged <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 PreviewFontSizeChanged<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">,</span> e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CalculateLayout<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="update-history">Update History</h2>
<ul>
<li>2011-02-19 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-a-wysiwyg-font-combobox-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comMVC actions, AcceptVerbs, HEAD requests and 404 errorsurn:uuid:fc0cd46d-8c49-4679-b632-1b36c92e59d02012-01-24T16:53:18Z2010-11-20T21:04:32Z<p>When running Sitemap Creator on the development version of
cyotek.com, we found all links pointing to articles returned a
404 status code when crawling was attempted. But if same URL was
copied into a browser, it would load correctly.</p>
<p>This surprised us, as cyotek.com is the main site we test
Sitemap Creator and WebCopy on and they've always worked in the
past. Next, we tried it directly on cyotek.com, and got the same
result. However, this being the release version of the web, we
started receiving error emails from the website (these are not
sent from the debug builds).</p>
<p>The exception being reported was this:</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
System.Web.HttpException: A public action method &#39;display&#39; could not be found on controller &#39;Cyotek.Web.Controllers.ArticleController&#39;.
 at System.Web.Mvc.Controller.HandleUnknownAction(String actionName)
 at System.Web.Mvc.Controller.ExecuteCore()
 at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
 at System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext)
 at System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext)
 at System.Web.Mvc.MvcHandler.ProcessRequest(HttpContext httpContext)
 at System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext httpContext)
 at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
 at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean&amp; completedSynchronously)
</pre>
</figure>
<p>This error message certainly raised eyebrows, as of course, this
action does exist.</p>
<p>This is the current definition of the display article action:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>OutputCache<span class="symbol">(</span>CacheProfile <span class="symbol">=</span> <span class="string">&quot;Short&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="symbol">[</span>AcceptVerbs<span class="symbol">(</span>HttpVerbs<span class="symbol">.</span>Get<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> ActionResult Display<span class="symbol">(</span><span class="keyword">string</span> id<span class="symbol">,</span> <span class="keyword">bool</span><span class="symbol">?</span> posted<span class="symbol">)</span>
<span class="symbol">{</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>As soon as we looked at the code, we realised what had happened.
By default both Sitemap Creator and WebCopy make <code>HEAD</code> requests
to obtain the headers for a given URL, such as the content type.
They use these headers to determine if they should go ahead and
download the entire file - Sitemap Creator won't download
anything that isn't <code>text/html</code> for example.</p>
<p>And this is the problem - in the last update to cyotek.com, we
changed a few site settings to stop the number of error emails
occurring due to spammer activity. For some reason the
<code>AcceptVerbs</code> attribute was applied to the <code>Display</code> action method
at this point. And as it is only set to accept <code>GET</code>, it means our
<code>HEAD</code> calls automatically fail.</p>
<p>One changing the attribute, everything started working nicely
again.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>AcceptVerbs<span class="symbol">(</span>HttpVerbs<span class="symbol">.</span>Get <span class="symbol">|</span> HttpVerbs<span class="symbol">.</span>Head<span class="symbol">)</span><span class="symbol">]</span>
</pre>
</figure>
<p>For once, a nice and simple mystery to solve, and a nice little
tip which will hopefully help anyone else who has a similar
issue.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-11-20 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/mvc-actions-acceptverbs-head-requests-and-404-errors .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comUsing the XmlReader class with C#urn:uuid:5221783c-d98d-4451-9fc5-acf0e0e7674b2013-11-05T18:41:01Z2010-11-05T23:20:03Z<p>Some of the project files created by Cyotek Sitemap Creator and
WebCopy are fairly large and the load performance of such files
is poor. The files are saved using a <code>XmlWriter</code> class which is
nice and fast. When reading the files back however, currently
the whole file is loaded into a <code>XmlDocument</code> and then XPath
expressions are used to pull out the values. This article
describes our effort at converting the load code to use a
<code>XmlReader</code> instead.</p>
<h2 id="sample-xml">Sample XML</h2>
<p>The following XML snippet can be used as a base for testing the
code in this article, if required.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;?</span><span class="name">xml</span> <span class="name">version</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1.0</span><span class="symbol">&quot;</span> <span class="name">encoding</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">utf-8</span><span class="symbol">&quot;</span> <span class="name">standalone</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">yes</span><span class="symbol">&quot;</span><span class="symbol">?&gt;</span>
<span class="symbol">&lt;</span><span class="name">cyotek.webcopy.project</span> <span class="name">version</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1.0.0.0</span><span class="symbol">&quot;</span> <span class="name">generator</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Cyotek WebCopy 1.0.0.2 (BETA))&quot; edition=&quot;</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">uri</span> <span class="name">lastCrawled</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">-8589156546443756722</span><span class="symbol">&quot;</span> <span class="name">includeSubDomains</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>http://saturn/cyotekdev/<span class="symbol">&lt;/</span><span class="name">uri</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">additionalUri</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">uri</span><span class="symbol">&gt;</span>first url<span class="symbol">&lt;/</span><span class="name">uri</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">uri</span><span class="symbol">&gt;</span>second url<span class="symbol">&lt;/</span><span class="name">uri</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">additionalUri</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">authentication</span> <span class="name">doNotAskForPasswords</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">credential</span> <span class="name">uri</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">/</span><span class="symbol">&quot;</span> <span class="name">userName</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">username</span><span class="symbol">&quot;</span> <span class="name">password</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">password</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">authentication</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">saveFolder</span> <span class="name">path</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">C:\Downloaded Web Sites</span><span class="symbol">&quot;</span> <span class="name">emptyBeforeCrawl</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span> <span class="name">createFolderForDomain</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span> <span class="name">flattenWebsiteDirectories</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">remapExtensions</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">crawler</span> <span class="name">removeFragments</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span> <span class="name">followRedirects</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span> <span class="name">disableUriRemapping</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">slashedRootRemapMode</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1</span><span class="symbol">&quot;</span> <span class="name">sort</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="name">acceptDeflate</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span> <span class="name">acceptGZip</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span> <span class="name">bufferSize</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">0</span><span class="symbol">&quot;</span> <span class="name">crawlAboveRoot</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">defaultDocuments</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">linkInfo</span> <span class="name">save</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span> <span class="name">clearBeforeCrawl</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">stripQueryString</span><span class="symbol">&gt;</span>false<span class="symbol">&lt;/</span><span class="name">stripQueryString</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">useHeaderChecking</span><span class="symbol">&gt;</span>true<span class="symbol">&lt;/</span><span class="name">useHeaderChecking</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">userAgent</span> <span class="name">useDefault</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span><span class="symbol">&lt;/</span><span class="name">userAgent</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">rules</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">rule</span> <span class="name">options</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1</span><span class="symbol">&quot;</span> <span class="name">enabled</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>trackback\?id=<span class="symbol">&lt;/</span><span class="name">rule</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">rule</span> <span class="name">options</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1</span><span class="symbol">&quot;</span> <span class="name">enabled</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>/downloads/get<span class="symbol">&lt;/</span><span class="name">rule</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">rule</span> <span class="name">options</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1</span><span class="symbol">&quot;</span> <span class="name">enabled</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>/article<span class="symbol">&lt;/</span><span class="name">rule</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">rule</span> <span class="name">options</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1</span><span class="symbol">&quot;</span> <span class="name">enabled</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>/sitemap<span class="symbol">&lt;/</span><span class="name">rule</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">rule</span> <span class="name">options</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1</span><span class="symbol">&quot;</span> <span class="name">enabled</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>image/get/<span class="symbol">&lt;/</span><span class="name">rule</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">rule</span> <span class="name">options</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1</span><span class="symbol">&quot;</span> <span class="name">enabled</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>products<span class="symbol">&lt;/</span><span class="name">rule</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">rule</span> <span class="name">options</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1</span><span class="symbol">&quot;</span> <span class="name">enabled</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">false</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>zipviewer<span class="symbol">&lt;/</span><span class="name">rule</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">rules</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">domainAliases</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">alias</span><span class="symbol">&gt;</span>(?:http(?:s?):\/\/)?saturn/cyotekdev/<span class="symbol">&lt;/</span><span class="name">alias</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">domainAliases</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">forms</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">page</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">&quot; uri=&quot;login</span><span class="symbol">&quot;</span> <span class="name">enabled</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span> <span class="name">method</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">POST</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">parameters</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">parameter</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">rememberMe</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>true<span class="symbol">&lt;/</span><span class="name">parameter</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">parameter</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">username</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>username<span class="symbol">&lt;/</span><span class="name">parameter</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">parameter</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">password</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>password<span class="symbol">&lt;/</span><span class="name">parameter</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">parameters</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">page</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">forms</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">linkMap</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">link</span> <span class="name">id</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">b1b85626f9984279b5e033c30a0a3f65</span><span class="symbol">&quot;</span> <span class="name">uri</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">&quot; source=&quot;1</span><span class="symbol">&quot;</span> <span class="name">contentType</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">text/html</span><span class="symbol">&quot;</span> <span class="name">httpStatus</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">200</span><span class="symbol">&quot;</span> <span class="name">lastDownloaded</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">-8589156550177150260</span><span class="symbol">&quot;</span> <span class="name">hash</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">0333961593BD555C49ABF2355140225A07DA9297</span><span class="symbol">&quot;</span> <span class="name">fileName</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">index.htm</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">title</span><span class="symbol">&gt;</span>Cyotek<span class="symbol">&lt;/</span><span class="name">title</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">incomingLinks</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">link</span> <span class="name">id</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">b1b85626f9984279b5e033c30a0a3f65</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">incomingLinks</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">outgoingLinks</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">link</span> <span class="name">id</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">96a358d21135449eb6561f25399e24de</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">outgoingLinks</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">headers</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">header</span> <span class="name">key</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Content-Encoding</span><span class="symbol">&quot;</span> <span class="name">value</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">gzip</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">header</span> <span class="name">key</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Vary</span><span class="symbol">&quot;</span> <span class="name">value</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Accept-Encoding</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">header</span> <span class="name">key</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">X-AspNetMvc-Version</span><span class="symbol">&quot;</span> <span class="name">value</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1.0</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">header</span> <span class="name">key</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Content-Length</span><span class="symbol">&quot;</span> <span class="name">value</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">3415</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">header</span> <span class="name">key</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Cache-Control</span><span class="symbol">&quot;</span> <span class="name">value</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">private</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">header</span> <span class="name">key</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Content-Type</span><span class="symbol">&quot;</span> <span class="name">value</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">text/html; charset=utf-8</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">header</span> <span class="name">key</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Date</span><span class="symbol">&quot;</span> <span class="name">value</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Fri, 01 Oct 2010 16:51:07 GMT</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">header</span> <span class="name">key</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Expires</span><span class="symbol">&quot;</span> <span class="name">value</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Fri, 01 Oct 2010 16:51:07 GMT</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">header</span> <span class="name">key</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">ETag&quot; value=&quot;</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">header</span> <span class="name">key</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Server</span><span class="symbol">&quot;</span> <span class="name">value</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Microsoft-IIS/7.5</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">header</span> <span class="name">key</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">X-Powered-By</span><span class="symbol">&quot;</span> <span class="name">value</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">UrlRewriter.NET 2.0.0</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">headers</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">link</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">linkMap</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">cyotek.webcopy.project</span><span class="symbol">&gt;</span>
</pre>
</figure>
<h2 id="writing-xml-using-a-xmlwriter">Writing XML using a XmlWriter</h2>
<p>Before I start discussing how to load the data, here is a quick
overview of how it is originally saved. For clarity I'm only
showing the bare bones of the method.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">string</span> workFile<span class="symbol">;</span>

workFile <span class="symbol">=</span> Path<span class="symbol">.</span>GetTempFileName<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">using</span> <span class="symbol">(</span>FileStream stream <span class="symbol">=</span> File<span class="symbol">.</span>Create<span class="symbol">(</span>workFile<span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 XmlWriterSettings settings<span class="symbol">;</span>

 settings <span class="symbol">=</span> <span class="keyword">new</span> XmlWriterSettings <span class="symbol">{</span> Indent <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">,</span> Encoding <span class="symbol">=</span> Encoding<span class="symbol">.</span>UTF<span class="number">8</span> <span class="symbol">}</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>XmlWriter writer <span class="symbol">=</span> XmlWriter<span class="symbol">.</span>Create<span class="symbol">(</span>stream<span class="symbol">,</span> settings<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 writer<span class="symbol">.</span>WriteStartDocument<span class="symbol">(</span><span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>

 writer<span class="symbol">.</span>WriteStartElement<span class="symbol">(</span><span class="string">&quot;uri&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>LastCrawled<span class="symbol">.</span>HasValue<span class="symbol">)</span>
 writer<span class="symbol">.</span>WriteAttributeString<span class="symbol">(</span><span class="string">&quot;lastCrawled&quot;</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>LastCrawled<span class="symbol">.</span>Value<span class="symbol">.</span>ToBinary<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 writer<span class="symbol">.</span>WriteAttributeString<span class="symbol">(</span><span class="string">&quot;includeSubDomains&quot;</span><span class="symbol">,</span> _includeSubDomains<span class="symbol">)</span><span class="symbol">;</span>
 writer<span class="symbol">.</span>WriteValue<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Uri<span class="symbol">)</span><span class="symbol">;</span>
 writer<span class="symbol">.</span>WriteEndElement<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 writer<span class="symbol">.</span>WriteEndDocument<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

File<span class="symbol">.</span>Copy<span class="symbol">(</span>workFile<span class="symbol">,</span> fileName<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>
File<span class="symbol">.</span>Delete<span class="symbol">(</span>workFile<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>The above code creates a new temporary file and opens this into
a <code>FileSteam</code>. A <code>XmlSettings</code> object is created to specify
some options (by default it won't indent, making the output
files difficult to read if you open then in a text editor), and
then a <code>XmlWriter</code> is created from both the settings and
stream.</p>
<p>Once you have a writer, you can quickly save data in compliant
format, with the caveat that you must ensure that your
WriteStarts have a corresponding WriteEnd, that you only have a
single document element, and so on.</p>
<p>Assuming the writer gets to the end without any errors, the
stream is closed, then temporary file is copied to the final
destination before being deleted. (This is a good tip in its own
right, as this means you won't destroy the user's existing if an
error occurs, which you would if you directly wrote to the
destination file.)</p>
<h2 id="reading-xml-using-a-xmldocument">Reading XML using a XmlDocument</h2>
<p>As discussed above, currently we use a <code>XmlDocument</code> to load
data. The following snippet shows an example of this.</p>
<p>Note that the code below won't work &quot;out of the box&quot; as we use a
number extension methods to handle data type conversion, which
makes the code a lot more readable!</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
document <span class="symbol">=</span> <span class="keyword">new</span> XmlDocument<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
document<span class="symbol">.</span>Load<span class="symbol">(</span>fileName<span class="symbol">)</span><span class="symbol">;</span>

_uri <span class="symbol">=</span> documentElement<span class="symbol">.</span>SelectSingleNode<span class="symbol">(</span><span class="string">&quot;uri&quot;</span><span class="symbol">)</span><span class="symbol">.</span>AsString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
_lastCrawled <span class="symbol">=</span> documentElement<span class="symbol">.</span>SelectSingleNode<span class="symbol">(</span><span class="string">&quot;uri/@lastCrawled&quot;</span><span class="symbol">)</span><span class="symbol">.</span>AsDate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
_includeSubDomains <span class="symbol">=</span> documentElement<span class="symbol">.</span>SelectSingleNode<span class="symbol">(</span><span class="string">&quot;uri/@includeSubDomains&quot;</span><span class="symbol">)</span><span class="symbol">.</span>AsBoolean<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>So, as you can see we load a <code>XmlDocument</code> with the contents of
our file. We then call <code>SelectSingleNode</code> several times with a
different XPath expression.</p>
<p>And in the case of a crawler project, we do this a <strong>lot</strong>, as
there is a large amount of information stored in the file.</p>
<p>I haven't tried to benchmark XPath, but I would assume that we
could have optimized this by first getting the appropriate
element (uri in this case) and then run additional XPath to read
text/attributes. But this article would be rather pointless then
as we want to discuss the <code>XmlReader</code>!</p>
<p>As an example, we have a 2MB project file which represents the
development version of cyotek.com. Using
<code>System.Diagnostics.Stopwatch</code> we timed how long it took to load
this project 10 times, and it averaged 25<strong>seconds</strong> per load.
Which is definitely unacceptable.</p>
<h2 id="reading-using-a-xmlreader">Reading using a XmlReader</h2>
<p>Which brings us to the point of this article, doing the job
using a <code>XmlReader</code> and hopefully improving the performance
dramatically.</p>
<p>Before we continue though, a caveat:</p>
<blockquote>
<p>This is the first time I've tried to use the <code>XmlReader</code>
class, therefore it is possible this article doesn't take the
best approach. I also wrote this article at the same time as
getting the reader to work in my application so I've gone back
and forth already correcting errors and misconceptions, which
at times (and possible still) left the article a little
disjointed. If you spot any errors in this article, please
<a href="https://cyotek.com/contact">let us know</a></p>
</blockquote>
<p>The <code>XmlReader</code> seems to operate in the same principle as the
<code>XmlWriter</code>, in that you need to read the data in more or less
the same order as it was written. I suppose the most convenient
analogy is a forward cursor in SQL Server, where you can only
move forward through the records and not back.</p>
<h3 id="creating-the-reader">Creating the reader</h3>
<p>So, first things first - we need to create an object. But the
<code>XmlReader</code> (like the <code>XmlWriter</code>) is abstract. Fortunately
exactly like the writer, there is a static <code>Create</code> method we
can use.</p>
<p>Continuing in the reader-is-just-like-writer vein, there is also
a <code>XmlReaderSettings</code> class which you can use to fine tune
certain aspects.</p>
<p>Lets get the document opened then. Unlike <code>XmlDocument</code> where
you just provide a file name, <code>XmlReader</code> uses a stream.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> <span class="symbol">(</span>FileStream fileSteam <span class="symbol">=</span> File<span class="symbol">.</span>OpenRead<span class="symbol">(</span>fileName<span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 XmlReaderSettings settings<span class="symbol">;</span>

 settings <span class="symbol">=</span> <span class="keyword">new</span> XmlReaderSettings<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 settings<span class="symbol">.</span>ConformanceLevel <span class="symbol">=</span> ConformanceLevel<span class="symbol">.</span>Document<span class="symbol">;</span>

 <span class="keyword">using</span><span class="symbol">(</span>XmlReader reader <span class="symbol">=</span> XmlReader<span class="symbol">.</span>Create<span class="symbol">(</span>fileSteam<span class="symbol">,</span> settings<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>This sets us up nicely. Continuing my analogy from earlier, if
you're familiar with record sets, there's usually a <code>MoveNext</code>
or a <code>Read</code> method you call to read the next record in the set.
The <code>XmlReader</code> doesn't seem to be different in this respect, as
there's a dedicated <code>Read</code> method for iterating through all
elements in the document. In addition, there are a number of
other read methods for performing more specific actions.</p>
<p>There is also a <code>NodeType</code> property which lets you know what the
current node type is, such as the start of an element, or the
end of an element.</p>
<p>I'm going to use the <code>IsStartElement</code> method to work out if the
current node is the start of an element, then perform processing
based on the element name.</p>
<h3 id="enumerating-elements-regardless-of-their-position-in-the-hierarchy">Enumerating elements, regardless of their position in the hierarchy</h3>
<p>The following snippet will iterate all nodes and check to see if
they are the start of an element. Note that this includes top
level elements <strong>and</strong> child elements.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">while</span> <span class="symbol">(</span>reader<span class="symbol">.</span>Read<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>reader<span class="symbol">.</span>IsStartElement<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The <code>Name</code> property will return the name of the active node. So
I'm going to compare the name against the names written into the
XML and do custom processing for each.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">switch</span> <span class="symbol">(</span>reader<span class="symbol">.</span>Name<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">case</span> <span class="string">&quot;uri&quot;</span><span class="symbol">:</span>
 <span class="keyword">break</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="reading-attributes-on-the-active-element">Reading attributes on the active element</h3>
<p>I mentioned above that there are a number of <code>Read*</code> methods.
There are also several <code>Move*</code> methods. The one that caught my
eye is <code>MoveToNextAttribute*,</code>which I'm going to use for
converting attributes to property values.</p>
<p>The <code>Value</code> property will return the value of the current node.
If <code>MoveToNextAttribute</code> returns <code>true</code>, then I know I'm in a
valid attribute and I can use the aforementioned <code>Name</code> property
and the <code>Value</code> property to update property assignments.</p>
<p>The following snipped demonstrates the <code>MoveToNextAttribute</code>
method and <code>Value</code> property:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">while</span> <span class="symbol">(</span>reader<span class="symbol">.</span>MoveToNextAttribute<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">switch</span> <span class="symbol">(</span>reader<span class="symbol">.</span>Name<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> <span class="string">&quot;lastCrawled&quot;</span><span class="symbol">:</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>reader<span class="symbol">.</span>Value<span class="symbol">)</span><span class="symbol">)</span>
 _lastCrawled <span class="symbol">=</span> DateTime<span class="symbol">.</span>FromBinary<span class="symbol">(</span>Convert<span class="symbol">.</span>ToInt<span class="number">64</span><span class="symbol">(</span>reader<span class="symbol">.</span>Value<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="string">&quot;includeSubDomains&quot;</span><span class="symbol">:</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>reader<span class="symbol">.</span>Value<span class="symbol">)</span><span class="symbol">)</span>
 _includeSubDomains <span class="symbol">=</span> Convert<span class="symbol">.</span>ToBoolean<span class="symbol">(</span>reader<span class="symbol">.</span>Value<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>This is actually quite a lot of work. Another alternative is to
use the <code>GetAttribute</code> method - this reads an attribute value
without moving the reader. I found this very handy when I was
loading an object who's identifying property wasn't the first
attribute in the XML block. It also takes up a lot less code</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
entry<span class="symbol">.</span>Headers<span class="symbol">.</span>Add<span class="symbol">(</span>reader<span class="symbol">.</span>GetAttribute<span class="symbol">(</span><span class="string">&quot;key&quot;</span><span class="symbol">)</span><span class="symbol">,</span> reader<span class="symbol">.</span>GetAttribute<span class="symbol">(</span><span class="string">&quot;value&quot;</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h3 id="reading-the-content-value-of-an-element">Reading the content value of an element</h3>
<p>I've now got two values out of hundreds in the file loaded and
I'm finished with that element. Or am I? Actually I'm not - the
original save code demonstrates that in addition to a pair of
attributes, we're also saving data directly into to the element.</p>
<p>As we have been iterating attributes, the active node type is
the last attribute, not the original element. Fortunately
there's another method we can use - <code>MoveToContent</code>. This time
though, we can't use the <code>Value</code> property. Instead, we'll call
the <code>ReadString</code> method, giving us the following snippet:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">if</span> <span class="symbol">(</span>reader<span class="symbol">.</span>IsStartElement<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">||</span> reader<span class="symbol">.</span>MoveToContent<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">==</span> XmlNodeType<span class="symbol">.</span>Element<span class="symbol">)</span>
 _uri <span class="symbol">=</span> reader<span class="symbol">.</span>ReadString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>I've included a call to <code>IsStartElement</code> in the above snippet as
I found if I called <code>MoveToContent</code> when I was already on a
content node (for example if no attributes were present), then
it skipped the current node and moved to the next one.</p>
<p>If required, you can call <code>ReadElementContentAsString</code> instead
of <code>ReadString</code>.</p>
<p>Some node values aren't strings though - in this case the
<code>XmlReader</code> offers a number of strongly typed methods to
return and convert the data for you, such as
<code>ReadElementContentAsBoolean</code>, <code>ReadElementContentAsDateTime</code>,
etc.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">case</span> <span class="string">&quot;useHeaderChecking&quot;</span><span class="symbol">:</span>
 _useHeaderChecking <span class="symbol">=</span> reader<span class="symbol">.</span>ReadElementContentAsBoolean<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
</pre>
</figure>
<h3 id="processing-nodes-where-the-same-names-are-reused-for-different-purposes">Processing nodes where the same names are reused for different purposes</h3>
<p>In the sample XML document at the start of this article, we have
two different types of nodes named <code>uri</code>. The top level one
has one purpose, and the children of <code>additionalUri</code> have
another.</p>
<p>The problem we now face is as we have a single loop which
processes all elements the case statement for <code>uri</code> will be
triggered multiple times. We're going to need some way of
determining which is which.</p>
<p>There are a few of ways we could do this, for example</p>
<ul>
<li>Continue to use the main processing loop, just add a means of
identifying which type of element is being processed</li>
<li>Adding another loop to process the children of the
<code>additionalUri</code> element</li>
<li>Using the <code>ReadSubtree</code> method to create a brand new
<code>XmlReader</code> containing the children and process that
accordingly.</li>
</ul>
<p>As we already have a loop which handles the elements we should
probably reuse this - there'll be a lot of duplicate code if we
suddenly start adding new loops.</p>
<p>Unfortunately there doesn't seem to an equivalent of the parent
functionality of the <code>XmlDocument</code> class, the closest thing I
could see was the <code>Depth</code> property. This returned <code>1</code> for the
top level <code>uri</code> node, and <code>2</code> for the child versions. You need
to be careful at what point you read this property, it also
returned <code>2</code> when iterating the attributes of the top level
<code>*</code>ri** node.</p>
<p>One workaround would be to use boolean flags to identify the
type of node you are loading. This would also mean checking to
see if the <code>NodeType</code> was <code>XmlNodeType.EndElement</code>, doing
another name comparison, and resetting flags as appropriate.
This might be more reliable (or understandable) than simply
checking node depths, your mileage may vary.</p>
<p>Another alternative could be to combine depth and element
start/end in order to push and pop a stack which would represent
the current node hierarchy.</p>
<p>In order to get my converted code running, I've went with the
boolean flag route. I suspect a future version of the crawler
format is going to ensure the nodes have unique names so I don't
have to do this hoop jumping again though!</p>
<p>Combined together, the load data code now looks like this:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">while</span> <span class="symbol">(</span>reader<span class="symbol">.</span>Read<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>reader<span class="symbol">.</span>IsStartElement<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">switch</span> <span class="symbol">(</span>reader<span class="symbol">.</span>Name<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> <span class="string">&quot;uri&quot;</span><span class="symbol">:</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>isLoadingAdditionalUris<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">while</span> <span class="symbol">(</span>reader<span class="symbol">.</span>MoveToNextAttribute<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">switch</span> <span class="symbol">(</span>reader<span class="symbol">.</span>Name<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> <span class="string">&quot;lastCrawled&quot;</span><span class="symbol">:</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>reader<span class="symbol">.</span>Value<span class="symbol">)</span><span class="symbol">)</span>
 _lastCrawled <span class="symbol">=</span> DateTime<span class="symbol">.</span>FromBinary<span class="symbol">(</span>Convert<span class="symbol">.</span>ToInt<span class="number">64</span><span class="symbol">(</span>reader<span class="symbol">.</span>Value<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="string">&quot;includeSubDomains&quot;</span><span class="symbol">:</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>reader<span class="symbol">.</span>Value<span class="symbol">)</span><span class="symbol">)</span>
 _includeSubDomains <span class="symbol">=</span> Convert<span class="symbol">.</span>ToBoolean<span class="symbol">(</span>reader<span class="symbol">.</span>Value<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>reader<span class="symbol">.</span>IsStartElement<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">||</span> reader<span class="symbol">.</span>MoveToContent<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">==</span> XmlNodeType<span class="symbol">.</span>Element<span class="symbol">)</span>
 _uri <span class="symbol">=</span> reader<span class="symbol">.</span>ReadString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>reader<span class="symbol">.</span>IsStartElement<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">||</span> reader<span class="symbol">.</span>MoveToContent<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">==</span> XmlNodeType<span class="symbol">.</span>EndElement<span class="symbol">)</span>
 _additionalRootUris<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> Uri<span class="symbol">(</span>UriHelpers<span class="symbol">.</span>CombineUri<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>GetBaseUri<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">,</span> reader<span class="symbol">.</span>ReadString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>SlashedRootRemapMode<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="string">&quot;additionalUri&quot;</span><span class="symbol">:</span>
 isLoadingAdditionalUris <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>reader<span class="symbol">.</span>NodeType <span class="symbol">==</span> XmlNodeType<span class="symbol">.</span>EndElement<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">switch</span> <span class="symbol">(</span>reader<span class="symbol">.</span>Name<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> <span class="string">&quot;additionalUri&quot;</span><span class="symbol">:</span>
 isLoadingAdditionalUris <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Which is significantly more code than the original version, and
it's only handling a few values.</p>
<h2 id="using-the-readsubtree-method">Using the ReadSubtree Method</h2>
<p>The save functionality of crawler projects isn't centralized,
child objects such as rules perform their own loading and saving
via the following interface:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">interface</span> IXmlPersistance
<span class="symbol">{</span>
 <span class="keyword">void</span> Write<span class="symbol">(</span><span class="keyword">string</span> fileName<span class="symbol">,</span> XmlWriter writer<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">void</span> Read<span class="symbol">(</span><span class="keyword">string</span> fileName<span class="symbol">,</span> XmlNode reader<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>And the current <code>XmlDocument</code> based code will call it like this:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
_rules<span class="symbol">.</span>Clear<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="keyword">foreach</span> <span class="symbol">(</span>XmlNode child <span class="keyword">in</span> documentElement<span class="symbol">.</span>SelectNodes<span class="symbol">(</span><span class="string">&quot;rules/rule&quot;</span><span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 Rule rule<span class="symbol">;</span>
 rule <span class="symbol">=</span> <span class="keyword">new</span> Rule<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">(</span><span class="symbol">(</span>IXmlPersistance<span class="symbol">)</span>rule<span class="symbol">)</span><span class="symbol">.</span>Read<span class="symbol">(</span>fileName<span class="symbol">,</span> child<span class="symbol">)</span><span class="symbol">;</span>
 _rules<span class="symbol">.</span>Add<span class="symbol">(</span>rule<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>None of this code will work now with the switch to use
<code>XmlReader</code> so it all needs changing. For this, I'll create a
new interface</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">interface</span> IXmlPersistance<span class="number">2</span>
<span class="symbol">{</span>
 <span class="keyword">void</span> Write<span class="symbol">(</span><span class="keyword">string</span> fileName<span class="symbol">,</span> XmlWriter writer<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">void</span> Read<span class="symbol">(</span><span class="keyword">string</span> fileName<span class="symbol">,</span> XmlReader reader<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The only difference is the <code>Read</code> method is now using a
<code>XmlReader</code> rather than a <code>XmlNode</code>.</p>
<p>The next issue is that if I pass the original reader to this
interface, the implementer will be able to read outside the
boundaries of the element it is supposed to be reading, which
could prevent the rest of the document from loading
successfully.</p>
<p>We can resolve this particular issue by calling the
<code>ReadSubtree</code> method which returns a brand new <code>XmlReader</code>
object that only contains the active element and it's children.
This means our other settings objects can happily (mis)use the
passed reader without affecting the underlying load.</p>
<p>Note in the snippet below what we have wrapped the new reader in
a using statement. The MSDN documentation states that the result
of <code>ReadSubtree</code> should be closed before you continue reading
from the original reader.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Rule rule<span class="symbol">;</span>

rule <span class="symbol">=</span> <span class="keyword">new</span> Rule<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="keyword">using</span> <span class="symbol">(</span>XmlReader childReader <span class="symbol">=</span> reader<span class="symbol">.</span>ReadSubtree<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">(</span><span class="symbol">(</span>IXmlPersistance<span class="number">2</span><span class="symbol">)</span>rule<span class="symbol">)</span><span class="symbol">.</span>Read<span class="symbol">(</span>fileName<span class="symbol">,</span> childReader<span class="symbol">)</span><span class="symbol">;</span>
_rules<span class="symbol">.</span>Add<span class="symbol">(</span>rule<span class="symbol">)</span><span class="symbol">;</span>
<span class="keyword">break</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="getting-a-xmldocument-from-a-xmlreader">Getting a XmlDocument from a XmlReader</h2>
<p>One of the issues I did have was classes which extended the load
behaviour of an existing class. For example, one abstract class
has a number of base properties, which I easily converted to use
<code>XmlReader</code>. However, this class is inherited by other classes
and these load additional properties. Using the loop method
outlined above it wasn't possible for these child classes to
read their data as the reader had already been fully read. I
didn't want to have these derived classes has to do the loading
of base properties, and I didn't want to implement any half
thought out idea. So, instead these classes continue to use the
original loading of the <code>XmlDocument</code>. So, given a source of a
<code>XmlReader</code>, how do you get an <code>XmlDocument</code>?</p>
<p>Turns out this is also very simple - the <code>Load</code> method of the
<code>XmlDocument</code> can accept a reader. The only disadvantage is the
constructor of the <code>XmlDocument</code> doesn't support this, which
means you have to explicitly declare a document, load it, then
pass it on, demonstrated below.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">void</span> IXmlPersistance<span class="number">2</span><span class="symbol">.</span>Read<span class="symbol">(</span><span class="keyword">string</span> fileName<span class="symbol">,</span> XmlReader reader<span class="symbol">)</span>
<span class="symbol">{</span>
 XmlDocument document<span class="symbol">;</span>

 document <span class="symbol">=</span> <span class="keyword">new</span> XmlDocument<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 document<span class="symbol">.</span>Load<span class="symbol">(</span>reader<span class="symbol">)</span><span class="symbol">;</span>

 <span class="symbol">(</span><span class="symbol">(</span>IXmlPersistance<span class="symbol">)</span><span class="keyword">this</span><span class="symbol">)</span><span class="symbol">.</span>Read<span class="symbol">(</span>fileName<span class="symbol">,</span> document<span class="symbol">.</span>DocumentElement<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Fortunately these classes aren't used frequently and so they
shouldn't adversely affect the performance tuning I'm trying to
do.</p>
<p>I could have used the <code>GetAttribute</code> method I discussed earlier
as this doesn't move the reader, but firstly I didn't discover
that method until after I'd wrote this section of the article
and I thought it had enough value to remain, and secondly I
don't think there is an equivalent for elements.</p>
<h2 id="the-final-verdict">The final verdict</h2>
<p>Using the <code>XmlReader</code> is certainly long winded compared to the
original code. The core of the original code is around 100
lines. The core of the new code is more than triple this. I'll
probably replace all the &quot;move to next attribute&quot; loops with
direct calls to <code>GetAttribute</code> which will cut down the amount of
code a fair bit. I may also try to do a generic approach using
reflection, although this will then have its own performance
drawback.</p>
<p>However, the XML load performance increase was certainly worth
the extra code - the average went from 25seconds down to
12seconds. This is still quite slow and I certainly want to
improve it further, but at less than half the original load time
I'm pleased with the result.</p>
<p>You also need to be careful when writing the document. In Cyotek
crawler projects, as we are using XPath to query an entire
document, we can load values no matter where they are located.
When using a <code>XmlReader</code>, the values are read in the same order
as they were written - so if you have saved a critical piece of
information near the end of the document, but you require it
when loading information at the start, you're going to run into
problems.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-11-05 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/using-the-xmlreader-class .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comComparing the properties of two objects via Reflection and C#urn:uuid:5a0bc407-991b-4c81-b3b2-1e8c4af467a92010-11-05T23:33:54Z2010-11-05T23:18:48Z<p>As part of the refactoring I was doing to the load code for
crawler projects I needed a way of verifying that new code was
loading data correctly. As it would be extremely time consuming
to manually compare the objects, I used Reflection to compare
the different objects and their properties. This article briefly
describes the process and provides a complete helper function
you can use in your own projects.</p>
<p>This code is loosely based on <a href="https://stackoverflow.com/a/16132925/148962" rel="external nofollow noopener">a Stack Overflow question</a>,
but I have heavily modified and expanded the original concept.</p>
<h2 id="obtaining-a-list-of-properties">Obtaining a list of properties</h2>
<p>The ability to analyze assemblies and their component pieces is
directly built into the .NET Framework, and something I really
appreciate - I remember the nightmares of trying to work with
COM type libraries in Visual Basic many years ago!</p>
<p>The <code>Type</code> class represents a type declaration in your project,
such as a class or enumeration. You can either use the <code>GetType</code>
method of any object to get its underlying type, or use the
<code>typeof</code> keyword to access a type from its type name.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Type typeA<span class="symbol">;</span>
Type typeB<span class="symbol">;</span>
<span class="keyword">int</span> value<span class="symbol">;</span>

value <span class="symbol">=</span> <span class="number">1</span><span class="symbol">;</span>

typeA <span class="symbol">=</span> value<span class="symbol">.</span>GetType<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
typeB <span class="symbol">=</span> <span class="keyword">typeof</span><span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>Once you have a type, you can call the <code>GetProperties</code> method to
return a list of <code>PropertyInfo</code> objects representing the
available properties of the type. Several methods, including
<code>GetProperties</code>, accept an argument of <code>BindingFlags</code>, these
flags allow you to define the type of information return, such
as public members or instance members.</p>
<p>In this case, I want all public instance members which can be
read from and which are not included in a custom ignore list.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">foreach</span> <span class="symbol">(</span>PropertyInfo propertyInfo <span class="keyword">in</span> objectType<span class="symbol">.</span>GetProperties<span class="symbol">(</span>BindingFlags<span class="symbol">.</span>Public <span class="symbol">|</span> BindingFlags<span class="symbol">.</span>Instance<span class="symbol">)</span><span class="symbol">.</span>Where<span class="symbol">(</span>p <span class="symbol">=&gt;</span> p<span class="symbol">.</span>CanRead <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span>ignoreList<span class="symbol">.</span>Contains<span class="symbol">(</span>p<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="retrieving-the-value-of-a-property">Retrieving the value of a property</h2>
<p>The <code>PropertyInfo</code> class has a <code>GetValue</code> method that can be
used to read the value of a property. Its most basic usage is to
pass in the instance object (or null if you want to read a
static property) and any index parameters (or null if no index
parameters are supported).</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">object</span> valueA<span class="symbol">;</span>
<span class="keyword">object</span> valueB<span class="symbol">;</span>

valueA <span class="symbol">=</span> propertyInfo<span class="symbol">.</span>GetValue<span class="symbol">(</span>objectA<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>
valueB <span class="symbol">=</span> propertyInfo<span class="symbol">.</span>GetValue<span class="symbol">(</span>objectB<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>The sample function described in this article doesn't currently
support indexed properties.</p>
<h2 id="determining-if-a-property-can-be-directly-compared">Determining if a property can be directly compared</h2>
<p>Some properties are simple types, such as an <code>int</code> or a <code>string</code>
and are very easy to compare. What happens if a property returns
some other object such as a collection of strings, or a complex
class?</p>
<p>In this case, I try and see if the type supports <code>IComparable</code>
by calling the <code>IsAssignableFrom</code> method. You need to call this
from the type you would like to create, passing in the source
type.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">return</span> <span class="keyword">typeof</span><span class="symbol">(</span>IComparable<span class="symbol">)</span><span class="symbol">.</span>IsAssignableFrom<span class="symbol">(</span>type<span class="symbol">)</span>
</pre>
</figure>
<p>I also check the <code>IsPrimitive</code> and <code>IsValueType</code> properties of
the source type, although this is possibly redundant as all the
base types I've checked so far all support <code>IComparable</code>.</p>
<h2 id="directly-comparing-values">Directly comparing values</h2>
<p>Assuming that I can directly compare a value, first I check if
one of the values is null - if one value is null and one false,
I immediately return a mismatch.</p>
<p>Otherwise, if <code>IComparable</code> is available, then I obtain an
instance of it from the first value and call its <code>CompareTo</code>
method, passing in the second value.</p>
<p>If <code>IComparable</code> is not supported, then I fallback to
<code>object.Equals</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">bool</span> result<span class="symbol">;</span>
IComparable selfValueComparer<span class="symbol">;</span>

selfValueComparer <span class="symbol">=</span> valueA <span class="keyword">as</span> IComparable<span class="symbol">;</span>

<span class="keyword">if</span> <span class="symbol">(</span>valueA <span class="symbol">==</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> valueB <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">||</span> valueA <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> valueB <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span> <span class="comment">// one of the values is null</span>
<span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>selfValueComparer <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> selfValueComparer<span class="symbol">.</span>CompareTo<span class="symbol">(</span>valueB<span class="symbol">)</span> <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">)</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span> <span class="comment">// the comparison using IComparable failed</span>
<span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">object</span><span class="symbol">.</span>Equals<span class="symbol">(</span>valueA<span class="symbol">,</span> valueB<span class="symbol">)</span><span class="symbol">)</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span> <span class="comment">// the comparison using Equals failed</span>
<span class="keyword">else</span>
 result <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span> <span class="comment">// match</span>

<span class="keyword">return</span> result<span class="symbol">;</span>
</pre>
</figure>
<h2 id="comparing-objects">Comparing objects</h2>
<p>If the values could not be directly compared, and do not
implement <code>IEnumerable</code> (as described in the next section) then
I assume the properties are objects and call the compare objects
function again on the properties.</p>
<p>This works nicely, but has one critical flaw - if you have a
child object which has a property reference to a parent item,
then the function will get stuck in a recursive loop. Currently
the only workaround is to ensure that such parent properties are
excluded via the ignore list functionality of the compare
function.</p>
<h2 id="comparing-collections">Comparing collections</h2>
<p>If the direct compare check failed, but the property type
supports <code>IEnumerable</code>, then some Linq is used to obtain the
collection of items.</p>
<p>To save time, a count check is made and if the counts do not
match (or one of the collections is null and the other is not),
then an automatic mismatch is returned. If the counts do match,
then all items are compared in the same manner as the parent
objects.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
IEnumerable<span class="symbol">&lt;</span><span class="keyword">object</span><span class="symbol">&gt;</span> collectionItems<span class="number">1</span><span class="symbol">;</span>
IEnumerable<span class="symbol">&lt;</span><span class="keyword">object</span><span class="symbol">&gt;</span> collectionItems<span class="number">2</span><span class="symbol">;</span>
<span class="keyword">int</span> collectionItemsCount<span class="number">1</span><span class="symbol">;</span>
<span class="keyword">int</span> collectionItemsCount<span class="number">2</span><span class="symbol">;</span>

collectionItems<span class="number">1</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="symbol">(</span>IEnumerable<span class="symbol">)</span>valueA<span class="symbol">)</span><span class="symbol">.</span>Cast<span class="symbol">&lt;</span><span class="keyword">object</span><span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
collectionItems<span class="number">2</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="symbol">(</span>IEnumerable<span class="symbol">)</span>valueB<span class="symbol">)</span><span class="symbol">.</span>Cast<span class="symbol">&lt;</span><span class="keyword">object</span><span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
collectionItemsCount<span class="number">1</span> <span class="symbol">=</span> collectionItems<span class="number">1</span><span class="symbol">.</span>Count<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
collectionItemsCount<span class="number">2</span> <span class="symbol">=</span> collectionItems<span class="number">2</span><span class="symbol">.</span>Count<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>I have tested this code on generic lists such as <code>List&lt;string&gt;</code>,
and on strongly typed collections which inherit from
<code>Collection&lt;TValue&gt;</code> with success.</p>
<h2 id="the-code">The code</h2>
<p>Below is the comparison code. Please note that it won't handle
all situations - as mentioned indexed properties aren't
supported. In addition, if you throw a complex object such as a
<code>DataReader</code> I suspect it will throw a fit on that. I also
haven't tested it on generic properties, it'll probably crash on
those too. But it has worked nicely for the original purpose I
wrote it for.</p>
<p>Also, as I was running this from a Console application, you may
wish to replace the calls to <code>Console.WriteLine</code> with either
<code>Debug.WriteLine</code> or even return them as an out parameter.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="selector-tag">///</span> <span class="selector-tag">&lt;summary&gt;</span>
<span class="selector-tag">///</span><span class="comment"> Compares the properties of two objects of the same type and returns if all properties are equal.</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;/summary&gt;</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;param name=&quot;objectA&quot;&gt;</span><span class="comment">The first object to compare.&lt;/param&gt;</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;param name=&quot;objectB&quot;&gt;</span><span class="comment">The second object to compre.&lt;/param&gt;</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;param name=&quot;ignoreList&quot;&gt;</span><span class="comment">A list of property names to ignore from the comparison.&lt;/param&gt;</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;returns&gt;</span><span class="selector-tag">&lt;c&gt;</span><span class="comment">true&lt;/c&gt; if all property values are equal, otherwise &lt;c&gt;false&lt;/c&gt;.&lt;/returns&gt;</span>
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">bool</span> AreObjectsEqual<span class="symbol">(</span><span class="keyword">object</span> objectA<span class="symbol">,</span> <span class="keyword">object</span> objectB<span class="symbol">,</span> <span class="keyword">params</span> <span class="keyword">string</span><span class="symbol">[</span><span class="symbol">]</span> ignoreList<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>objectA <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> objectB <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Type objectType<span class="symbol">;</span>

 objectType <span class="symbol">=</span> objectA<span class="symbol">.</span>GetType<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 result <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span> <span class="comment">// assume by default they are equal</span>

 <span class="keyword">foreach</span> <span class="symbol">(</span>PropertyInfo propertyInfo <span class="keyword">in</span> objectType<span class="symbol">.</span>GetProperties<span class="symbol">(</span>BindingFlags<span class="symbol">.</span>Public <span class="symbol">|</span> BindingFlags<span class="symbol">.</span>Instance<span class="symbol">)</span><span class="symbol">.</span>Where<span class="symbol">(</span>p <span class="symbol">=&gt;</span> p<span class="symbol">.</span>CanRead <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span>ignoreList<span class="symbol">.</span>Contains<span class="symbol">(</span>p<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">object</span> valueA<span class="symbol">;</span>
 <span class="keyword">object</span> valueB<span class="symbol">;</span>

 valueA <span class="symbol">=</span> propertyInfo<span class="symbol">.</span>GetValue<span class="symbol">(</span>objectA<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>
 valueB <span class="symbol">=</span> propertyInfo<span class="symbol">.</span>GetValue<span class="symbol">(</span>objectB<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// if it is a primitive type, value type or implements IComparable, just directly try and compare the value</span>
 <span class="keyword">if</span> <span class="symbol">(</span>CanDirectlyCompare<span class="symbol">(</span>propertyInfo<span class="symbol">.</span>PropertyType<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>AreValuesEqual<span class="symbol">(</span>valueA<span class="symbol">,</span> valueB<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span><span class="string">&quot;Mismatch with property &#39;{0}.{1}&#39; found.&quot;</span><span class="symbol">,</span> objectType<span class="symbol">.</span>FullName<span class="symbol">,</span> propertyInfo<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">;</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="comment">// if it implements IEnumerable, then scan any items</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>IEnumerable<span class="symbol">)</span><span class="symbol">.</span>IsAssignableFrom<span class="symbol">(</span>propertyInfo<span class="symbol">.</span>PropertyType<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 IEnumerable<span class="symbol">&lt;</span><span class="keyword">object</span><span class="symbol">&gt;</span> collectionItems<span class="number">1</span><span class="symbol">;</span>
 IEnumerable<span class="symbol">&lt;</span><span class="keyword">object</span><span class="symbol">&gt;</span> collectionItems<span class="number">2</span><span class="symbol">;</span>
 <span class="keyword">int</span> collectionItemsCount<span class="number">1</span><span class="symbol">;</span>
 <span class="keyword">int</span> collectionItemsCount<span class="number">2</span><span class="symbol">;</span>

 <span class="comment">// null check</span>
 <span class="keyword">if</span> <span class="symbol">(</span>valueA <span class="symbol">==</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> valueB <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">||</span> valueA <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> valueB <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span><span class="string">&quot;Mismatch with property &#39;{0}.{1}&#39; found.&quot;</span><span class="symbol">,</span> objectType<span class="symbol">.</span>FullName<span class="symbol">,</span> propertyInfo<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">;</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>valueA <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> valueB <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 collectionItems<span class="number">1</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="symbol">(</span>IEnumerable<span class="symbol">)</span>valueA<span class="symbol">)</span><span class="symbol">.</span>Cast<span class="symbol">&lt;</span><span class="keyword">object</span><span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 collectionItems<span class="number">2</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="symbol">(</span>IEnumerable<span class="symbol">)</span>valueB<span class="symbol">)</span><span class="symbol">.</span>Cast<span class="symbol">&lt;</span><span class="keyword">object</span><span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 collectionItemsCount<span class="number">1</span> <span class="symbol">=</span> collectionItems<span class="number">1</span><span class="symbol">.</span>Count<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 collectionItemsCount<span class="number">2</span> <span class="symbol">=</span> collectionItems<span class="number">2</span><span class="symbol">.</span>Count<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// check the counts to ensure they match</span>
 <span class="keyword">if</span> <span class="symbol">(</span>collectionItemsCount<span class="number">1</span> <span class="symbol">!=</span> collectionItemsCount<span class="number">2</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span><span class="string">&quot;Collection counts for property &#39;{0}.{1}&#39; do not match.&quot;</span><span class="symbol">,</span> objectType<span class="symbol">.</span>FullName<span class="symbol">,</span> propertyInfo<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">;</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="comment">// and if they do, compare each item... this assumes both collections have the same order</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> i <span class="symbol">&lt;</span> collectionItemsCount<span class="number">1</span><span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">object</span> collectionItem<span class="number">1</span><span class="symbol">;</span>
 <span class="keyword">object</span> collectionItem<span class="number">2</span><span class="symbol">;</span>
 Type collectionItemType<span class="symbol">;</span>

 collectionItem<span class="number">1</span> <span class="symbol">=</span> collectionItems<span class="number">1</span><span class="symbol">.</span>ElementAt<span class="symbol">(</span>i<span class="symbol">)</span><span class="symbol">;</span>
 collectionItem<span class="number">2</span> <span class="symbol">=</span> collectionItems<span class="number">2</span><span class="symbol">.</span>ElementAt<span class="symbol">(</span>i<span class="symbol">)</span><span class="symbol">;</span>
 collectionItemType <span class="symbol">=</span> collectionItem<span class="number">1</span><span class="symbol">.</span>GetType<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>CanDirectlyCompare<span class="symbol">(</span>collectionItemType<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>AreValuesEqual<span class="symbol">(</span>collectionItem<span class="number">1</span><span class="symbol">,</span> collectionItem<span class="number">2</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span><span class="string">&quot;Item {0} in property collection &#39;{1}.{2}&#39; does not match.&quot;</span><span class="symbol">,</span> i<span class="symbol">,</span> objectType<span class="symbol">.</span>FullName<span class="symbol">,</span> propertyInfo<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">;</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>AreObjectsEqual<span class="symbol">(</span>collectionItem<span class="number">1</span><span class="symbol">,</span> collectionItem<span class="number">2</span><span class="symbol">,</span> ignoreList<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span><span class="string">&quot;Item {0} in property collection &#39;{1}.{2}&#39; does not match.&quot;</span><span class="symbol">,</span> i<span class="symbol">,</span> objectType<span class="symbol">.</span>FullName<span class="symbol">,</span> propertyInfo<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">;</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>propertyInfo<span class="symbol">.</span>PropertyType<span class="symbol">.</span>IsClass<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>AreObjectsEqual<span class="symbol">(</span>propertyInfo<span class="symbol">.</span>GetValue<span class="symbol">(</span>objectA<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">,</span> propertyInfo<span class="symbol">.</span>GetValue<span class="symbol">(</span>objectB<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">,</span> ignoreList<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span><span class="string">&quot;Mismatch with property &#39;{0}.{1}&#39; found.&quot;</span><span class="symbol">,</span> objectType<span class="symbol">.</span>FullName<span class="symbol">,</span> propertyInfo<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">;</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span><span class="string">&quot;Cannot compare property &#39;{0}.{1}&#39;.&quot;</span><span class="symbol">,</span> objectType<span class="symbol">.</span>FullName<span class="symbol">,</span> propertyInfo<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">;</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 result <span class="symbol">=</span> <span class="keyword">object</span><span class="symbol">.</span>Equals<span class="symbol">(</span>objectA<span class="symbol">,</span> objectB<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="selector-tag">///</span> <span class="selector-tag">&lt;summary&gt;</span>
<span class="selector-tag">///</span><span class="comment"> Determines whether value instances of the specified type can be directly compared.</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;/summary&gt;</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;param name=&quot;type&quot;&gt;</span><span class="comment">The type.&lt;/param&gt;</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;returns&gt;</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;c&gt;</span><span class="comment">true&lt;/c&gt; if this value instances of the specified type can be directly compared; otherwise, &lt;c&gt;false&lt;/c&gt;.</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;/returns&gt;</span>
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">bool</span> CanDirectlyCompare<span class="symbol">(</span>Type type<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">typeof</span><span class="symbol">(</span>IComparable<span class="symbol">)</span><span class="symbol">.</span>IsAssignableFrom<span class="symbol">(</span>type<span class="symbol">)</span> <span class="symbol">||</span> type<span class="symbol">.</span>IsPrimitive <span class="symbol">||</span> type<span class="symbol">.</span>IsValueType<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="selector-tag">///</span> <span class="selector-tag">&lt;summary&gt;</span>
<span class="selector-tag">///</span><span class="comment"> Compares two values and returns if they are the same.</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;/summary&gt;</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;param name=&quot;valueA&quot;&gt;</span><span class="comment">The first value to compare.&lt;/param&gt;</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;param name=&quot;valueB&quot;&gt;</span><span class="comment">The second value to compare.&lt;/param&gt;</span>
<span class="selector-tag">///</span> <span class="selector-tag">&lt;returns&gt;</span><span class="selector-tag">&lt;c&gt;</span><span class="comment">true&lt;/c&gt; if both values match, otherwise &lt;c&gt;false&lt;/c&gt;.&lt;/returns&gt;</span>
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">bool</span> AreValuesEqual<span class="symbol">(</span><span class="keyword">object</span> valueA<span class="symbol">,</span> <span class="keyword">object</span> valueB<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>
 IComparable selfValueComparer<span class="symbol">;</span>

 selfValueComparer <span class="symbol">=</span> valueA <span class="keyword">as</span> IComparable<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>valueA <span class="symbol">==</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> valueB <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">||</span> valueA <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> valueB <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span> <span class="comment">// one of the values is null</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>selfValueComparer <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> selfValueComparer<span class="symbol">.</span>CompareTo<span class="symbol">(</span>valueB<span class="symbol">)</span> <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">)</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span> <span class="comment">// the comparison using IComparable failed</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">object</span><span class="symbol">.</span>Equals<span class="symbol">(</span>valueA<span class="symbol">,</span> valueB<span class="symbol">)</span><span class="symbol">)</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span> <span class="comment">// the comparison using Equals failed</span>
 <span class="keyword">else</span>
 result <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span> <span class="comment">// match</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>I hope you find these helper methods useful, this article will
be updated if and when the methods are expanded with new
functionality.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-11-05 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/comparing-the-properties-of-two-objects-via-reflection .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a trackback handler using C#urn:uuid:787e3c16-2fe4-4e70-b211-65776f508c0a2010-09-26T19:24:24Z2010-09-22T19:50:32Z<p>Cyotek.com runs on its own custom CMS/blog engine developed in
ASP.NET MVC 1.0, which has a number of advantages and
disadvantages. One of these disadvantages is no automatic
support for some common blog features such as trackbacks and
pingbacks.</p>
<p>This article will describe how to create a trackback handler for
use with MVC and the more traditional webforms.</p>
<h2 id="what-is-a-trackback">What is a trackback?</h2>
<p>A trackback is a way to be notified when a website links to a
resource on your own site. Some blogging software supports
automatic linking, so if a post on that site links to another,
when the post is submitted, it will automatically detect the
link and attempt to send a trackback to the original author. If
successful, a link is generally created from the original author
to the new post, thus building a web of interconnected resources
(in theory). You can learn a little more about trackbacks from
<a href="http://en.wikipedia.org/wiki/Trackback" rel="external nofollow noopener">Wikipedia</a>.</p>
<p>The full trackback specification can be viewed at the <a href="http://www.sixapart.com/pronet/docs/trackback_spec" rel="external nofollow noopener">SixApart
website</a></p>
<h2 id="a-trackback-handler-in-c">A trackback handler in C#</h2>
<p>Unlike pingbacks (which we'll address in a future article),
trackbacks use standard HTTP requests and so are extremely easy
to implement.</p>
<p>Available for download at the end of this article is a sample
library which you can use to implement your trackbacks.</p>
<p>As a trackback is comprised of several pieces of information
which we'll be passing about, we'll start by defining a
structure to hold this information.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">struct</span> TrackbackInfo
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">string</span> BlogName <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">string</span> Excerpt <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">string</span> Id <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">string</span> Title <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> Uri Uri <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The properties of this structure mirror the required information
from the trackback specification.</p>
<p>Next, we'll define an enum for the different result codes you
can return. The specification states 0 for success and 1 for
error, but I'm uncertain if you can extend this, ie is any
non-zero is classed as an error. We'll play it safe and just use
a single error code.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">enum</span> TrackbackErrorCode
<span class="symbol">{</span>
 Success<span class="symbol">,</span>
 Error
<span class="symbol">}</span>
</pre>
</figure>
<p>I'd considered two ways of implementing this, the first being an
abstract class containing methods which must be implemented in
order to provide the functionality for saving a trackback into
your chosen data source, or using delegates. In order to make it
a simple as possible to use, I've went with the latter.
Therefore, we need two delegates, one which will resolve the
&quot;permalink&quot; for the given ID, and another to actually save the
trackback.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">delegate</span> Uri GetTrackbackUrlDelegate<span class="symbol">(</span>TrackbackInfo trackback<span class="symbol">)</span><span class="symbol">;</span>
<span class="keyword">public</span> <span class="keyword">delegate</span> <span class="keyword">void</span> SaveTrackbackDelegate<span class="symbol">(</span>TrackbackInfo trackback<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="implementing-the-handler">Implementing the handler</h2>
<p>We've created a static class named <code>TrackbackHandler</code> which
contains all the functionality we'll need. We expose a single
public method, <code>GetTrackback</code>, which will return the XML block
required to notify the sender of the result of the request.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">string</span> GetTrackback<span class="symbol">(</span>NameValueCollection form<span class="symbol">,</span> SaveTrackbackDelegate saveTrackbackDelegate<span class="symbol">,</span> GetTrackbackUrlDelegate getTrackbackUrlDelegate<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">string</span> url<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>form <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException<span class="symbol">(</span><span class="string">&quot;form&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>saveTrackbackDelegate <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException<span class="symbol">(</span><span class="string">&quot;saveTrackbackDelegate&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>getTrackbackUrlDelegate <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException<span class="symbol">(</span><span class="string">&quot;getTrackbackUrlDelegate&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 url <span class="symbol">=</span> form<span class="symbol">[</span><span class="string">&quot;url&quot;</span><span class="symbol">]</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>url<span class="symbol">)</span> <span class="symbol">&amp;&amp;</span> url<span class="symbol">.</span>Contains<span class="symbol">(</span><span class="string">&quot;,&quot;</span><span class="symbol">)</span><span class="symbol">)</span>
 url <span class="symbol">=</span> url<span class="symbol">.</span>Split<span class="symbol">(</span><span class="string">&#39;,&#39;</span><span class="symbol">)</span><span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span><span class="symbol">;</span>

 <span class="keyword">return</span> TrackbackHandler<span class="symbol">.</span>GetTrackback<span class="symbol">(</span>saveTrackbackDelegate<span class="symbol">,</span> getTrackbackUrlDelegate<span class="symbol">,</span> form<span class="symbol">[</span><span class="string">&quot;id&quot;</span><span class="symbol">]</span><span class="symbol">,</span> url<span class="symbol">,</span> form<span class="symbol">[</span><span class="string">&quot;title&quot;</span><span class="symbol">]</span><span class="symbol">,</span> form<span class="symbol">[</span><span class="string">&quot;excerpt&quot;</span><span class="symbol">]</span><span class="symbol">,</span> form<span class="symbol">[</span><span class="string">&quot;blog_name&quot;</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>This function accepts the following arguments:</p>
<ul>
<li>A <code>NameValueCollection</code> holding the submitted trackback data -
supporting both the MVC <code>FormCollection</code> or <code>Request.Form</code> for
ASP.NET.</li>
<li>An implementation of the <code>SaveTrackbackDelegate</code> delegate for
saving the trackback to your chosen data store.</li>
<li>An implementation of the <code>GetTrackbackUrlDelegate</code> for
resolving a permalink URL of the given ID.</li>
</ul>
<p>Assuming none of these are null, the method then calls a private
overload, explicitly specifying the individual items of data.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">string</span> GetTrackback<span class="symbol">(</span>SaveTrackbackDelegate saveTrackbackDelegate<span class="symbol">,</span> GetTrackbackUrlDelegate getTrackbackUrlDelegate<span class="symbol">,</span> <span class="keyword">string</span> id<span class="symbol">,</span> <span class="keyword">string</span> url<span class="symbol">,</span> <span class="keyword">string</span> title<span class="symbol">,</span> <span class="keyword">string</span> excerpt<span class="symbol">,</span> <span class="keyword">string</span> blogName<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>
 <span class="keyword">try</span>
 <span class="symbol">{</span>
 HttpRequest request<span class="symbol">;</span>

 request <span class="symbol">=</span> HttpContext<span class="symbol">.</span>Current<span class="symbol">.</span>Request<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>id<span class="symbol">)</span><span class="symbol">)</span>
 result <span class="symbol">=</span> GetTrackbackResponse<span class="symbol">(</span>TrackbackErrorCode<span class="symbol">.</span>Error<span class="symbol">,</span> <span class="string">&quot;The entry ID is missing&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>request<span class="symbol">.</span>HttpMethod <span class="symbol">!=</span> <span class="string">&quot;POST&quot;</span><span class="symbol">)</span>
 result <span class="symbol">=</span> GetTrackbackResponse<span class="symbol">(</span>TrackbackErrorCode<span class="symbol">.</span>Error<span class="symbol">,</span> <span class="string">&quot;An invalid request was made.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>url<span class="symbol">)</span><span class="symbol">)</span>
 result <span class="symbol">=</span> TrackbackHandler<span class="symbol">.</span>GetTrackbackResponse<span class="symbol">(</span>TrackbackErrorCode<span class="symbol">.</span>Error<span class="symbol">,</span> <span class="string">&quot;Trackback URI not specified.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>First, we validate that the request is being made via a <code>POST</code>
and not any other HTTP request, and that both the entry ID and
the URL of the sender are specified.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 TrackbackInfo trackbackInfo<span class="symbol">;</span>
 <span class="keyword">string</span> trackbackTitle<span class="symbol">;</span>
 Uri targetUri<span class="symbol">;</span>

 trackbackInfo <span class="symbol">=</span> <span class="keyword">new</span> TrackbackInfo<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Id <span class="symbol">=</span> id<span class="symbol">,</span>
 Title <span class="symbol">=</span> title<span class="symbol">,</span>
 BlogName <span class="symbol">=</span> blogName<span class="symbol">,</span>
 Excerpt <span class="symbol">=</span> excerpt<span class="symbol">,</span>
 Uri <span class="symbol">=</span> <span class="keyword">new</span> Uri<span class="symbol">(</span>url<span class="symbol">)</span>
 <span class="symbol">}</span><span class="symbol">;</span>

 targetUri <span class="symbol">=</span> getTrackbackUrlDelegate<span class="symbol">.</span>Invoke<span class="symbol">(</span>trackbackInfo<span class="symbol">)</span><span class="symbol">;</span>

</pre>
</figure>
<p>If everything is fine, we then construct our <code>TrackbackInfo</code>
object for passing to our delegates, and then try and get the
permalink for the trackback ID.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">if</span> <span class="symbol">(</span>targetUri <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 result <span class="symbol">=</span> GetTrackbackResponse<span class="symbol">(</span>TrackbackErrorCode<span class="symbol">.</span>Error<span class="symbol">,</span> <span class="string">&quot;The entry ID could not be matched.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>TrackbackHandler<span class="symbol">.</span>CheckSourceLinkExists<span class="symbol">(</span>targetUri<span class="symbol">,</span> trackbackInfo<span class="symbol">.</span>Uri<span class="symbol">,</span> <span class="keyword">out</span> trackbackTitle<span class="symbol">)</span><span class="symbol">)</span>
 result <span class="symbol">=</span> GetTrackbackResponse<span class="symbol">(</span>TrackbackErrorCode<span class="symbol">.</span>Error<span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">.</span>Format<span class="symbol">(</span><span class="string">&quot;Sorry couldn&#39;t find a link for \&quot;{0}\&quot; in \&quot;{1}\&quot;&quot;</span><span class="symbol">,</span> targetUri<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">,</span> trackbackInfo<span class="symbol">.</span>Uri<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>If we don't have a URL, we return an error code to the sender.</p>
<p>If we do have a URL another method, <code>CheckSourceLinkExists</code> is
called. This method will download the HTML of the caller and
attempt to verify if the senders page does in fact contain a
link matching the permalink. If it doesn't, then we'll abort
here.</p>
<p>If the method is successful and a link is detected, the method
will return the title of the senders HTML page as an out
parameter. This will be used if the trackback information didn't
include a blog name (as this is an optional field).</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>blogName<span class="symbol">)</span><span class="symbol">)</span>
 trackbackInfo<span class="symbol">.</span>BlogName <span class="symbol">=</span> trackbackTitle<span class="symbol">;</span>

 saveTrackbackDelegate<span class="symbol">.</span>Invoke<span class="symbol">(</span>trackbackInfo<span class="symbol">)</span><span class="symbol">;</span>

 result <span class="symbol">=</span> TrackbackHandler<span class="symbol">.</span>GetTrackbackResponse<span class="symbol">(</span>TrackbackErrorCode<span class="symbol">.</span>Success<span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>Exception ex<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">//handle the error.</span>
 result <span class="symbol">=</span> TrackbackHandler<span class="symbol">.</span>GetTrackbackResponse<span class="symbol">(</span>TrackbackErrorCode<span class="symbol">.</span>Error<span class="symbol">,</span> ex<span class="symbol">.</span>Message<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Finally, if everything went to plan, we save the trackback to
our data store, and return a success code. In the event of any
part of this process failing, then we return an error result.</p>
<h2 id="downloading-the-senders-html-and-checking-if-a-link-exists">Downloading the senders html and checking if a link exists</h2>
<p>In this implementation, we won't link to the senders site unless
they have already linked to us. We do this by downloading the
HTML of the senders site and checking to see if our link is
present.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">bool</span> CheckSourceLinkExists<span class="symbol">(</span>Uri lookingFor<span class="symbol">,</span> Uri lookingIn<span class="symbol">,</span> <span class="keyword">out</span> <span class="keyword">string</span> pageTitle<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>

 pageTitle <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> html<span class="symbol">;</span>

 html <span class="symbol">=</span> GetPageHtml<span class="symbol">(</span>lookingIn<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>html<span class="symbol">.</span>Trim<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span> <span class="symbol">|</span> html<span class="symbol">.</span>IndexOf<span class="symbol">(</span>lookingFor<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">,</span> StringComparison<span class="symbol">.</span>InvariantCultureIgnoreCase<span class="symbol">)</span> <span class="symbol">&lt;</span> <span class="number">0</span><span class="symbol">)</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 HtmlDocument document<span class="symbol">;</span>

 document <span class="symbol">=</span> <span class="keyword">new</span> HtmlDocument<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 document<span class="symbol">.</span>LoadHtml<span class="symbol">(</span>html<span class="symbol">)</span><span class="symbol">;</span>
 pageTitle <span class="symbol">=</span> document<span class="symbol">.</span>GetDocumentTitle<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 result <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">string</span> GetPageHtml<span class="symbol">(</span>Uri uri<span class="symbol">)</span>
<span class="symbol">{</span>
 WebRequest request<span class="symbol">;</span>
 HttpWebResponse response<span class="symbol">;</span>
 <span class="keyword">string</span> encodingName<span class="symbol">;</span>
 Encoding encoding<span class="symbol">;</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>

 request <span class="symbol">=</span> WebRequest<span class="symbol">.</span>Create<span class="symbol">(</span>uri<span class="symbol">)</span><span class="symbol">;</span>
 response <span class="symbol">=</span> <span class="symbol">(</span>HttpWebResponse<span class="symbol">)</span>request<span class="symbol">.</span>GetResponse<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 encodingName <span class="symbol">=</span> response<span class="symbol">.</span>ContentEncoding<span class="symbol">.</span>Trim<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>encodingName<span class="symbol">)</span><span class="symbol">)</span>
 encodingName <span class="symbol">=</span> <span class="string">&quot;utf-8&quot;</span><span class="symbol">;</span>
 encoding <span class="symbol">=</span> Encoding<span class="symbol">.</span>GetEncoding<span class="symbol">(</span>encodingName<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Stream stream <span class="symbol">=</span> response<span class="symbol">.</span>GetResponseStream<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>StreamReader reader <span class="symbol">=</span> <span class="keyword">new</span> StreamReader<span class="symbol">(</span>stream<span class="symbol">,</span> encoding<span class="symbol">)</span><span class="symbol">)</span>
 result <span class="symbol">=</span> reader<span class="symbol">.</span>ReadToEnd<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">string</span> GetDocumentTitle<span class="symbol">(</span><span class="keyword">this</span> HtmlDocument document<span class="symbol">)</span>
<span class="symbol">{</span>
 HtmlNode titleNode<span class="symbol">;</span>
 <span class="keyword">string</span> title<span class="symbol">;</span>

 titleNode <span class="symbol">=</span> document<span class="symbol">.</span>DocumentNode<span class="symbol">.</span>SelectSingleNode<span class="symbol">(</span><span class="string">&quot;//head/title&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>titleNode <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 title <span class="symbol">=</span> titleNode<span class="symbol">.</span>InnerText<span class="symbol">;</span>
 <span class="keyword">else</span>
 title <span class="symbol">=</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">;</span>

 title <span class="symbol">=</span> title<span class="symbol">.</span>Replace<span class="symbol">(</span><span class="string">&quot;\n&quot;</span><span class="symbol">,</span> <span class="string">&quot;&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 title <span class="symbol">=</span> title<span class="symbol">.</span>Replace<span class="symbol">(</span><span class="string">&quot;\r&quot;</span><span class="symbol">,</span> <span class="string">&quot;&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">while</span> <span class="symbol">(</span>title<span class="symbol">.</span>Contains<span class="symbol">(</span><span class="string">&quot; &quot;</span><span class="symbol">)</span><span class="symbol">)</span>
 title <span class="symbol">=</span> title<span class="symbol">.</span>Replace<span class="symbol">(</span><span class="string">&quot; &quot;</span><span class="symbol">,</span> <span class="string">&quot; &quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> title<span class="symbol">.</span>Trim<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The function <code>GetDocumentTitle</code> uses the <a href="http://htmlagilitypack.codeplex.com/" rel="external nofollow noopener">Html Agility Pack</a>
to parse the HTML looking for the title tag. As the
<code>CheckSourceLinkExists</code> function is only checking to see if the
link exists somewhere inside the HTML you may wish to update
this to ensure that the link is actually within an anchor tag -
the Html Agility Pack makes this extremely easy.</p>
<h2 id="returning-a-response">Returning a response</h2>
<p>In several places, the <code>GetTrackback</code> method calls
<code>GetTrackbackResponse</code>. This helper function returns a block of
XML which describes the result of the operation.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">string</span> GetTrackbackResponse<span class="symbol">(</span>TrackbackErrorCode errorCode<span class="symbol">,</span> <span class="keyword">string</span> errorText<span class="symbol">)</span>
<span class="symbol">{</span>
 StringBuilder builder<span class="symbol">;</span>

 builder <span class="symbol">=</span> <span class="keyword">new</span> StringBuilder<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>StringWriter writer <span class="symbol">=</span> <span class="keyword">new</span> StringWriter<span class="symbol">(</span>builder<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 XmlWriterSettings settings<span class="symbol">;</span>
 XmlWriter xmlWriter<span class="symbol">;</span>

 settings <span class="symbol">=</span> <span class="keyword">new</span> XmlWriterSettings<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 settings<span class="symbol">.</span>Indent <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 settings<span class="symbol">.</span>Encoding <span class="symbol">=</span> Encoding<span class="symbol">.</span>UTF<span class="number">8</span><span class="symbol">;</span>

 xmlWriter <span class="symbol">=</span> XmlWriter<span class="symbol">.</span>Create<span class="symbol">(</span>writer<span class="symbol">,</span> settings<span class="symbol">)</span><span class="symbol">;</span>

 xmlWriter<span class="symbol">.</span>WriteStartDocument<span class="symbol">(</span><span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>
 xmlWriter<span class="symbol">.</span>WriteStartElement<span class="symbol">(</span><span class="string">&quot;response&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 xmlWriter<span class="symbol">.</span>WriteElementString<span class="symbol">(</span><span class="string">&quot;response&quot;</span><span class="symbol">,</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>errorCode<span class="symbol">)</span><span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>errorText<span class="symbol">)</span><span class="symbol">)</span>
 xmlWriter<span class="symbol">.</span>WriteElementString<span class="symbol">(</span><span class="string">&quot;message&quot;</span><span class="symbol">,</span> errorText<span class="symbol">)</span><span class="symbol">;</span>
 xmlWriter<span class="symbol">.</span>WriteEndElement<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 xmlWriter<span class="symbol">.</span>WriteEndDocument<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 xmlWriter<span class="symbol">.</span>Close<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> builder<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="implementing-an-mvc-action-for-handling-trackbacks">Implementing an MVC Action for handling trackbacks</h2>
<p>In order to use the handler from MVC, define a new action which
returns a <code>ContentResult</code>. It should only be callable from a
<code>POST</code>, and ideally it shouldn't validate input. Even if you
don't want HTML present in your trackbacks, you should strip any
HTML yourself - if you have ASP.NET validation enabled and an
attempt is made to post data containing HTML, then ASP.NET will
return the yellow screen of death HTML to the sender, not the
nice block of XML it was expecting.</p>
<p>Simply return a new <code>ContentResult</code> containing the result of the
<code>GetTrackback</code> method and a mime type of <code>text/xml</code>, as
shown below.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>AcceptVerbs<span class="symbol">(</span>HttpVerbs<span class="symbol">.</span>Post<span class="symbol">)</span><span class="symbol">]</span>
<span class="symbol">[</span>ValidateInput<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> ContentResult Trackback<span class="symbol">(</span>FormCollection form<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">string</span> xml<span class="symbol">;</span>

 <span class="comment">// get the ID of the article to link to from the URL query string</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>form<span class="symbol">[</span><span class="string">&quot;id&quot;</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">)</span>
 form<span class="symbol">.</span>Add<span class="symbol">(</span><span class="string">&quot;id&quot;</span><span class="symbol">,</span> Request<span class="symbol">.</span>QueryString<span class="symbol">[</span><span class="string">&quot;id&quot;</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// get the response from the trackback handler</span>
 xml <span class="symbol">=</span> TrackbackHandler<span class="symbol">.</span>GetTrackback<span class="symbol">(</span>form<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>SaveTrackbackComment<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>GetArticleUrl<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>Content<span class="symbol">(</span>xml<span class="symbol">,</span> <span class="string">&quot;text/xml&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>In this case, I'm also checking the query string for the ID of
the article to link to as we use a single trackback action to
handle all resources. If your trackback submission URL is unique
for resource supporting trackbacks, then you wouldn't need to do
this.</p>
<p>The implementations of your two delegates will vary depending on
how your own website is structured and how it stores data. As an
example I have included the ones used here at Cyotek.com (Entity
Framework on SQL Server 2005 using a repository pattern):</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> Uri GetArticleUrl<span class="symbol">(</span>TrackbackInfo trackback<span class="symbol">)</span>
<span class="symbol">{</span>
 Article article<span class="symbol">;</span>
 <span class="keyword">int</span> articleId<span class="symbol">;</span>
 Uri result<span class="symbol">;</span>

 Int<span class="number">32</span><span class="symbol">.</span>TryParse<span class="symbol">(</span>trackback<span class="symbol">.</span>Id<span class="symbol">,</span> <span class="keyword">out</span> articleId<span class="symbol">)</span><span class="symbol">;</span>

 article <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ArticleService<span class="symbol">.</span>GetItem<span class="symbol">(</span>articleId<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>article <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 result <span class="symbol">=</span> <span class="keyword">new</span> Uri<span class="symbol">(</span>Url<span class="symbol">.</span>Action<span class="symbol">(</span><span class="string">&quot;display&quot;</span><span class="symbol">,</span> <span class="string">&quot;article&quot;</span><span class="symbol">,</span> <span class="keyword">new</span> <span class="symbol">{</span> id <span class="symbol">=</span> article<span class="symbol">.</span>Name <span class="symbol">}</span><span class="symbol">,</span> <span class="string">&quot;http&quot;</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 result <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> SaveTrackbackComment<span class="symbol">(</span>TrackbackInfo trackback<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">try</span>
 <span class="symbol">{</span>
 Comment comment<span class="symbol">;</span>
 Article article<span class="symbol">;</span>
 StringBuilder body<span class="symbol">;</span>
 <span class="keyword">string</span> blogName<span class="symbol">;</span>

 article <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ArticleService<span class="symbol">.</span>GetItem<span class="symbol">(</span>Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span>trackback<span class="symbol">.</span>Id<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 blogName <span class="symbol">=</span> <span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>trackback<span class="symbol">.</span>BlogName<span class="symbol">)</span> <span class="symbol">?</span> trackback<span class="symbol">.</span>BlogName <span class="symbol">:</span> trackback<span class="symbol">.</span>Uri<span class="symbol">.</span>AbsolutePath<span class="symbol">;</span>

 body <span class="symbol">=</span> <span class="keyword">new</span> StringBuilder<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 body<span class="symbol">.</span>AppendFormat<span class="symbol">(</span><span class="string">&quot;[b]{0}[/b]\n&quot;</span><span class="symbol">,</span> trackback<span class="symbol">.</span>Title<span class="symbol">)</span><span class="symbol">;</span>
 body<span class="symbol">.</span>Append<span class="symbol">(</span>trackback<span class="symbol">.</span>Excerpt<span class="symbol">)</span><span class="symbol">;</span>
 body<span class="symbol">.</span>AppendFormat<span class="symbol">(</span><span class="string">&quot; - Trackback from {0}&quot;</span><span class="symbol">,</span> blogName<span class="symbol">)</span><span class="symbol">;</span>

 comment <span class="symbol">=</span> <span class="keyword">new</span> Comment<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 comment<span class="symbol">.</span>Article <span class="symbol">=</span> article<span class="symbol">;</span>
 comment<span class="symbol">.</span>AuthorName <span class="symbol">=</span> blogName<span class="symbol">;</span>
 comment<span class="symbol">.</span>AuthorUrl <span class="symbol">=</span> trackback<span class="symbol">.</span>Uri<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 comment<span class="symbol">.</span>DateCreated <span class="symbol">=</span> DateTime<span class="symbol">.</span>Now<span class="symbol">;</span>
 comment<span class="symbol">.</span>Body <span class="symbol">=</span> body<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 comment<span class="symbol">.</span>IsPublished <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 comment<span class="symbol">.</span>AuthorEmail <span class="symbol">=</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">;</span>
 comment<span class="symbol">.</span>AuthorUserName <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CommentService<span class="symbol">.</span>CreateItem<span class="symbol">(</span>comment<span class="symbol">)</span><span class="symbol">;</span>

 ModelHelpers<span class="symbol">.</span>SendCommentEmail<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">,</span> article<span class="symbol">,</span> comment<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Url<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>System<span class="symbol">.</span>Exception ex<span class="symbol">)</span>
 <span class="symbol">{</span>
 CyotekApplication<span class="symbol">.</span>LogException<span class="symbol">(</span>ex<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">throw</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="implementing-an-asp.net-webforms-trackback-handler">Implementing an ASP.NET Webforms trackback handler</h2>
<p>Using this library from ASP.NET webforms is almost as
straightforward. You could, as in the example below, create a
normal page containing no HTML such as trackback.aspx which will
omit the XML when called.</p>
<p>Ideally however, you would probably want to implement this as a
HTTP Handler, although this is beyond the scope of this article.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> System<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Text<span class="symbol">;</span>
<span class="keyword">using</span> Cyotek<span class="symbol">.</span>Web<span class="symbol">.</span>Trackback<span class="symbol">;</span>

<span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> TrackbackHandlerPage <span class="symbol">:</span> System<span class="symbol">.</span>Web<span class="symbol">.</span>UI<span class="symbol">.</span>Page
<span class="symbol">{</span>
 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnInit<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnInit<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 Response<span class="symbol">.</span>ContentEncoding <span class="symbol">=</span> Encoding<span class="symbol">.</span>UTF<span class="number">8</span><span class="symbol">;</span>
 Response<span class="symbol">.</span>ContentType <span class="symbol">=</span> <span class="string">&quot;text/xml&quot;</span><span class="symbol">;</span>

 Response<span class="symbol">.</span>Clear<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 Response<span class="symbol">.</span>Write<span class="symbol">(</span>TrackbackHandler<span class="symbol">.</span>GetTrackback<span class="symbol">(</span>Request<span class="symbol">.</span>Form<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>SaveTrackback<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>GetTrackbackUrl<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> Uri GetTrackbackUrl<span class="symbol">(</span>TrackbackInfo trackbackInfo<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> NotImplementedException<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">void</span> SaveTrackback<span class="symbol">(</span>TrackbackInfo trackbackInfo<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> NotImplementedException<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="providing-the-trackback-url">Providing the trackback URL</h2>
<p>Of course, having a trackback handler is of no use if third
party sites can't find it! For sites to discover your trackback
URLs, you need to embed a block of HTML inside your blog
articles containing a link to your trackback handler. This URL
should be unique for each article. For cyotek.com, we append the
ID of the article as part of the query string of the URL, then
extract this in the controller action, but this isn't the only
way to do it - choose whatever suits the needs of your site.</p>
<p>The following shows the auto discovery information for this URL:</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">rdf</span><span class="symbol">:</span><span class="name">RDF</span> <span class="name">xmlns:rdf</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://www.w3.org/1999/02/22-rdf-syntax-ns#</span><span class="symbol">&quot;</span> <span class="name">xmlns:dc</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://purl.org/dc/elements/1.1/</span><span class="symbol">&quot;</span> <span class="name">xmlns:trackback</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://madskills.com/public/xml/rss/module/trackback/</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;</span><span class="name">rdf</span><span class="symbol">:</span><span class="name">Description</span> <span class="name">rdf:about</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://cyotek.com/article/display/creating-a-trackback-handler-using-csharp</span><span class="symbol">&quot;</span> <span class="name">dc:identifier</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://cyotek.com/article/display/creating-a-trackback-handler-using-csharp</span><span class="symbol">&quot;</span> <span class="name">dc:title</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Creating a trackback handler using C#</span><span class="symbol">&quot;</span> <span class="name">trackback:ping</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://cyotek.com/trackback?id=21</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span><span class="symbol">&lt;/</span><span class="name">rdf</span><span class="symbol">:</span><span class="name">Description</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">rdf</span><span class="symbol">:</span><span class="name">RDF</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>It includes the trackback URL (with article ID 21) and the title
of the article, plus the permalink.</p>
<h2 id="next-steps">Next steps</h2>
<p>Cyotek.com doesn't get a huge amount of traffic, and so this
library has not been extensively tested. It has worked so far,
but I can't guarantee it to be bug free!</p>
<p>Possible enhancements would be to add some form of deny list,
so if you were getting spam requests, you could more easily
disable these. Also the link checking could be made more robust
by ensure its within a valid anchor, although there's only so
much you can do.</p>
<p>I hope you find this library useful, the download link is below.
As mentioned, this library uses the Html Agility Pack for
parsing HTML, however you can replace this if required with your
own custom solution.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-09-22 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-a-trackback-handler-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a scrollable and zoomable image viewer in C# Part 4urn:uuid:6344e9eb-fd6a-42d7-9a0d-3d09d3883bfa2012-11-25T09:20:14Z2010-08-28T15:49:10Z<p>In the conclusion to our series on building a scrollable and
zoomable image viewer, we'll add support for zooming, auto
centering, size to fit and some display optimizations and
enhancements.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/imgbox-4a.png" class="gallery" title="The ImageBox sample application showing a zoomed in image." ><img src="https://images.cyotek.com/image/thumbnail/devblog/imgbox-4a.png" alt="The ImageBox sample application showing a zoomed in image." decoding="async" loading="lazy" /></a><figcaption>The ImageBox sample application showing a zoomed in image.</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/imgbox-4b.png" class="gallery" title="The ImageBox sample application showing a zoomed out image, with auto centering and the transparency grid only displayed behind the image." ><img src="https://images.cyotek.com/image/thumbnail/devblog/imgbox-4b.png" alt="The ImageBox sample application showing a zoomed out image, with auto centering and the transparency grid only displayed behind the image." decoding="async" loading="lazy" /></a><figcaption>The ImageBox sample application showing a zoomed out image, with auto centering and the transparency grid only displayed behind the image.</figcaption></figure><h2 id="getting-started">Getting Started</h2>
<p>Unlike parts 2 and 3, we're actually adding quite a lot of new
functionality, some of it more complicated than others.</p>
<p>First, we're going to remove the <code>ShowGrid</code> property. This
originally was a simple on/off flag, but we want more control
this time.</p>
<p>We've also got a number of new properties and backing events to
add:</p>
<ul>
<li><code>AutoCenter</code> - controls if the image is automatically centered
in the display area if the image isn't scrolled.</li>
<li><code>SizeToFit</code> - if this property is set, the image will
automatically zoom to the maximum size for displaying the
entire image.</li>
<li><code>GridDisplayMode</code> - this property, which replaces <code>ShowGrid</code>
will determine how the background grid is to be drawn.</li>
<li><code>InterpolationMode</code> - determines how the zoomed image will be
rendered.</li>
<li><code>Zoom</code> - allows you to specify the zoom level.</li>
<li><code>ZoomIncrement</code> - specifies how much the zoom is increased or
decreased using the scroll wheel.</li>
<li><code>ZoomFactor</code> - this protected property returns the current
zoom as used internally for scaling.</li>
<li><code>ScaledImageWidth</code> and <code>ScaledImageHeight</code> - these protected
properties return the size of the image adjusted for the
current zoom.</li>
</ul>
<p>Usually the properties are simple assignments, which compare the
values before assignment and raise an event. The zoom property
is slightly different as it will ensure that the new value fits
within a given range before setting it.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> <span class="keyword">int</span> MinZoom <span class="symbol">=</span> <span class="number">10</span><span class="symbol">;</span>
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> <span class="keyword">int</span> MaxZoom <span class="symbol">=</span> <span class="number">3500</span><span class="symbol">;</span>

<span class="symbol">[</span>DefaultValue<span class="symbol">(</span><span class="number">100</span><span class="symbol">)</span><span class="symbol">,</span> Category<span class="symbol">(</span><span class="string">&quot;Appearance&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">int</span> Zoom
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _zoom<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>value <span class="symbol">&lt;</span> ImageBox<span class="symbol">.</span>MinZoom<span class="symbol">)</span>
 value <span class="symbol">=</span> ImageBox<span class="symbol">.</span>MinZoom<span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>value <span class="symbol">&gt;</span> ImageBox<span class="symbol">.</span>MaxZoom<span class="symbol">)</span>
 value <span class="symbol">=</span> ImageBox<span class="symbol">.</span>MaxZoom<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_zoom <span class="symbol">!=</span> value<span class="symbol">)</span>
 <span class="symbol">{</span>
 _zoom <span class="symbol">=</span> value<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnZoomChanged<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Using the <code>MinZoom</code> and <code>MaxZoom</code> constants we are specifying a
minimum value of 10% and a maximum of 3500%. The values you are
assign are more or less down to your own personal preferences -
I don't have any indications of what a &quot;best&quot; maximum value
would be.</p>
<p>Setting the <code>SizeToFit</code> property should disable the <code>AutoPan</code>
property and vice versa.</p>
<h2 id="layout-updates">Layout Updates</h2>
<p>Several parts of the component work from the image size, however
as these now need to account for any zoom level, all such calls
now use the <code>ScaledImageWidth</code> and <code>ScaledImageHeight</code>
properties.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">int</span> ScaledImageHeight
<span class="symbol">{</span> <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>Image <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">?</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Size<span class="symbol">.</span>Height <span class="symbol">*</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span> <span class="symbol">:</span> <span class="number">0</span><span class="symbol">;</span> <span class="symbol">}</span> <span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">int</span> ScaledImageWidth
<span class="symbol">{</span> <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>Image <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">?</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Size<span class="symbol">.</span>Width <span class="symbol">*</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span> <span class="symbol">:</span> <span class="number">0</span><span class="symbol">;</span> <span class="symbol">}</span> <span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">double</span> ZoomFactor
<span class="symbol">{</span> <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="symbol">(</span><span class="keyword">double</span><span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">/</span> <span class="number">100</span><span class="symbol">;</span> <span class="symbol">}</span> <span class="symbol">}</span>
</pre>
</figure>
<p>The <code>AdjustLayout</code> method which determines the appropriate
course of action when certain properties are changed has been
updated to support the size to fit functionality by calling the
new <code>ZoomToFit</code> method.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> AdjustLayout<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>AutoSize<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>AdjustSize<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>SizeToFit<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>ZoomToFit<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>AutoScroll<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>AdjustViewPort<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">virtual</span> <span class="keyword">void</span> ZoomToFit<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Image <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Rectangle innerRectangle<span class="symbol">;</span>
 <span class="keyword">double</span> zoom<span class="symbol">;</span>
 <span class="keyword">double</span> aspectRatio<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>AutoScrollMinSize <span class="symbol">=</span> Size<span class="symbol">.</span>Empty<span class="symbol">;</span>

 innerRectangle <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetInsideViewPort<span class="symbol">(</span><span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Width <span class="symbol">&gt;</span> <span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Height<span class="symbol">)</span>
 <span class="symbol">{</span>
 aspectRatio <span class="symbol">=</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">double</span><span class="symbol">)</span>innerRectangle<span class="symbol">.</span>Width<span class="symbol">)</span> <span class="symbol">/</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">double</span><span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Width<span class="symbol">)</span><span class="symbol">;</span>
 zoom <span class="symbol">=</span> aspectRatio <span class="symbol">*</span> <span class="number">100.0</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>innerRectangle<span class="symbol">.</span>Height <span class="symbol">&lt;</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Height <span class="symbol">*</span> zoom<span class="symbol">)</span> <span class="symbol">/</span> <span class="number">100.0</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 aspectRatio <span class="symbol">=</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">double</span><span class="symbol">)</span>innerRectangle<span class="symbol">.</span>Height<span class="symbol">)</span> <span class="symbol">/</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">double</span><span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Height<span class="symbol">)</span><span class="symbol">;</span>
 zoom <span class="symbol">=</span> aspectRatio <span class="symbol">*</span> <span class="number">100.0</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 aspectRatio <span class="symbol">=</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">double</span><span class="symbol">)</span>innerRectangle<span class="symbol">.</span>Height<span class="symbol">)</span> <span class="symbol">/</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">double</span><span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Height<span class="symbol">)</span><span class="symbol">;</span>
 zoom <span class="symbol">=</span> aspectRatio <span class="symbol">*</span> <span class="number">100.0</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>innerRectangle<span class="symbol">.</span>Width <span class="symbol">&lt;</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Width <span class="symbol">*</span> zoom<span class="symbol">)</span> <span class="symbol">/</span> <span class="number">100.0</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 aspectRatio <span class="symbol">=</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">double</span><span class="symbol">)</span>innerRectangle<span class="symbol">.</span>Width<span class="symbol">)</span> <span class="symbol">/</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">double</span><span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Width<span class="symbol">)</span><span class="symbol">;</span>
 zoom <span class="symbol">=</span> aspectRatio <span class="symbol">*</span> <span class="number">100.0</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>Math<span class="symbol">.</span>Round<span class="symbol">(</span>Math<span class="symbol">.</span>Floor<span class="symbol">(</span>zoom<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Due to the additional complexity in positioning and sizing,
we're also adding functions to return the different regions in
use by the control.</p>
<ul>
<li><code>GetImageViewPort</code> - returns a rectangle representing the size
of the drawn image.</li>
<li><code>GetInsideViewPort</code> - returns a rectangle representing the
client area of the control, offset by the current border
style, and optionally padding.</li>
<li><code>GetSourceImageRegion</code> - returns a rectangle representing the
area of the source image that will be drawn onto the control.</li>
</ul>
<p>The sample project has been updated to be able to display the
results of the <code>GetImageViewPort</code> and <code>GetSourceImageRegion</code>
functions.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">virtual</span> Rectangle GetImageViewPort<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 Rectangle viewPort<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Image <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Rectangle innerRectangle<span class="symbol">;</span>
 Point offset<span class="symbol">;</span>

 innerRectangle <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetInsideViewPort<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>AutoCenter<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> x<span class="symbol">;</span>
 <span class="keyword">int</span> y<span class="symbol">;</span>

 x <span class="symbol">=</span> <span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>HScroll <span class="symbol">?</span> <span class="symbol">(</span>innerRectangle<span class="symbol">.</span>Width <span class="symbol">-</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ScaledImageWidth <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Horizontal<span class="symbol">)</span><span class="symbol">)</span> <span class="symbol">/</span> <span class="number">2</span> <span class="symbol">:</span> <span class="number">0</span><span class="symbol">;</span>
 y <span class="symbol">=</span> <span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>VScroll <span class="symbol">?</span> <span class="symbol">(</span>innerRectangle<span class="symbol">.</span>Height <span class="symbol">-</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ScaledImageHeight <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Vertical<span class="symbol">)</span><span class="symbol">)</span> <span class="symbol">/</span> <span class="number">2</span> <span class="symbol">:</span> <span class="number">0</span><span class="symbol">;</span>

 offset <span class="symbol">=</span> <span class="keyword">new</span> Point<span class="symbol">(</span>x<span class="symbol">,</span> y<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 offset <span class="symbol">=</span> Point<span class="symbol">.</span>Empty<span class="symbol">;</span>

 viewPort <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>offset<span class="symbol">.</span>X <span class="symbol">+</span> innerRectangle<span class="symbol">.</span>Left <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Left<span class="symbol">,</span> offset<span class="symbol">.</span>Y <span class="symbol">+</span> innerRectangle<span class="symbol">.</span>Top <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Top<span class="symbol">,</span> innerRectangle<span class="symbol">.</span>Width <span class="symbol">-</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Horizontal <span class="symbol">+</span> <span class="symbol">(</span>offset<span class="symbol">.</span>X <span class="symbol">*</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">,</span> innerRectangle<span class="symbol">.</span>Height <span class="symbol">-</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Vertical <span class="symbol">+</span> <span class="symbol">(</span>offset<span class="symbol">.</span>Y <span class="symbol">*</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 viewPort <span class="symbol">=</span> Rectangle<span class="symbol">.</span>Empty<span class="symbol">;</span>

 <span class="keyword">return</span> viewPort<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> Rectangle GetInsideViewPort<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>GetInsideViewPort<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">virtual</span> Rectangle GetInsideViewPort<span class="symbol">(</span><span class="keyword">bool</span> includePadding<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> left<span class="symbol">;</span>
 <span class="keyword">int</span> top<span class="symbol">;</span>
 <span class="keyword">int</span> width<span class="symbol">;</span>
 <span class="keyword">int</span> height<span class="symbol">;</span>
 <span class="keyword">int</span> borderOffset<span class="symbol">;</span>

 borderOffset <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetBorderOffset<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 left <span class="symbol">=</span> borderOffset<span class="symbol">;</span>
 top <span class="symbol">=</span> borderOffset<span class="symbol">;</span>
 width <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ClientSize<span class="symbol">.</span>Width <span class="symbol">-</span> <span class="symbol">(</span>borderOffset <span class="symbol">*</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>
 height <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ClientSize<span class="symbol">.</span>Height <span class="symbol">-</span> <span class="symbol">(</span>borderOffset <span class="symbol">*</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>includePadding<span class="symbol">)</span>
 <span class="symbol">{</span>
 left <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Left<span class="symbol">;</span>
 top <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Top<span class="symbol">;</span>
 width <span class="symbol">-=</span> <span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Horizontal<span class="symbol">;</span>
 height <span class="symbol">-=</span> <span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Vertical<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>left<span class="symbol">,</span> top<span class="symbol">,</span> width<span class="symbol">,</span> height<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">virtual</span> Rectangle GetSourceImageRegion<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> sourceLeft<span class="symbol">;</span>
 <span class="keyword">int</span> sourceTop<span class="symbol">;</span>
 <span class="keyword">int</span> sourceWidth<span class="symbol">;</span>
 <span class="keyword">int</span> sourceHeight<span class="symbol">;</span>
 Rectangle viewPort<span class="symbol">;</span>
 Rectangle region<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Image <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 viewPort <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetImageViewPort<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 sourceLeft <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span><span class="symbol">-</span><span class="keyword">this</span><span class="symbol">.</span>AutoScrollPosition<span class="symbol">.</span>X <span class="symbol">/</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span><span class="symbol">;</span>
 sourceTop <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span><span class="symbol">-</span><span class="keyword">this</span><span class="symbol">.</span>AutoScrollPosition<span class="symbol">.</span>Y <span class="symbol">/</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span><span class="symbol">;</span>
 sourceWidth <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span>viewPort<span class="symbol">.</span>Width <span class="symbol">/</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span><span class="symbol">;</span>
 sourceHeight <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span>viewPort<span class="symbol">.</span>Height <span class="symbol">/</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomFactor<span class="symbol">)</span><span class="symbol">;</span>

 region <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>sourceLeft<span class="symbol">,</span> sourceTop<span class="symbol">,</span> sourceWidth<span class="symbol">,</span> sourceHeight<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 region <span class="symbol">=</span> Rectangle<span class="symbol">.</span>Empty<span class="symbol">;</span>

 <span class="keyword">return</span> region<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="drawing-the-control">Drawing the control</h2>
<p>As with the previous versions, the control is drawn by
overriding <code>OnPaint</code>, this time we are not using clip regions or
drawing the entire image even if only a portion of it is
visible.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="comment">// draw the borders</span>
<span class="keyword">switch</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>BorderStyle<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">case</span> BorderStyle<span class="symbol">.</span>FixedSingle<span class="symbol">:</span>
 ControlPaint<span class="symbol">.</span>DrawBorder<span class="symbol">(</span>e<span class="symbol">.</span>Graphics<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ClientRectangle<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ForeColor<span class="symbol">,</span> ButtonBorderStyle<span class="symbol">.</span>Solid<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> BorderStyle<span class="symbol">.</span>Fixed<span class="number">3</span>D<span class="symbol">:</span>
 ControlPaint<span class="symbol">.</span>DrawBorder<span class="number">3</span>D<span class="symbol">(</span>e<span class="symbol">.</span>Graphics<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ClientRectangle<span class="symbol">,</span> Border<span class="number">3</span>DStyle<span class="symbol">.</span>Sunken<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Depending on the value of the <code>GridDisplayMode</code> property, the
background tile grid will either not be displayed, will be
displayed to fill the client area of the control, or new for
this update, to only fill the area behind the image. The
remainder of the control is filled with the background color.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
Rectangle innerRectangle<span class="symbol">;</span>

innerRectangle <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetInsideViewPort<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

<span class="comment">// draw the background</span>
<span class="keyword">using</span> <span class="symbol">(</span>SolidBrush brush <span class="symbol">=</span> <span class="keyword">new</span> SolidBrush<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>BackColor<span class="symbol">)</span><span class="symbol">)</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>FillRectangle<span class="symbol">(</span>brush<span class="symbol">,</span> innerRectangle<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">if</span> <span class="symbol">(</span>_texture <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>GridDisplayMode <span class="symbol">!=</span> ImageBoxGridDisplayMode<span class="symbol">.</span>None<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">switch</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>GridDisplayMode<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> ImageBoxGridDisplayMode<span class="symbol">.</span>Image<span class="symbol">:</span>
 Rectangle fillRectangle<span class="symbol">;</span>

 fillRectangle <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetImageViewPort<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>FillRectangle<span class="symbol">(</span>_texture<span class="symbol">,</span> fillRectangle<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>fillRectangle<span class="symbol">.</span>Equals<span class="symbol">(</span>innerRectangle<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 fillRectangle<span class="symbol">.</span>Inflate<span class="symbol">(</span><span class="number">1</span><span class="symbol">,</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 ControlPaint<span class="symbol">.</span>DrawBorder<span class="symbol">(</span>e<span class="symbol">.</span>Graphics<span class="symbol">,</span> fillRectangle<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ForeColor<span class="symbol">,</span> ButtonBorderStyle<span class="symbol">.</span>Solid<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> ImageBoxGridDisplayMode<span class="symbol">.</span>Client<span class="symbol">:</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>FillRectangle<span class="symbol">(</span>_texture<span class="symbol">,</span> innerRectangle<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Previous versions of the control drew the entire image using the
<code>DrawImageUnscaled</code> method of the <code>Graphics</code> object. In this
final version, we're going to be a little more intelligent and
only draw the visible area, removing the need for the previous
clip region. The <code>InterpolationMode</code> is used to determine how
the image is drawn when it is zoomed in or out.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="comment">// draw the image</span>
g<span class="symbol">.</span>InterpolationMode <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>InterpolationMode<span class="symbol">;</span>
g<span class="symbol">.</span>DrawImage<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>GetImageViewPort<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>GetSourceImageRegion<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">,</span> GraphicsUnit<span class="symbol">.</span>Pixel<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="zooming-support">Zooming Support</h2>
<p>With the control now all set up and fully supporting zoom, it's
time to allow the end user to be able to change the zoom.</p>
<p>The first step is to disable the ability to double click the
control, by modifying the control styles in the constructor.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">this</span><span class="symbol">.</span>SetStyle<span class="symbol">(</span>ControlStyles<span class="symbol">.</span>StandardDoubleClick<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>We're going to allow the zoom to be changed two ways - by either
scrolling the mouse wheel, or left/right clicking the control.</p>
<p>By overriding <code>OnMouseWheel</code>, we can be notified when the user
spins the wheel, and in which direction. We then adjust the zoom
using the value of the <code>ZoomIncrement</code> property. If a modifier
key such as Shift or Control is pressed, then we'll modify the
zoom by five times the increment.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseWheel<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>SizeToFit<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> increment<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>Control<span class="symbol">.</span>ModifierKeys <span class="symbol">==</span> Keys<span class="symbol">.</span>None<span class="symbol">)</span>
 increment <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomIncrement<span class="symbol">;</span>
 <span class="keyword">else</span>
 increment <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ZoomIncrement <span class="symbol">*</span> <span class="number">5</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Delta <span class="symbol">&lt;</span> <span class="number">0</span><span class="symbol">)</span>
 increment <span class="symbol">=</span> <span class="symbol">-</span>increment<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">+=</span> increment<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Normally, whenever we override a method, we always call it's
base implementation. However, in this case we will not; the
<code>ScrollbableControl</code> that we inherit from uses the mouse wheel
to scroll the viewport and there doesn't seem to be a way to
disable this undesirable behaviour.</p>
<p>As we also want to allow the user to be able to click the
control with the left mouse button to zoom in, and either the
right mouse button or left button holding a modifier key to zoom
out, we'll also override <code>OnMouseClick</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseClick<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>IsPanning <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>SizeToFit<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Button <span class="symbol">==</span> MouseButtons<span class="symbol">.</span>Left <span class="symbol">&amp;&amp;</span> Control<span class="symbol">.</span>ModifierKeys <span class="symbol">==</span> Keys<span class="symbol">.</span>None<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">&gt;=</span> <span class="number">100</span><span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>Math<span class="symbol">.</span>Round<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">double</span><span class="symbol">)</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">+</span> <span class="number">100</span><span class="symbol">)</span> <span class="symbol">/</span> <span class="number">100</span><span class="symbol">)</span> <span class="symbol">*</span> <span class="number">100</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">&gt;=</span> <span class="number">75</span><span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">=</span> <span class="number">100</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 <span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">/</span> <span class="number">0.75</span>F<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Button <span class="symbol">==</span> MouseButtons<span class="symbol">.</span>Right <span class="symbol">||</span> <span class="symbol">(</span>e<span class="symbol">.</span>Button <span class="symbol">==</span> MouseButtons<span class="symbol">.</span>Left <span class="symbol">&amp;&amp;</span> Control<span class="symbol">.</span>ModifierKeys <span class="symbol">!=</span> Keys<span class="symbol">.</span>None<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">&gt;</span> <span class="number">100</span> <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">&lt;=</span> <span class="number">125</span><span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">=</span> <span class="number">100</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">&gt;</span> <span class="number">100</span><span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>Math<span class="symbol">.</span>Round<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">double</span><span class="symbol">)</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">-</span> <span class="number">100</span><span class="symbol">)</span> <span class="symbol">/</span> <span class="number">100</span><span class="symbol">)</span> <span class="symbol">*</span> <span class="number">100</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 <span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Zoom <span class="symbol">*</span> <span class="number">0.75</span>F<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnMouseClick<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Unlike with the mouse wheel and it's fixed increment, we want to
use a different approach with clicking. If zooming out and the
percentage is more than 100, then the zoom level will be set to
the current zoom level + 100, but rounded to the nearest 100,
and the same in reverse for zooming in.</p>
<p>If the current zoom is less than 100, then the new value will +-
75% of the current zoom, or reset to 100 if the new value falls
between 75 and 125.</p>
<p>This results in a nicer zoom experience then just using a fixed
value.</p>
<h2 id="sample-project">Sample Project</h2>
<p>You can download the final sample project from the link below.</p>
<h2 id="whats-next">What's next?</h2>
<p>One of the really annoying issues with this control that has
plagued me during writing this series is scrolling the
component. During scrolling there is an annoying flicker as the
original contents are moved, then the new contents are drawn. At
present I don't have a solution for this, I've tried overriding
various <code>WM_*</code> messages but without success. A future update to
this component will either fix this issue, or do it's own
scrollbar support without inheriting from <code>ScrollableControl</code>,
although I'd like to avoid this latter solution.</p>
<p>If anyone knows of a solution please let us know!</p>
<p>Another enhancement would be intelligent use of the
interpolation mode. Currently the control uses a fixed value,
but some values are better when zoomed in, and some better when
zoomed out. The ability for the control to automatically select
the most appropriate mode would be useful.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-08-28 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-a-scrollable-and-zoomable-image-viewer-in-csharp-part-4 .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a scrollable and zoomable image viewer in C# Part 3urn:uuid:4e72fcb8-5729-4038-a693-087d0e9e8f592012-11-25T09:21:03Z2010-08-23T18:08:01Z<p>After part 2 added scrolling support, we are now going to extend
this to support keyboard scrolling and panning with the mouse.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/imgbox-3.png" class="gallery" title="The ImageBox component, demonstrated in a sample application" ><img src="https://images.cyotek.com/image/thumbnail/devblog/imgbox-3.png" alt="The ImageBox component, demonstrated in a sample application" decoding="async" loading="lazy" /></a><figcaption>The ImageBox component, demonstrated in a sample application</figcaption></figure><h2 id="design-support">Design support</h2>
<p>In order to enable panning, we're going to add three new
properties. The <code>AutoPan</code> property will control if the user can
click and drag the image with the mouse in order to scroll.
Also, we'll add an <code>InvertMouse</code> property to control how the
scrolling works. Finally the <code>IsPanning</code> property; however it
can only be read publicly, not set.</p>
<p>As well as the backing events for the above properties, we'll
also add extra events - <code>PanStart</code> and <code>PanEnd</code> The normal
<code>Scroll</code> event will be utilized while panning is in progress
rather than a custom event.</p>
<h2 id="mouse-panning">Mouse Panning</h2>
<p>To pan with the mouse, the user needs to &quot;grab&quot; the control by
clicking and holding down the left mouse button. As they move
the mouse, the control should automatically scroll in the
opposite direction the mouse is moving (or if <code>InvertMouse</code> is
set, in the same direction). Once the button is released,
scrolling should stop.</p>
<p>We'll implement this by overriding <code>OnMouseMove</code> and
<code>OnMouseUp</code>, shown below.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseMove<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnMouseMove<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Button <span class="symbol">==</span> MouseButtons<span class="symbol">.</span>Left <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>AutoPan <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>Image <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>IsPanning<span class="symbol">)</span>
 <span class="symbol">{</span>
 _startMousePosition <span class="symbol">=</span> e<span class="symbol">.</span>Location<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>IsPanning <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsPanning<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> x<span class="symbol">;</span>
 <span class="keyword">int</span> y<span class="symbol">;</span>
 Point position<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>InvertMouse<span class="symbol">)</span>
 <span class="symbol">{</span>
 x <span class="symbol">=</span> <span class="symbol">-</span>_startScrollPosition<span class="symbol">.</span>X <span class="symbol">+</span> <span class="symbol">(</span>_startMousePosition<span class="symbol">.</span>X <span class="symbol">-</span> e<span class="symbol">.</span>Location<span class="symbol">.</span>X<span class="symbol">)</span><span class="symbol">;</span>
 y <span class="symbol">=</span> <span class="symbol">-</span>_startScrollPosition<span class="symbol">.</span>Y <span class="symbol">+</span> <span class="symbol">(</span>_startMousePosition<span class="symbol">.</span>Y <span class="symbol">-</span> e<span class="symbol">.</span>Location<span class="symbol">.</span>Y<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 x <span class="symbol">=</span> <span class="symbol">-</span><span class="symbol">(</span>_startScrollPosition<span class="symbol">.</span>X <span class="symbol">+</span> <span class="symbol">(</span>_startMousePosition<span class="symbol">.</span>X <span class="symbol">-</span> e<span class="symbol">.</span>Location<span class="symbol">.</span>X<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 y <span class="symbol">=</span> <span class="symbol">-</span><span class="symbol">(</span>_startScrollPosition<span class="symbol">.</span>Y <span class="symbol">+</span> <span class="symbol">(</span>_startMousePosition<span class="symbol">.</span>Y <span class="symbol">-</span> e<span class="symbol">.</span>Location<span class="symbol">.</span>Y<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 position <span class="symbol">=</span> <span class="keyword">new</span> Point<span class="symbol">(</span>x<span class="symbol">,</span> y<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>UpdateScrollPosition<span class="symbol">(</span>position<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseUp<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnMouseUp<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsPanning<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>IsPanning <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> UpdateScrollPosition<span class="symbol">(</span>Point position<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>AutoScrollPosition <span class="symbol">=</span> position<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnScroll<span class="symbol">(</span><span class="keyword">new</span> ScrollEventArgs<span class="symbol">(</span>ScrollEventType<span class="symbol">.</span>ThumbPosition<span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p><code>UpdateScrollPosition</code> is a common method to set the viewport
and refresh the control. The <code>IsPanning</code> property is used to
notify the control internally that a pan operation has been
started. It will also set a semi-appropriate cursor (we'll look
at custom cursors another time), and raise either the <code>PanStart</code>
or <code>PanEnd</code> events.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>DefaultValue<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">,</span> DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">,</span> Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">bool</span> IsPanning
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _isPanning<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">protected</span> <span class="keyword">set</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_isPanning <span class="symbol">!=</span> value<span class="symbol">)</span>
 <span class="symbol">{</span>
 _isPanning <span class="symbol">=</span> value<span class="symbol">;</span>
 _startScrollPosition <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>AutoScrollPosition<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>value<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Cursor <span class="symbol">=</span> Cursors<span class="symbol">.</span>SizeAll<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnPanStart<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Cursor <span class="symbol">=</span> Cursors<span class="symbol">.</span>Default<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnPanEnd<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="keyboard-scrolling">Keyboard Scrolling</h2>
<p>The first two versions of this component effectively disabled
keyboard support via the <code>ControlStyles.Selectable</code> control
style and <code>TabStop</code> property. However, we now want to allow
keyboard support. So the first thing we do is remove the call to
disable the selectable style and resetting of the tab stop
property from the constructor. We also remove the custom
<code>TabStop</code> property we had implemented for attribute overriding.</p>
<p>With this done, we can now add some keyboard support. As the
<code>ScrollableControl</code> doesn't natively support this, we'll do it
ourselves by overriding <code>OnKeyDown</code>. One of the initial
drawbacks is that it won't always capture special keys, such as
the arrow keys.</p>
<p>In order for it to do so we need to let the control know that
such keys are required by overriding <code>IsInputKey</code> - if this
returns <code>true</code>, then the specified key is required and will be
captured in <code>OnKeyDown</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">bool</span> IsInputKey<span class="symbol">(</span>Keys keyData<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">(</span>keyData <span class="symbol">&amp;</span> Keys<span class="symbol">.</span>Right<span class="symbol">)</span> <span class="symbol">==</span> Keys<span class="symbol">.</span>Right <span class="symbol">|</span> <span class="symbol">(</span>keyData <span class="symbol">&amp;</span> Keys<span class="symbol">.</span>Left<span class="symbol">)</span> <span class="symbol">==</span> Keys<span class="symbol">.</span>Left <span class="symbol">|</span> <span class="symbol">(</span>keyData <span class="symbol">&amp;</span> Keys<span class="symbol">.</span>Up<span class="symbol">)</span> <span class="symbol">==</span> Keys<span class="symbol">.</span>Up <span class="symbol">|</span> <span class="symbol">(</span>keyData <span class="symbol">&amp;</span> Keys<span class="symbol">.</span>Down<span class="symbol">)</span> <span class="symbol">==</span> Keys<span class="symbol">.</span>Down<span class="symbol">)</span>
 result <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 result <span class="symbol">=</span> <span class="keyword">base</span><span class="symbol">.</span>IsInputKey<span class="symbol">(</span>keyData<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnKeyDown<span class="symbol">(</span>KeyEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnKeyDown<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">switch</span> <span class="symbol">(</span>e<span class="symbol">.</span>KeyCode<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> Keys<span class="symbol">.</span>Left<span class="symbol">:</span>
 <span class="keyword">this</span><span class="symbol">.</span>AdjustScroll<span class="symbol">(</span><span class="symbol">-</span><span class="symbol">(</span>e<span class="symbol">.</span>Modifiers <span class="symbol">==</span> Keys<span class="symbol">.</span>None <span class="symbol">?</span> <span class="keyword">this</span><span class="symbol">.</span>HorizontalScroll<span class="symbol">.</span>SmallChange <span class="symbol">:</span> <span class="keyword">this</span><span class="symbol">.</span>HorizontalScroll<span class="symbol">.</span>LargeChange<span class="symbol">)</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> Keys<span class="symbol">.</span>Right<span class="symbol">:</span>
 <span class="keyword">this</span><span class="symbol">.</span>AdjustScroll<span class="symbol">(</span>e<span class="symbol">.</span>Modifiers <span class="symbol">==</span> Keys<span class="symbol">.</span>None <span class="symbol">?</span> <span class="keyword">this</span><span class="symbol">.</span>HorizontalScroll<span class="symbol">.</span>SmallChange <span class="symbol">:</span> <span class="keyword">this</span><span class="symbol">.</span>HorizontalScroll<span class="symbol">.</span>LargeChange<span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> Keys<span class="symbol">.</span>Up<span class="symbol">:</span>
 <span class="keyword">this</span><span class="symbol">.</span>AdjustScroll<span class="symbol">(</span><span class="number">0</span><span class="symbol">,</span> <span class="symbol">-</span><span class="symbol">(</span>e<span class="symbol">.</span>Modifiers <span class="symbol">==</span> Keys<span class="symbol">.</span>None <span class="symbol">?</span> <span class="keyword">this</span><span class="symbol">.</span>VerticalScroll<span class="symbol">.</span>SmallChange <span class="symbol">:</span> <span class="keyword">this</span><span class="symbol">.</span>VerticalScroll<span class="symbol">.</span>LargeChange<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> Keys<span class="symbol">.</span>Down<span class="symbol">:</span>
 <span class="keyword">this</span><span class="symbol">.</span>AdjustScroll<span class="symbol">(</span><span class="number">0</span><span class="symbol">,</span> e<span class="symbol">.</span>Modifiers <span class="symbol">==</span> Keys<span class="symbol">.</span>None <span class="symbol">?</span> <span class="keyword">this</span><span class="symbol">.</span>VerticalScroll<span class="symbol">.</span>SmallChange <span class="symbol">:</span> <span class="keyword">this</span><span class="symbol">.</span>VerticalScroll<span class="symbol">.</span>LargeChange<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> AdjustScroll<span class="symbol">(</span><span class="keyword">int</span> x<span class="symbol">,</span> <span class="keyword">int</span> y<span class="symbol">)</span>
<span class="symbol">{</span>
 Point scrollPosition<span class="symbol">;</span>

 scrollPosition <span class="symbol">=</span> <span class="keyword">new</span> Point<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>HorizontalScroll<span class="symbol">.</span>Value <span class="symbol">+</span> x<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>VerticalScroll<span class="symbol">.</span>Value <span class="symbol">+</span> y<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>UpdateScrollPosition<span class="symbol">(</span>scrollPosition<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>When the left, right, up or down arrow keys are pressed, the
control checks to see if a modifier such as shift or control is
active. If not, then the control is scrolled either horizontally
or vertically using the &quot;small change&quot; value of the appropriate
scrollbar. If a modifier was set, then the scroll is made using
the &quot;large change&quot; value.</p>
<p>The <code>AdjustScroll</code> method is used to &quot;nudge&quot; the scrollbars in
the given direction, using values read from the
<code>HorizontalScroll</code> and <code>VerticalScroll</code> - reading the
<code>AutoScrollPosition</code> property didn't return appropriate results
in our testing.</p>
<h4 id="sample-project">Sample Project</h4>
<p>You can download the third sample project from the links below.
The final article in the series will add autofit, centering and
of course, zoom support.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-08-23 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-a-scrollable-and-zoomable-image-viewer-in-csharp-part-3 .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a scrollable and zoomable image viewer in C# Part 2urn:uuid:16fc4d3c-b9d4-469f-88b2-3b920b63e7e92012-11-25T09:20:51Z2010-08-13T20:22:52Z<p>In the second part of our <a href="/post/creating-a-scrollable-and-zoomable-image-viewer-in-csharp-part-1">Creating a scrollable and zoomable
image viewer in C#</a> series we will update our component to
support automatic scrolling when auto size is disabled and the
image is larger than the client area of the control.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/imgbox-2.png" class="gallery" title="The ImageBox component, demonstrated in a sample application" ><img src="https://images.cyotek.com/image/thumbnail/devblog/imgbox-2.png" alt="The ImageBox component, demonstrated in a sample application" decoding="async" loading="lazy" /></a><figcaption>The ImageBox component, demonstrated in a sample application</figcaption></figure><h2 id="setting-up-auto-scrolling">Setting up auto scrolling</h2>
<p>Originally we inherited from <code>Control</code>, however this does not
support automatic scrolling. Rather than reinventing the wheel
at this point, we'll change the control to inherit from
<code>ScrollableControl</code> instead. This will expose a number of new
members, the ones we need are:</p>
<ul>
<li><code>AutoScroll</code> - Enables or disables automatic scrolling</li>
<li><code>AutoScrollMinSize</code> - Specifies the minimum size before
scrollbars appear</li>
<li><code>AutoScrollPosition</code> - Specifies the current scroll position</li>
<li><code>OnScroll</code> - Raised when the scroll position is changed</li>
</ul>
<p>Using the above we can now offer full scrolling.</p>
<p>As the control will take care of the scrolling behaviour, we
don't want the <code>AutoScrollMinSize</code> property to be available, so
we'll declare a new version of it and hide it with attributes.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">,</span> EditorBrowsable<span class="symbol">(</span>EditorBrowsableState<span class="symbol">.</span>Never<span class="symbol">)</span><span class="symbol">,</span> DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">new</span> Size AutoScrollMainSize
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">base</span><span class="symbol">.</span>AutoScrollMinSize<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>AutoScrollMinSize <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Initially the component only offered auto sizing and so we had
defined an <code>AdjustSize</code> method which was called in response to
various events and property changes. As we now need to set up
the scrolling area if <code>AutoScroll</code> is enabled, this method is no
longer as suitable. Instead, we add a pair of new methods,
<code>AdjustLayout</code> and <code>AdjustScrolling</code>. Existing calls to
<code>AdjustSize</code> are changed to call <code>AdjustLayout</code> instead, and
this method now calls either <code>AdjustScrolling</code> or <code>AdjustSize</code>
depending on the state of the <code>AutoSize</code> and <code>AutoScroll</code>
properties.</p>
<p>The <code>AdjustScrolling</code> method is used to set the
<code>AutoScrollMainSize</code> property. When this is correctly set, the
<code>ScrollableControl</code> will automatically take care of displaying
scrollbars.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> AdjustLayout<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>AutoSize<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>AdjustSize<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>AutoScroll<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>AdjustScrolling<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> AdjustScrolling<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>AutoScroll <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>Image <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>AutoScrollMinSize <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">.</span>Size<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="reacting-to-scroll-changes">Reacting to scroll changes</h2>
<p>By overriding the <code>OnScroll</code> event we get notifications whenever
the user scrolls the control, and can therefore redraw the
image.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnScroll<span class="symbol">(</span>ScrollEventArgs se<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnScroll<span class="symbol">(</span>se<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="painting-adjustments">Painting adjustments</h2>
<p>The initial version of our <code>ImageBox</code> tiled a bitmap across the
client area of the control. In this new version, when we create
the background tile, we now create a new <code>TextureBrush</code>. During
drawing we can call <code>FillRectangle</code> and pass in the new brush
and it will be tiled for us.</p>
<p>Another shortcoming of the first version was the borders. These
were painted last, so that if the image was larger than the
controls client area, the image wouldn't be painted on top of
the borders. Now, the borders are drawn first and a clip region
applied to prevent any overlap.</p>
<p>Finally of course, the position of the drawn image needs to
reflect any scrollbar offset.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnPaint<span class="symbol">(</span>PaintEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> borderOffset<span class="symbol">;</span>
 Rectangle innerRectangle<span class="symbol">;</span>

 borderOffset <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetBorderOffset<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>borderOffset <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// draw the borders</span>
 <span class="keyword">switch</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>BorderStyle<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> BorderStyle<span class="symbol">.</span>FixedSingle<span class="symbol">:</span>
 ControlPaint<span class="symbol">.</span>DrawBorder<span class="symbol">(</span>e<span class="symbol">.</span>Graphics<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ClientRectangle<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ForeColor<span class="symbol">,</span> ButtonBorderStyle<span class="symbol">.</span>Solid<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> BorderStyle<span class="symbol">.</span>Fixed<span class="number">3</span>D<span class="symbol">:</span>
 ControlPaint<span class="symbol">.</span>DrawBorder<span class="number">3</span>D<span class="symbol">(</span>e<span class="symbol">.</span>Graphics<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ClientRectangle<span class="symbol">,</span> Border<span class="number">3</span>DStyle<span class="symbol">.</span>Sunken<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="comment">// clip the background so we don&#39;t overwrite the border</span>
 innerRectangle <span class="symbol">=</span> Rectangle<span class="symbol">.</span>Inflate<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ClientRectangle<span class="symbol">,</span> <span class="symbol">-</span>borderOffset<span class="symbol">,</span> <span class="symbol">-</span>borderOffset<span class="symbol">)</span><span class="symbol">;</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>SetClip<span class="symbol">(</span>innerRectangle<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 innerRectangle <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ClientRectangle<span class="symbol">;</span>

 <span class="comment">// draw the background</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_texture <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>ShowGrid<span class="symbol">)</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>FillRectangle<span class="symbol">(</span>_texture<span class="symbol">,</span> innerRectangle<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>SolidBrush brush <span class="symbol">=</span> <span class="keyword">new</span> SolidBrush<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>BackColor<span class="symbol">)</span><span class="symbol">)</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>FillRectangle<span class="symbol">(</span>brush<span class="symbol">,</span> innerRectangle<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="comment">// draw the image</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Image <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> left<span class="symbol">;</span>
 <span class="keyword">int</span> top<span class="symbol">;</span>

 left <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Left <span class="symbol">+</span> borderOffset<span class="symbol">;</span>
 top <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Top <span class="symbol">+</span> borderOffset<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>AutoScroll<span class="symbol">)</span>
 <span class="symbol">{</span>
 left <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>AutoScrollPosition<span class="symbol">.</span>X<span class="symbol">;</span>
 top <span class="symbol">+=</span> <span class="keyword">this</span><span class="symbol">.</span>AutoScrollPosition<span class="symbol">.</span>Y<span class="symbol">;</span>
 <span class="symbol">}</span>

 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>DrawImageUnscaled<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">,</span> <span class="keyword">new</span> Point<span class="symbol">(</span>left<span class="symbol">,</span> top<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="comment">// reset the clipping</span>
 <span class="keyword">if</span> <span class="symbol">(</span>borderOffset <span class="symbol">!=</span> <span class="number">0</span><span class="symbol">)</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>ResetClip<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="sample-project">Sample Project</h2>
<p>You can download the second sample project from the link below.
The next article in the series will look at panning the image
using the mouse within the client area of the image control.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-08-13 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-a-scrollable-and-zoomable-image-viewer-in-csharp-part-2 .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a scrollable and zoomable image viewer in C# Part 1urn:uuid:02fd4283-ddf1-4672-9a64-39b46b02c3f72012-11-25T09:20:38Z2010-08-12T20:14:30Z<p>This is the first part in a series of articles that will result
in a component for viewing an image. The final component will
support zooming and scrolling.</p>
<p>In this first part, we're going to create a basic image viewer,
without the scrolling and zooming. Rather than having a plain
background however, we're going to create a two tone checker box
effect which is often used for showing transparent images. We'll
also allow this to be disabled and a solid colour used instead.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/imgbox.png" class="gallery" title="The sample project in action." ><img src="https://images.cyotek.com/image/thumbnail/devblog/imgbox.png" alt="The sample project in action." decoding="async" loading="lazy" /></a><figcaption>The sample project in action.</figcaption></figure><h2 id="creating-the-component">Creating the component</h2>
<p>The component inherits from <code>Control</code> rather than something
like <code>PictureBox</code> or <code>Panel</code> as we want to provide a lot of
our own behaviour.</p>
<p>The first thing we'll do is override some properties - to hide
the ones we won't be using such as <code>Text</code> and <code>Font</code>, and to
modify others, such as making <code>AutoSize</code> visible, and changing
the default value of <code>BackColor</code>.</p>
<p>Next is to add some new properties. We'll create the following
properties and respective change events:</p>
<ul>
<li><code>BorderStyle</code> - A standard border style.</li>
<li><code>GridCellSize</code> - The basic cell size.</li>
<li><code>GridColor</code> and <code>GridColorAlternate</code> - The colors used to
create the checkerboard style background.</li>
<li><code>GridScale</code> - A property for scaling the <code>GridCellSize</code> for
user interface options.</li>
<li><code>Image</code> - The image to be displayed.</li>
<li><code>ShowGrid</code> - Flag to determine if the checkerboard background
should be displayed.</li>
</ul>
<p>As we are offering auto size support, we also override some
existing events so we can resize when certain actions occur,
such as changing the control's padding or parent.</p>
<h2 id="setting-control-styles">Setting control styles</h2>
<p>As well as setting up default property values, the component's
constructor also adjusts several control styles.</p>
<ul>
<li><code>AllPaintingInWmPaint</code> - We don't need a separate
<code>OnPaintBackground</code> and <code>OnPaint</code> mechanism, <code>OnPaint</code> will do
fine.</li>
<li><code>UserPaint</code> - As we are doing entirely our own painting, we
disable the base <strong>Control</strong>'s painting.</li>
<li><code>OptimizedDoubleBuffer</code> - Double buffering means the painting
will occur in a memory buffer before being transferred to the
screen, reducing flicker.</li>
<li><code>ResizeRedraw</code> - Automatically redraw the component if it is
resized.</li>
<li><code>Selectable</code> - We disable this flag as we don't want the
control to be receiving focus.</li>
</ul>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> ImageBox<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 InitializeComponent<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>SetStyle<span class="symbol">(</span>ControlStyles<span class="symbol">.</span>AllPaintingInWmPaint <span class="symbol">|</span> ControlStyles<span class="symbol">.</span>UserPaint <span class="symbol">|</span> ControlStyles<span class="symbol">.</span>OptimizedDoubleBuffer<span class="symbol">|</span> ControlStyles<span class="symbol">.</span>ResizeRedraw<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>SetStyle<span class="symbol">(</span>ControlStyles<span class="symbol">.</span>Selectable<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>UpdateStyles<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>BackColor <span class="symbol">=</span> Color<span class="symbol">.</span>White<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>TabStop <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>AutoSize <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>GridScale <span class="symbol">=</span> ImageBoxGridScale<span class="symbol">.</span>Small<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>ShowGrid <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>GridColor <span class="symbol">=</span> Color<span class="symbol">.</span>Gainsboro<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>GridColorAlternate <span class="symbol">=</span> Color<span class="symbol">.</span>White<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>GridCellSize <span class="symbol">=</span> <span class="number">8</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>BorderStyle <span class="symbol">=</span> BorderStyle<span class="symbol">.</span>FixedSingle<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="creating-the-background">Creating the background</h2>
<p>The <code>CreateGridTileImage</code> method creates a tile of a 2x2 grid
using many of the properties listed above which is then tiled
across the background of the control.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> Bitmap CreateGridTileImage<span class="symbol">(</span><span class="keyword">int</span> cellSize<span class="symbol">,</span> Color firstColor<span class="symbol">,</span> Color secondColor<span class="symbol">)</span>
<span class="symbol">{</span>
 Bitmap result<span class="symbol">;</span>
 <span class="keyword">int</span> width<span class="symbol">;</span>
 <span class="keyword">int</span> height<span class="symbol">;</span>
 <span class="keyword">float</span> scale<span class="symbol">;</span>

 <span class="comment">// rescale the cell size</span>
 <span class="keyword">switch</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>GridScale<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> ImageBoxGridScale<span class="symbol">.</span>Medium<span class="symbol">:</span>
 scale <span class="symbol">=</span> <span class="number">1.5</span>F<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> ImageBoxGridScale<span class="symbol">.</span>Large<span class="symbol">:</span>
 scale <span class="symbol">=</span> <span class="number">2</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">default</span><span class="symbol">:</span>
 scale <span class="symbol">=</span> <span class="number">1</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 cellSize <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span>cellSize <span class="symbol">*</span> scale<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// draw the tile</span>
 width <span class="symbol">=</span> cellSize <span class="symbol">*</span> <span class="number">2</span><span class="symbol">;</span>
 height <span class="symbol">=</span> cellSize <span class="symbol">*</span> <span class="number">2</span><span class="symbol">;</span>
 result <span class="symbol">=</span> <span class="keyword">new</span> Bitmap<span class="symbol">(</span>width<span class="symbol">,</span> height<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">using</span> <span class="symbol">(</span>Graphics g <span class="symbol">=</span> Graphics<span class="symbol">.</span>FromImage<span class="symbol">(</span>result<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>SolidBrush brush <span class="symbol">=</span> <span class="keyword">new</span> SolidBrush<span class="symbol">(</span>firstColor<span class="symbol">)</span><span class="symbol">)</span>
 g<span class="symbol">.</span>FillRectangle<span class="symbol">(</span>brush<span class="symbol">,</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span><span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> width<span class="symbol">,</span> height<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>SolidBrush brush <span class="symbol">=</span> <span class="keyword">new</span> SolidBrush<span class="symbol">(</span>secondColor<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 g<span class="symbol">.</span>FillRectangle<span class="symbol">(</span>brush<span class="symbol">,</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span><span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> cellSize<span class="symbol">,</span> cellSize<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 g<span class="symbol">.</span>FillRectangle<span class="symbol">(</span>brush<span class="symbol">,</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>cellSize<span class="symbol">,</span> cellSize<span class="symbol">,</span> cellSize<span class="symbol">,</span> cellSize<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="painting-the-control">Painting the control</h2>
<p>As described above, we've disabled all default painting, so we
simply need to override <code>OnPaint</code> and do our custom painting
here.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnPaint<span class="symbol">(</span>PaintEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_gridTile <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>ShowGrid<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// draw the background</span>
 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> x <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> x <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>ClientSize<span class="symbol">.</span>Width<span class="symbol">;</span> x <span class="symbol">+=</span> _gridTile<span class="symbol">.</span>Size<span class="symbol">.</span>Width<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> y <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> y <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>ClientSize<span class="symbol">.</span>Height<span class="symbol">;</span> y <span class="symbol">+=</span> _gridTile<span class="symbol">.</span>Size<span class="symbol">.</span>Height<span class="symbol">)</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>DrawImageUnscaled<span class="symbol">(</span>_gridTile<span class="symbol">,</span> x<span class="symbol">,</span> y<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>SolidBrush brush <span class="symbol">=</span> <span class="keyword">new</span> SolidBrush<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>BackColor<span class="symbol">)</span><span class="symbol">)</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>FillRectangle<span class="symbol">(</span>brush<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ClientRectangle<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="comment">// draw the image</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Image <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>DrawImageUnscaled<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Image<span class="symbol">,</span> <span class="keyword">new</span> Point<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Left <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>GetBorderOffset<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Top <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>GetBorderOffset<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="comment">// draw the borders</span>
 <span class="keyword">switch</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>BorderStyle<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> BorderStyle<span class="symbol">.</span>FixedSingle<span class="symbol">:</span>
 ControlPaint<span class="symbol">.</span>DrawBorder<span class="symbol">(</span>e<span class="symbol">.</span>Graphics<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ClientRectangle<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ForeColor<span class="symbol">,</span> ButtonBorderStyle<span class="symbol">.</span>Solid<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> BorderStyle<span class="symbol">.</span>Fixed<span class="number">3</span>D<span class="symbol">:</span>
 ControlPaint<span class="symbol">.</span>DrawBorder<span class="number">3</span>D<span class="symbol">(</span>e<span class="symbol">.</span>Graphics<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>ClientRectangle<span class="symbol">,</span> Border<span class="number">3</span>DStyle<span class="symbol">.</span>Sunken<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>First, we either draw a solid background using the <code>BackColor</code>
property if <code>ShowGrid</code> is <code>false</code>, otherwise we tile the grid
image created earlier.</p>
<p>Next we draw the actual image, if one has been set. The image is
offset based on the border style and padding.</p>
<p>Finally we draw the border style to ensure it appears on top of
the image if autosize is disabled and the control is too small.</p>
<h4 id="sample-project">Sample Project</h4>
<p>You can download the first sample project from the links below.
The next article in the series will look at implementing
scrolling for when the image is larger than the display area of
the control.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-08-12 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-a-scrollable-and-zoomable-image-viewer-in-csharp-part-1 .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comAdding a horizontal scrollbar to a ComboBox using C#urn:uuid:15bcc036-d651-472f-8556-48a77261dae62010-07-13T17:29:48Z2010-07-13T17:29:48Z<p>In our <a href="https://cyotek.com/cyotek-webcopy">WebCopy</a> application we decided to
update the User Agent configuration to allow selection from a
predefined list of common agents, but still allow the user to
enter their own custom agent if required.</p>
<p>Rather than use two separate fields, we choose to use a
<code>ComboBox</code> in <strong>simple</strong> mode, which is both a <code>TextBox</code> and a
<code>ListBox</code> in a single control. This mode seems somewhat out of
fashion, I think the only place I see it used is in the Font
common dialog, virtually unchanged since Windows 3.1.</p>
<p>The problem was immediately apparent however on firing up
WebCopy and going to select a user agent - the agent strings can
be very long, far longer than the width of the control.</p>
<p>Unfortunately however, the .NET ComboBox doesn't allow you to
directly enable horizontal scrolling. So we'll do it the old
fashioned way using the Windows API.</p>
<p>In order for a window to support horizontal scrolling, it needs
to have the <code>WS_HSCROLL</code> style applied to it. And to setup the
horizontal scrollbar, we need to call the <code>SendMessage</code> API with
the <code>CB_SETHORIZONTALEXTENT</code> message.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/cbhscroll.png" class="gallery" title="A sample project showing a horizontal scrollbar attached to each display type of a ComboBox." ><img src="https://images.cyotek.com/image/thumbnail/devblog/cbhscroll.png" alt="A sample project showing a horizontal scrollbar attached to each display type of a ComboBox." decoding="async" loading="lazy" /></a><figcaption>A sample project showing a horizontal scrollbar attached to each display type of a ComboBox.</figcaption></figure>
<p>As usual, we'll be starting off by creating a new <em>Component</em>,
which we'll inherit from <code>ComboBox</code>.</p>
<p>Traditionally, you would call <code>GetWindowLong</code> and
<code>SetWindowLong</code> API's with the <code>GWL_STYLE</code> or <code>GWL_EXSTYLE</code>
flags. However, we can more simply override the <code>CreateParams</code>
property of our component and set the new style when the control
is created.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> CreateParams CreateParams
<span class="symbol">{</span>
 <span class="keyword">get</span>
 <span class="symbol">{</span>
 CreateParams createParams<span class="symbol">;</span>

 createParams <span class="symbol">=</span> <span class="keyword">base</span><span class="symbol">.</span>CreateParams<span class="symbol">;</span>
 createParams<span class="symbol">.</span>Style <span class="symbol">|=</span> WS_HSCROLL<span class="symbol">;</span>

 <span class="keyword">return</span> createParams<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>With that done, we can now inform Windows of the size of the
horizontal scroll area, and it will automatically add the
scrollbar if required. To do this, I'll add two new methods to
the component. The first will set the horizontal extent to a
given value. The second will calculate the length of the longest
piece of text in the control and then set the extent to match.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">void</span> SetHorizontalExtent<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> maxWith<span class="symbol">;</span>

 maxWith <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span><span class="keyword">object</span> item <span class="keyword">in</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">)</span>
 <span class="symbol">{</span>
 Size textSize<span class="symbol">;</span>

 textSize <span class="symbol">=</span> TextRenderer<span class="symbol">.</span>MeasureText<span class="symbol">(</span>item<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Font<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>textSize<span class="symbol">.</span>Width <span class="symbol">&gt;</span> maxWith<span class="symbol">)</span>
 maxWith <span class="symbol">=</span> textSize<span class="symbol">.</span>Width<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">this</span><span class="symbol">.</span>SetHorizontalExtent<span class="symbol">(</span>maxWith<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">void</span> SetHorizontalExtent<span class="symbol">(</span><span class="keyword">int</span> width<span class="symbol">)</span>
<span class="symbol">{</span>
 SendMessage<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> CB_SETHORIZONTALEXTENT<span class="symbol">,</span> <span class="keyword">new</span> IntPtr<span class="symbol">(</span>width<span class="symbol">)</span><span class="symbol">,</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The first overload of <code>SetHorizontalExtent</code> iterates through all
the items in the control and uses the <code>TextRenderer</code> object to
measure the size of the text. Once it has found the largest
piece of text, it calls the second overload with the size.</p>
<p>The second overload does the actual work of notifying Windows
using the <code>SendMessage</code> call, <code>CB_SETHORIZONTALEXTENT</code> message
and the given width. <code>SendMessage</code> takes two configuration
parameters per message, but <code>CB_SETHORIZONTALEXTENT</code> only
requires one, and so we send <code>0</code> for the second.</p>
<p>The above function works with all display modes of the ComboBox.</p>
<p>For completeness, here are the API declarations we are using:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">int</span> WS_HSCROLL <span class="symbol">=</span> <span class="number">0x100000</span><span class="symbol">;</span>
<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">int</span> CB_SETHORIZONTALEXTENT <span class="symbol">=</span> <span class="number">0x015E</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">extern</span> IntPtr SendMessage<span class="symbol">(</span>IntPtr hWnd<span class="symbol">,</span> UInt<span class="number">32</span> msg<span class="symbol">,</span> IntPtr wParam<span class="symbol">,</span> IntPtr lParam<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>As usual, a demonstration project is available from the link
below.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-07-13 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/adding-a-horizontal-scrollbar-to-a-combobox-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comBoulder Dash Part 2: Collision Detectionurn:uuid:08709d1e-5daf-4332-9558-213d45a2942f2012-01-24T16:52:27Z2010-07-07T19:59:48Z<p>In <a href="/post/boulderdash-part-1-implementing-sprite-ai">our previous post</a> we introduced the Firefly and
Butterfly sprites and their movement rules around a random map.
Now we're going to update the project to include collision
detection and a new player sprite.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/bd2.png" class="gallery" title="A sample project showing sprites colliding with each other and moving on. The blue sprite will be destroyed if it comes in contact with any other sprite." ><img src="https://images.cyotek.com/image/thumbnail/devblog/bd2.png" alt="A sample project showing sprites colliding with each other and moving on. The blue sprite will be destroyed if it comes in contact with any other sprite." decoding="async" loading="lazy" /></a><figcaption>A sample project showing sprites colliding with each other and moving on. The blue sprite will be destroyed if it comes in contact with any other sprite.</figcaption></figure><h2 id="refactoring-aitest">Refactoring AiTest</h2>
<p>To start with though, we're going to do a little refactoring.
The <code>ButterflySprite</code> and <code>FireflySprite</code> classes share pretty
much the same movement code and will share exactly the same
collision code, so we'll merge the common behaviour into a new
abstract <code>EnemySprite</code> class which these will inherit from.
We'll also add a protected constructor which will allow us to
specify the differences between the inherited sprites and their
movement rules.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> EnemySprite<span class="symbol">(</span>Direction preferredDirection<span class="symbol">,</span> Direction fallbackDirection<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>PreferredDirection <span class="symbol">=</span> preferredDirection<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>FallbackDirection <span class="symbol">=</span> fallbackDirection<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> Direction FallbackDirection <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

<span class="keyword">protected</span> Direction PreferredDirection <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
</pre>
</figure>
<p>With that done, we'll change <code>FireflySprite</code> and
<code>ButterflySprite</code> to inherit from <code>EnemySprite</code> instead of
<code>Sprite</code>, and update the constructors of these classes to call
the protected constructor to supply the movement rule
differences.</p>
<p>Finally, we'll remove the <code>Move</code> methods from the two sprite
classes and instead let <code>EnemySprite</code> implement the movement
code.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> ButterflySprite<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">:</span> <span class="keyword">base</span><span class="symbol">(</span>Direction<span class="symbol">.</span>Right<span class="symbol">,</span> Direction<span class="symbol">.</span>Left<span class="symbol">)</span>

<span class="keyword">public</span> FireflySprite<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">:</span> <span class="keyword">base</span><span class="symbol">(</span>Direction<span class="symbol">.</span>Left<span class="symbol">,</span> Direction<span class="symbol">.</span>Right<span class="symbol">)</span>
</pre>
</figure>
<p>Although I won't go into this here, other refactoring we did was
to move the <code>Sprites</code> collection from <code>MainForm</code> into <code>Map</code>. I
also added an <code>IsScenery</code> method to the <code>Map</code> class which
returns if a tile is considered scenery, for example a piece of
solid earth, or a boulder which can't currently move.</p>
<p>A basic load map system was also added. You can still use the
&quot;Create Random Map&quot; to generate a mostly empty canvas for the
sprites to move around in (clicking with the left button will
add a new firefly, with the right a butterfly) or you can load
the predefined map.</p>
<h2 id="collision-detection">Collision Detection</h2>
<p>Now it's time to implement the actual collision detection. We'll
do this by adding a new function to the base <em>Sprite</em> class that
will check to see if a the location of any sprite matches a
given location.</p>
<p><strong>Note:</strong> This implementation assumes that only one sprite can
occupy a tile at any one time, which is the case in Boulder
Dash.</p>
<p>We're also using LINQ in this function for convenience. If you
haven't yet upgraded to Visual Studio 2008/2010 you'll need to
replace the call with a manual loop</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">bool</span> IsCollision<span class="symbol">(</span>Point location<span class="symbol">,</span> <span class="keyword">out</span> Sprite sprite<span class="symbol">)</span>
<span class="symbol">{</span>
 sprite <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Map<span class="symbol">.</span>Sprites<span class="symbol">.</span>SingleOrDefault<span class="symbol">(</span>s <span class="symbol">=&gt;</span> s<span class="symbol">.</span>Location <span class="symbol">==</span> location<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> sprite <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>I choose to implement the function as a <code>bool</code> to allow it to be
easily used in an <code>if</code> statement, but providing an <code>out</code>
parameter to return the matching sprite (or null otherwise).</p>
<p>With that done, it's time to update our movement code to also
perform the collision detection. The two conditions in the
<code>Move</code> method which check if a tile is part of the scenery
will be modified to call out new method.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>Map<span class="symbol">.</span>IsScenery<span class="symbol">(</span>tile<span class="symbol">)</span> <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>IsCollision<span class="symbol">(</span>tile<span class="symbol">.</span>Location<span class="symbol">,</span> <span class="keyword">out</span> collision<span class="symbol">)</span><span class="symbol">)</span>
</pre>
</figure>
<p>With this change, sprites on the map are now aware of each other
and when they bump into each other they will automatically turn
away.</p>
<h2 id="collision-actions">Collision Actions</h2>
<p>Our example project now has collision detection in place for the
enemy sprites. Being enemies of the player nothing happens when
they bump into each other. If they bump into the player on the
other hand...</p>
<p>Time to add a new sprite. The <code>PlayerSprite</code> will be a non
functioning sprite masquerading as a player character.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">class</span> PlayerSprite <span class="symbol">:</span> Sprite
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">void</span> Move<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// Do nothing, this sprite doesn&#39;t automatically move</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">override</span> Color Color
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> Color<span class="symbol">.</span>Aquamarine<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>In our previous modification the <code>Move</code> method of our
<code>EnemySprite</code> implementations we grab the sprite that we are
colliding with, but we don't do anything with it. Time to change
that.</p>
<p>We'll add a basic enum that will control what happens when a
sprite hits another. For this demo, that will either be nothing,
or &quot;explode&quot; killing both sprites.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">enum</span> CollisionAction
<span class="symbol">{</span>
 None<span class="symbol">,</span>
 Explode
<span class="symbol">}</span>
</pre>
</figure>
<p>We're also going to modify the base <em>Sprite</em> class with a new
method:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">abstract</span> CollisionAction GetCollisionAction<span class="symbol">(</span>Sprite collidedWith<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>Sprite implementations will override this method and return a
<code>CollisionAction</code> based on the sprite they collided with.</p>
<p>The implementation for our new <code>Player</code> class is quite
straightforward:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> CollisionAction GetCollisionAction<span class="symbol">(</span>Sprite collidedWith<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> CollisionAction<span class="symbol">.</span>Explode<span class="symbol">;</span> <span class="comment">// Player dies if it touches any other sprites</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>And the one for <code>EnemySprite</code> is almost as easy:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> CollisionAction GetCollisionAction<span class="symbol">(</span>Sprite collidedWith<span class="symbol">)</span>
<span class="symbol">{</span>
 CollisionAction result<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>collidedWith <span class="keyword">is</span> PlayerSprite<span class="symbol">)</span>
 result <span class="symbol">=</span> CollisionAction<span class="symbol">.</span>Explode<span class="symbol">;</span> <span class="comment">// Kill player</span>
 <span class="keyword">else</span>
 result <span class="symbol">=</span> CollisionAction<span class="symbol">.</span>None<span class="symbol">;</span> <span class="comment">// Do nothing</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Now we have this, we'll update the <code>Move</code> method of our
<code>EnemySprite</code> to take care of the action:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="comment">// if we collided with a sprite, lets execute the action</span>
<span class="keyword">if</span> <span class="symbol">(</span>collision <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>GetCollisionAction<span class="symbol">(</span>collision<span class="symbol">)</span><span class="symbol">==</span> CollisionAction<span class="symbol">.</span>Explode<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// kill both this sprite and the one we collided with</span>
 <span class="keyword">this</span><span class="symbol">.</span>Map<span class="symbol">.</span>Sprites<span class="symbol">.</span>Remove<span class="symbol">(</span>collision<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Map<span class="symbol">.</span>Sprites<span class="symbol">.</span>Remove<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Note that if the <code>Player</code> could move as well then it too would
need collision detection. However, as we only have one class
capable of movement we'll add the code just to that for now.</p>
<p>Also note that we had to adjust the original <code>NextMove</code> method
in <code>MainForm</code> otherwise it would crash when looping through the
sprite list and a removal occurred.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> i <span class="symbol">=</span> _map<span class="symbol">.</span>Sprites<span class="symbol">.</span>Count<span class="symbol">;</span> i <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">;</span> i<span class="symbol">--</span><span class="symbol">)</span>
 _map<span class="symbol">.</span>Sprites<span class="symbol">[</span>i <span class="symbol">-</span> <span class="number">1</span><span class="symbol">]</span><span class="symbol">.</span>Move<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="sample-project">Sample Project</h2>
<p>You can download an updated version of the sample project from
the link below.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-07-07 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/boulder-dash-part-2-collision-detection .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a Windows Forms RadioButton that supports the double click eventurn:uuid:a65a8ab3-ac96-4875-8bb3-b0c4a80ed8232010-06-25T19:32:07Z2010-06-25T18:56:38Z<p>Another of the peculiarities of Windows Forms is that the
<code>RadioButton</code> control doesn't support double clicking. Granted,
it is not often you require the functionality but it's a little
odd it's not supported.</p>
<p>As an example, one of our earlier products which never made it
to production uses a popup dialog to select a zoom level for a
<code>RichTexBox</code>. Common zoom levels are provided via a list of
radio buttons. Rather than the user having to first click a zoom
level and then click the <strong>OK</strong> button, we wanted the user to be
able to simply double click an option to have it selected and
the dialog close.</p>
<p>However, once again with a simple bit of overriding magic we can
enable this functionality.</p>
<p>Create a new component and paste in the code below (<code>using</code> and
<code>namespace</code> statements omitted for clarity).</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> RadioButton <span class="symbol">:</span> System<span class="symbol">.</span>Windows<span class="symbol">.</span>Forms<span class="symbol">.</span>RadioButton
<span class="symbol">{</span>
 <span class="keyword">public</span> RadioButton<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 InitializeComponent<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>SetStyle<span class="symbol">(</span>ControlStyles<span class="symbol">.</span>StandardClick <span class="symbol">|</span> ControlStyles<span class="symbol">.</span>StandardDoubleClick<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="symbol">[</span>EditorBrowsable<span class="symbol">(</span>EditorBrowsableState<span class="symbol">.</span>Always<span class="symbol">)</span><span class="symbol">,</span> Browsable<span class="symbol">(</span><span class="keyword">true</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">new</span> <span class="keyword">event</span> MouseEventHandler MouseDoubleClick<span class="symbol">;</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMouseDoubleClick<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnMouseDoubleClick<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// raise the event</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>MouseDoubleClick <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>MouseDoubleClick<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">,</span> e<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>This new component inherits from the standard <code>RadioButton</code>
control and unlocks the functionality we need.</p>
<p>The first thing we do in the constructor is to modify the
components control styles to enable the <code>StandardDoubleClick</code>
style. At the same time we also set the <code>StandardClick</code> style as
the MSDN documentation states that <code>StandardDoubleClick</code> will be
ignored if <code>StandardClick</code> is not set.</p>
<p>As you can't override an event, we declare a new version of the
<code>MouseDoubleClick</code> event using the <code>new</code> keyword. To this new
definition we add the <code>EditorBrowsable</code> and <code>Browsable</code>
attributes so that the event appears in the IDE property
inspectors and intellisense.</p>
<p>Finally, we override the <code>OnMouseDoubleClick</code> method and invoke
the <code>MouseDoubleClick</code> event whenever this method is called.</p>
<p>And there we have it. Three short steps and we now have a radio
button that you can double click.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-06-25 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-a-windows-forms-radiobutton-that-supports-the-double-click-event .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comBoulder Dash Part 1: Implementing Sprite AIurn:uuid:aba8f1b8-f317-4c05-ae92-fff63f4434022012-01-24T16:52:21Z2010-06-19T23:45:40Z<p>One of the projects I've had on the back-burner for over a year
now was a Boulder Dash clone. While I was working on this clone
I written a basic game engine using GDI, another using managed
DirectX, editing tools, and even a conversion tool for the
BDCFF. Everything but the game itself.</p>
<p>After working pretty much nonstop on the Sitemap Creator and
WebCopy tools recently, I wanted to take things a bit easy
between releases and wanted to resurrect this project.</p>
<p>If you haven't heard of Boulder Dash you're missing out on some
classic gaming of yesteryear. Basically, it involved collecting
a given number of diamonds in a cave, and there were various
enemies (butterflies and fireflies) and game elements (diamonds,
boulders, various types of walls, slime, amoeba) which you use
to beat each cave. There's lots more than this basic synopsis of
course, but it covers the essential elements you will see.</p>
<p>This series of articles will describe some of the design of the
game using sample projects to demonstrate the different
elements, starting with the AI of the enemies.</p>
<p>In Boulder Dash, enemies don't follow a specific path, nor do
they chase you as such. Instead, they are governed by a series
of rules.</p>
<h2 id="firefly-movement-rules">Firefly Movement Rules</h2>
<ul>
<li>if the space to the firefly's left is empty then turn 90
degrees to firefly's left and move one space in this new
direction</li>
<li>otherwise if the space ahead is empty then move one space
forwards</li>
<li>otherwise turn to the right, but do not move</li>
</ul>
<p>This pattern means a firefly can instantly turn left, but takes
double the time when turning right.</p>
<h2 id="butterfly-movement-rules">Butterfly Movement Rules</h2>
<p>The butterfly shares the same basic rules as the firefly, the
exception being that the directions are reversed. For the
butterfly, the preferred turning direction is right rather than
left. So the butterfly can instantly turn right, but is slower
at moving left.</p>
<h2 id="the-sample-project">The sample project</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/bd1.png" class="gallery" title="The sample project in action." ><img src="https://images.cyotek.com/image/thumbnail/devblog/bd1.png" alt="The sample project in action." decoding="async" loading="lazy" /></a><figcaption>The sample project in action.</figcaption></figure>
<p>The sample project (available to download from the link below)
creates a basic testing environment. A map is randomly generated
to which you can add fireflies or butterflies. A directional
arrow displays the current facing of the sprites. Each second
the sprites will be updated.</p>
<p>In this first article we aren't interested in further topics
such as collision detection, we just want to make sure our
sprites move according to the rules above.</p>
<p>The basic logic for each sprite is:</p>
<ul>
<li>can I move in my preferred direction?</li>
<li>can I move straight ahead?</li>
</ul>
<p>If the answer to either of these questions is &quot;Yes&quot;, then our
sprite will move. If &quot;No&quot;, then it will turn in the opposite
direction to its preferred direction.</p>
<p>In Boulder Dash, each cave (level) is comprised of a grid of
tiles, nothing fancy. The player can move up, down, left or
right, but not diagonally. All other game elements are
constrained in the same way.</p>
<p>The following snippet shows the movement logic for the Firefly:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="comment">// first see if we can move in our preferred direction, left</span>
tile <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetAdjacentTile<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>GetNewDirection<span class="symbol">(</span>Direction<span class="symbol">.</span>Left<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>tile<span class="symbol">.</span>Solid<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="comment">// we can move here, update our position and also set our new direction</span>
 <span class="keyword">this</span><span class="symbol">.</span>Location <span class="symbol">=</span> tile<span class="symbol">.</span>Location<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Direction <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetNewDirection<span class="symbol">(</span>Direction<span class="symbol">.</span>Left<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
<span class="keyword">else</span>
<span class="symbol">{</span>
 <span class="comment">// can&#39;t move in our preferred direction, so lets try the direction the sprite is facing</span>
 tile <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetAdjacentTile<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Direction<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>tile<span class="symbol">.</span>Solid<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// we can move here, update our position, but not the direction</span>
 <span class="keyword">this</span><span class="symbol">.</span>Location <span class="symbol">=</span> tile<span class="symbol">.</span>Location<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="comment">// can&#39;t move forwards either, so finally lets just turn right</span>
 <span class="keyword">this</span><span class="symbol">.</span>Direction <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetNewDirection<span class="symbol">(</span>Direction<span class="symbol">.</span>Right<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>The above code relies on two helper methods, one to return a new
direction based on the current direction, and a second to return
an adjacent cell from a given direction.</p>
<h3 id="getnewdirection">GetNewDirection</h3>
<p>The GetNewDirection method below calculates a new direction
based on the current sprites direction and a new facing of
either left or right.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> Direction GetNewDirection<span class="symbol">(</span>Direction turnDirection<span class="symbol">)</span>
<span class="symbol">{</span>
 Direction result<span class="symbol">;</span>

 <span class="keyword">switch</span> <span class="symbol">(</span>turnDirection<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> Direction<span class="symbol">.</span>Left<span class="symbol">:</span>
 result <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Direction <span class="symbol">-</span> <span class="number">1</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>result <span class="symbol">&lt;</span> Direction<span class="symbol">.</span>Up<span class="symbol">)</span>
 result <span class="symbol">=</span> Direction<span class="symbol">.</span>Right<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> Direction<span class="symbol">.</span>Right<span class="symbol">:</span>
 result <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Direction <span class="symbol">+</span> <span class="number">1</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>result <span class="symbol">&gt;</span> Direction<span class="symbol">.</span>Right<span class="symbol">)</span>
 result <span class="symbol">=</span> Direction<span class="symbol">.</span>Up<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">default</span><span class="symbol">:</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentException<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="getadjacenttile">GetAdjacentTile</h3>
<p>The GetAdjacentTile method simply returns the text next to the
current sprite in a given direction.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> Tile GetAdjacentTile<span class="symbol">(</span>Direction direction<span class="symbol">)</span>
<span class="symbol">{</span>
 Tile result<span class="symbol">;</span>

 <span class="keyword">switch</span> <span class="symbol">(</span>direction<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> Direction<span class="symbol">.</span>Up<span class="symbol">:</span>
 result <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Map<span class="symbol">.</span>Tiles<span class="symbol">[</span><span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>X<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>Y <span class="symbol">-</span> <span class="number">1</span><span class="symbol">]</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> Direction<span class="symbol">.</span>Left<span class="symbol">:</span>
 result <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Map<span class="symbol">.</span>Tiles<span class="symbol">[</span><span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>X <span class="symbol">+</span> <span class="number">1</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>Y<span class="symbol">]</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> Direction<span class="symbol">.</span>Down<span class="symbol">:</span>
 result <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Map<span class="symbol">.</span>Tiles<span class="symbol">[</span><span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>X<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>Y <span class="symbol">+</span> <span class="number">1</span><span class="symbol">]</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> Direction<span class="symbol">.</span>Right<span class="symbol">:</span>
 result <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Map<span class="symbol">.</span>Tiles<span class="symbol">[</span><span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>X <span class="symbol">-</span> <span class="number">1</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>Y<span class="symbol">]</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">default</span><span class="symbol">:</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentException<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Once the sample has gotten a tile, it will check to see if the
sprite can move into the tile. For our example, we are just
using a bit flag to state if the tile is solid or not, but in
future we'll need to add collision detection for all manner of
game elements.</p>
<p>If the sprite can move into the first tile into its preferred
direction, it will do this. Otherwise, the movement routine will
next check to see if the tile in front of the sprite is solid,
and if so again it will move. If neither of the two movements
were possible then it will update it's current facing to be the
opposite of it's preferred direction. The process will be
repeated for each &quot;scan&quot; of the game elements.</p>
<p>Using these rules it is quite easy to setup scenarios where the
sprites can &quot;guard&quot; a game element by endless circling it. And
just as easily the unwary player will be chased mercilessly if
they are unwary.</p>
<p>Please let us know if you'd like to see more of this type of
article here on cyotek!</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-06-19 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/boulderdash-part-1-implementing-sprite-ai .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a Windows Forms Label that wraps with C#urn:uuid:8470ef98-31a6-4c60-81e2-1a43f55aa3622012-01-24T16:52:14Z2010-05-21T23:26:06Z<p>One of the few annoyances I occasionally get with C# is the lack
of a word wrap facility for the standard <code>Label</code> control.</p>
<p>Instead, if the <code>AutoSize</code> property is set to <code>true</code>, the label
will just get wider and wider. In order to wrap it, you have to
disable auto resize then manually ensure the height of the label
is sufficient.</p>
<p>The base <code>Control</code> class has method named <code>GetPreferredSize</code>
which is overridden by derived classes. This method will
calculate the size of a control based on a suggested value. By
calling this method and overriding the <code>OnTextChanged</code> and
<code>OnResize</code> methods, we can very easily create a custom label
that automatically wraps and resizes itself vertically to fit
its contents.</p>
<p>Paste in the following code into a new <strong>Component</strong> to have a
read-to-run wrappable label.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> System<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>ComponentModel<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Drawing<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Windows<span class="symbol">.</span>Forms<span class="symbol">;</span>

<span class="keyword">namespace</span> Cyotek<span class="symbol">.</span>Windows<span class="symbol">.</span>Forms
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> WrapLabel <span class="symbol">:</span> Label
 <span class="symbol">{</span>
 <span class="keyword">public</span> WrapLabel<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>AutoSize <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnResize<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnResize<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>FitToContents<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnTextChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnTextChanged<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>FitToContents<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> FitToContents<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Size size<span class="symbol">;</span>

 size <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetPreferredSize<span class="symbol">(</span><span class="keyword">new</span> Size<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Width<span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Height <span class="symbol">=</span> size<span class="symbol">.</span>Height<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="symbol">[</span>DefaultValue<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">,</span> Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">,</span> EditorBrowsable<span class="symbol">(</span>EditorBrowsableState<span class="symbol">.</span>Never<span class="symbol">)</span><span class="symbol">,</span> DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">bool</span> AutoSize
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">base</span><span class="symbol">.</span>AutoSize<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>AutoSize <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>So, what is the code doing? It's very straightforward.</p>
<p>In the constructor, we are disabling the built in auto resize
functionality, otherwise you won't be able to resize the control
in the designer.</p>
<p>Next, we want to override the <code>OnTextChanged</code> and <code>OnResize</code>
methods to call our new resize functionality. By overriding
these, we can ensure that the control will correctly resize as
required.</p>
<p>Now to implement the actual resize functionality. The
<code>FitToContents</code> method calls the label's <code>GetPreferredSize</code>
method, passing in the width of the control. This method returns
a <code>Size</code> structure which is large enough to hold the entire
contents of the control. We take the Height of this (but not the
width) and apply it to the label to make it resize vertically.</p>
<p>When calling <code>GetPreferredSize</code>, the size we passed in only
had the width specified, which will be the maximum width
returning. As we passed in zero for the height, the method
defines its own maximum height.</p>
<p>Finally, you'll note that we have overridden the <code>AutoSize</code>
property itself and added a number of attributes to it to make
sure it doesn't appear in any property or code windows, and to
prevent its value from being serialized.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-05-21 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-a-windows-forms-label-that-wraps-with-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comSnippet: Mime types and file extensionsurn:uuid:a46c77ea-4d64-4380-b756-59e4b84875ae2012-01-24T16:52:06Z2010-04-04T17:17:55Z<p>If you have a mime type and you want to find the default
extension for it, you can get this from the <code>Extension</code> value in
the following registry key:</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
HKEY_CLASSES_ROOT\MIME\Database\Content Type\&lt;mime type&gt;
</pre>
</figure>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">string</span> GetDefaultExtension<span class="symbol">(</span><span class="keyword">string</span> mimeType<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>
 RegistryKey key<span class="symbol">;</span>
 <span class="keyword">object</span> value<span class="symbol">;</span>

 key <span class="symbol">=</span> Registry<span class="symbol">.</span>ClassesRoot<span class="symbol">.</span>OpenSubKey<span class="symbol">(</span><span class="string">@&quot;MIME\Database\Content Type\&quot;</span> <span class="symbol">+</span> mimeType<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>
 value <span class="symbol">=</span> key <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">?</span> key<span class="symbol">.</span>GetValue<span class="symbol">(</span><span class="string">&quot;Extension&quot;</span><span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span> <span class="symbol">:</span> <span class="keyword">null</span><span class="symbol">;</span>
 result <span class="symbol">=</span> value <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">?</span> value<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">:</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">;</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>One the other hand, if you have a file extension and you want to
know what that mime type is, you can get that via the <code>Content Type</code> value of this key:</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
HKEY_CLASSES_ROOT\&lt;extension&gt;
</pre>
</figure>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">string</span> GetMimeTypeFromExtension<span class="symbol">(</span><span class="keyword">string</span> extension<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>
 RegistryKey key<span class="symbol">;</span>
 <span class="keyword">object</span> value<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>extension<span class="symbol">.</span>StartsWith<span class="symbol">(</span><span class="string">&quot;.&quot;</span><span class="symbol">)</span><span class="symbol">)</span>
 extension <span class="symbol">=</span> <span class="string">&quot;.&quot;</span> <span class="symbol">+</span> extension<span class="symbol">;</span>

 key <span class="symbol">=</span> Registry<span class="symbol">.</span>ClassesRoot<span class="symbol">.</span>OpenSubKey<span class="symbol">(</span>extension<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>
 value <span class="symbol">=</span> key <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">?</span> key<span class="symbol">.</span>GetValue<span class="symbol">(</span><span class="string">&quot;Content Type&quot;</span><span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span> <span class="symbol">:</span> <span class="keyword">null</span><span class="symbol">;</span>
 result <span class="symbol">=</span> value <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">?</span> value<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">:</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">;</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-04-04 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/mime-types-and-file-extensions .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comUnable to update the EntitySet because it has a DefiningQuery and no element exists in the element to support the current operation.urn:uuid:3154493f-a9b1-4f94-968d-955487a4ac392012-01-24T16:52:01Z2010-03-26T19:59:11Z<p>After integrating the new forum code, I added basic subscription
support. When replying to a topic and opting to subscribe to
notifications, the following exception would be thrown:</p>
<blockquote>
<p>Unable to update the EntitySet 'ThreadSubscriptions' because
it has a DefiningQuery and no element exists in the element
to support the current operation.</p>
</blockquote>
<p>I'd already checked the Entity model to ensure the relationships
were set up correctly as a many to many, as one user may be
subscribed to many threads, and any given thread can have many
subscribed users, so I was a little perplexed as to where this
was coming from.</p>
<p>After looking at the database table which links threads and
users, I realized the problem was the table didn't have a unique
key, only the relationships. After creating a primary key on the
two columns in this table, and regenerating the Entity model,
the exception disappeared and subscriptions are now working as
expected.</p>
<p>It's always the little things...</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-03-26 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/unable-to-update-the-entityset-because-it-has-a-definingquery-and-no-element-exists-in-the-element-to-support-the-current-operation .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comConverting BBCode into HTML using C#urn:uuid:b2cb9395-73d0-4833-97ff-eb38c52c99812010-03-18T21:28:37Z2010-03-18T21:14:04Z<p>Although the dynamic content in the Cyotek website is written
using Markdown syntax using the <a href="http://code.google.com/p/markdownsharp/" rel="external nofollow noopener">MarkdownSharp</a> library, we
decided to use the more commonly used BBCode tags for the
forums.</p>
<p>Some of the source code on this site is also preformatted using
the <a href="http://manoli.net/csharpformat/" rel="external nofollow noopener">CSharpFormat</a> library, and we wanted to provide access
to this via forum tags too.</p>
<p>A quick Google search brought up a <a href="http://forums.asp.net/p/1087581/1635776.aspx#1624611" rel="external nofollow noopener">post by Mike343</a> which
had a BBCode parser that more or less worked, but didn't cover
everything we wanted.</p>
<p>You can download below an updated version of this parser which
has been modified to correct some problems with the original
implementation and add some missing BBCode tags, including a set
of custom tags for providing the syntax highlighting offered by
CSharpFormat. Using the provided formatter classes you can
easily create additional tags to suit the needs of your
application.</p>
<p>To transform a block of BBCode into HTML, call the static
<code>Format</code> method of the <code>BbCodeProcessor</code> class, for example:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">string</span> exampleBbcCode <span class="symbol">=</span> <span class="string">&quot;[b]this text is bold[/b]\n[i]this text is italic[/i]\n[u]this text is underlined[/u]&quot;</span><span class="symbol">;</span>
<span class="keyword">string</span> html <span class="symbol">=</span> BbCodeProcessor<span class="symbol">.</span>Format<span class="symbol">(</span>exampleBbcCode<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>is transformed into</p>
<figure class="lang-html highlight"><figcaption><span>html</span></figcaption><pre class="code">
<span class="literal">&lt;</span><span class="name">p</span><span class="literal">&gt;</span>
<span class="literal">&lt;</span><span class="name">strong</span><span class="literal">&gt;</span>this text is bold<span class="literal">&lt;/</span><span class="name">strong</span><span class="literal">&gt;</span><span class="literal">&lt;</span><span class="name">br</span><span class="literal">&gt;</span>
<span class="literal">&lt;</span><span class="name">em</span><span class="literal">&gt;</span>this text is italic<span class="literal">&lt;/</span><span class="name">em</span><span class="literal">&gt;</span><span class="literal">&lt;</span><span class="name">br</span><span class="literal">&gt;</span>
<span class="literal">&lt;</span><span class="name">u</span><span class="literal">&gt;</span>this text is underlined<span class="literal">&lt;/</span><span class="name">u</span><span class="literal">&gt;</span>
<span class="literal">&lt;/</span><span class="name">p</span><span class="literal">&gt;</span>
</pre>
</figure>
<p>Much of the formatting is also customisable via CSS - several of
the BBCode tags such as <code>[code]</code>, <code>[quote]</code>, <code>[list]</code> etc are
assigned a class which you can configure in your style sheets.
Listed below are the default rules used by the Cyotek site as a
starting point for your own:</p>
<figure class="lang-css highlight"><figcaption><span>css</span></figcaption><pre class="code">
<span class="selector-tag">.bbc-codetitle</span>, <span class="selector-tag">.bbc-quotetitle </span>{ <span class="name">margin</span>: <span class="variable">1em 1.5em 0</span>; <span class="name">padding</span>: <span class="variable">2px 4px</span>; <span class="name">background-color</span>: <span class="variable">#A0B3CA</span>; <span class="name">font-weight</span>: <span class="variable">bold</span>; }
<span class="selector-tag">.bbc-codecontent</span>, <span class="selector-tag">.bbc-quotecontent </span>{ <span class="name">margin</span>: <span class="variable">0 1.5em 1em</span>; <span class="name">padding</span>: <span class="variable">5px</span>; <span class="name">border</span>: <span class="variable">solid 1px #A0B3CA</span>; <span class="name">background-color</span>: <span class="variable">#fff</span>; }
<span class="selector-tag">.bbc-codecontent pre </span>{ <span class="name">margin</span>: <span class="variable">0</span>; <span class="name">padding</span>: <span class="variable">0</span>; }
<span class="selector-tag">.bbc-highlight </span>{ <span class="name">background-color</span>: <span class="variable">#FFFF00</span>; <span class="name">color</span>: <span class="variable">#333399</span>; }
<span class="selector-tag">.bbc-spoiler </span>{ <span class="name">color</span>: <span class="variable">#C0C0C0</span>; <span class="name">background-color</span>: <span class="variable">#C0C0C0</span>; }
<span class="selector-tag">.bbc-indent </span>{ <span class="name">padding</span>: <span class="variable">0 1em</span>; }
<span class="selector-tag">.bbc-list </span>{ <span class="name">margin</span>: <span class="variable">1em</span>; }
</pre>
</figure>
<p>Finally, if you are using MVC, you may find the following HTML
Helper useful for transforming code from within your views.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">string</span> FormatBbCode<span class="symbol">(</span><span class="keyword">this</span> HtmlHelper helper<span class="symbol">,</span> <span class="keyword">string</span> text<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> BbCodeProcessor<span class="symbol">.</span>Format<span class="symbol">(</span>helper<span class="symbol">.</span>Encode<span class="symbol">(</span>text<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>If you create any additional formatting codes for use with this
library, please let us know via either comments or the Contact
Us link, and we'll integrate them into the library for others to
use.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-03-18 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/converting-bbcode-into-html-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comUsing XSLT to display an ASP.net sitemap without using tablesurn:uuid:6e622ab1-77f6-4c89-ba56-42cf611c08302010-09-21T19:52:22Z2010-02-06T13:13:14Z<p>The quick and easy way of displaying an ASP.net site map
(<code>web.sitemap</code>) in an ASP.net page is to use a <code>TreeView</code>
control bound to a <code>SiteMapDataSource</code> component as shown in the
following example:</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">asp</span><span class="symbol">:</span><span class="name">SiteMapDataSource</span> <span class="name">runat</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">server</span><span class="symbol">&quot;</span> <span class="name">ID</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">siteMapDataSource</span><span class="symbol">&quot;</span> <span class="name">EnableViewState</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">False</span><span class="symbol">&quot;</span> <span class="name">ShowStartingNode</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">False</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
<span class="symbol">&lt;</span><span class="name">asp</span><span class="symbol">:</span><span class="name">TreeView</span> <span class="name">runat</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">server</span><span class="symbol">&quot;</span> <span class="name">ID</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">siteMapTreeView</span><span class="symbol">&quot;</span> <span class="name">DataSourceID</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">siteMapDataSource</span><span class="symbol">&quot;</span> <span class="name">EnableClientScript</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">False</span><span class="symbol">&quot;</span> <span class="name">EnableViewState</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">False</span><span class="symbol">&quot;</span> <span class="name">ShowExpandCollapse</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">False</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span><span class="symbol">&lt;/</span><span class="name">asp</span><span class="symbol">:</span><span class="name">TreeView</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>Which results in a mass of nested tables, in-line styles, and
generally messy mark-up.</p>
<p>With just a little more effort however, you can display the
sitemap using a XSLT transform, resulting in slim, clean and
configurable mark-up - and not a table to be seen.</p>
<p>This approach can be used with both Web Forms and MVC.</p>
<blockquote>
<p>This article assumes you already have a pre-made ASP.net
sitemap file.</p>
</blockquote>
<h2 id="defining-the-xslt">Defining the XSLT</h2>
<p>Add a new <strong>XSLT File</strong> to your project. In this case, it's
named <code>sitemap.xslt</code>.</p>
<p>Next, paste in the mark-up below.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;?</span><span class="name">xml</span> <span class="name">version</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1.0</span><span class="symbol">&quot;</span> <span class="name">encoding</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">utf-8</span><span class="symbol">&quot;</span><span class="symbol">?&gt;</span>
<span class="symbol">&lt;</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">stylesheet</span> <span class="name">version</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1.0</span><span class="symbol">&quot;</span> <span class="name">xmlns:xsl</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://www.w3.org/1999/XSL/Transform</span><span class="symbol">&quot;</span> <span class="name">xmlns:map</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://schemas.microsoft.com/AspNet/SiteMap-File-1.0</span><span class="symbol">&quot;</span> <span class="name">exclude-result-prefixes</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">map</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">output</span> <span class="name">method</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">xml</span><span class="symbol">&quot;</span> <span class="name">encoding</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">utf-8</span><span class="symbol">&quot;</span> <span class="name">indent</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">yes</span><span class="symbol">&quot;</span><span class="symbol">/&gt;</span>

 <span class="symbol">&lt;</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">template</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">mapNode</span><span class="symbol">&quot;</span> <span class="name">match</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">map:siteMap</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">ul</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">apply-templates</span><span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">ul</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">template</span><span class="symbol">&gt;</span>

 <span class="symbol">&lt;</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">template</span> <span class="name">match</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">map:siteMapNode</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">li</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">a</span> <span class="name">href</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://cyotek.com{substring(@url, 2)}</span><span class="symbol">&quot;</span> <span class="name">title</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">{@description}</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">value-of</span> <span class="name">select</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">@title</span><span class="symbol">&quot;</span><span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">a</span><span class="symbol">&gt;</span>

 <span class="symbol">&lt;</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">if</span> <span class="name">test</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">map:siteMapNode</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">call-template</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">mapNode</span><span class="symbol">&quot;</span><span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">if</span><span class="symbol">&gt;</span>

 <span class="symbol">&lt;/</span><span class="name">li</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">template</span><span class="symbol">&gt;</span>
 
<span class="symbol">&lt;/</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">stylesheet</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p><strong>Note:</strong> As generally all URLs in ASP.net site maps start with
<code>~/</code>, the <code>href</code> tag in the above example has been customized to
include the domain <em><a href="http://cyotek.com">http://cyotek.com</a></em> at the start, then use
the XSLT <code>substring</code> function to strip the <code>~/</code> from the start
of the URL. Don't forget to modify the URL to point to your own
domain!</p>
<h2 id="declaratively-transforming-the-document">Declaratively transforming the document</h2>
<p>If you are using Web forms controls, then this may be the more
convenient approach for you.</p>
<p>Just add the <strong>XML</strong> component to your page, and set the
<code>DocumentSource</code> property to the name of the sitemap, and the
<code>TransformSource</code> property to the name of your XSLT file.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">asp</span><span class="symbol">:</span><span class="name">Xml</span> <span class="name">runat</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">server</span><span class="symbol">&quot;</span> <span class="name">ID</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">xmlSiteMapViewer</span><span class="symbol">&quot;</span> <span class="name">DocumentSource</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">~/web.sitemap</span><span class="symbol">&quot;</span> <span class="name">TransformSource</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">~/sitemap.xslt</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
</pre>
</figure>
<h3 id="programmatically-transforming-the-document">Programmatically transforming the document</h3>
<p>The ASP.net XML control doesn't need to be inside a server side
<code>form</code> tag, so you can use the exact same code above in your
MVC views.</p>
<p>However, if you want to do this programmatically, the following
code works too.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">var</span> xmlFileName <span class="symbol">=</span> Server<span class="symbol">.</span>MapPath<span class="symbol">(</span><span class="string">&quot;~/web.sitemap&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="keyword">var</span> xslFileName <span class="symbol">=</span> Server<span class="symbol">.</span>MapPath<span class="symbol">(</span><span class="string">&quot;~/sitemap.xslt&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="keyword">var</span> result <span class="symbol">=</span> <span class="keyword">new</span> System<span class="symbol">.</span>IO<span class="symbol">.</span>StringWriter<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="keyword">var</span> transform <span class="symbol">=</span> <span class="keyword">new</span> System<span class="symbol">.</span>Xml<span class="symbol">.</span>Xsl<span class="symbol">.</span>XslCompiledTransform<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

transform<span class="symbol">.</span>Load<span class="symbol">(</span>xslFileName<span class="symbol">)</span><span class="symbol">;</span>
transform<span class="symbol">.</span>Transform<span class="symbol">(</span>xmlFileName<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">,</span> result<span class="symbol">)</span><span class="symbol">;</span>

Response<span class="symbol">.</span>Write<span class="symbol">(</span>result<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="the-result">The result</h2>
<p>The output of the transform will be simple series of nested
unordered lists, clean and ready to be styled with CSS. And for
little more effort than it took to do the original tree view
solution.</p>
<p>With a bit more tweaking you can probably expand this to show
only a single branch, useful for navigation within a section of
a website, or creating breadcrumb trails.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-02-06 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/using-xslt-to-display-an-asp-net-sitemap-without-using-tables .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a GroupBox containing an image and a custom display rectangleurn:uuid:d2b83d63-1dfb-4621-a5be-89c0662409fa2010-04-03T12:42:56Z2009-08-10T16:16:28Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/groupbox-1.png" class="gallery" title="An example of the GroupBox component in action" ><img src="https://images.cyotek.com/image/devblog/groupbox-1.png" alt="An example of the GroupBox component in action" decoding="async" loading="lazy" /></a><figcaption>An example of the GroupBox component in action</figcaption></figure>
<p>One of our applications required a GroupBox which was more like
the one featured in the Options dialog of Microsoft Outlook
2003. This article describes how to create a custom GroupBox
component which allows this type of user interface, and also a
neat trick on adjusting the client area so that when you drag
controls inside the GroupBox, the handy little margin guides
allow you to position without overlapping the icon.</p>
<p>Add a new <strong>Component</strong> class to your project, and inherit this
from the standard <code>GroupBox</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>ToolboxItem<span class="symbol">(</span><span class="keyword">true</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="symbol">[</span>DefaultEvent<span class="symbol">(</span><span class="string">&quot;Click&quot;</span><span class="symbol">)</span><span class="symbol">,</span> DefaultProperty<span class="symbol">(</span><span class="string">&quot;Text&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> GroupBox <span class="symbol">:</span> System<span class="symbol">.</span>Windows<span class="symbol">.</span>Forms<span class="symbol">.</span>GroupBox
</pre>
</figure>
<p>I personally don't like assigning variables at the same time as
defining them, so I've added a default constructor to assign the
defaults and also to set-up the component as we need to set a
few <em>ControlStyles</em>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> GroupBox<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 _iconMargin <span class="symbol">=</span> <span class="keyword">new</span> Size<span class="symbol">(</span><span class="number">0</span><span class="symbol">,</span> <span class="number">6</span><span class="symbol">)</span><span class="symbol">;</span>
 _lineColorBottom <span class="symbol">=</span> SystemColors<span class="symbol">.</span>ButtonHighlight<span class="symbol">;</span>
 _lineColorTop <span class="symbol">=</span> SystemColors<span class="symbol">.</span>ButtonShadow<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>SetStyle<span class="symbol">(</span>ControlStyles<span class="symbol">.</span>DoubleBuffer <span class="symbol">|</span> ControlStyles<span class="symbol">.</span>AllPaintingInWmPaint <span class="symbol">|</span> ControlStyles<span class="symbol">.</span>ResizeRedraw <span class="symbol">|</span>
 ControlStyles<span class="symbol">.</span>UserPaint <span class="symbol">|</span> ControlStyles<span class="symbol">.</span>SupportsTransparentBackColor<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CreateResources<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Although this is a simple component, we need at the minimum an
<code>Image</code> property to specify the image. We're also adding color
properties in case we decide to use the component in a
non-standard interface later on.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> Size _iconMargin<span class="symbol">;</span>
<span class="keyword">private</span> Image _image<span class="symbol">;</span>
<span class="keyword">private</span> Color _lineColorBottom<span class="symbol">;</span>
<span class="keyword">private</span> Color _lineColorTop<span class="symbol">;</span>

<span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Appearance&quot;</span><span class="symbol">)</span><span class="symbol">,</span> DefaultValue<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>Size<span class="symbol">)</span><span class="symbol">,</span> <span class="string">&quot;0, 6&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> Size IconMargin
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _iconMargin<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 _iconMargin <span class="symbol">=</span> value<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Appearance&quot;</span><span class="symbol">)</span><span class="symbol">,</span> DefaultValue<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>Image<span class="symbol">)</span><span class="symbol">,</span> <span class="string">&quot;&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> Image Image
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _image<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 _image <span class="symbol">=</span> value<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Appearance&quot;</span><span class="symbol">)</span><span class="symbol">,</span> DefaultValue<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>Color<span class="symbol">)</span><span class="symbol">,</span> <span class="string">&quot;ButtonHighlight&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> Color LineColorBottom
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _lineColorBottom<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 _lineColorBottom <span class="symbol">=</span> value<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>CreateResources<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Appearance&quot;</span><span class="symbol">)</span><span class="symbol">,</span> DefaultValue<span class="symbol">(</span><span class="keyword">typeof</span><span class="symbol">(</span>Color<span class="symbol">)</span><span class="symbol">,</span> <span class="string">&quot;ButtonShadow&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> Color LineColorTop
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _lineColorTop<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 _lineColorTop <span class="symbol">=</span> value<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>CreateResources<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="symbol">[</span>DefaultValue<span class="symbol">(</span><span class="string">&quot;&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">string</span> Text
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">base</span><span class="symbol">.</span>Text<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>Text <span class="symbol">=</span> value<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>If you wanted you could create and destroy required GDI objects
every time the control is painted, but in this example I've
opted to create them once for the lifetime of the control.
Therefore I've added <code>CreateResources</code> and <code>CleanUpResources</code> to
create and destroy these. Although not demonstrated in this
in-line listing, <code>CleanUpResources</code> is also called from the
components <code>Dispose</code> method. You'll also notice
<code>CreateResources</code> is called whenever a property value changes,
and that it first releases resources in use.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> CleanUpResources<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_topPen <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 _topPen<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_bottomPen <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 _bottomPen<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_textBrush <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 _textBrush<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> CreateResources<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>CleanUpResources<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 _topPen <span class="symbol">=</span> <span class="keyword">new</span> Pen<span class="symbol">(</span>_lineColorTop<span class="symbol">)</span><span class="symbol">;</span>
 _bottomPen <span class="symbol">=</span> <span class="keyword">new</span> Pen<span class="symbol">(</span>_lineColorBottom<span class="symbol">)</span><span class="symbol">;</span>
 _textBrush <span class="symbol">=</span> <span class="keyword">new</span> SolidBrush<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>ForeColor<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Now that all the initialization is performed, we're going to add
our drawing routine which is to simply override the <code>OnPaint</code>
method.</p>
<p>Remember that as we are overriding an existing component, we
should override the base components methods whenever possible -
this means overriding <code>OnPaint</code> and <em>not</em> hooking into the
<code>Paint</code> event.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnPaint<span class="symbol">(</span>PaintEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 SizeF size<span class="symbol">;</span>
 <span class="keyword">int</span> y<span class="symbol">;</span>

 size <span class="symbol">=</span> e<span class="symbol">.</span>Graphics<span class="symbol">.</span>MeasureString<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Text<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Font<span class="symbol">)</span><span class="symbol">;</span>
 y <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span><span class="symbol">(</span>size<span class="symbol">.</span>Height <span class="symbol">+</span> <span class="number">3</span><span class="symbol">)</span> <span class="symbol">/</span> <span class="number">2</span><span class="symbol">;</span>

 <span class="comment">// draw the header text and line</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>DrawString<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Text<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Font<span class="symbol">,</span> _textBrush<span class="symbol">,</span> <span class="number">1</span><span class="symbol">,</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>DrawLine<span class="symbol">(</span>_topPen<span class="symbol">,</span> size<span class="symbol">.</span>Width <span class="symbol">+</span> <span class="number">3</span><span class="symbol">,</span> y<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Width <span class="symbol">-</span> <span class="number">5</span><span class="symbol">,</span> y<span class="symbol">)</span><span class="symbol">;</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>DrawLine<span class="symbol">(</span>_bottomPen<span class="symbol">,</span> size<span class="symbol">.</span>Width <span class="symbol">+</span> <span class="number">3</span><span class="symbol">,</span> y <span class="symbol">+</span> <span class="number">1</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Width <span class="symbol">-</span> <span class="number">5</span><span class="symbol">,</span> y <span class="symbol">+</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// draw the image</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">(</span>_image <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">)</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>DrawImage<span class="symbol">(</span>_image<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Left <span class="symbol">+</span> _iconMargin<span class="symbol">.</span>Width<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Padding<span class="symbol">.</span>Top <span class="symbol">+</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>size<span class="symbol">.</span>Height <span class="symbol">+</span> _iconMargin<span class="symbol">.</span>Height<span class="symbol">,</span> _image<span class="symbol">.</span>Width<span class="symbol">,</span> _image<span class="symbol">.</span>Height<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">//draw a designtime outline</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>DesignMode<span class="symbol">)</span>
 <span class="symbol">{</span>
 Pen pen<span class="symbol">;</span>
 pen <span class="symbol">=</span> <span class="keyword">new</span> Pen<span class="symbol">(</span>SystemColors<span class="symbol">.</span>ButtonShadow<span class="symbol">)</span><span class="symbol">;</span>
 pen<span class="symbol">.</span>DashStyle <span class="symbol">=</span> DashStyle<span class="symbol">.</span>Dot<span class="symbol">;</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>DrawRectangle<span class="symbol">(</span>pen<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> Width <span class="symbol">-</span> <span class="number">1</span><span class="symbol">,</span> Height <span class="symbol">-</span> <span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 pen<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>In the code above you'll also notice a block specifically for
design time. As this control only has borders at the top of the
control, at design time it may not be obvious where the
boundaries of the control are when laying out your interface.
This code adds a dotted outline to the control at design time,
and is ignored at runtime.</p>
<p>Another method we are overriding is <code>OnSystemColorsChanged</code>.
As our default colors are based on system colors, should these
change we need to recreate our objects and repaint the control.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnSystemColorsChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnSystemColorsChanged<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CreateResources<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Invalidate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/groupbox-2.png" class="gallery" title="The default client area" ><img src="https://images.cyotek.com/image/devblog/groupbox-2.png" alt="The default client area" decoding="async" loading="lazy" /></a><figcaption>The default client area</figcaption></figure>
<p>The client area of a standard group box accounts for the text
header and the borders. Our component however, needs an
additional offset on the left to account for the icon. If you
try and place controls into the group box, you will see the
snapping guides appear in the &quot;wrong&quot; place.</p>
<p>Fortunately however, it is very easy for us to suggest our own
client area via the <code>DisplayRectangle</code> property. We just
override this and provide a new rectangle which includes
provisions for the width of the image.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">override</span> Rectangle DisplayRectangle
<span class="symbol">{</span>
 <span class="keyword">get</span>
 <span class="symbol">{</span>
 Size clientSize<span class="symbol">;</span>
 <span class="keyword">int</span> fontHeight<span class="symbol">;</span>
 <span class="keyword">int</span> imageSize<span class="symbol">;</span>

 clientSize <span class="symbol">=</span> <span class="keyword">base</span><span class="symbol">.</span>ClientSize<span class="symbol">;</span>
 fontHeight <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Font<span class="symbol">.</span>Height<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_image <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 imageSize <span class="symbol">=</span> _iconMargin<span class="symbol">.</span>Width <span class="symbol">+</span> _image<span class="symbol">.</span>Width <span class="symbol">+</span> <span class="number">3</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 imageSize <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>

 <span class="keyword">return</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span><span class="number">3</span> <span class="symbol">+</span> imageSize<span class="symbol">,</span> fontHeight <span class="symbol">+</span> <span class="number">3</span><span class="symbol">,</span> Math<span class="symbol">.</span>Max<span class="symbol">(</span>clientSize<span class="symbol">.</span>Width <span class="symbol">-</span> <span class="symbol">(</span>imageSize <span class="symbol">+</span> <span class="number">6</span><span class="symbol">)</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">,</span> Math<span class="symbol">.</span>Max<span class="symbol">(</span><span class="symbol">(</span>clientSize<span class="symbol">.</span>Height <span class="symbol">-</span> fontHeight<span class="symbol">)</span> <span class="symbol">-</span> <span class="number">6</span><span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/groupbox-3.png" class="gallery" title="The custom client area" ><img src="https://images.cyotek.com/image/devblog/groupbox-3.png" alt="The custom client area" decoding="async" loading="lazy" /></a><figcaption>The custom client area</figcaption></figure>
<p>Now as you can see the snapping guides suggest a suitable left
margin based on the current image width.</p>
<p>You can download the complete source for the <code>GroupBox</code>
component below.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2009-08-10 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-a-groupbox-containing-an-image-and-a-custom-display-rectangle .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.com