Cyotek Development Bloghttps://devblog.cyotek.com/atom.xml2024-02-02T23:00:16ZTools we use - 2023 editionurn:uuid:ee793c5e-4b93-41a4-a354-dec1158ed6652024-02-02T23:00:16Z2024-02-02T22:51:50Z<p>Oh look, here were are with the traditional new years post one
month after the new year started. So clearly I don't have my
publishing mojo back yet. When I look back over the last years
drafts I see two partially written blog posts, one a review of
<a href="https://craftinginterpreters.com/" rel="external nofollow noopener">Crafting Interpreters</a> (tldr; Go Buy It!)
and another of the Synology DS1821+ NAS (tldr; it's not bad!)
and that is it.</p>
<p>The main change this year is almost fully retiring multiple
Windows micro servers, supplanted by Docker containers running
on a Synology NAS. I thought hell would freeze over before I did
something like this.</p>
<h2 id="operating-systems">Operating Systems</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>Windows 11</td>
<td>Secondary development environment. (Still) Not a fan.</td>
</tr>
<tr>
<td>Windows 10 (Virtualized)</td>
<td>Testing VM (32bit and 64bit)</td>
</tr>
<tr>
<td>Windows 10 Professional</td>
<td>Development environment</td>
</tr>
<tr>
<td>Windows 7 (Virtualized)</td>
<td>Testing VM (32bit)</td>
</tr>
<tr>
<td>Windows Home Server 2011</td>
<td><del>File server, SVN server, Git server</del>, backup host, <del>CI server</del>. Recently I bought a Synology NAS and with the exception of CrashPlan, have pretty much retired these micro servers. While I'm not exactly thrilled with Synology's &quot;almost&quot; walled garden, it is far better than my previous attempts to DIY this via non-Windows solutions.</td>
</tr>
<tr>
<td>Windows Vista (Virtualized)</td>
<td>Testing VM (32bit)</td>
</tr>
</tbody>
</table>
<h2 id="development-tools">Development Tools</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://sqlitebrowser.org/" rel="external nofollow noopener">DB Browser for SQLite</a></td>
<td>GUI for working with SQLite databases</td>
</tr>
<tr>
<td><a href="https://devtoys.app/" rel="external nofollow noopener">DevToys</a></td>
<td>A bunch of different developer tools, such as Base64 encoding/decoding and hash generation</td>
</tr>
<tr>
<td><a href="https://github.com/0xd4d/dnSpy" rel="external nofollow noopener">dnSpy</a></td>
<td>Speedy .NET assembly debugger and editor. Oddly, this has been archived for no public reason I can find</td>
</tr>
<tr>
<td><a href="https://www.ndepend.com/" rel="external nofollow noopener">NDepend</a></td>
<td>Static code analysis. I run this mostly as part of CI pipelines</td>
</tr>
<tr>
<td><a href="https://insomnia.rest/" rel="external nofollow noopener">Insomnia</a></td>
<td>Client for testing REST services. I switched to Insomnia over Postman as the latter doesn't work without an account and I'm little tired of having scores of accounts with scores of services for no actual benefit to me as the end user</td>
</tr>
<tr>
<td><a href="https://visualstudio.microsoft.com/" rel="external nofollow noopener">Visual Studio 2022</a></td>
<td>Although I use Visual Studio Code more and more, Visual Studio 2022 remains my IDE of choice</td>
</tr>
<tr>
<td><a href="https://code.visualstudio.com/" rel="external nofollow noopener">Visual Studio Code</a></td>
<td>Wonderful editor, once you install enough extensions to configure it &quot;your way&quot;. I use it for most non-.NET tasks, such as PHP or editing markdown. Workspaces that can include multiple folders are incredibly useful</td>
</tr>
<tr>
<td><a href="https://loic-sharma.github.io/BaGet/" rel="external nofollow noopener">Baget</a></td>
<td>I've been using <a href="https://github.com/NuGet/NuGet.Server" rel="external nofollow noopener">NuGet.Server</a> for some years for internal NuGet package management, but that tool is long in the tooth and somewhat limited in functionality. Baget is a much more modern (and functional!) alternative</td>
</tr>
<tr>
<td><a href="https://notepad-plus-plus.org/" rel="external nofollow noopener">Notepad++</a></td>
<td>My go to plain text editor. I tend to use Visual Studio Code now for most text <em>formats</em>.</td>
</tr>
</tbody>
</table>
<h2 id="visual-studio-extensions">Visual Studio Extensions</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.codemaid.net/" rel="external nofollow noopener">CodeMaid</a></td>
<td>Code formatting and organising. Lets be fair to ReSharper, there's <em>nothing</em> else available which does a better job, but CodeMaid is an acceptable substitute</td>
</tr>
<tr>
<td><del>Cyotek Add Projects</del></td>
<td>A simple extension for easily adding multiple projects to your solutions. As I converted all my common projects to packages years ago, my need for this extension has fully evaporated</td>
</tr>
<tr>
<td><a href="https://marketplace.visualstudio.com/items?itemName=MadsKristensen.OpenCommandLine" rel="external nofollow noopener">Open Command Line</a></td>
<td>Easily open command prompts, PowerShell prompts, or other tools to your project / solution directories</td>
</tr>
<tr>
<td><a href="https://github.com/JosefPihrt/Roslynator" rel="external nofollow noopener">Roslynator</a></td>
<td>C# code analyzers, refactoring and fixes. I use this to replace some of the more critical functionality I previously enjoyed in ReSharper. Not using in VS2022 at the moment as I went back to the dark side</td>
</tr>
<tr>
<td><a href="https://github.com/Tim-Maes/T4Editor" rel="external nofollow noopener">T4Editor</a></td>
<td>I use this as a replacement for the ReSharper ForTea extension and I'm quite happy with it - it does a great job of showing me the T4 specific aspects of my templates. And even here in 2023 I still use T4 over source generators.</td>
</tr>
<tr>
<td><a href="https://mike-ward.net/vscoloroutput/" rel="external nofollow noopener">VSColorOutput</a></td>
<td>Add colour coding to Visual Studio's Output window</td>
</tr>
<tr>
<td><a href="https://www.jetbrains.com/resharper/" rel="external nofollow noopener">Resharper</a></td>
<td>What is the definition of insanity? Here I am suffering with dire performance problems caused by Resharper once again. Although I really wish JetBrains would focus more on fixing the performance issues their software has caused for <em>years</em>, instead of jumping on the AI bandwagon. Or maybe their AI will be the one fixing the problems they seemingly can't.</td>
</tr>
<tr>
<td><a href="https://marketplace.visualstudio.com/items?itemName=DennisStanze.nordic-001" rel="external nofollow noopener">nordic</a></td>
<td>When I need a dark mode theme, I tend to use something like <a href="https://www.nordtheme.com/" rel="external nofollow noopener">Nord</a>. This extension themes Visual Studio to use a more nord-esque colour scheme</td>
</tr>
</tbody>
</table>
<h2 id="installation-and-deployment">Installation and Deployment</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://jrsoftware.org/isinfo.php" rel="external nofollow noopener">Inno Setup</a></td>
<td>Installer with a wealth of features</td>
</tr>
<tr>
<td><a href="https://www.kymoto.org/products/inno-script-studio" rel="external nofollow noopener">Inno Script Studio</a></td>
<td>IDE for working with Inno Setup scripts</td>
</tr>
<tr>
<td><a href="http://innounp.sourceforge.net/" rel="external nofollow noopener">Inno Setup Unpacker</a></td>
<td>Unpacks installations created with Inno Setup. I use this as part of the CI process to perform dependency checking</td>
</tr>
<tr>
<td><a href="https://learn.microsoft.com/en-us/windows/package-manager/winget/" rel="external nofollow noopener">winget</a></td>
<td>Package manager for Windows, similar to apt except with less features and way less packages. I started using these for setting up fresh environments instead of complicated homebrew PowerShell scripts. It is a shame that more software isn't available via this, and some that is isn't fully compatible with it.</td>
</tr>
</tbody>
</table>
<h2 id="analytics">Analytics</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://matomo.org/" rel="external nofollow noopener">Matomo</a></td>
<td>I use this web based analytics software to gain anonymous insights into cyotek.com usage</td>
</tr>
<tr>
<td>Unnamed Analytics</td>
<td>After dropping Luminitix, I replaced the data collection with a home grown solution, although I've yet to write a front end to look at the data effectively</td>
</tr>
</tbody>
</table>
<h2 id="profiling">Profiling</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.jetbrains.com/profiler/" rel="external nofollow noopener">dotTrace</a></td>
<td>Although I prefer the ANTS profiler, dotTrace is a very usable profiler and given it is included in my ReSharper Ultimate subscription, it's a no-brainer to use</td>
</tr>
<tr>
<td><a href="https://www.jetbrains.com/dotmemory/" rel="external nofollow noopener">dotMemory</a></td>
<td>As with dotTrace it is probably time to explore alternatives if I let the ReSharper subscription lapse (yet another reason why perpetual licenses are better than the modern trend of renting software)</td>
</tr>
</tbody>
</table>
<h2 id="documentation-tools">Documentation Tools</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.atomineerutils.com/" rel="external nofollow noopener">Atomineer Pro Documentation</a></td>
<td>Automatically generate XML comment documentation in your source code (Visual Studio extension)</td>
</tr>
<tr>
<td>HelpWrite</td>
<td>The first application offered by Ariad in the mists of time, now reincarnated and producing no-frills documentation from simple markdown and YAML</td>
</tr>
</tbody>
</table>
<h2 id="continuous-integration">Continuous Integration</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://jenkins.io/index.html" rel="external nofollow noopener">Jenkins</a></td>
<td>Continuous integration that is easy to install, doesn't need a database server and has a rich plugin ecosystem, even for .NET developers. I use this to build, test and deploy all our products and libraries</td>
</tr>
</tbody>
</table>
<h2 id="testing">Testing</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.ncrunch.net/" rel="external nofollow noopener">NCrunch</a></td>
<td>(Visual Studio Extension) Frequently updated automated parallel continuous testing tool (there's a mouthful). Works with NUnit, MSTest, SpecFlow and a variety of other test systems. This is by far the best continuous testing tool on the market in my humble opinion. Buy it!</td>
</tr>
<tr>
<td><a href="https://nunit.org/" rel="external nofollow noopener">NUnit</a></td>
<td>Our test framework of choice, for no particular reason other than it was the first one we tried after getting fed up of MSTest's limitations</td>
</tr>
</tbody>
</table>
<h2 id="graphics">Graphics</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://affinity.serif.com/en-gb/designer/" rel="external nofollow noopener">Affinity Designer</a></td>
<td>Vector editing software</td>
</tr>
<tr>
<td><a href="https://affinity.serif.com/en-gb/photo/" rel="external nofollow noopener">Affinity Photo</a></td>
<td>Photo editing software. Don't use this often as Paint.NET starts faster, but again, Serif offers perpetual licenses and thus they are worthy of my money.</td>
</tr>
<tr>
<td><a href="https://www.angelcode.com/products/bmfont/" rel="external nofollow noopener">AngelCode BMFont</a></td>
<td>Utility for creating bitmap fonts. We also have a <a href="https://github.com/cyotek/Cyotek.Drawing.BitmapFont" rel="external nofollow noopener">library</a> for working with BMFont files in C#</td>
</tr>
<tr>
<td><a href="https://www.axialis.com/iconworkshop/" rel="external nofollow noopener">Axialis IconWorkshop</a></td>
<td>Very nice icon editor, I have been using this for untold years now since Microangelo was abandoned. However, it itself hasn't seen any updates for some years now</td>
</tr>
<tr>
<td><a href="https://www.cyotek.com/cyotek-gif-animator/">Cyotek GIF Animator</a></td>
<td>GIF animation creator that was shaping up nicely, although it is another application I really want to spend more time improving</td>
</tr>
<tr>
<td><a href="https://www.cyotek.com/cyotek-spriter">Cyotek Spriter</a></td>
<td>Sprite / image map generation software that is still in sore need of optimisation and TLC</td>
</tr>
<tr>
<td><a href="https://getgreenshot.org/" rel="external nofollow noopener">Greenshot</a></td>
<td>Screenshot capturing utility. Another wheel I almost reinvented but this is nice software that fits the bill. Release hasn't been updated for years even though the source repository is rife with activity</td>
</tr>
<tr>
<td><a href="https://inkscape.org/" rel="external nofollow noopener">Inkscape</a></td>
<td>Open source vector graphics editing software</td>
</tr>
<tr>
<td><a href="https://www.getpaint.net/" rel="external nofollow noopener">Paint.NET</a></td>
<td>Brilliant bitmap editor with extensive plugins</td>
</tr>
<tr>
<td><a href="https://www.irfanview.com/" rel="external nofollow noopener">IrfanView</a></td>
<td>I use this for quickly browsing photographs - it's also useful for viewing EXIF data. It's much more than a simple image viewer, but that's only what I've used it for so far</td>
</tr>
<tr>
<td><a href="http://optipng.sourceforge.net/" rel="external nofollow noopener">OptiPNG</a></td>
<td>CLI tool for reducing the size of PNG images without loosing information. I've used this for what feels like forever</td>
</tr>
<tr>
<td><a href="https://jpegclub.org/jpegtran/" rel="external nofollow noopener">jpegtran</a></td>
<td>CLI tool for reducing the size of JPEG images without sacrificing quality. Most of the images I publish are PNG so I haven't use this tool much yet but seems positive thus far</td>
</tr>
</tbody>
</table>
<h2 id="audio">Audio</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.audacityteam.org/" rel="external nofollow noopener">Audacity</a></td>
<td><ins>New for 2023</ins> I have a number of audio cassettes, some of which haven't been played for nearly 40 years - I thought it might be a good idea to digitally archive these, with the aid of Audacity and a USB tape cassette. I also have a small pile of equally elder LP's and singles to hopefully get the archive treatment this year.</td>
</tr>
</tbody>
</table>
<h2 id="virtualization">Virtualization</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.virtualbox.org/" rel="external nofollow noopener">Oracle VM VirtualBox</a></td>
<td>Virtualization software. I prefer this to Hyper-V but when I finally stop procrastinating and finish the server 2019 Core rebuild I'll be reluctantly switching to that, unless I try ProxMox</td>
</tr>
<tr>
<td><a href="https://www.docker.com/" rel="external nofollow noopener">Docker</a></td>
<td><ins>New for 2023</ins> I use this on a NAS which has replaced virtually all of what I used several Windows micro servers for</td>
</tr>
</tbody>
</table>
<h2 id="version-control">Version Control</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://gitforwindows.org/" rel="external nofollow noopener">Git for Windows</a></td>
<td>Git client, tools and GUI for use on Windows</td>
</tr>
<tr>
<td><a href="https://gitea.io/en-us/" rel="external nofollow noopener">Gitea</a></td>
<td>Self-hosting for Git repositories. An impressive piece of software. I use this for our internal projects</td>
</tr>
<tr>
<td><a href="https://github.com/" rel="external nofollow noopener">GitHub</a></td>
<td>Git hosting for our public repositories</td>
</tr>
<tr>
<td><a href="https://www.syntevo.com/smartgit/" rel="external nofollow noopener">SmartGit</a></td>
<td>Git client. I liked this so much I paid for a lifetime <em>perpetual</em> license. Yes, that's right, there are some companies not screwing their customers over with bullshit subscription models for offline software.</td>
</tr>
<tr>
<td><a href="https://tortoisesvn.net/" rel="external nofollow noopener">TortoiseSVN</a></td>
<td>Windows Explorer integration for SVN</td>
</tr>
<tr>
<td><del>VisualSVN Server</del></td>
<td>Subversion Server for Windows. While new projects use Git, all legacy code is in SVN and at this point I don't have plans to migrate the entire mono-repository. After many, many years, now replaced by svnserve running in a Docker container.</td>
</tr>
</tbody>
</table>
<h2 id="file-and-directory">File and Directory</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.7-zip.org/" rel="external nofollow noopener">7-Zip</a></td>
<td>I've mentioned before on this blog that I used to love WinZip, until it turned into a bloated mess several years back. Since then, I have used 7-Zip for all my archiving needs</td>
</tr>
<tr>
<td><a href="https://www.tc4shell.com/en/7zip/modern7z/" rel="external nofollow noopener">Modern7z</a></td>
<td><ins>New for 2023</ins> Plugin for 7-Zip that adds the ability to decompress ZStd. Phew.</td>
</tr>
<tr>
<td><a href="https://filezilla-project.org/" rel="external nofollow noopener">FileZilla</a></td>
<td>Simple FTP client that has served my needs for years now</td>
</tr>
<tr>
<td><a href="https://tools.stefankueng.com/grepWin.html" rel="external nofollow noopener">grepWin</a></td>
<td>Another excellent tool for swiftly searching for files containing specific strings or expressions</td>
</tr>
<tr>
<td><a href="https://mh-nexus.de/en/hxd/" rel="external nofollow noopener">HxD</a></td>
<td>Another program I've used on and off for years but omitted from this list. Useful hex editor</td>
</tr>
<tr>
<td><a href="https://www.jam-software.com/treesize_free" rel="external nofollow noopener">TreeSize</a></td>
<td>Find out what is using all the space on your disks. Another utility I have used for untold years.</td>
</tr>
<tr>
<td><a href="https://winmerge.org/" rel="external nofollow noopener">WinMerge</a></td>
<td>Excellent file and directory comparison utility</td>
</tr>
<tr>
<td><del>WinRar</del></td>
<td>Archiver. I started using this after Plesk changed their databases backups to create zip files that use ZStd compression which 7-Zip doesn't support. WinRar was one of the few tools I tested (the other being WinZip, no thanks) that could extract files from these zips. After all, a backup isn't a backup unless you can restore! After apparently being one of the few people who actually buy a licence for WinRar, I stopped using it after discovering Modern7z</td>
</tr>
<tr>
<td><a href="https://diskanalyzer.com/" rel="external nofollow noopener">WizTree</a></td>
<td><ins>New for 2023</ins> I tried this on a whim and was absolutely blown away by its speed. It is no different to most other tools when scanning UNC shares, but when scanning local it is the faster than anything else I've seen. I've been using TreeSize for more years than I could count, but after buying a license for WizTree, I don't think I'll have much use for TreeSize</td>
</tr>
</tbody>
</table>
<h2 id="backups">Backups</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.crashplan.com/en-us/business/features/" rel="external nofollow noopener">CrashPlan</a></td>
<td>CrashPlan creates an online backup of the different offline backups that CopyTools does. If you've ever lost a hard-disk before with critical data on it that's nowhere else, you'll have backups squirrelled away everywhere too! Doesn't support docker, so this one is one the seeking-to-replace list.</td>
</tr>
<tr>
<td><a href="https://www.cyotek.com/cyotek-copytools">Cyotek CopyTools</a></td>
<td>We use this for offline backups of source code, assets and resources, documents, actually pretty much anything we generate; including backing up the backups!</td>
</tr>
<tr>
<td><a href="https://www.macrium.com/home-8" rel="external nofollow noopener">Macrium Reflect</a></td>
<td>Used to create DR images of some physical machines</td>
</tr>
<tr>
<td><a href="https://aws.amazon.com/s3/" rel="external nofollow noopener">Amazon S3</a></td>
<td><ins>New for 2023</ins> Via the Cloud Sync application on my Synology NAS I back up some critical documents to S3. However, I still rely on CrashPlan for the multiple-TB backups as they are cost prohibitive in S3.</td>
</tr>
<tr>
<td><a href="https://www.backblaze.com/" rel="external nofollow noopener">Backblaze</a></td>
<td><ins>New for 2023</ins> Via the Hyper Backup and Cloud Sync applications on my Synology NAS I back up some critical documents to Backblaze. Like S3, it is cost prohibitive to have my large backups here.</td>
</tr>
</tbody>
</table>
<h2 id="security">Security</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://bitwarden.com/" rel="external nofollow noopener">Bitwarden</a></td>
<td>Password manager with a variety of clients. Syncs data</td>
</tr>
<tr>
<td><a href="https://someonewhocares.org/hosts/" rel="external nofollow noopener">Dan Pollock's Hosts File</a></td>
<td>A hosts file blocks your computer from connecting to many thousands of dubious internet hosts and is continuously updated. A pain to update, but useful if you don't have access to something better</td>
</tr>
<tr>
<td><a href="https://keepass.info/" rel="external nofollow noopener">KeePass</a></td>
<td>Offline password manager</td>
</tr>
<tr>
<td><a href="https://letsencrypt.org/" rel="external nofollow noopener">Let's Encrypt</a></td>
<td>Short term SSL certificates for free. If you (or your host) are able to automate the process, this is an exceptional way to get basic SSL for your sites</td>
</tr>
<tr>
<td><a href="https://sectigo.com/" rel="external nofollow noopener">Sectigo</a></td>
<td>Code signing certificates, and domain SSL if a particular host doesn't support Let's Encrypt. 2024 marks the first year having to use hardware token and while it is probably more secure, it is almost maliciously developer unfriendly.</td>
</tr>
<tr>
<td><a href="https://www.virustotal.com/" rel="external nofollow noopener">Virus Total</a></td>
<td>Analyze files for malware. It is a helpful tool, except for when you find that one given engine will flag all your submissions as malicious and then when that finally clears up another one decides to join in the &quot;fun&quot; instead</td>
</tr>
</tbody>
</table>
<h2 id="issue-tracking">Issue Tracking</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.mantisbt.org/" rel="external nofollow noopener">Mantis Bug Tracker</a></td>
<td>Open source issue tracker</td>
</tr>
<tr>
<td><a href="https://github.com/cyotek/MantisSharp" rel="external nofollow noopener">MantisSharp</a></td>
<td>I use our MantisSharp library to add integration between various applications and our MantisBT instance, notable for raising new issues from our automated error monitor, and for creating road-maps on cyotek.com product pages although as usual I haven't had much time to maintain it</td>
</tr>
</tbody>
</table>
<h2 id="help-desk">Help Desk</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.maiansupport.com/" rel="external nofollow noopener">Maian Support</a></td>
<td>Basic help desk. Much easier than trying to keep track of emails</td>
</tr>
</tbody>
</table>
<h2 id="web-browsers-email-calendering">Web Browsers, Email, Calendering</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://sabre.io/baikal/" rel="external nofollow noopener">Baïkal</a></td>
<td>Self hosted CalDAV (calendar) and CardDAV (contacts) server</td>
</tr>
<tr>
<td><a href="https://www.davx5.com/" rel="external nofollow noopener">DAVx5</a></td>
<td>Two way sync for CalDAV data. I use this on my Android (RIP Windows Phone) phone to sync my calendar with my Baïkal instance</td>
</tr>
<tr>
<td><a href="https://duckduckgo.com/" rel="external nofollow noopener">DuckDuckGo</a></td>
<td><em>The search engine that doesn't track you</em> - I can't remember when I made the switch to DuckDuckGo as it was several years ago, but it does a great job and I rarely have to fall back to &quot;another&quot; search engine</td>
</tr>
<tr>
<td><a href="https://www.mozilla.org/firefox/" rel="external nofollow noopener">Firefox</a></td>
<td>Last bastion from a Chromium world. I switched to this as my primary browser in 2018 as my own protest against Chrome's dominance (and don't get me started on Microsoft's ill advised capitulation)</td>
</tr>
<tr>
<td><a href="https://www.mailstore.com/en/products/mailstore-home/" rel="external nofollow noopener">MailStore Home</a></td>
<td>Email archiving. Also I tend to find its search interface quicker and more compact than the one in Thunderbird</td>
</tr>
<tr>
<td><a href="https://microsoftedgewelcome.microsoft.com/en-gb/" rel="external nofollow noopener">Microsoft Edge</a></td>
<td>I liked the Trident based Edge just fine and wish they had simply decoupled it from the OS and kept it updated. But at least with this Chromium version I don't need to ever install Chrome again. Of course, Microsoft being Microsoft they are pushing more and more bloat into it, and thanks to idiotic Brexit, some of the European decisions to curtail them don't apply.</td>
</tr>
<tr>
<td><a href="https://www.thunderbird.net/en-GB/" rel="external nofollow noopener">Thunderbird</a></td>
<td>Email client. A bit rough around the edges but preferable to Outlook and lets me store emails in maildir format, as well as natively supporting CalDAV and CardDAV</td>
</tr>
</tbody>
</table>
<h2 id="other">Other</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.autohotkey.com/" rel="external nofollow noopener">AutoHotkey</a></td>
<td>I was resurrecting a tool from 2002, and decided that its documentation needed screenshots. I stopped doing these years ago because of the pain in updating them, but AutoHotkey is impressively powerful and I was able to use it to write a script that would run the application to set up scenarios and then capture screenshots.</td>
</tr>
<tr>
<td><a href="https://calibre-ebook.com/" rel="external nofollow noopener">Calibre</a></td>
<td>Ebook management. Although I still prefer paper books, I don't buy them as often as I did. I tend to read on e-ink devices and Calibre makes it simple to update these. It works with my Kindle, my Nooks, and now my PocketBooks</td>
</tr>
<tr>
<td><a href="https://ditto-cp.sourceforge.io/" rel="external nofollow noopener">Ditto</a></td>
<td>Clipboard manager. Another extremely useful piece of software that I have used for many, many years. Now on the Windows Store which means silent updates... wonderful!</td>
</tr>
<tr>
<td><a href="https://eartrumpet.app/" rel="external nofollow noopener">EarTrumpet</a></td>
<td>Per-application volume manager. I can't remember exactly when I started using this, probably for a game that didn't have built in volume controls but did have obnoxious levels</td>
</tr>
<tr>
<td><a href="https://justgetflux.com/" rel="external nofollow noopener">f.lux</a></td>
<td>I've been using this utterly fantastic software for years. It adapts your monitor to the time of day, removing blue light as evening approaches and helps reduce eye strain when coding at night</td>
</tr>
<tr>
<td><a href="https://docs.microsoft.com/en-us/windows/powertoys/" rel="external nofollow noopener">PowerToys</a></td>
<td>Although <em>nothing</em> like the old Win9x PowerToys, there is at least something useful in this new bag. I have an ultra-wide monitor and I use Fancy Zones to virtually break it up into 3 columns. As it works with the core Win+Arrow hotkeys it makes for a pretty decent window manager</td>
</tr>
<tr>
<td><a href="https://ravendb.net/" rel="external nofollow noopener">RavenDB</a></td>
<td>Still not using this for much as I can't seem to effectively query the data from Raven Studio, and at heart I still think NoSQL is a fad. Transitioned some data back to SQL Server, the rest to follow</td>
</tr>
<tr>
<td><a href="https://rufus.akeo.ie/" rel="external nofollow noopener">Rufus</a></td>
<td>I use this utility for writing ISO images to USB or SD cards, useful for setting up new physical machines in an age where CD drives are fairly obsolete</td>
</tr>
<tr>
<td><a href="https://sourceforge.net/projects/win32diskimager/" rel="external nofollow noopener">Win32 Disk Imager</a></td>
<td>Useful for burning ISO images to SD cards, although I now prefer Rufus for that. It is also massively useful for creating (and restoring) images of SD cards, so I use it to backup my assorted Raspberry Pi devices before major updates</td>
</tr>
<tr>
<td><a href="https://docs.microsoft.com/en-gb/sysinternals/downloads/rdcman" rel="external nofollow noopener">Remote Desktop Connection Manager</a></td>
<td>A valuable tool if you need to remote into one than one server as you can create multiple profiles and groups, use setting inheritance and more</td>
</tr>
<tr>
<td><a href="https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer" rel="external nofollow noopener">Process Explorer</a></td>
<td>Another tool I've used for umpteen years, ProcessExplorer is great for viewing information about running processes. I mostly use this to find what process is locking a given file and closing these handles - this is built into PowerToys these days but I still find myself using Process Explorer first and foremost</td>
</tr>
<tr>
<td><a href="https://docs.microsoft.com/en-us/sysinternals/downloads/procmon" rel="external nofollow noopener">Process Monitor</a></td>
<td>I don't often use this tool, but when I need it is very valuable as it lets me trace any file or registry access</td>
</tr>
<tr>
<td><a href="https://www.microsoft.com/en-gb/p/wake-on-lan/9wzdncrdfshb" rel="external nofollow noopener">Wake On Lan</a></td>
<td>Some of my old generation micro-servers don't run 24/7, and being able to remotely start them (they don't have ILO) is pleasant. This software is a little buggy but it gets the job done for the most part and that is all I need</td>
</tr>
<tr>
<td><a href="https://quiterss.org/en" rel="external nofollow noopener">QuiteRSS</a></td>
<td>Easy to use RSS reader</td>
</tr>
</tbody>
</table>
<h2 id="media-players">Media Players</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.foobar2000.org/" rel="external nofollow noopener">foobar2000</a></td>
<td>This used to be my go to music player before switching to an external Raspberry Pi based solution. However, I still keep foobar2000 hanging around for ripping music CDs</td>
</tr>
<tr>
<td><a href="https://kodi.tv/" rel="external nofollow noopener">Kodi</a></td>
<td>I've used this for years now to watch video on various generations of Raspberry Pi. I found the Films and TV (or Movies and TV) application that ships with Windows 10 to be absolute rubbish and was very glad when Kodi became available on the Microsoft Store</td>
</tr>
<tr>
<td><a href="https://mopidy.com/" rel="external nofollow noopener">Mopidy</a> / <a href="https://github.com/jaedb/Iris" rel="external nofollow noopener">Iris</a></td>
<td>Music player. I have this software installed on a Raspberry Pi Zero with a <a href="https://thepihut.com/products/phat-beat" rel="external nofollow noopener">pHAT BEAT</a> for playing music.</td>
</tr>
<tr>
<td><a href="https://www.videolan.org/vlc/" rel="external nofollow noopener">VLC media player</a></td>
<td>Media player. I rarely use this as an actual player, it is mainly to double check tracks on DVDs when there is an ambiguity with which one(s) to rip</td>
</tr>
</tbody>
</table>
<h2 id="third-party-apis">Third Party APIs</h2>
<p>A new category for 2024 as a tool doesn't just have to be
something on a desktop. Within reason.</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://currencyapi.com/" rel="external nofollow noopener">currencyapi</a></td>
<td>I use this for currency conversions in a web application</td>
</tr>
<tr>
<td><a href="https://www.stopforumspam.com/" rel="external nofollow noopener">stopforumspam</a></td>
<td>I suppose the clue is in the name, but I use this to try and stem the flood of garbage posted to my web sites</td>
</tr>
<tr>
<td><a href="https://cleantalk.org/" rel="external nofollow noopener">CleanTalk</a></td>
<td>Another API integration to try and block spam</td>
</tr>
</tbody>
</table>
<h2 id="docker-containers">Docker Containers</h2>
<p>A new category for 2024 as I dip my feet into the world of
containers.</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>garethflowers/svn-server</td>
<td>A lightweight SVN server</td>
</tr>
<tr>
<td>gitea/gitea</td>
<td>Official Gitea container for hosting Git repositories</td>
</tr>
<tr>
<td>jenkins/jenkins</td>
<td>Official Jenkins container for my CI/CD fix</td>
</tr>
<tr>
<td>ravendb/ravendb</td>
<td>Official RavenDB container to tide me over while I decide if I'm scapping RavenDB or not. I've only been deciding for 3 or 4 years now.</td>
</tr>
<tr>
<td>loicsharma/baget</td>
<td>Official Baget container. This software is starting to get frustratingly long in the tooth</td>
</tr>
</tbody>
</table>
<h2 id="hardware">Hardware</h2>
<p>A new category for 2023, but I thought I would include some of
the hardware we use. Not entirely sure how much of a point there
is to this, so I may drop it... I don't see the point of listing
CPU's etc (and besides, most of our equipment has ancient
processors and GPU's!)</p>
<table>
<thead>
<tr>
<th>Device</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.brother.co.uk/printers/laser-printers/mfc-l2750dw" rel="external nofollow noopener">Brother MFC-L2750DW</a></td>
<td>Black and white laser printer, A4 scanner, A4 ADF scanner</td>
<td>Overpriced ink? Subscriptions for said overpriced ink? Ink cartridge DRM? All reasons why I'll never again use HP printers. This laser supports duplex printing and an automated document feeder - and take it from me, if you've spent days scanning hundreds of pieces of paper on a flatbed scanner, you will rate ADFs as much as I do!</td>
</tr>
<tr>
<td><a href="https://www.canon.co.uk/support/consumer_products/products/scanners/lide_series/canoscan_lide_100.html" rel="external nofollow noopener">CanoScan LiDE 100</a></td>
<td>A4 flatbed scanner</td>
<td>I don't really use this much any more, except to test that it still works on newer versions of Windows, but with that said - it is still a perfectly functional unit I wouldn't hesitate to use</td>
</tr>
<tr>
<td><a href="https://www.canon.co.uk/support/consumer_products/products/scanners/lide_series/canoscan_lide_220.html" rel="external nofollow noopener">CanoScan LiDE 220</a></td>
<td>A4 flatbed scanner</td>
<td>Although I wanted a LiDE 400, it seems to be out of stock since forever, so I use this in the interim. It's a nice little device.</td>
</tr>
<tr>
<td><a href="https://www.microsoft.com/en-gb/d/new-microsoft-bluetooth-ergonomic-mouse/94jnl0t5kdck?activetab=pivot:overviewtab" rel="external nofollow noopener">Microsoft Bluetooth Ergonomic Mouse</a></td>
<td>Ergonomic Mouse</td>
<td>For more than a decade I've used a Razor DeathAdder, but I tried this mouse recently and switched to it. One of the nice little extras is has is the ability to sync with 3 different devices, so I have it tuned to my desktop PC, a ThinkPad laptop and also a Surface Pro 6</td>
</tr>
<tr>
<td><a href="https://www.microsoft.com/en-gb/d/microsoft-sculpt-ergonomic-desktop/8xk02kz6k69w" rel="external nofollow noopener">Microsoft Sculpt Ergonomic Desktop</a></td>
<td>Ergonomic Keyboard</td>
<td>Like the DeathAdder, I've used the Comfort Curve 2000 and Comfort 4000 keyboards for more than a decade, but I recently decided to give this a try. It took a little while to get used to the changed right hand layout, but I really like this keyboard</td>
</tr>
<tr>
<td><a href="https://plustek.com/eu/products/flatbed-scanners/opticslim-1180/index.php" rel="external nofollow noopener">Plustek OpticSlim 1180</a></td>
<td>A3 flatbed scanner</td>
<td>I rarely use this, unless I'm scanning content bigger than A4. It is slow, noisy and the colour reproduction is abysmal so the scanned content doesn't look accurate. However, it is affordable. Next time I need a A3 scanner though, I'll save the pennies and get something with a CCD sensor</td>
</tr>
<tr>
<td><a href="https://www.microsoft.com/en-gb/d/surface-arc-mouse/8p5sv2rx3rn5" rel="external nofollow noopener">Surface Arc Mouse</a></td>
<td>Mouse</td>
<td>Not the most comfortable of mice, but it folds flat which is great for travelling - I use this with my Surface Pro 6. One interesting feature of this mouse is the buttons seem more like a trackpad than mouse buttons, so you can do gestures for scrolling</td>
</tr>
<tr>
<td><a href="https://www.synology.com/en-us/products/DS1821+" rel="external nofollow noopener">DiskStation DS1821+</a></td>
<td>NAS</td>
<td><ins>New for 2023</ins> I finally decided to splash some cash on something modern instead of buying obsolete second hand equipment or trying to DIY with inferior hardware. While I'm not too enthused with some of the walled garden aspects (branded &quot;compatible&quot; memory cost a small fortune over the exact same unbranded chips) and find the software a bit limited in some respects, I am really happy with this purchase</td>
</tr>
<tr>
<td>Seagate Exos X16 Hard Drives</td>
<td>Hard Drive</td>
<td><ins>New for 2023</ins> I ended up buying 8 of these 16TB drives for use in the above NAS, 6 in a RAID 6 configuration for a total capacity of of 58TB, with two as hot spares.</td>
</tr>
<tr>
<td><a href="https://pocketbook.ch/en-ch/catalog/e-readers-and-e-notes/pocketbook-inkpad-lite-ch" rel="external nofollow noopener">PocketBook InkPad Lite</a></td>
<td>E-Reader</td>
<td><ins>New for 2023</ins> Most of my reading is done on either an older generation of Kindle PaperWhite, or an even older Nook Glow. I don't use Amazon's DRM entrenched ecosystem, but the device is nice. However, I have a lot of technical books which I find to be folly trying to read on a 6&quot; device. I wanted something larger, but am not interested in being locked in the eco systems of some of the other big name players. I bought a InkPad Lite, a 9.7&quot; device, to test with and it is surprisingly good, while also being surprisingly cheap. And I don't need to register an account to use it - so far, I like a lot!</td>
</tr>
</tbody>
</table>
<h2 id="honourable-mentions">Honourable Mentions</h2>
<p>A new category for 2024, whatever appears here will be transient
from year to year and present programs I used, but only for a
short time.</p>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.codeblocks.org/" rel="external nofollow noopener">Code::Blocks</a></td>
<td>I bought myself the heavy slab paperback of <a href="https://craftinginterpreters.com/" rel="external nofollow noopener">Crafting Interpreters</a> and spend several happy hours reading and implementing it. When it came to the latter half of the book using C for the implementation, I used Code::Blocks as my IDE for a while. While it worked, it had some annoyances and niggles, and eventually I discovered that Visual Studio 2022 actually supported C quite nicely, despite its C++ monikers at which point I eventually stopped using it. So here it is as the first Honourable Mention!</td>
</tr>
</tbody>
</table>
<p>What tools do you find useful? I'd love to know... maybe I'll
find a new gem myself!</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/tools-we-use-2023-edition .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comResolving SVN error "Could not open the requested SVN filesystem"urn:uuid:08f8af10-b24e-4d80-ac74-a9b496421f962023-04-25T17:48:24Z2023-04-25T17:48:24Z<p>Recently I bought a Synology DS1821+ to complement a pair of
long-in-the-tooth HP N40L micro servers running Windows.
Although I originally bought it just for storage and &quot;real&quot;
RAID, I found it such a delightful device that I decided that it
should replace the micro servers completely. Prior to this I've
never really used Docker, but the Synology DiskStation Manager
(DSM) software makes it a breeze - in no time at all I had
Jenkins and Gitea running on it via official packages, and a
third party for SVN as I still have both a legacy and a
&quot;current&quot; repository, the latter one that I'm piecemeal
converting to git.</p>
<p>The only problem was, the truly ancient VB6 repository would
load fine, but the &quot;current&quot; C# one failed when trying to access
it:</p>
<blockquote>
<p>Could not open the requested SVN filesystem</p>
</blockquote>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svn-filesystem-error-browser.png" class="gallery" title="An unwelcome guest" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svn-filesystem-error-browser.png" alt="An unwelcome guest" decoding="async" loading="lazy" /></a><figcaption>An unwelcome guest</figcaption></figure>
<p>I then had a rather frustrating session trying assorted
fruitless attempts to get the repository accessible, copying it
directly, doing <code>svnadmin dump</code> and <code>svnadmin load</code>, deleting
Windows-based hooks, etc etc without success.</p>
<h2 id="seeing-the-light">Seeing the light</h2>
<p>Worried that the original repository was corrupt, I finally did
a <code>svnadmin verify</code> on it, which passed with flying colours.
However, when I tried the same command on my most recent clone,
instead of the generic error I'd been getting with other
commands I got something specific and helpful.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svn-filesystem-error-verify.png" class="gallery" title="The error message to unlock the puzzle" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svn-filesystem-error-verify.png" alt="The error message to unlock the puzzle" decoding="async" loading="lazy" /></a><figcaption>The error message to unlock the puzzle</figcaption></figure>
<pre><code>svnadmin: E160043: Expected FS format between '1' and '7'; found format '8'
</code></pre>
<p>Now we're getting somewhere! It appears that the third party
image I'm using isn't using an up to date version of SVN, but
1.9.7 from 6 years ago. Alas this blog doesn't support emoji
otherwise there'd be a facepalm here for sure, both for this
image being woefully out of date, and for me forgetting I <a href="https://devblog.cyotek.com/post/upgrading-a-svn-repository-using-visualsvn-server">wrote
about how I upgraded my repository from an older
version</a> some years back.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svn-filesystem-error-version.png" class="gallery" title="I don't run software on the bleeding edge, but this is absurdly old" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svn-filesystem-error-version.png" alt="I don't run software on the bleeding edge, but this is absurdly old" decoding="async" loading="lazy" /></a><figcaption>I don't run software on the bleeding edge, but this is absurdly old</figcaption></figure><h2 id="making-it-go">Making it go</h2>
<p>When you create a repository via <code>svnadmin create</code>, there is a
<code>--compatible-version</code> switch you can use to force an older
version. So back on the Windows box I created a new repository,
using the 1.7 format.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svn-filesystem-error-create.png" class="gallery" title="Creating a repository tailored for older versions of SVN" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svn-filesystem-error-create.png" alt="Creating a repository tailored for older versions of SVN" decoding="async" loading="lazy" /></a><figcaption>Creating a repository tailored for older versions of SVN</figcaption></figure>
<pre><code>svnadmin create clone --compatible-version 1.7
</code></pre>
<p>I then loaded in a previous dump</p>
<pre><code>svnadmin load clone &lt; cyotekdump.svn
</code></pre>
<p>Once done, I zipped up the repository, dropped it on the NAS,
deleted the &quot;broken&quot; clone, and extracted the zip in-situ. Then
I re-ran the verify command and...</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svn-filesystem-error-verify-docker.png" class="gallery" title="It works!" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svn-filesystem-error-verify-docker.png" alt="It works!" decoding="async" loading="lazy" /></a><figcaption>It works!</figcaption></figure>
<p>Huzzah! And the web interface works too.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svn-filesystem-error-browser-success.png" class="gallery" title="It really works!" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svn-filesystem-error-browser-success.png" alt="It really works!" decoding="async" loading="lazy" /></a><figcaption>It really works!</figcaption></figure><h2 id="closing-notes">Closing Notes</h2>
<p>Of course, once you look at the Edge and SSH screenshot you can
see the same error code is present in both, not that I know SVN
error codes off the top of my head! In addition I only used Edge
to create the screenshots for this post, originally my testing
was with Firefox - and this didn't show the XML at all, only the
error text.</p>
<p>Although I have this working, it seems rather pointless having
this shiny new device and then installing software on it that is
obsolete and so I need to find a newer image. (Of course, I
really should focus on migrating away from SVN as well!)</p>
<p>But I haven't blogged for a very long time and who knows, this
particular information might help someone else down the line.</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/resolving-svn-error-could-not-open-the-requested-svn-filesystem .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comTools we use - 2022 editionurn:uuid:e5bb1a2e-bf66-49b6-be63-08ae175b1ded2023-01-01T15:27:24Z2023-01-01T15:25:47Z<p>Happy New Year! So much for 2022 being better than 2021, I think
the lack of updates to this blog speaks on how unproductive I've
been this year but this time I haven't even really worked on our
different software offerings or looked at support tickets or
forums in months. Will 2023 be any better? Right now it doesn't
feel like it, so I'm not even going to try to make any promises.</p>
<h2 id="operating-systems">Operating Systems</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>Windows 11</td>
<td>Secondary development environment. Not a fan.</td>
</tr>
<tr>
<td>Windows 10 (Virtualized)</td>
<td>Testing VM (32bit and 64bit)</td>
</tr>
<tr>
<td>Windows 10 Professional</td>
<td>Development environment</td>
</tr>
<tr>
<td>Windows 7 (Virtualized)</td>
<td>Testing VM (32bit)</td>
</tr>
<tr>
<td>Windows Home Server 2011</td>
<td>File server, SVN server, Git server, backup host, CI server. I've been thinking of replacing this with a Linux server</td>
</tr>
<tr>
<td>Windows Vista (Virtualized)</td>
<td>Testing VM (32bit)</td>
</tr>
</tbody>
</table>
<h2 id="development-tools">Development Tools</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://sqlitebrowser.org/" rel="external nofollow noopener">DB Browser for SQLite</a></td>
<td>GUI for working with SQLite databases</td>
</tr>
<tr>
<td><a href="https://devtoys.app/" rel="external nofollow noopener">DevToys</a></td>
<td><ins>New for 2022</ins> A bunch of different developer tools, such as Base64 encoding/decoding and hash generation</td>
</tr>
<tr>
<td><a href="https://github.com/0xd4d/dnSpy" rel="external nofollow noopener">dnSpy</a></td>
<td>Speedy .NET assembly debugger and editor. Oddly, this has been archived for no public reason I can find</td>
</tr>
<tr>
<td><del>EditorConfig</del></td>
<td>Useful for OSS projects to avoid space-vs-tab wars or to configure code style rules. No need for an extension now as built into Visual Studio</td>
</tr>
<tr>
<td><a href="https://www.ndepend.com/" rel="external nofollow noopener">NDepend</a></td>
<td>Static code analysis. I run this mostly as part of CI pipelines</td>
</tr>
<tr>
<td><a href="https://insomnia.rest/" rel="external nofollow noopener">Insomnia</a></td>
<td>Client for testing REST services. I switched to Insomnia over Postman as the latter doesn't work without an account and I'm little tired of having scores of accounts with scores of services for no actual benefit to me as the end user</td>
</tr>
<tr>
<td><a href="https://visualstudio.microsoft.com/" rel="external nofollow noopener">Visual Studio 2022</a></td>
<td>Although I use Visual Studio Code more and more, Visual Studio 2022 remains my IDE of choice</td>
</tr>
<tr>
<td><a href="https://code.visualstudio.com/" rel="external nofollow noopener">Visual Studio Code</a></td>
<td>Wonderful editor, once you install enough extensions to configure it &quot;your way&quot;. I use it for most non-.NET tasks, such as PHP or editing markdown. Workspaces that can include multiple folders are incredibly useful</td>
</tr>
<tr>
<td><a href="https://loic-sharma.github.io/BaGet/" rel="external nofollow noopener">Baget</a></td>
<td>I've been using <a href="https://github.com/NuGet/NuGet.Server" rel="external nofollow noopener">NuGet.Server</a> for some years for internal NuGet package management, but that tool is long in the tooth and somewhat limited in functionality. Baget is a much more modern (and functional!) alternative</td>
</tr>
<tr>
<td><a href="https://notepad-plus-plus.org/" rel="external nofollow noopener">Notepad++</a></td>
<td>My go to plain text editor. I tend to use Visual Studio Code now for most text <em>formats</em>. However, I am seeking an alternative as its shell extension doesn't support the Windows 11 context menu</td>
</tr>
</tbody>
</table>
<h2 id="visual-studio-extensions">Visual Studio Extensions</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.codemaid.net/" rel="external nofollow noopener">CodeMaid</a></td>
<td>Code formatting and organising. Lets be fair to ReSharper, there's <em>nothing</em> else available which does a better job, but CodeMaid is an acceptable substitute</td>
</tr>
<tr>
<td><a href="https://github.com/cyotek/Cyotek.AddProjects/releases" rel="external nofollow noopener">Cyotek Add Projects</a></td>
<td>A simple extension for easily adding multiple projects to your solutions. Although I use it far less now that most of my projects are packages, it is still useful</td>
</tr>
<tr>
<td><a href="https://marketplace.visualstudio.com/items?itemName=MadsKristensen.OpenCommandLine" rel="external nofollow noopener">Open Command Line</a></td>
<td>Easily open command prompts, PowerShell prompts, or other tools to your project / solution directories</td>
</tr>
<tr>
<td><a href="https://github.com/JosefPihrt/Roslynator" rel="external nofollow noopener">Roslynator</a></td>
<td>C# code analyzers, refactoring and fixes. I use this to replace some of the more critical functionality I previously enjoyed in ReSharper. Not using in VS2022 at the moment as I went back to the dark side</td>
</tr>
<tr>
<td><a href="https://github.com/Tim-Maes/T4Editor" rel="external nofollow noopener">T4Editor</a></td>
<td>I use this as a replacement for the ReSharper ForTea extension and I'm quite happy with it - it does a great job of showing me the T4 specific aspects of my templates. And even here in 2023 I still use T4 over source generators.</td>
</tr>
<tr>
<td><a href="https://mike-ward.net/vscoloroutput/" rel="external nofollow noopener">VSColorOutput</a></td>
<td>Add colour coding to Visual Studio's Output window</td>
</tr>
<tr>
<td><a href="https://www.jetbrains.com/resharper/" rel="external nofollow noopener">Resharper</a></td>
<td>What is the definition of insanity? Here I am suffering with dire performance problems caused by Resharper once again</td>
</tr>
<tr>
<td><a href="https://marketplace.visualstudio.com/items?itemName=DennisStanze.nordic-001" rel="external nofollow noopener">nordic</a></td>
<td>When I need a dark mode theme, I tend to use something like <a href="https://www.nordtheme.com/" rel="external nofollow noopener">Nord</a>. This extension themes Visual Studio to use a more nord-esque colour scheme</td>
</tr>
</tbody>
</table>
<h2 id="installation-and-deployment">Installation and Deployment</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://jrsoftware.org/isinfo.php" rel="external nofollow noopener">Inno Setup</a></td>
<td>Installer with a wealth of features</td>
</tr>
<tr>
<td><a href="https://www.kymoto.org/products/inno-script-studio" rel="external nofollow noopener">Inno Script Studio</a></td>
<td>IDE for working with Inno Setup scripts</td>
</tr>
<tr>
<td><a href="http://innounp.sourceforge.net/" rel="external nofollow noopener">Inno Setup Unpacker</a></td>
<td>Unpacks installations created with Inno Setup. I use this as part of the CI process to perform dependency checking</td>
</tr>
<tr>
<td><a href="https://learn.microsoft.com/en-us/windows/package-manager/winget/" rel="external nofollow noopener">winget</a></td>
<td><ins>New for 2022</ins> Package manager for Windows, similar to apt except with less features and way less packages. I started using these for setting up fresh environments instead of complicated homebrew PowerShell scripts. It is a shame that more software isn't available via this, and some that is isn't fully compatible with it.</td>
</tr>
</tbody>
</table>
<h2 id="analytics">Analytics</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://matomo.org/" rel="external nofollow noopener">Matomo</a></td>
<td>I use this web based analytics software to gain anonymous insights into cyotek.com usage</td>
</tr>
<tr>
<td>Unnamed Analytics</td>
<td>After dropping Luminitix, I replaced the data collection with a home grown solution, although I've yet to write a front end to look at the data effectively</td>
</tr>
</tbody>
</table>
<h2 id="profiling">Profiling</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.jetbrains.com/profiler/" rel="external nofollow noopener">dotTrace</a></td>
<td>Although I prefer the ANTS profiler, dotTrace is a very usable profiler and given it is included in my ReSharper Ultimate subscription, it's a no-brainer to use</td>
</tr>
<tr>
<td><a href="https://www.jetbrains.com/dotmemory/" rel="external nofollow noopener">dotMemory</a></td>
<td>As with dotTrace it is probably time to explore alternatives if I let the ReSharper subscription lapse (yet another reason why perpetual licenses are better than the modern trend of renting software)</td>
</tr>
</tbody>
</table>
<h2 id="documentation-tools">Documentation Tools</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.atomineerutils.com/" rel="external nofollow noopener">Atomineer Pro Documentation</a></td>
<td>Automatically generate XML comment documentation in your source code (Visual Studio extension)</td>
</tr>
<tr>
<td>HelpWrite</td>
<td>The first application offered by Ariad in the mists of time, now reincarnated and producing no-frills documentation from simple markdown and YAML</td>
</tr>
</tbody>
</table>
<h2 id="continuous-integration">Continuous Integration</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://jenkins.io/index.html" rel="external nofollow noopener">Jenkins</a></td>
<td>Continuous integration that is easy to install, doesn't need a database server and has a rich plugin ecosystem, even for .NET developers. I use this to build, test and deploy all our products and libraries</td>
</tr>
</tbody>
</table>
<h2 id="testing">Testing</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.ncrunch.net/" rel="external nofollow noopener">NCrunch</a></td>
<td>(Visual Studio Extension) Frequently updated automated parallel continuous testing tool (there's a mouthful). Works with NUnit, MSTest, SpecFlow and a variety of other test systems. This is by far the best continuous testing tool on the market in my humble opinion. Buy it!</td>
</tr>
<tr>
<td><a href="https://nunit.org/" rel="external nofollow noopener">NUnit</a></td>
<td>Our test framework of choice, for no particular reason other than it was the first one we tried after getting fed up of MSTest's limitations</td>
</tr>
<tr>
<td><a href="https://specflow.org/" rel="external nofollow noopener">SpecFlow</a></td>
<td>(Visual Studio Extension) I only used this for one project (my implementation of <a href="https://devblog.cyotek.com/post/book-review-the-ray-tracer-challenge">The Ray Tracer Challenge</a>) and after I a while I really found this way of implementing tests a bit of a game changer. However, I feel that I would quickly loose my sanity if I had to write all these specifications up front and so this is still sitting in my &quot;todo&quot; pile to look into further</td>
</tr>
</tbody>
</table>
<h2 id="graphics">Graphics</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://affinity.serif.com/en-gb/designer/" rel="external nofollow noopener">Affinity Designer</a></td>
<td>Vector editing software</td>
</tr>
<tr>
<td><a href="https://affinity.serif.com/en-gb/photo/" rel="external nofollow noopener">Affinity Photo</a></td>
<td>Photo editing software. Don't use this often as Paint.NET starts faster, but again, Serif offers perpetual licenses and thus they are worthy of my money.</td>
</tr>
<tr>
<td><a href="https://www.angelcode.com/products/bmfont/" rel="external nofollow noopener">AngelCode BMFont</a></td>
<td>Utility for creating bitmap fonts. We also have a <a href="https://github.com/cyotek/Cyotek.Drawing.BitmapFont" rel="external nofollow noopener">library</a> for working with BMFont files in C#</td>
</tr>
<tr>
<td><a href="https://www.axialis.com/iconworkshop/" rel="external nofollow noopener">Axialis IconWorkshop</a></td>
<td>Very nice icon editor, I have been using this for untold years now since Microangelo was abandoned. However, it itself hasn't seen any updates for some years now</td>
</tr>
<tr>
<td><a href="https://www.cyotek.com/cyotek-gif-animator/">Cyotek GIF Animator</a></td>
<td>GIF animation creator that was shaping up nicely, although it is another application I really want to spend more time improving</td>
</tr>
<tr>
<td><a href="https://www.cyotek.com/cyotek-spriter">Cyotek Spriter</a></td>
<td>Sprite / image map generation software that is still in sore need of optimisation and TLC</td>
</tr>
<tr>
<td><a href="https://getgreenshot.org/" rel="external nofollow noopener">Greenshot</a></td>
<td>Screenshot capturing utility. Another wheel I almost reinvented but this is nice software that fits the bill. Release hasn't been updated for years even though the source repository is rife with activity</td>
</tr>
<tr>
<td><a href="https://inkscape.org/" rel="external nofollow noopener">Inkscape</a></td>
<td>Open source vector graphics editing software</td>
</tr>
<tr>
<td><a href="https://www.getpaint.net/" rel="external nofollow noopener">Paint.NET</a></td>
<td>Brilliant bitmap editor with extensive plugins</td>
</tr>
<tr>
<td><a href="https://www.irfanview.com/" rel="external nofollow noopener">IrfanView</a></td>
<td>I use this for quickly browsing photographs - it's also useful for viewing EXIF data. It's much more than a simple image viewer, but that's only what I've used it for so far</td>
</tr>
<tr>
<td><a href="http://optipng.sourceforge.net/" rel="external nofollow noopener">OptiPNG</a></td>
<td>CLI tool for reducing the size of PNG images without loosing information. I've used this for what feels like forever</td>
</tr>
<tr>
<td><a href="https://jpegclub.org/jpegtran/" rel="external nofollow noopener">jpegtran</a></td>
<td>CLI tool for reducing the size of JPEG images without sacrificing quality. Most of the images I publish are PNG so I haven't use this tool much yet but seems positive thus far</td>
</tr>
</tbody>
</table>
<h2 id="virtualization">Virtualization</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.virtualbox.org/" rel="external nofollow noopener">Oracle VM VirtualBox</a></td>
<td>Virtualization software. I prefer this to Hyper-V but when I finally stop procrastinating and finish the server 2019 Core rebuild I'll be reluctantly switching to that, unless I try ProxMox</td>
</tr>
</tbody>
</table>
<h2 id="version-control">Version Control</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://gitforwindows.org/" rel="external nofollow noopener">Git for Windows</a></td>
<td>Git client, tools and GUI for use on Windows</td>
</tr>
<tr>
<td><a href="https://gitea.io/en-us/" rel="external nofollow noopener">Gitea</a></td>
<td>Self-hosting for Git repositories. An impressive piece of software. I use this for our internal projects</td>
</tr>
<tr>
<td><del>GitHub Desktop</del></td>
<td>Easy to use Git client that masks some of the more complicated functionality. Despite the name works with any Git repository. Thoroughly replaced by SmartGit.</td>
</tr>
<tr>
<td><a href="https://github.com/" rel="external nofollow noopener">GitHub</a></td>
<td>Git hosting for our public repositories</td>
</tr>
<tr>
<td><a href="https://www.syntevo.com/smartgit/" rel="external nofollow noopener">SmartGit</a></td>
<td><ins>New for 2022</ins> Git client. I liked this so much I paid for a lifetime <em>perpetual</em> license. Yes, that's right, there are some companies not screwing their customers over with bullshit subscription models for offline software.</td>
</tr>
<tr>
<td><del>SourceTree</del></td>
<td>Git client. Far more powerful than GitHub Desktop yet nowhere near as easy to use. Thoroughly replaced by SmartGit.</td>
</tr>
<tr>
<td><a href="https://tortoisesvn.net/" rel="external nofollow noopener">TortoiseSVN</a></td>
<td>Windows Explorer integration for SVN</td>
</tr>
<tr>
<td><a href="https://www.visualsvn.com/server/" rel="external nofollow noopener">VisualSVN Server</a></td>
<td>Subversion Server for Windows. While new projects use Git, all legacy code is in SVN and at this point I don't have plans to migrate the entire mono-repository</td>
</tr>
</tbody>
</table>
<h2 id="file-and-directory">File and Directory</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.7-zip.org/" rel="external nofollow noopener">7-Zip</a></td>
<td>I've mentioned before on this blog that I used to love WinZip, until it turned into a bloated mess several years back. Since then, I have used 7-Zip for all my archiving needs</td>
</tr>
<tr>
<td><a href="https://filezilla-project.org/" rel="external nofollow noopener">FileZilla</a></td>
<td>Simple FTP client that has served my needs for years now</td>
</tr>
<tr>
<td><a href="https://tools.stefankueng.com/grepWin.html" rel="external nofollow noopener">grepWin</a></td>
<td>Another excellent tool for swiftly searching for files containing specific strings or expressions</td>
</tr>
<tr>
<td><a href="https://mh-nexus.de/en/hxd/" rel="external nofollow noopener">HxD</a></td>
<td>Another program I've used on and off for years but omitted from this list. Useful hex editor</td>
</tr>
<tr>
<td><a href="https://www.jam-software.com/treesize_free" rel="external nofollow noopener">TreeSize</a></td>
<td>Find out what is using all the space on your disks. Another utility I have used for untold years.</td>
</tr>
<tr>
<td><a href="https://winmerge.org/" rel="external nofollow noopener">WinMerge</a></td>
<td>Excellent file and directory comparison utility</td>
</tr>
<tr>
<td><a href="https://www.win-rar.com/start.html" rel="external nofollow noopener">WinRar</a></td>
<td><ins>New for 2022</ins> Archiver. I started using this after Plesk changed their databases backups to create zip files that use ZStd compression which 7-Zip doesn't support. WinRar was one of the few tools I tested (the other being WinZip, no thanks) that could extract files from these zips. After all, a backup isn't a backup unless you can restore!</td>
</tr>
</tbody>
</table>
<h2 id="backups">Backups</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.crashplan.com/en-us/business/features/" rel="external nofollow noopener">CrashPlan</a></td>
<td>CrashPlan creates an online backup of the different offline backups that CopyTools does. If you've ever lost a hard-disk before with critical data on it that's nowhere else, you'll have backups squirrelled away everywhere too!</td>
</tr>
<tr>
<td><a href="https://www.cyotek.com/cyotek-copytools">Cyotek CopyTools</a></td>
<td>We use this for offline backups of source code, assets and resources, documents, actually pretty much anything we generate; including backing up the backups!</td>
</tr>
<tr>
<td><a href="https://www.macrium.com/home-8" rel="external nofollow noopener">Macrium Reflect</a></td>
<td>Used to create DR images of some physical machines</td>
</tr>
</tbody>
</table>
<h2 id="security">Security</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://bitwarden.com/" rel="external nofollow noopener">Bitwarden</a></td>
<td>Password manager with a variety of clients. Syncs data</td>
</tr>
<tr>
<td><a href="https://someonewhocares.org/hosts/" rel="external nofollow noopener">Dan Pollock's Hosts File</a></td>
<td>A hosts file blocks your computer from connecting to many thousands of dubious internet hosts and is continuously updated. A pain to update, but useful if you don't have access to something better</td>
</tr>
<tr>
<td><a href="https://keepass.info/" rel="external nofollow noopener">KeePass</a></td>
<td>Offline password manager</td>
</tr>
<tr>
<td><a href="https://letsencrypt.org/" rel="external nofollow noopener">Let's Encrypt</a></td>
<td>Short term SSL certificates for free. If you (or your host) are able to automate the process, this is an exceptional way to get basic SSL for your sites</td>
</tr>
<tr>
<td><a href="https://sectigo.com/" rel="external nofollow noopener">Sectigo</a></td>
<td>Code signing certificates, and domain SSL if a particular host doesn't support Let's Encrypt</td>
</tr>
<tr>
<td><a href="https://www.virustotal.com/" rel="external nofollow noopener">Virus Total</a></td>
<td>Analyze files for malware. It is a helpful tool, except for when you find that one given engine will flag all your submissions as malicious and then when that finally clears up another one decides to join in the &quot;fun&quot; instead</td>
</tr>
</tbody>
</table>
<h2 id="issue-tracking">Issue Tracking</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.mantisbt.org/" rel="external nofollow noopener">Mantis Bug Tracker</a></td>
<td>Open source issue tracker</td>
</tr>
<tr>
<td><a href="https://github.com/cyotek/MantisSharp" rel="external nofollow noopener">MantisSharp</a></td>
<td>I use our MantisSharp library to add integration between various applications and our MantisBT instance, notable for raising new issues from our automated error monitor, and for creating road-maps on cyotek.com product pages although as usual I haven't had much time to maintain it</td>
</tr>
</tbody>
</table>
<h2 id="help-desk">Help Desk</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.maiansupport.com/" rel="external nofollow noopener">Maian Support</a></td>
<td>Basic help desk. Much easier than trying to keep track of emails</td>
</tr>
</tbody>
</table>
<h2 id="web-browsers-email-calendering">Web Browsers, Email, Calendering</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://sabre.io/baikal/" rel="external nofollow noopener">Baïkal</a></td>
<td>Self hosted CalDAV (calendar) and CardDAV (contacts) server</td>
</tr>
<tr>
<td><a href="https://www.davx5.com/" rel="external nofollow noopener">DAVx5</a></td>
<td>Two way sync for CalDAV data. I use this on my Android (RIP Windows Phone) phone to sync my calendar with my Baïkal instance</td>
</tr>
<tr>
<td><a href="https://duckduckgo.com/" rel="external nofollow noopener">DuckDuckGo</a></td>
<td><em>The search engine that doesn't track you</em> - I can't remember when I made the switch to DuckDuckGo as it was several years ago, but it does a great job and I rarely have to fall back to &quot;another&quot; search engine</td>
</tr>
<tr>
<td><a href="https://www.mozilla.org/firefox/" rel="external nofollow noopener">Firefox</a></td>
<td>Last bastion from a Chromium world. I switched to this as my primary browser in 2018 as my own protest against Chrome's dominance (and don't get me started on Microsoft's ill advised capitulation)</td>
</tr>
<tr>
<td><a href="https://www.mailstore.com/en/products/mailstore-home/" rel="external nofollow noopener">MailStore Home</a></td>
<td>Email archiving. Also I tend to find its search interface quicker and more compact than the one in Thunderbird</td>
</tr>
<tr>
<td><a href="https://microsoftedgewelcome.microsoft.com/en-gb/" rel="external nofollow noopener">Microsoft Edge</a></td>
<td>I liked the Trident based Edge just fine and wish they had simply decoupled it from the OS and kept it updated. But at least with this Chromium version I don't need to ever install Chrome again</td>
</tr>
<tr>
<td><a href="https://www.thunderbird.net/en-GB/" rel="external nofollow noopener">Thunderbird</a></td>
<td>Email client. A bit rough around the edges but preferable to Outlook and lets me store emails in maildir format, as well as natively supporting CalDAV and CardDAV</td>
</tr>
</tbody>
</table>
<h2 id="other">Other</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.autohotkey.com/" rel="external nofollow noopener">AutoHotkey</a></td>
<td><ins>New for 2022</ins> I was resurrecting a tool from 2002, and decided that its documentation needed screenshots. I stopped doing these years ago because of the pain in updating them, but AutoHotkey is impressively powerful and I was able to use it to write a script that would run the application to set up scenarios and then capture screenshots.</td>
</tr>
<tr>
<td><a href="https://calibre-ebook.com/" rel="external nofollow noopener">Calibre</a></td>
<td>Ebook management. Although I still prefer paper books, I don't buy them as often as I did. I tend to read on e-ink devices and Calibre makes it simple to update these</td>
</tr>
<tr>
<td><a href="https://ditto-cp.sourceforge.io/" rel="external nofollow noopener">Ditto</a></td>
<td>Clipboard manager. Another extremely useful piece of software that I have used for many, many years. Now on the Windows Store which means silent updates... wonderful!</td>
</tr>
<tr>
<td><a href="https://eartrumpet.app/" rel="external nofollow noopener">EarTrumpet</a></td>
<td>Per-application volume manager. I can't remember exactly when I started using this, probably for a game that didn't have built in volume controls but did have obnoxious levels</td>
</tr>
<tr>
<td><a href="https://justgetflux.com/" rel="external nofollow noopener">f.lux</a></td>
<td>I've been using this utterly fantastic software for years. It adapts your monitor to the time of day, removing blue light as evening approaches and helps reduce eye strain when coding at night</td>
</tr>
<tr>
<td><a href="https://docs.microsoft.com/en-us/windows/powertoys/" rel="external nofollow noopener">PowerToys</a></td>
<td>Although <em>nothing</em> like the old Win9x PowerToys, there is at least something useful in this new bag. I have an ultra-wide monitor and I use Fancy Zones to virtually break it up into 3 columns. As it works with the core Win+Arrow hotkeys it makes for a pretty decent window manager</td>
</tr>
<tr>
<td><a href="https://ravendb.net/" rel="external nofollow noopener">RavenDB</a></td>
<td>Still not using this for much as I can't seem to effectively query the data from Raven Studio, and at heart I still think NoSQL is a fad. Transitioned some data back to SQL Server, the rest to follow</td>
</tr>
<tr>
<td><a href="https://rufus.akeo.ie/" rel="external nofollow noopener">Rufus</a></td>
<td>I use this utility for writing ISO images to USB or SD cards, useful for setting up new physical machines in an age where CD drives are fairly obsolete</td>
</tr>
<tr>
<td><a href="https://sourceforge.net/projects/win32diskimager/" rel="external nofollow noopener">Win32 Disk Imager</a></td>
<td>Useful for burning ISO images to SD cards, although I now prefer Rufus for that. It is also massively useful for creating (and restoring) images of SD cards, so I use it to backup my assorted Raspberry Pi devices before major updates</td>
</tr>
<tr>
<td><a href="https://docs.microsoft.com/en-gb/sysinternals/downloads/rdcman" rel="external nofollow noopener">Remote Desktop Connection Manager</a></td>
<td>A valuable tool if you need to remote into one than one server as you can create multiple profiles and groups, use setting inheritance and more</td>
</tr>
<tr>
<td><a href="https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer" rel="external nofollow noopener">Process Explorer</a></td>
<td>Another tool I've used for umpteen years, ProcessExplorer is great for viewing information about running processes. I mostly use this to find what process is locking a given file and closing these handles - this is built into PowerToys these days but I still find myself using Process Explorer first and foremost</td>
</tr>
<tr>
<td><a href="https://docs.microsoft.com/en-us/sysinternals/downloads/procmon" rel="external nofollow noopener">Process Monitor</a></td>
<td>I don't often use this tool, but when I need it is very valuable as it lets me trace any file or registry access</td>
</tr>
<tr>
<td><a href="https://www.microsoft.com/en-gb/p/wake-on-lan/9wzdncrdfshb" rel="external nofollow noopener">Wake On Lan</a></td>
<td>Some of my old generation micro-servers don't run 24/7, and being able to remotely start them (they don't have ILO) is pleasant. This software is a little buggy but it gets the job done for the most part and that is all I need</td>
</tr>
<tr>
<td><a href="https://quiterss.org/en" rel="external nofollow noopener">QuiteRSS</a></td>
<td>Easy to use RSS reader</td>
</tr>
</tbody>
</table>
<h2 id="media-players">Media Players</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.foobar2000.org/" rel="external nofollow noopener">foobar2000</a></td>
<td>This used to be my go to music player before switching to an external Raspberry Pi based solution. However, I still keep foobar2000 hanging around for ripping music CDs</td>
</tr>
<tr>
<td><a href="https://kodi.tv/" rel="external nofollow noopener">Kodi</a></td>
<td>I've used this for years now to watch video on various generations of Raspberry Pi. I found the Films and TV (or Movies and TV) application that ships with Windows 10 to be absolute rubbish and was very glad when Kodi became available on the Microsoft Store</td>
</tr>
<tr>
<td><a href="https://mopidy.com/" rel="external nofollow noopener">Mopidy</a> / <a href="https://github.com/jaedb/Iris" rel="external nofollow noopener">Iris</a></td>
<td>Music player. I have this software installed on a Raspberry Pi Zero with a <a href="https://thepihut.com/products/phat-beat" rel="external nofollow noopener">pHAT BEAT</a> for playing music.</td>
</tr>
<tr>
<td><a href="https://www.videolan.org/vlc/" rel="external nofollow noopener">VLC media player</a></td>
<td>Media player. I rarely use this as an actual player, it is mainly to double check tracks on DVDs when there is an ambiguity with which one(s) to rip</td>
</tr>
</tbody>
</table>
<h2 id="hardware">Hardware</h2>
<p>A new category for 2023, but I thought I would include some of
the hardware we use. Not entirely sure how much of a point there
is to this, so I may drop it... I don't see the point of listing
CPU's etc (and besides, most of our equipment has ancient
processors and GPU's!)</p>
<table>
<thead>
<tr>
<th>Device</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.brother.co.uk/printers/laser-printers/mfc-l2750dw" rel="external nofollow noopener">Brother MFC-L2750DW</a></td>
<td>Black and white laser printer, A4 scanner, A4 ADF scanner</td>
<td>Overpriced ink? Subscriptions for said overpriced ink? Ink cartridge DRM? All reasons why I'll never again use HP printers. This laser supports duplex printing and an automated document feeder - and take it from me, if you've spent days scanning hundreds of pieces of paper on a flatbed scanner, you will rate ADFs as much as I do!</td>
</tr>
<tr>
<td><a href="https://www.canon.co.uk/support/consumer_products/products/scanners/lide_series/canoscan_lide_100.html" rel="external nofollow noopener">CanoScan LiDE 100</a></td>
<td>A4 flatbed scanner</td>
<td>I don't really use this much any more, except to test that it still works on newer versions of Windows, but with that said - it is still a perfectly functional unit I wouldn't hesitate to use</td>
</tr>
<tr>
<td><a href="https://www.canon.co.uk/support/consumer_products/products/scanners/lide_series/canoscan_lide_220.html" rel="external nofollow noopener">CanoScan LiDE 220</a></td>
<td>A4 flatbed scanner</td>
<td>Although I wanted a LiDE 400, it seems to be out of stock since forever, so I use this in the interim. It's a nice little device.</td>
</tr>
<tr>
<td><a href="https://www.microsoft.com/en-gb/d/new-microsoft-bluetooth-ergonomic-mouse/94jnl0t5kdck?activetab=pivot:overviewtab" rel="external nofollow noopener">Microsoft Bluetooth Ergonomic Mouse</a></td>
<td>Ergonomic Mouse</td>
<td>For more than a decade I've used a Razor DeathAdder, but I tried this mouse recently and switched to it. One of the nice little extras is has is the ability to sync with 3 different devices, so I have it tuned to my desktop PC, a ThinkPad laptop and also a Surface Pro 6</td>
</tr>
<tr>
<td><a href="https://www.microsoft.com/en-gb/d/microsoft-sculpt-ergonomic-desktop/8xk02kz6k69w" rel="external nofollow noopener">Microsoft Sculpt Ergonomic Desktop</a></td>
<td>Ergonomic Keyboard</td>
<td>Like the DeathAdder, I've used the Comfort Curve 2000 and Comfort 4000 keyboards for more than a decade, but I recently decided to give this a try. It took a little while to get used to the changed right hand layout, but I really like this keyboard</td>
</tr>
<tr>
<td><a href="https://plustek.com/eu/products/flatbed-scanners/opticslim-1180/index.php" rel="external nofollow noopener">Plustek OpticSlim 1180</a></td>
<td>A3 flatbed scanner</td>
<td>I rarely use this, unless I'm scanning content bigger than A4. It is slow, noisy and the colour reproduction is abysmal so the scanned content doesn't look accurate. However, it is affordable. Next time I need a A3 scanner though, I'll save the pennies and get something with a CCD sensor</td>
</tr>
<tr>
<td><a href="https://www.microsoft.com/en-gb/d/surface-arc-mouse/8p5sv2rx3rn5" rel="external nofollow noopener">Surface Arc Mouse</a></td>
<td>Mouse</td>
<td>Not the most comfortable of mice, but it folds flat which is great for travelling - I use this with my Surface Pro 6. One interesting feature of this mouse is the buttons seem more like a trackpad than mouse buttons, so you can do gestures for scrolling</td>
</tr>
</tbody>
</table>
<p>What tools do you find useful? I'd love to know... maybe I'll
find a new gem myself!</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/tools-we-use-2022-edition .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comPainting 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.comTools we use - 2021 editionurn:uuid:b8f68566-7d75-4a9b-9fef-dd9e9d8545ca2022-03-11T20:01:14Z2022-01-01T12:06:33Z<p>Happy New Year! There is no denying it, last year was pretty
dour. Although I worked on this, that, and the other, my
motivation was at an all time low and I wrote virtually nothing
for this blog. On top of that, I didn't manage, or even make
progress, on any of my goals for the year.</p>
<p>Here is hoping 2022 is better than 2021.</p>
<h2 id="operating-systems">Operating Systems</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>Windows 10 (Virtualized)</td>
<td>Testing VM (32bit and 64bit)</td>
</tr>
<tr>
<td>Windows 10 Professional</td>
<td>Development environment</td>
</tr>
<tr>
<td>Windows 7 (Virtualized)</td>
<td>Testing VM (32bit)</td>
</tr>
<tr>
<td>Windows Home Server 2011</td>
<td>File server, SVN server, Git server, backup host, CI server. I've been thinking of replacing this with a Linux server</td>
</tr>
<tr>
<td>Windows Vista (Virtualized)</td>
<td>Testing VM (32bit)</td>
</tr>
</tbody>
</table>
<p>I did look at Windows 11 in a VM but the level of dumbing down
seems somewhat excessive. That start panel sucks, the task bar
sucks, context menus suck. My computer isn't some crappy
handheld device for consuming simple content, so how about
having an OS designed for the professional.</p>
<h2 id="development-tools">Development Tools</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://sqlitebrowser.org/" rel="external nofollow noopener">DB Browser for SQLite</a></td>
<td>GUI for working with SQLite databases</td>
</tr>
<tr>
<td><a href="https://github.com/0xd4d/dnSpy" rel="external nofollow noopener">dnSpy</a></td>
<td>Speedy .NET assembly debugger and editor. Oddly, this has been archived for no public reason I can find</td>
</tr>
<tr>
<td><a href="https://editorconfig.org/" rel="external nofollow noopener">EditorConfig</a></td>
<td>Useful for OSS projects to avoid space-vs-tab wars or to configure code style rules. No need for an extension now as built into Visual Studio</td>
</tr>
<tr>
<td><a href="https://www.ndepend.com/" rel="external nofollow noopener">NDepend</a></td>
<td>Static code analysis. I run this mostly as part of CI pipelines</td>
</tr>
<tr>
<td><del>Postman</del> <a href="https://insomnia.rest/" rel="external nofollow noopener">Insomnia</a></td>
<td><ins>New for 2021</ins> Client for testing REST services. I switched to Insomnia over Postman as the latter doesn't work without an account and I'm little tired of having scores of accounts with scores of services for no actual benefit to me as the end user</td>
</tr>
<tr>
<td><a href="https://visualstudio.microsoft.com/" rel="external nofollow noopener">Visual Studio 2022</a></td>
<td><ins>New for 2021</ins> Although I use Visual Studio Code more and more, Visual Studio 2022 remains my IDE of choice</td>
</tr>
<tr>
<td><a href="https://code.visualstudio.com/" rel="external nofollow noopener">Visual Studio Code</a></td>
<td>Wonderful editor, once you install enough extensions to configure it &quot;your way&quot;. I use it for most non-.NET tasks, such as PHP or editing markdown. Workspaces that can include multiple folders are incredibly useful</td>
</tr>
<tr>
<td><a href="https://loic-sharma.github.io/BaGet/" rel="external nofollow noopener">Baget</a></td>
<td><ins>New for 2021</ins> I've been using <a href="https://github.com/NuGet/NuGet.Server" rel="external nofollow noopener">NuGet.Server</a> for some years for internal NuGet package management, but that tool is long in the tooth and somewhat limited in functionality. Baget is a much more modern (and functional!) alternative</td>
</tr>
<tr>
<td><a href="https://notepad-plus-plus.org/" rel="external nofollow noopener">Notepad++</a></td>
<td>My go to plain text editor. I tend to use Visual Studio Code now for most text <em>formats</em></td>
</tr>
</tbody>
</table>
<h2 id="visual-studio-extensions">Visual Studio Extensions</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.codemaid.net/" rel="external nofollow noopener">CodeMaid</a></td>
<td>Code formatting and organising. Lets be fair to ReSharper, there's <em>nothing</em> else available which does a better job, but CodeMaid is an acceptable substitute</td>
</tr>
<tr>
<td><a href="https://github.com/cyotek/Cyotek.AddProjects/releases" rel="external nofollow noopener">Cyotek Add Projects</a></td>
<td>A simple extension for easily adding multiple projects to your solutions. Although I use it far less now that most of my projects are packages, it is still useful</td>
</tr>
<tr>
<td><del>File Nesting</del></td>
<td>Allows you to easily nest (or un-nest) files, great for TypeScript or T4 templates. However, as this doesn't seem to work with SDK style projects (and VS2022 for that matter), I no longer use it</td>
</tr>
<tr>
<td><a href="https://marketplace.visualstudio.com/items?itemName=MadsKristensen.OpenCommandLine" rel="external nofollow noopener">Open Command Line</a></td>
<td>Easily open command prompts, PowerShell prompts, or other tools to your project / solution directories</td>
</tr>
<tr>
<td><del>OzCode</del></td>
<td>A once exceptional debugging aid. Things like exception predication, condition visualisation, reveal, and a data tip that doesn't suck really should be part of the core Visual Studio experience. Unfortunately, OzCode was recently bought out by Datadog and had seemingly abandoned OzCode (the extension) in favour of their production debugger regardless. As no Visual Studio 2022 version is available I can no longer recommend this extension.</td>
</tr>
<tr>
<td><a href="https://github.com/JosefPihrt/Roslynator" rel="external nofollow noopener">Roslynator</a></td>
<td>C# code analyzers, refactoring and fixes. I use this to replace some of the more critical functionality I previously enjoyed in ReSharper. Not using in VS2022 at the moment as I went back to the dark side</td>
</tr>
<tr>
<td><a href="https://github.com/Tim-Maes/T4Editor" rel="external nofollow noopener">T4Editor</a></td>
<td>I use this as a replacement for the ReSharper ForTea extension and I'm quite happy with it - it does a great job of showing me the T4 specific aspects of my templates</td>
</tr>
<tr>
<td><del><a href="https://marketplace.visualstudio.com/items?itemName=EWoodruff.VisualStudioSpellCheckerVS2022andLater" rel="external nofollow noopener">Visual Studio Spell Checker</a></del></td>
<td>After I found one too many spelling errors in comments and GUI text. At some point I switched to the spell checker that comes with Atomineer, but there isn't a VS2022 version of this yet</td>
</tr>
<tr>
<td><a href="https://mike-ward.net/vscoloroutput/" rel="external nofollow noopener">VSColorOutput</a></td>
<td>Add colour coding to Visual Studio's Output window</td>
</tr>
<tr>
<td><a href="https://www.jetbrains.com/resharper/" rel="external nofollow noopener">Resharper</a></td>
<td>What is the definition of insanity? Here I am suffering with dire performance problems caused by Resharper once again</td>
</tr>
<tr>
<td><a href="https://marketplace.visualstudio.com/items?itemName=DennisStanze.nordic-001" rel="external nofollow noopener">nordic</a></td>
<td><ins>New for 2021</ins> When I need a dark mode theme, I tend to use something like <a href="https://www.nordtheme.com/" rel="external nofollow noopener">Nord</a>. This extension themes Visual Studio to use a more nord-esque colour scheme</td>
</tr>
</tbody>
</table>
<h2 id="installation-and-deployment">Installation and Deployment</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://jrsoftware.org/isinfo.php" rel="external nofollow noopener">Inno Setup</a></td>
<td>Installer with a wealth of features</td>
</tr>
<tr>
<td><a href="https://www.kymoto.org/products/inno-script-studio" rel="external nofollow noopener">Inno Script Studio</a></td>
<td>IDE for working with Inno Setup scripts</td>
</tr>
<tr>
<td><a href="http://innounp.sourceforge.net/" rel="external nofollow noopener">Inno Setup Unpacker</a></td>
<td>Unpacks installations created with Inno Setup. I use this as part of the CI process to perform dependency checking</td>
</tr>
</tbody>
</table>
<h2 id="analytics">Analytics</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://matomo.org/" rel="external nofollow noopener">Matomo</a></td>
<td>I use this web based analytics software to gain anonymous insights into cyotek.com usage</td>
</tr>
<tr>
<td>Unnamed Analytics</td>
<td>After dropping Luminitix, I replaced the data collection with a home grown solution, although I've yet to write a front end to look at the data effectively</td>
</tr>
</tbody>
</table>
<h2 id="profiling">Profiling</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.jetbrains.com/profiler/" rel="external nofollow noopener">dotTrace</a></td>
<td>Although I prefer the ANTS profiler, dotTrace is a very usable profiler and given it is included in my ReSharper Ultimate subscription, it's a no-brainer to use</td>
</tr>
<tr>
<td><a href="https://www.jetbrains.com/dotmemory/" rel="external nofollow noopener">dotMemory</a></td>
<td>As with dotTrace it is probably time to explore alternatives if I let the ReSharper subscription lapse (yet another reason why perpetual licenses are better than the modern trend of renting software)</td>
</tr>
</tbody>
</table>
<h2 id="documentation-tools">Documentation Tools</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.atomineerutils.com/" rel="external nofollow noopener">Atomineer Pro Documentation</a></td>
<td>Automatically generate XML comment documentation in your source code (Visual Studio extension)</td>
</tr>
<tr>
<td>HelpWrite</td>
<td>The first application offered by Ariad in the mists of time, now reincarnated and producing no-frills documentation from simple markdown and YAML</td>
</tr>
</tbody>
</table>
<h2 id="continuous-integration">Continuous Integration</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://jenkins.io/index.html" rel="external nofollow noopener">Jenkins</a></td>
<td>Continuous integration that is easy to install, doesn't need a database server and has a rich plugin ecosystem, even for .NET developers. I use this to build, test and deploy all our products and libraries</td>
</tr>
</tbody>
</table>
<h2 id="testing">Testing</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.ncrunch.net/" rel="external nofollow noopener">NCrunch</a></td>
<td>(Visual Studio Extension) Frequently updated automated parallel continuous testing tool (there's a mouthful). Works with NUnit, MSTest, SpecFlow and a variety of other test systems. This is by far the best continuous testing tool on the market in my humble opinion. Buy it!</td>
</tr>
<tr>
<td><a href="https://nunit.org/" rel="external nofollow noopener">NUnit</a></td>
<td>Our test framework of choice, for no particular reason other than it was the first one we tried after getting fed up of MSTest's limitations</td>
</tr>
<tr>
<td><a href="https://specflow.org/" rel="external nofollow noopener">SpecFlow</a></td>
<td>(Visual Studio Extension) I only used this for one project (my implementation of <a href="https://devblog.cyotek.com/post/book-review-the-ray-tracer-challenge">The Ray Tracer Challenge</a>) and after I a while I really found this way of implementing tests a bit of a game changer. However, I feel that I would quickly loose my sanity if I had to write all these specifications up front and so this is still sitting in my &quot;todo&quot; pile to look into further</td>
</tr>
</tbody>
</table>
<h2 id="graphics">Graphics</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://affinity.serif.com/en-gb/designer/" rel="external nofollow noopener">Affinity Designer</a></td>
<td>Vector editing software. I thought this would replace Inkscape but it doesn't even pretend to support open formats, open an SVG file and you can only save to a proprietary format</td>
</tr>
<tr>
<td><a href="https://affinity.serif.com/en-gb/photo/" rel="external nofollow noopener">Affinity Photo</a></td>
<td>Photo editing software. I thought I would use this to complement Paint.NET but not impressed with how it tries to force you to use its proprietary format</td>
</tr>
<tr>
<td><a href="https://www.angelcode.com/products/bmfont/" rel="external nofollow noopener">AngelCode BMFont</a></td>
<td>Utility for creating bitmap fonts. We also have a <a href="https://github.com/cyotek/Cyotek.Drawing.BitmapFont" rel="external nofollow noopener">library</a> for working with BMFont files in C#</td>
</tr>
<tr>
<td><a href="https://www.axialis.com/iconworkshop/" rel="external nofollow noopener">Axialis IconWorkshop</a></td>
<td>Very nice icon editor, I have been using this for untold years now since Microangelo was abandoned. However, it itself hasn't seen any updates for some years now</td>
</tr>
<tr>
<td><a href="https://www.cyotek.com/cyotek-gif-animator/">Cyotek GIF Animator</a></td>
<td>GIF animation creator that was shaping up nicely, although it is another application I really want to spend more time improving</td>
</tr>
<tr>
<td><a href="https://www.cyotek.com/cyotek-spriter">Cyotek Spriter</a></td>
<td>Sprite / image map generation software that is still in sore need of optimisation and TLC</td>
</tr>
<tr>
<td><a href="https://getgreenshot.org/" rel="external nofollow noopener">Greenshot</a></td>
<td>Screenshot capturing utility. Another wheel I almost reinvented but this is nice software that fits the bill. Release hasn't been updated for years even though the source repository is rife with activity</td>
</tr>
<tr>
<td><a href="https://inkscape.org/" rel="external nofollow noopener">Inkscape</a></td>
<td>Open source vector graphics editing software</td>
</tr>
<tr>
<td><a href="https://www.getpaint.net/" rel="external nofollow noopener">Paint.NET</a></td>
<td>Brilliant bitmap editor with extensive plugins</td>
</tr>
<tr>
<td><a href="https://www.irfanview.com/" rel="external nofollow noopener">IrfanView</a></td>
<td><ins>New for 2021</ins> I use this for quickly browsing photographs - it's also useful for viewing EXIF data. It's much more than a simple image viewer, but that's only what I've used it for so far</td>
</tr>
<tr>
<td><a href="http://optipng.sourceforge.net/" rel="external nofollow noopener">OptiPNG</a></td>
<td>CLI tool for reducing the size of PNG images without loosing information. I've used this for what feels like forever</td>
</tr>
<tr>
<td><a href="https://jenkins.io/index.html" rel="external nofollow noopener">jpegtran</a></td>
<td><ins>New for 2021</ins> CLI tool for reducing the size of JPEG images without sacrificing quality. Most of the images I publish are PNG so I haven't use this tool much yet but seems positive thus far</td>
</tr>
</tbody>
</table>
<h2 id="virtualization">Virtualization</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.virtualbox.org/" rel="external nofollow noopener">Oracle VM VirtualBox</a></td>
<td>Virtualization software. I prefer this to Hyper-V but hopefully will be migrating to Hyper-V running on Server 2019 Core this year</td>
</tr>
</tbody>
</table>
<h2 id="version-control">Version Control</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://gitforwindows.org/" rel="external nofollow noopener">Git for Windows</a></td>
<td>Git client, tools and GUI for use on Windows</td>
</tr>
<tr>
<td><a href="https://gitea.io/en-us/" rel="external nofollow noopener">Gitea</a></td>
<td>Self-hosting for Git repositories. An impressive piece of software</td>
</tr>
<tr>
<td><a href="https://desktop.github.com/" rel="external nofollow noopener">GitHub Desktop</a></td>
<td>Easy to use Git client that masks some of the more complicated functionality. Despite the name works with any Git repository</td>
</tr>
<tr>
<td><a href="https://github.com/" rel="external nofollow noopener">GitHub</a></td>
<td>Git hosting for our public repositories</td>
</tr>
<tr>
<td><a href="https://www.sourcetreeapp.com/" rel="external nofollow noopener">SourceTree</a></td>
<td>Git client. Far more powerful than GitHub Desktop yet nowhere near as easy to use</td>
</tr>
<tr>
<td><a href="https://tortoisesvn.net/" rel="external nofollow noopener">TortoiseSVN</a></td>
<td>Windows Explorer integration for SVN</td>
</tr>
<tr>
<td><a href="https://www.visualsvn.com/server/" rel="external nofollow noopener">VisualSVN Server</a></td>
<td>Subversion Server for Windows</td>
</tr>
</tbody>
</table>
<h2 id="file-and-directory">File and Directory</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.7-zip.org/" rel="external nofollow noopener">7-Zip</a></td>
<td>I've mentioned before on this blog that I used to love WinZip, until it turned into a bloated mess several years back. Since then, I have used 7-Zip for all my archiving needs</td>
</tr>
<tr>
<td><a href="https://filezilla-project.org/" rel="external nofollow noopener">FileZilla</a></td>
<td>Simple FTP client that has served my needs for years now</td>
</tr>
<tr>
<td><a href="https://tools.stefankueng.com/grepWin.html" rel="external nofollow noopener">grepWin</a></td>
<td>Another excellent tool for swiftly searching for files containing specific strings or expressions</td>
</tr>
<tr>
<td><a href="https://mh-nexus.de/en/hxd/" rel="external nofollow noopener">HxD</a></td>
<td>Another program I've used on and off for years but omitted from this list. Useful hex editor</td>
</tr>
<tr>
<td><a href="https://www.jam-software.com/treesize_free" rel="external nofollow noopener">TreeSize</a></td>
<td>Find out what is using all the space on your disks. Another utility I have used for untold years.</td>
</tr>
<tr>
<td><a href="https://winmerge.org/" rel="external nofollow noopener">WinMerge</a></td>
<td>Excellent file and directory comparison utility</td>
</tr>
</tbody>
</table>
<h2 id="backups">Backups</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.crashplan.com/en-us/business/features/" rel="external nofollow noopener">CrashPlan</a></td>
<td>CrashPlan creates an online backup of the different offline backups that CopyTools does. If you've ever lost a hard-disk before with critical data on it that's nowhere else, you'll have backups squirrelled away everywhere too! A big part of the reason why I haven't upgraded our micro server to either a newer version of Windows or replaced with a Linux box</td>
</tr>
<tr>
<td><a href="https://www.cyotek.com/cyotek-copytools">Cyotek CopyTools</a></td>
<td>We use this for offline backups of source code, assets and resources, documents, actually pretty much anything we generate; including backing up the backups!</td>
</tr>
<tr>
<td><a href="https://www.macrium.com/home-8" rel="external nofollow noopener">Macrium Reflect</a></td>
<td><ins>New for 2021</ins> Not really new, I've been using this to create DR images of some physical machines for a few years now</td>
</tr>
</tbody>
</table>
<h2 id="security">Security</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://bitwarden.com/" rel="external nofollow noopener">Bitwarden</a></td>
<td>Password manager with a variety of clients. Syncs data</td>
</tr>
<tr>
<td><a href="https://someonewhocares.org/hosts/" rel="external nofollow noopener">Dan Pollock's Hosts File</a></td>
<td>A hosts file blocks your computer from connecting to many thousands of dubious internet hosts and is continuously updated. A pain to update, but useful if you don't have access to something better</td>
</tr>
<tr>
<td><a href="https://keepass.info/" rel="external nofollow noopener">KeePass</a></td>
<td>Offline password manager</td>
</tr>
<tr>
<td><a href="https://letsencrypt.org/" rel="external nofollow noopener">Let's Encrypt</a></td>
<td>Short term SSL certificates for free. If you (or your host) are able to automate the process, this is an exceptional way to get basic SSL for your sites</td>
</tr>
<tr>
<td><a href="https://sectigo.com/" rel="external nofollow noopener">Sectigo</a></td>
<td>Code signing certificates, and domain SSL if a particular host doesn't support Let's Encrypt</td>
</tr>
<tr>
<td><a href="https://www.virustotal.com/" rel="external nofollow noopener">Virus Total</a></td>
<td>Analyze files for malware. It is a helpful tool, except for when you find that one given engine will flag all your submissions as malicious and then when that finally clears up another one decides to join in the &quot;fun&quot; instead</td>
</tr>
</tbody>
</table>
<h2 id="issue-tracking">Issue Tracking</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.mantisbt.org/" rel="external nofollow noopener">Mantis Bug Tracker</a></td>
<td>Open source issue tracker</td>
</tr>
<tr>
<td><a href="https://github.com/cyotek/MantisSharp" rel="external nofollow noopener">MantisSharp</a></td>
<td>I use our MantisSharp library to add integration between various applications and our MantisBT instance, notable for raising new issues from our automated error monitor, and for creating road-maps on cyotek.com product pages although as usual I haven't had much time to maintain it</td>
</tr>
</tbody>
</table>
<h2 id="help-desk">Help Desk</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.maiansupport.com/" rel="external nofollow noopener">Maian Support</a></td>
<td>Basic help desk. Much easier than trying to keep track of emails</td>
</tr>
</tbody>
</table>
<h2 id="web-browsers-email-calendering">Web Browsers, Email, Calendering</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://sabre.io/baikal/" rel="external nofollow noopener">Baïkal</a></td>
<td>Self hosted CalDAV (calendar) and CardDAV (contacts) server</td>
</tr>
<tr>
<td><a href="https://www.davx5.com/" rel="external nofollow noopener">DAVx5</a></td>
<td>Two way sync for CalDAV data. I use this on my Android (RIP Windows Phone) phone to sync my calendar with my Baïkal instance</td>
</tr>
<tr>
<td><a href="https://duckduckgo.com/" rel="external nofollow noopener">DuckDuckGo</a></td>
<td><em>The search engine that doesn't track you</em> - I can't remember when I made the switch to DuckDuckGo as it was several years ago, but it does a great job and I rarely have to fall back to &quot;another&quot; search engine</td>
</tr>
<tr>
<td><a href="https://www.mozilla.org/firefox/" rel="external nofollow noopener">Firefox</a></td>
<td>Last bastion from a Chromium world. I switched to this as my primary browser in 2018 as my own protest against Chrome's dominance (and don't get me started on Microsoft's recent ill advised capitulation)</td>
</tr>
<tr>
<td><a href="https://www.mailstore.com/en/products/mailstore-home/" rel="external nofollow noopener">MailStore Home</a></td>
<td>Email archiving. Also I tend to find its search interface quicker and more compact than the one in Thunderbird</td>
</tr>
<tr>
<td><a href="https://microsoftedgewelcome.microsoft.com/en-gb/" rel="external nofollow noopener">Microsoft Edge</a></td>
<td>I liked the Trident based Edge just fine. But at least with this Chromium version I don't need to ever install Chrome again</td>
</tr>
<tr>
<td><a href="https://www.thunderbird.net/en-GB/" rel="external nofollow noopener">Thunderbird</a></td>
<td>Email client. A bit rough around the edges but preferable to Outlook and lets me store emails in maildir format, as well as natively supporting CalDAV and CardDAV</td>
</tr>
</tbody>
</table>
<h2 id="other">Other</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://calibre-ebook.com/" rel="external nofollow noopener">Calibre</a></td>
<td>Ebook management. Although I still prefer paper books, I don't buy them as often as I did. I tend to read on e-ink devices and Calibre makes it simple to update these</td>
</tr>
<tr>
<td><a href="https://ditto-cp.sourceforge.io/" rel="external nofollow noopener">Ditto</a></td>
<td>Clipboard manager. Another extremely useful piece of software that I have used for many, many years. Now on the Windows Store which means silent updates... wonderful!</td>
</tr>
<tr>
<td><a href="https://eartrumpet.app/" rel="external nofollow noopener">EarTrumpet</a></td>
<td>Per-application volume manager. I can't remember exactly when I started using this, probably for a game that didn't have built in volume controls but did have obnoxious levels</td>
</tr>
<tr>
<td><a href="https://justgetflux.com/" rel="external nofollow noopener">f.lux</a></td>
<td>I've been using this utterly fantastic software for years. It adapts your monitor to the time of day, removing blue light as evening approaches and helps reduce eye strain when coding at night</td>
</tr>
<tr>
<td><a href="https://docs.microsoft.com/en-us/windows/powertoys/" rel="external nofollow noopener">PowerToys</a></td>
<td>Although <em>nothing</em> like the old Win9x PowerToys, there is at least something useful in this new bag. I have an ultra-wide monitor and I use Fancy Zones to virtually break it up into 3 columns. As it works with the core Win+Arrow hotkeys it makes for a pretty decent window manager</td>
</tr>
<tr>
<td><a href="https://ravendb.net/" rel="external nofollow noopener">RavenDB</a></td>
<td>Still not using this for much as I can't seem to effectively query the data from Raven Studio, and at heart I still think NoSQL is a fad. Transitioned some data back to SQL Server, the rest to follow</td>
</tr>
<tr>
<td><a href="https://rufus.akeo.ie/" rel="external nofollow noopener">Rufus</a></td>
<td>I use this utility for writing ISO images to USB or SD cards, useful for setting up new physical machines in an age where CD drives are fairly obsolete</td>
</tr>
<tr>
<td><a href="https://sourceforge.net/projects/win32diskimager/" rel="external nofollow noopener">Win32 Disk Imager</a></td>
<td>Useful for burning ISO images to SD cards, although I now prefer Rufus for that. It is also massively useful for creating (and restoring) images of SD cards, so I use it to backup my assorted Raspberry Pi devices before major updates</td>
</tr>
<tr>
<td><a href="https://docs.microsoft.com/en-gb/sysinternals/downloads/rdcman" rel="external nofollow noopener">Remote Desktop Connection Manager</a></td>
<td>A valuable tool if you need to remote into one than one server as you can create multiple profiles and groups, use setting inheritance and more</td>
</tr>
<tr>
<td><a href="https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer" rel="external nofollow noopener">Process Explorer</a></td>
<td>Another tool I've used for umpteen years, ProcessExplorer is great for viewing information about running processes. I mostly use this to find what process is locking a given file and closing these handles</td>
</tr>
<tr>
<td><a href="https://docs.microsoft.com/en-us/sysinternals/downloads/procmon" rel="external nofollow noopener">Process Monitor</a></td>
<td>I don't often use this tool, but when I need it is very valuable as it lets me trace any file or registry access</td>
</tr>
<tr>
<td><a href="https://www.microsoft.com/en-gb/p/wake-on-lan/9wzdncrdfshb" rel="external nofollow noopener">Wake On Lan</a></td>
<td><ins>New for 2021</ins> Some of my old generation micro-servers don't run 24/7, and being able to remotely start them (they don't have ILO) is pleasant. This software is a little buggy but it gets the job done for the most part and that is all I need</td>
</tr>
<tr>
<td><a href="https://quiterss.org/en" rel="external nofollow noopener">QuiteRSS</a></td>
<td>Easy to use RSS reader</td>
</tr>
</tbody>
</table>
<h2 id="media-players">Media Players</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://kodi.tv/" rel="external nofollow noopener">Kodi</a></td>
<td>I've used this for years now to watch video on various generations of Raspberry Pi. I found the Films and TV (or Movies and TV) application that ships with Windows 10 to be absolute rubbish and was very glad when Kodi became available on the Microsoft Store</td>
</tr>
<tr>
<td><a href="https://mopidy.com/" rel="external nofollow noopener">Mopidy</a> / <a href="https://github.com/jaedb/Iris" rel="external nofollow noopener">Iris</a></td>
<td>Music player. I have this software installed on a Raspberry Pi Zero with a <a href="https://thepihut.com/products/phat-beat" rel="external nofollow noopener">pHAT BEAT</a> for playing music.</td>
</tr>
<tr>
<td><a href="https://www.foobar2000.org/" rel="external nofollow noopener">foobar2000</a></td>
<td>This used to be my go to music player before switching to an external Raspberry Pi based solution. However, I still keep foobar2000 hanging around for ripping music CDs</td>
</tr>
<tr>
<td><a href="https://www.videolan.org/vlc/" rel="external nofollow noopener">VLC media player</a></td>
<td>Media player. I rarely use this as an actual player, it is mainly to double check tracks on DVDs when there is an ambiguity with which one(s) to rip</td>
</tr>
</tbody>
</table>
<p>What tools do you find useful? I'd love to know... maybe I'll
find a new gem myself!</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/tools-we-use-2021-edition .
</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.comTools we use - 2020 editionurn:uuid:b8f68566-7d75-4a9b-9fef-dd9e9d8545ca2021-01-04T12:09:48Z2021-01-01T10:16:17Z<p>Happy New Year! Last year was... interesting to say the least.
Hopefully 2021 will be somewhat better but it seems more and
more likely that we've dug our own graves and care not a whit.</p>
<p>With that morbid thought out of the way, here we are with
another edition of Tools We Use. While I still haven't got the
blog redirects switched on for the <a href="https://devblog.cyotek.com">new platform</a>, I
recently upgraded that through various iterations of .NET Core,
finishing with .NET 5. I also created several smaller .NET Core
websites (that were also upgraded to .NET 5). On the
non-development side, I finally unshackled myself from having to
use Microsoft or Google's calendaring solutions, by self hosting
my own CalDAV instance. <em>Own your own data!</em></p>
<p>Looking ahead, this year I will be migrating our exception
manager from a Web API / React hybrid to Blazor, along with
giving our products other than WebCopy some long overdue TLC.
And finally getting on with writing a game.</p>
<p>My slow progress at migrating from SVN to Git also continues as
I refactor the mono repository to make it easier to split up
using a <a href="https://github.com/cyotek/SvnGitMigrate" rel="external nofollow noopener">migration tool</a>.</p>
<h2 id="operating-systems">Operating Systems</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>Windows 10 (Virtualized)</td>
<td>Testing VM (32bit and 64bit)</td>
</tr>
<tr>
<td>Windows 10 Professional</td>
<td>Development environment</td>
</tr>
<tr>
<td>Windows 7 (Virtualized)</td>
<td>Testing VM (32bit)</td>
</tr>
<tr>
<td>Windows Home Server 2011</td>
<td>File server, SVN server, Git server, backup host, CI server. I've been thinking of replacing this with a Linux server</td>
</tr>
<tr>
<td>Windows Vista (Virtualized)</td>
<td>Testing VM (32bit)</td>
</tr>
</tbody>
</table>
<h2 id="development-tools">Development Tools</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://sqlitebrowser.org/" rel="external nofollow noopener">DB Browser for SQLite</a></td>
<td>GUI for working with SQLite databases</td>
</tr>
<tr>
<td><a href="https://github.com/0xd4d/dnSpy" rel="external nofollow noopener">dnSpy</a></td>
<td>Speedy .NET assembly debugger and editor</td>
</tr>
<tr>
<td><a href="https://editorconfig.org/" rel="external nofollow noopener">EditorConfig</a></td>
<td>Useful for OSS projects to avoid space-vs-tab wars or to configure code style rules. No need for an extension now as built into Visual Studio</td>
</tr>
<tr>
<td><a href="https://www.ndepend.com/" rel="external nofollow noopener">NDepend</a></td>
<td>Static code analysis. I run this mostly as part of CI pipelines</td>
</tr>
<tr>
<td><a href="https://www.getpostman.com/" rel="external nofollow noopener">Postman</a></td>
<td>Client for testing REST services</td>
</tr>
<tr>
<td><a href="https://visualstudio.microsoft.com/" rel="external nofollow noopener">Visual Studio 2019</a></td>
<td>Although I use Visual Studio Code more and more, Visual Studio 2019 remains my IDE of choice</td>
</tr>
<tr>
<td><a href="https://code.visualstudio.com/" rel="external nofollow noopener">Visual Studio Code</a></td>
<td>Wonderful editor, once you install enough extensions to configure it &quot;your way&quot;. I use it for most non-.NET tasks, such as PHP or editing markdown. Workspaces that can include multiple folders are incredibly useful</td>
</tr>
</tbody>
</table>
<h2 id="visual-studio-extensions">Visual Studio Extensions</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.codemaid.net/" rel="external nofollow noopener">CodeMaid</a></td>
<td>Code formatting and organising. Lets be fair to ReSharper, there's <em>nothing</em> else available which does a better job, but CodeMaid is an acceptable substitute</td>
</tr>
<tr>
<td><a href="https://github.com/cyotek/Cyotek.AddProjects/releases" rel="external nofollow noopener">Cyotek Add Projects</a></td>
<td>A simple extension for easily adding multiple projects to your solutions. Although I use it far less now that most of my projects are packages, it is still useful</td>
</tr>
<tr>
<td><a href="https://marketplace.visualstudio.com/items?itemName=MadsKristensen.FileNesting" rel="external nofollow noopener">File Nesting</a></td>
<td>Allows you to easily nest (or un-nest) files, great for TypeScript or T4 templates</td>
</tr>
<tr>
<td><a href="https://marketplace.visualstudio.com/items?itemName=MadsKristensen.OpenCommandLine" rel="external nofollow noopener">Open Command Line</a></td>
<td>Easily open command prompts, PowerShell prompts, or other tools to your project / solution directories</td>
</tr>
<tr>
<td><a href="https://oz-code.com/" rel="external nofollow noopener">OzCode</a></td>
<td>An exceptional debugging aid. Things like exception predication, condition visualisation, reveal, and a data tip that doesn't suck really should be part of the core Visual Studio experience</td>
</tr>
<tr>
<td><a href="https://github.com/JosefPihrt/Roslynator" rel="external nofollow noopener">Roslynator</a></td>
<td>C# code analyzers, refactoring and fixes. I use this to replace some of the more critical functionality I previously enjoyed in ReSharper</td>
</tr>
<tr>
<td><a href="https://github.com/Tim-Maes/T4Editor" rel="external nofollow noopener">T4Editor</a></td>
<td>I use this as a replacement for the ReSharper ForTea extension and I'm quite happy with it - it does a great job of showing me the T4 specific aspects of my templates</td>
</tr>
<tr>
<td><a href="https://ewsoftware.github.io/VSSpellChecker/html/027d2fbc-7bfb-4dc3-b4f5-85f95fcf7629.htm" rel="external nofollow noopener">Visual Studio Spell Checker</a></td>
<td>After I found one too many spelling errors in comments and GUI text</td>
</tr>
<tr>
<td><a href="https://mike-ward.net/vscoloroutput/" rel="external nofollow noopener">VSColorOutput</a></td>
<td>Add colour coding to Visual Studio's Output window</td>
</tr>
</tbody>
</table>
<h2 id="installation-and-deployment">Installation and Deployment</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://jrsoftware.org/isinfo.php" rel="external nofollow noopener">Inno Setup</a></td>
<td>Installer with a wealth of features</td>
</tr>
<tr>
<td><a href="https://www.kymoto.org/products/inno-script-studio" rel="external nofollow noopener">Inno Script Studio</a></td>
<td>IDE for working with Inno Setup scripts. Hadn't been updated for years but development seems to have picked up again</td>
</tr>
<tr>
<td><a href="http://innounp.sourceforge.net/" rel="external nofollow noopener">Inno Setup Unpacker</a></td>
<td>Unpacks installations created with Inno Setup. I use this as part of the CI process to perform dependency checking</td>
</tr>
</tbody>
</table>
<h2 id="analytics">Analytics</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://matomo.org/" rel="external nofollow noopener">Matomo</a></td>
<td>I use this web based analytics software to gain anonymous insights into cyotek.com usage</td>
</tr>
<tr>
<td>Unnamed Analytics</td>
<td>After dropping Luminitix, I replaced the data collection with a home grown solution, although I've yet to write a front end to look at the data effectively</td>
</tr>
</tbody>
</table>
<h2 id="profiling">Profiling</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.jetbrains.com/profiler/" rel="external nofollow noopener">dotTrace</a></td>
<td>Although I prefer the ANTS profiler, dotTrace is a very usable profiler and given it is included in my ReSharper Ultimate subscription, it's a no-brainer to use</td>
</tr>
<tr>
<td><a href="https://www.jetbrains.com/dotmemory/" rel="external nofollow noopener">dotMemory</a></td>
<td>As with dotTrace it is probably time to explore alternatives if I let the ReSharper subscription lapse (yet another reason why perpetual licenses are better than the modern trend of renting software)</td>
</tr>
</tbody>
</table>
<h2 id="documentation-tools">Documentation Tools</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.atomineerutils.com/" rel="external nofollow noopener">Atomineer Pro Documentation</a></td>
<td>Automatically generate XML comment documentation in your source code (Visual Studio extension)</td>
</tr>
<tr>
<td>HelpWrite</td>
<td>The first application offered by Ariad in the mists of time, now reincarnated and producing no-frills documentation from simple markdown and YAML</td>
</tr>
<tr>
<td><a href="https://notepad-plus-plus.org/" rel="external nofollow noopener">Notepad++</a></td>
<td>My go to plain text editor</td>
</tr>
</tbody>
</table>
<h2 id="continuous-integration">Continuous Integration</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://jenkins.io/index.html" rel="external nofollow noopener">Jenkins</a></td>
<td>Continuous integration that is easy to install, doesn't need a database server and has a rich plugin ecosystem, even for .NET developers. I use this to build, test and deploy all our products and libraries</td>
</tr>
</tbody>
</table>
<h2 id="testing">Testing</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.ncrunch.net/" rel="external nofollow noopener">NCrunch</a></td>
<td>(Visual Studio Extension) Frequently updated automated parallel continuous testing tool (there's a mouthful). Works with NUnit, MSTest, SpecFlow and a variety of other test systems. This is by far the best continuous testing tool on the market</td>
</tr>
<tr>
<td><a href="https://nunit.org/" rel="external nofollow noopener">NUnit</a></td>
<td>Our test framework of choice, for no particular reason other than it was the first one we tried after getting fed up of MSTest's limitations</td>
</tr>
<tr>
<td><a href="https://specflow.org/" rel="external nofollow noopener">SpecFlow</a></td>
<td>(Visual Studio Extension) I only used this for one project (my implementation of <a href="https://devblog.cyotek.com/post/book-review-the-ray-tracer-challenge">The Ray Tracer Challenge</a>) and after I a while I really found this way of implementing tests a bit of a game changer. However, I feel that I would quickly loose my sanity if I had to write all these specifications up front and so this is still sitting in my &quot;todo&quot; pile to look into further</td>
</tr>
</tbody>
</table>
<h2 id="graphics">Graphics</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://affinity.serif.com/en-gb/designer/" rel="external nofollow noopener">Affinity Designer</a></td>
<td><ins>New for 2020</ins> Vector editing software. I thought this would replace Inkscape but it doesn't even pretend to support open formats, open an SVG file and you can only save to a proprietary format</td>
</tr>
<tr>
<td><a href="https://affinity.serif.com/en-gb/photo/" rel="external nofollow noopener">Affinity Photo</a></td>
<td><ins>New for 2020</ins> Photo editing software. I thought I would use this to complement Paint.NET but not impressed with how it tries to force you to use its proprietary format</td>
</tr>
<tr>
<td><a href="https://www.angelcode.com/products/bmfont/" rel="external nofollow noopener">AngelCode BMFont</a></td>
<td>Utility for creating bitmap fonts. We also have a <a href="https://github.com/cyotek/Cyotek.Drawing.BitmapFont" rel="external nofollow noopener">library</a> for working with BMFont files in C#</td>
</tr>
<tr>
<td><a href="https://www.axialis.com/iconworkshop/" rel="external nofollow noopener">Axialis IconWorkshop</a></td>
<td>Very nice icon editor, I have been using this for untold years now since Microangelo was abandoned. However, it itself hasn't seen any updates for some years now</td>
</tr>
<tr>
<td><a href="https://www.cyotek.com/cyotek-gif-animator/">Cyotek GIF Animator</a></td>
<td>GIF animation creator that was shaping up nicely, although it is another application I really want to spend more time improving</td>
</tr>
<tr>
<td><a href="https://www.cyotek.com/cyotek-spriter">Cyotek Spriter</a></td>
<td>Sprite / image map generation software that is still in sore need of optimisation and TLC</td>
</tr>
<tr>
<td><a href="https://getgreenshot.org/" rel="external nofollow noopener">Greenshot</a></td>
<td><ins>New for 2020</ins> Screenshot capturing utility. Another wheel I almost reinvented but this is nice software that fits the bill. Release hasn't been updated for years even though the source repository is rife with activity</td>
</tr>
<tr>
<td><a href="https://inkscape.org/" rel="external nofollow noopener">Inkscape</a></td>
<td>Vector graphics editing software. I've been using this on and off for years but forgot to include it on this list before now</td>
</tr>
<tr>
<td><a href="https://www.getpaint.net/" rel="external nofollow noopener">Paint.NET</a></td>
<td>Brilliant bitmap editor with extensive plugins</td>
</tr>
</tbody>
</table>
<h2 id="virtualization">Virtualization</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.virtualbox.org/" rel="external nofollow noopener">Oracle VM VirtualBox</a></td>
<td>Virtualization software. I prefer this to Hyper-V</td>
</tr>
</tbody>
</table>
<h2 id="version-control">Version Control</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://gitforwindows.org/" rel="external nofollow noopener">Git for Windows</a></td>
<td><ins>New for 2020</ins> Git client, tools and GUI for use on Windows</td>
</tr>
<tr>
<td><a href="https://gitea.io/en-us/" rel="external nofollow noopener">Gitea</a></td>
<td>Self-hosting for Git repositories. An impressive piece of software</td>
</tr>
<tr>
<td><a href="https://desktop.github.com/" rel="external nofollow noopener">GitHub Desktop</a></td>
<td>Easy to use Git client that masks some of the more complicated functionality. Despite the name works with any Git repository</td>
</tr>
<tr>
<td><a href="https://github.com/" rel="external nofollow noopener">GitHub</a></td>
<td>Git hosting for our public repositories</td>
</tr>
<tr>
<td><a href="https://www.gitkraken.com/" rel="external nofollow noopener">GitKraken</a></td>
<td><ins>New for 2020</ins> <del>Git client. I tried to use this, but I point blank refuse to rent software anymore and current exceptions will be phased out</del></td>
</tr>
<tr>
<td><a href="https://www.sourcetreeapp.com/" rel="external nofollow noopener">SourceTree</a></td>
<td><ins>New for 2020</ins> Git client. Far more powerful than GitHub Desktop yet nowhere near as easy to use</td>
</tr>
<tr>
<td><a href="https://tortoisesvn.net/" rel="external nofollow noopener">TortoiseSVN</a></td>
<td>Windows Explorer integration for SVN</td>
</tr>
<tr>
<td><a href="https://www.visualsvn.com/server/" rel="external nofollow noopener">VisualSVN Server</a></td>
<td>Subversion Server for Windows</td>
</tr>
<tr>
<td><a href="https://www.visualsvn.com/visualsvn/" rel="external nofollow noopener">VisualSVN</a></td>
<td><del>(Visual Studio Extension) Subversion support for Visual Studio. Unlike AnhkSVN, VisualSVN uses TortoiseSVN under the hood, meaning that Explorer and Visual Studio are always in the same state no matter where I commit from, something which used to frustrate me no end with AnhkSVN. Stopped using as I slowly transition to Git</del></td>
</tr>
</tbody>
</table>
<h2 id="file-and-directory">File and Directory</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.7-zip.org/" rel="external nofollow noopener">7-Zip</a></td>
<td>I've mentioned before on this blog that I used to love WinZip, until it turned into a bloated mess several years back. Since then, I have used 7-Zip for all my archiving needs</td>
</tr>
<tr>
<td><a href="https://filezilla-project.org/" rel="external nofollow noopener">FileZilla</a></td>
<td>Simple FTP client that has served my needs for years now</td>
</tr>
<tr>
<td><a href="https://tools.stefankueng.com/grepWin.html" rel="external nofollow noopener">grepWin</a></td>
<td>Another excellent tool for swiftly searching for files containing specific strings or expressions</td>
</tr>
<tr>
<td><a href="https://mh-nexus.de/en/hxd/" rel="external nofollow noopener">HxD</a></td>
<td>Another program I've used on and off for years but omitted from this list. Useful hex editor</td>
</tr>
<tr>
<td><a href="https://www.jam-software.com/treesize_free" rel="external nofollow noopener">TreeSize</a></td>
<td>Find out what is using all the space on your disks. Another utility I have used for untold years.</td>
</tr>
<tr>
<td><a href="https://winmerge.org/" rel="external nofollow noopener">WinMerge</a></td>
<td>Excellent file and directory comparison utility</td>
</tr>
</tbody>
</table>
<h2 id="backups">Backups</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.crashplan.com/en-us/business/features/" rel="external nofollow noopener">CrashPlan</a></td>
<td>CrashPlan creates an online backup of the different offline backups that CopyTools does. If you've ever lost a hard-disk before with critical data on it that's nowhere else, you'll have backups squirrelled away everywhere too! A big part of the reason why I haven't upgraded our micro server to either a newer version of Windows or replaced with a Linux box</td>
</tr>
<tr>
<td><a href="https://www.cyotek.com/cyotek-copytools">Cyotek CopyTools</a></td>
<td>We use this for offline backups of source code, assets and resources, documents, actually pretty much anything we generate; including backing up the backups!</td>
</tr>
</tbody>
</table>
<h2 id="security">Security</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://bitwarden.com/" rel="external nofollow noopener">Bitwarden</a></td>
<td>Password manager with a variety of clients. Syncs data</td>
</tr>
<tr>
<td><a href="https://someonewhocares.org/hosts/" rel="external nofollow noopener">Dan Pollock's Hosts File</a></td>
<td>A hosts file blocks your computer from connecting to many thousands of dubious internet hosts and is continuously updated. A pain to update, but useful if you don't have access to something better</td>
</tr>
<tr>
<td><a href="https://keepass.info/" rel="external nofollow noopener">KeePass</a></td>
<td>Offline password manager</td>
</tr>
<tr>
<td><a href="https://letsencrypt.org/" rel="external nofollow noopener">Let's Encrypt</a></td>
<td>Short term SSL certificates for free. If you (or your host) are able to automate the process, this is an exceptional way to get basic SSL for your sites</td>
</tr>
<tr>
<td><del>Comodo</del> <a href="https://sectigo.com/" rel="external nofollow noopener">Sectigo</a></td>
<td>Code signing certificates, and domain SSL if a particular host doesn't support Let's Encrypt</td>
</tr>
<tr>
<td><a href="https://www.virustotal.com/" rel="external nofollow noopener">Virus Total</a></td>
<td>Analyze files for malware. It is a helpful tool, except for when you find that one given engine will flag all your submissions as malicious and then when that finally clears up another one decides to join in the &quot;fun&quot; instead</td>
</tr>
</tbody>
</table>
<h2 id="issue-tracking">Issue Tracking</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.mantisbt.org/" rel="external nofollow noopener">Mantis Bug Tracker</a></td>
<td>Open source issue tracker</td>
</tr>
<tr>
<td><a href="https://github.com/cyotek/MantisSharp" rel="external nofollow noopener">MantisSharp</a></td>
<td>I use our MantisSharp library to add integration between various applications and our MantisBT instance, notable for raising new issues from our automated error monitor, and for creating road-maps on cyotek.com product pages although as usual I haven't had much time to maintain it</td>
</tr>
</tbody>
</table>
<h2 id="help-desk">Help Desk</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.maiansupport.com/" rel="external nofollow noopener">Maian Support</a></td>
<td>Basic help desk. Much easier than trying to keep track of emails</td>
</tr>
</tbody>
</table>
<h2 id="web-browsers-email-calendering">Web Browsers, Email, Calendering</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://sabre.io/baikal/" rel="external nofollow noopener">Baïkal</a></td>
<td><ins>New for 2020</ins> Self hosted CalDAV (calendar) and CardDAV (tasks) server</td>
</tr>
<tr>
<td><a href="https://www.davx5.com/" rel="external nofollow noopener">DAVx5</a></td>
<td><ins>New for 2020</ins> Two way sync for CalDAV data. I use this on my Android (RIP Windows Phone) phone to sync my calendar with my Baïkal instance</td>
</tr>
<tr>
<td><a href="https://duckduckgo.com/" rel="external nofollow noopener">DuckDuckGo</a></td>
<td><em>The search engine that doesn't track you</em> - I can't remember when I made the switch to DuckDuckGo as it was several years ago, but it does a great job and I rarely have to fall back to &quot;another&quot; search engine</td>
</tr>
<tr>
<td><a href="https://www.mozilla.org/firefox/" rel="external nofollow noopener">Firefox</a></td>
<td>Last bastion from a Chromium world. I switched to this as my primary browser in 2018 as my own protest against Chrome's dominance (and don't get me started on Microsoft's recent ill advised capitulation)</td>
</tr>
<tr>
<td><a href="https://www.mailstore.com/en/products/mailstore-home/" rel="external nofollow noopener">MailStore Home</a></td>
<td><ins>New for 2020</ins> Email archiving. Also I tend to find its search interface quicker and more compact than the one in Thunderbird</td>
</tr>
<tr>
<td><a href="https://microsoftedgewelcome.microsoft.com/en-gb/" rel="external nofollow noopener">Microsoft Edge</a></td>
<td>I liked the Trident based Edge just fine. But at least with this Chromium version I don't need to ever install Chrome again</td>
</tr>
<tr>
<td><a href="https://www.thunderbird.net/en-GB/" rel="external nofollow noopener">Thunderbird</a></td>
<td>Email client that also supports CalDAV and CardDAV. A bit rough around the edges but preferable to Outlook and lets me store emails in maildir format</td>
</tr>
</tbody>
</table>
<h2 id="other">Other</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://calibre-ebook.com/" rel="external nofollow noopener">Calibre</a></td>
<td>Ebook management. Although I still prefer paper books, I don't buy them as often as I did. I tend to read on e-ink devices and Calibre makes it simple to update these</td>
</tr>
<tr>
<td><a href="https://ditto-cp.sourceforge.io/" rel="external nofollow noopener">Ditto</a></td>
<td>Clipboard manager. Another extremely useful piece of software that I have used for many, many years</td>
</tr>
<tr>
<td><a href="https://eartrumpet.app/" rel="external nofollow noopener">EarTrumpet</a></td>
<td>Per-application volume manager. I can't remember exactly when I started using this, probably for a game that didn't have built in volume controls but did have obnoxious levels</td>
</tr>
<tr>
<td><a href="https://justgetflux.com/" rel="external nofollow noopener">f.lux</a></td>
<td>I've been using this utterly fantastic software for years. It adapts your monitor to the time of day, removing blue light as evening approaches and helps reduce eye strain when coding at night</td>
</tr>
<tr>
<td><a href="https://kodi.tv/" rel="external nofollow noopener">Kodi</a></td>
<td>I've used this for years now to watch video on various generations of Raspberry Pi. I found the Films and TV (or Movies and TV) application that ships with Windows 10 to be absolute rubbish and was very glad when Kodi became available on the Microsoft Store</td>
</tr>
<tr>
<td><a href="https://docs.microsoft.com/en-us/windows/powertoys/" rel="external nofollow noopener">PowerToys</a></td>
<td><ins>New for 2020</ins> Although <em>nothing</em> like the old Win9x PowerToys, there is at least something useful in this new bag. Back in June, I bought an ultra-wide monitor and I use Fancy Zones to virtually break it up into 3 columns. As it works with the core Win+Arrow hotkeys it makes for a pretty decent window manager</td>
</tr>
<tr>
<td><a href="https://ravendb.net/" rel="external nofollow noopener">RavenDB</a></td>
<td>Still not using this for much as I can't seem to effectively query the data from Raven Studio, and at heart I still think NoSQL is a fad. Transitioned some data back to SQL Server, the rest to follow</td>
</tr>
<tr>
<td><a href="https://rufus.akeo.ie/" rel="external nofollow noopener">Rufus</a></td>
<td>I use this utility for writing ISO images to USB, useful for setting up new physical machines in an age where CD drives are fairly obsolete</td>
</tr>
<tr>
<td><a href="https://sourceforge.net/projects/win32diskimager/" rel="external nofollow noopener">Win32 Disk Imager</a></td>
<td>Useful for burning ISO images to SD cards which I do for Raspberry Pi distributions. I used to use this for USB as well but now I prefer Rufus for that. It is also massively useful for creating images of SD cards, so I use it to backup my assorted Raspberry Pi devices before major updates</td>
</tr>
</tbody>
</table>
<p>What tools do you find useful? I'd love to know... maybe I'll
find a new gem myself!</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2021-01-01 - First published</li>
<li>2021-01-04 - Added 7-Zip, EarTrumpet, TreeSize Free, PowerToys</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/tools-we-use-2020-edition .
</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.comInitial thoughts on the Cooler Master Pi Case 40urn:uuid:f174253a-013e-423e-8625-347cef3163842020-12-14T21:33:59Z2020-12-14T21:33:59Z<p>Today I received a pair of Cooler Master Pi Case 40 cases, a
<a href="https://www.kickstarter.com/projects/coolermaster/pi-case-40/" rel="external nofollow noopener">Kickstarter project</a> I backed in July. It has actually been
available for purchase from other retailers for some time whilst
backers such as myself cooled our collective heels.</p>
<p>This case is a passively cooled aluminium case for the Raspberry
Pi 4 that also features a programmable button (e.g. for
switching it on an off). It is also supposed to include the STL
files so that alternative parts can be printed, although I don't
think these are available yet.</p>
<p>These are my initial thoughts for the case, less than a review,
but more than a quick judgement.</p>
<h2 id="unboxing">Unboxing</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/pi-case-40-box.jpg" class="gallery" title="The outer packaging" ><img src="https://images.cyotek.com/image/thumbnail/devblog/pi-case-40-box.jpg" alt="The outer packaging" decoding="async" loading="lazy" /></a><figcaption>The outer packaging</figcaption></figure>
<p>I really like the packaging. It is a snug and unassuming box,
inside of which is the case, four screws plus the Allen key
required for them, and four VESA mounting brackets (sans
screws). There are also two thermal pads, although as the case
only directly touches one chip I'm not sure why two are
provided.</p>
<p>Installation instructions are printed on the insides of the box.
All in all, it is a really neat package.</p>
<h2 id="hardware">Hardware</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/pi-case-40-case-top.jpg" class="gallery" title="The top of the assembled case" ><img src="https://images.cyotek.com/image/thumbnail/devblog/pi-case-40-case-top.jpg" alt="The top of the assembled case" decoding="async" loading="lazy" /></a><figcaption>The top of the assembled case</figcaption></figure>
<p>The first thing I noted when I took the case out of the box was
that it was missing the Cooler Master logo - there is a silver
shield in the right shape but without the logo itself. Not sure
if that is by design or not. It has a nice compact size to it -
only slightly larger than the Pi itself, and much smaller than
the Argon ONE from my <a href="/post/a-review-of-the-argon-one-raspberry-pi-4-case">previous review</a>.</p>
<p>The case is comprised of three pieces, a smokey translucent
base, an aluminium upper, and a rubber bumper around the sides.
The top features a button that you are able to program for
rebooting or shutting down the Pi.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/pi-case-40-case.jpg" class="gallery" title="The inside of the case and the base" ><img src="https://images.cyotek.com/image/thumbnail/devblog/pi-case-40-case.jpg" alt="The inside of the case and the base" decoding="async" loading="lazy" /></a><figcaption>The inside of the case and the base</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/pi-case-40-with-pi.jpg" class="gallery" title="The inside of the case with a Raspberry Pi 4 fitted" ><img src="https://images.cyotek.com/image/thumbnail/devblog/pi-case-40-with-pi.jpg" alt="The inside of the case with a Raspberry Pi 4 fitted" decoding="async" loading="lazy" /></a><figcaption>The inside of the case with a Raspberry Pi 4 fitted</figcaption></figure>
<p>Just like the Argon, the Pi Case 40 redirects the GPIO ports,
this time to the side of the device. The rubber bumper folds
back to allow access.</p>
<p>The base has two rubber strips which help prevent the case from
sliding. One of these strips is slightly offset from the edge of
the case to allow space for the screw holes (see section on VESA
below). This is unfortunate as the programmable button happens
to be on this side of the case, meaning when you press it, the
case tilts and lifts off the surface.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/pi-case-40-base.jpg" class="gallery" title="The base of the case along with the mounting arms" ><img src="https://images.cyotek.com/image/thumbnail/devblog/pi-case-40-base.jpg" alt="The base of the case along with the mounting arms" decoding="async" loading="lazy" /></a><figcaption>The base of the case along with the mounting arms</figcaption></figure>
<p>Incidentally, the screw holes line up with the holes in the Pi
itself and so it all screws tightly together.</p>
<p>The thick section in the top of the above photo is a rubber
cover over the SD slot. Just like the GPIO cover, this peels
back to allow you to swap out the SD card easily.</p>
<h2 id="controversy">Controversy</h2>
<p>The case is actually a really nice package. Aside from the
rocking when pressing the power button, the design seems really
nice - the bumper means if you drop it it is less likely to get
damaged, the plastic base isn't ugly and as it is translucent,
you can see the power and activity LEDs of your device.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/pi-case-40-side.jpg" class="gallery" title="The side of the case showing activity LEDs and also a slot for ribbon cables" ><img src="https://images.cyotek.com/image/thumbnail/devblog/pi-case-40-side.jpg" alt="The side of the case showing activity LEDs and also a slot for ribbon cables" decoding="async" loading="lazy" /></a><figcaption>The side of the case showing activity LEDs and also a slot for ribbon cables</figcaption></figure>
<p>But here is the thing (aside from the fact that they started
selling the case on other sites before the backers ever got
their hands on it). If you use a Raspberry Pi, it's quite likely
you'll want to make use of the GPIO ports. For example, to plug
in an external sound board. I bought two of these cases as I
wanted to upgrade both my Kodi and <a href="/post/installing-mopidy-and-phat-beat-on-a-raspberry-pi">Mopidy</a> devices, the
latter using a <a href="https://shop.pimoroni.com/products/phat-beat" rel="external nofollow noopener">pHAT BEAT</a>.</p>
<p>Except, for reasons known only to Cooler Master, they reversed
the GPIO rows so <em>you cannot use any hats with the device!</em></p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/pi-case-40-gpio.jpg" class="gallery" title="The Pi Case 40 alongside the Argon ONE. The GPIO rows are the wrong way around" ><img src="https://images.cyotek.com/image/thumbnail/devblog/pi-case-40-gpio.jpg" alt="The Pi Case 40 alongside the Argon ONE. The GPIO rows are the wrong way around" decoding="async" loading="lazy" /></a><figcaption>The Pi Case 40 alongside the Argon ONE. The GPIO rows are the wrong way around</figcaption></figure>
<p>Their statement after previously claiming it was a labelling
error and would be fixed in the final product was</p>
<blockquote>
<p>Pi Case 40 is designed for the On-The-Go Raspberry Pi users
searching for a reliable, durable, and compact solution that
suits their nomad lifestyle.</p>
<p>The focus is on Thermal Performance, Durability, and
Portability while keeping full access to the board's
connectors.</p>
<p>Please note that Pi Case 40 is not meant to be used together
with HATS and other accessories that require connection to (or
part of) the standard GPIO of the Raspberry Pi 4.</p>
<p>This, together with our plans on developing a new top panel
with full hats compatibility, has been shared and communicated
multiple times.</p>
</blockquote>
<p>This is unmitigated poppycock. If this was the case, then they
may not have exposed them at all - and certainly wouldn't have
said it was an error that would be fixed. Not to mention... VESA
mounts for an &quot;on-the-go&quot; user?</p>
<p>I don't know if they just screwed up and didn't realise until it
was too late, but this excuse is a poor one and makes at least
one of the cases completely useless to me without modification.</p>
<p>Who in their right minds would design a case for a Raspberry Pi
that wasn't compatible with a Raspberry Pi?</p>
<p>As far as I'm aware, that statement was also the first time they
communicated this, bar an original comment where they stated it
was an error that would be fixed. And if they do fix it I'm
willing to bet they will ignore their backers who put misplaced
faith into this project.</p>
<p>Finally, their own product pages don't even make note of this
fairly important fact which also seems a touch suspicious.</p>
<h2 id="thermal-qualities">Thermal Qualities</h2>
<p>I have left the Pi running as I write this review. It is a fresh
install, no extra software and the Pi is just sitting at the
desktop with the Pi Tool software open. It has been constantly
at 42°C and the top of the case is warm to the touch.</p>
<p>It seems no better or worse than using the Argon. However, I
haven't done any extensive tests.</p>
<p>With that said, after dismantling my case in preparation for
putting it back in the Argon, I realised I didn't take a
screenshot or photo of the Pi Tool, so I plugged it back in. In
its bare state, the temperature of the Pi rapidly rose to 57°C
with the Pi Tool open. Closed, it dropped to 50°C. So clearly
the aluminium case is working to passively cool the device.</p>
<h2 id="software">Software</h2>
<p>In order to use the programmable button, you need to install
Cooler Master's Pi Tool, which is accessible from their <a href="https://github.com/CoolerMasterTechnology/Pi-Tool" rel="external nofollow noopener">GitHub
repository</a>.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/pi-case-40-software.jpg" class="gallery" title="The Pi Tool software" ><img src="https://images.cyotek.com/image/thumbnail/devblog/pi-case-40-software.jpg" alt="The Pi Tool software" decoding="async" loading="lazy" /></a><figcaption>The Pi Tool software</figcaption></figure>
<p>This 300MB+ tool includes some nice charts of system load and
can enable overclocking, all of which in all honestly I think is
completely pointless.</p>
<p>Frivolous features aside, it does let you configure the button
mappings for sequences of long and/or short presses. I did
notice some oddity though - after restarting, the button
mappings had reset to the default value of &quot;off&quot; and had lost my
short press binding. Later on when booting up again to take the
above photo I noted that the long press binding was back to
&quot;on&quot;, although my short press binding was still missing. So
there seems to be some sort of issue with the software.</p>
<p>When testing the button, unlike the Argon, there was no
immediate action. Each time I tested the long or short press,
there was a delay before the Pi shutdown or rebooted, each time
long enough for me to think it wasn't working.</p>
<p>Oh yes, and if you answer &quot;Yes&quot; to &quot;Install Desktop
Customisation&quot;, which I naturally assumed was a &quot;Do you want to
install the Pi Tool&quot;, it changes your desktop wallpaper to a
Cooler Master branded image.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/pi-case-40-software-install.jpg" class="gallery" title="No, I didn't actually want you to change my wallpaper" ><img src="https://images.cyotek.com/image/thumbnail/devblog/pi-case-40-software-install.jpg" alt="No, I didn't actually want you to change my wallpaper" decoding="async" loading="lazy" /></a><figcaption>No, I didn't actually want you to change my wallpaper</figcaption></figure><h2 id="vesa-mounts">VESA Mounts</h2>
<p>One of the nice features of this case is that it comes with four
plastic arms you can use to mount the case on televisions or
monitors which support the VESA standard. There are 3 notches
next to each screw hole on the base so that you can orient them
to fit various different sizes. I tested this with an ancient
spare monitor and although I didn't have screws of the right
length to hand, it clearly worked fine which should be useful
for the case I will use with the Kodi (after I find more
appropriate screws!).</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/pi-case-40-vesa.jpg" class="gallery" title="Temporarily mounted on a truly ancient monitor" ><img src="https://images.cyotek.com/image/thumbnail/devblog/pi-case-40-vesa.jpg" alt="Temporarily mounted on a truly ancient monitor" decoding="async" loading="lazy" /></a><figcaption>Temporarily mounted on a truly ancient monitor</figcaption></figure><h2 id="pros">Pro's</h2>
<ul>
<li>Provides passive cooling for a Raspberry Pi</li>
<li>Sturdy case which looks like it can take a tumble without
damaging the Pi</li>
<li>Power button!</li>
<li>Mounting arms</li>
<li>Ribbon cable access</li>
</ul>
<h2 id="cons">Con's</h2>
<ul>
<li>Hats cannot be used</li>
<li>Due to the case acting as a heat sink, it could get very hot</li>
<li>Pi Tool software seems to forget button mappings</li>
<li>Pressing the power button seems to have some form of lag
before it executes</li>
</ul>
<h2 id="closing-thoughts">Closing Thoughts</h2>
<p>If your intended use for a Raspberry Pi does not involve using
hats then this is a serviceable case.</p>
<p>I do not recommend this case. Frankly, I don't recommend Cooler
Master and will not buy their products again.</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/initial-thoughts-on-the-cooler-master-pi-case-40 .
</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.comCyotek.Drawing.BitmapFont Version 2 Releasedurn:uuid:f4b1e897-66f6-4393-852a-341782c8c2902020-11-02T11:30:10Z2020-10-14T07:42:21Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/viewer-characters.png" class="gallery" title="A screenshot of the updated demonstration application" ><img src="https://images.cyotek.com/image/thumbnail/devblog/viewer-characters.png" alt="A screenshot of the updated demonstration application" decoding="async" loading="lazy" /></a><figcaption>A screenshot of the updated demonstration application</figcaption></figure>
<p>I've slowly been picking up the reins on my languishing open
source projects, and the first to get a proper release is our
<a href="https://www.angelcode.com/products/bmfont/" rel="external nofollow noopener">BMFont</a> parsing library, <a href="https://github.com/cyotek/Cyotek.Drawing.BitmapFont" rel="external nofollow noopener">Cyotek.Drawing.BitmapFont</a>.</p>
<h2 id="whats-new-and-improved">What's New and Improved</h2>
<ul>
<li>The original library targeted .NET 2.0, the new package
targets .NET 3.5, 4.0, 4.5.2, 4.6.2, 4.7.2, and 4.8, .NET
Standard 1.3, 2.0 and 2.1, and .NET Core 2.1, 2.2 and 3.1.
Unfortunately <code>dotnet build</code> doesn't support .NET 2.0 as far
as I can tell so I dropped that target</li>
<li>Binary support. I originally added binary support in 2017 and
it sat gathering dust in a branch. Now tidied up and part of
the core library - I recommend using this format where
possible if you care about efficiency</li>
<li>Empty and invalid character support. The <code>BitmapFont</code> indexer
will no longer throw if the requested character is not found,
instead if the font defines the &quot;invalid&quot; character this be
returned, otherwise an empty character definition.</li>
<li>Assorted performance improvements for loading text based fonts</li>
<li>Although not a concern for consumers of the library, the
demonstration program has been fleshed out a bit more and an
additional demonstration added</li>
</ul>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/textmaker-preview.png" class="gallery" title="A new demonstration application included in the source code" ><img src="https://images.cyotek.com/image/thumbnail/devblog/textmaker-preview.png" alt="A new demonstration application included in the source code" decoding="async" loading="lazy" /></a><figcaption>A new demonstration application included in the source code</figcaption></figure>
<p>There are various other smaller changes and additions, see the
<a href="https://github.com/cyotek/Cyotek.Drawing.BitmapFont/releases/tag/v2.0" rel="external nofollow noopener">Release Notes</a> page for the full details.</p>
<h2 id="whats-next">What's Next</h2>
<p>Now that I have a multi-targeted mostly cross-platform version,
I can now consider more drastic changes that are breaking.
Tentative planning for v3 include</p>
<ul>
<li>Making all structures immutable (properties on the
<code>BitmapFont</code> class will remain read-write)</li>
<li>Changing all property values to match their binary types, e.g.
rather than using <code>int</code> for everything, <code>byte</code> and <code>short</code>
will be substituted as appropriate. This doesn't seem to
result in much of a drop in memory so this one is tentative</li>
<li>Removing the reference to <code>System.Drawing</code>; properties such as
<code>Bounds</code> will be removed and replaced with individual native
properties (e.g. <code>X</code>, <code>Y</code>, <code>Width</code>, <code>Height</code>). Some of these
properties were added in v2 and their original counterparts
marked as obsolete</li>
<li>Adding concrete collections instead of exposing <code>IDictionary</code>
which is a) tidier, and b) easier to use</li>
<li>Refactoring to move code around</li>
<li>Potentially adding the ability to save fonts in each of the 3
formats (no, I am not going to duplicate the BMFont
application, but I could see potential value in being able to
tweak values)</li>
</ul>
<p>Comments and suggestions on these potential updates welcome!</p>
<h2 id="downloading-the-update">Downloading the update</h2>
<p>The library is available as a <a href="https://www.nuget.org/packages/Cyotek.Drawing.BitmapFont/" rel="external nofollow noopener">NuGet package</a>, or you can
download release binaries and/or source from <a href="https://github.com/cyotek/Cyotek.Drawing.BitmapFont/releases" rel="external nofollow noopener">GitHub</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/cyotek-drawing-bitmap-version-2-released .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comInitial thoughts on the Adafruit I2S 3W Stereo Speaker Bonnet for Raspberry Piurn:uuid:fc2153b1-5841-40de-a93d-7161e783e1442020-09-06T10:20:50Z2020-09-04T23:16:13Z<p>In my previous <a href="/post/a-review-of-the-argon-one-raspberry-pi-4-case">Argon ONE review</a>, some of the photographs
show a pair of white cables, one going into a USB slot and
another the audio jack. These belong to a set of Logitech Z120
speakers I'd bought for use with things like the Raspberry Pi to
avoid more plugs.</p>
<p>Unfortunately however, I swiftly discovered that these are not a
very good fit for the Pi - when they are plugged in and switched
on, there is <em>constant</em> feedback issuing from the speakers. I
considered buying another <a href="/post/installing-mopidy-and-phat-beat-on-a-raspberry-pi">pHAT BEAT</a> but I decided that was
overkill - I don't need hardware audio buttons nor a VU meter
for a &quot;desktop&quot; Pi. But although that particular board was
overkill, perhaps another board with less features would be
suitable - the board would power the speakers so I could still
be plug-less.</p>
<blockquote>
<p>As always, I chose and bought these items myself, no-one is
asking me (or paying me!) to write this content. There are no
affiliated links.</p>
</blockquote>
<p>In the end, I settled on the <a href="https://thepihut.com/products/adafruit-i2s-3w-stereo-speaker-bonnet-for-raspberry-pi" rel="external nofollow noopener">Adafruit I2S 3W Stereo Speaker
Bonnet for Raspberry Pi</a>, paired with the Pi Hut's <a href="https://thepihut.com/products/stereo-enclosed-speaker-set-3w-4-ohm" rel="external nofollow noopener">Enclosed
Stereo Speaker Set (3W 4 Ohm)</a>. I choose these speakers as
firstly, they are a pair, secondly they are enclosed so less likely
to be damaged if I leave them bare and thirdly they include the
necessary cabling which will plug directly into the bonnet.</p>
<p>This article describes my initial thoughts and experiences - I
don't know if it qualifies as a review per se, so I'll leave it
at thoughts, and potentially follow up later if there's a need.</p>
<h2 id="unwrapping">Unwrapping</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/bonnet-front.jpg" class="gallery" title="The top of the bonnet along with the optional headers that can be soldered on to support wired hi-fi speakers" ><img src="https://images.cyotek.com/image/thumbnail/devblog/bonnet-front.jpg" alt="The top of the bonnet along with the optional headers that can be soldered on to support wired hi-fi speakers" decoding="async" loading="lazy" /></a><figcaption>The top of the bonnet along with the optional headers that can be soldered on to support wired hi-fi speakers</figcaption></figure>
<p>The board is the same size as a Raspberry Pi Zero so I have no
really idea why they called it a &quot;bonnet&quot; instead of a &quot;hat&quot;.
The kit includes the board itself, a half-height 40 pin GPIO
header already soldered in place, a speaker jack in the middle
(I don't know its official designation), and two additional
headers for allowing you to use traditional wired speakers. And,
unlike the pHAT BEAT, these headers let you screw the wires into
place. However, they weren't pre-soldered so you would need to
do this yourself. This is both a blessing and a curse I suppose
as they are about double the height of the pre-soldered jack so
if you do solder them on they will increase the total height of
the board.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/bonnet-speakers.jpg" class="gallery" title="The enclosed speaker set. Construction wise, these seem quite sturdy" ><img src="https://images.cyotek.com/image/thumbnail/devblog/bonnet-speakers.jpg" alt="The enclosed speaker set. Construction wise, these seem quite sturdy" decoding="async" loading="lazy" /></a><figcaption>The enclosed speaker set. Construction wise, these seem quite sturdy</figcaption></figure>
<p>The speakers appear to be quite nice. The enclosure feels solid,
and the speaker parts are inset slightly so hopefully less
likely to get damaged. The cable is around 45 centimetres long,
so if you fully split it you could have the speakers almost a
meter away from each other. Nothing fancy, no extra's, just a
small pair of speakers and their cable.</p>
<blockquote>
<p>Note: In all pictures you'll notice that the speakers are
plugged into the bonnet. I plugged them in prior to taking
photographs and it is wedged solid, I doubt I can separate
them again without damage.</p>
</blockquote>
<h2 id="hardware-installation">Hardware installation</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/bonnet-in-case.jpg" class="gallery" title="The bonnet plugged into the case. Thanks to the low height of the GPIO header, the board can easily come into contact with the metal case and spark or short" ><img src="https://images.cyotek.com/image/thumbnail/devblog/bonnet-in-case.jpg" alt="The bonnet plugged into the case. Thanks to the low height of the GPIO header, the board can easily come into contact with the metal case and spark or short" decoding="async" loading="lazy" /></a><figcaption>The bonnet plugged into the case. Thanks to the low height of the GPIO header, the board can easily come into contact with the metal case and spark or short</figcaption></figure>
<p>I had to admit to a moment of concern when I thought the board
wouldn't fit as the GPIO slot is inset in the case due to the
magnetic hatch, but I need not have worried - the designers of
the case have apparently planned for this and it nicely fits
facing backwards.</p>
<p>Except, to jump ahead a little, there's a massive problem with
the design. I mentioned earlier that the GPIO header was
half-height - the plastic header is 4mm high, compared to 8mm on
other devices. This means that when you push it into the slot on
the case, you can easily push it in enough so that it is resting
<em>on</em> the top of case. Twice when I did this I actually saw
sparks in the bottom right of the recess, a third time I
evidently shorted something and the Pi rebooted.</p>
<p>All of the photographs in this article show the bonnet almost
flush with the case, but rather unsurprisingly this was not
something I was willing to tolerate. As the header was
pre-soldered and I still don't have a soldering iron, I grabbed
a <a href="https://www.ebay.co.uk/itm/40-Pin-GPIO-2x20-Female-Header-2-54mm-for-Raspberry-Pi-Zero/161941307343" rel="external nofollow noopener">40 Pin GPIO 2x20 Female Header</a> from eBay, plugged that
into the GPIO slots on the case and then the bonnet into that,
pushing it far enough above the case that there's no chance of
further contact.</p>
<p>Of course, it sticks out like a sore thumb so maybe I should
look for a new case that can contain both the Pi and the bonnet.
Shame though, the Argon ONE is a fine looking case. Except
apparently not as durable as I thought given it is now sporting
a very visible scuff or scratch caused by who knows what.</p>
<h2 id="software-installation">Software installation</h2>
<p>Just like with the pHAT BEAT, a handy installation
script is available that will re-configure the system to use I2S digital
sound rather than the onboard chip. The installation script can
also install a service that will <em>always</em> &quot;play&quot; inaudible audio
when the sound card isn't otherwise in use to prevent popping.</p>
<p>Installation is in theory simple, just run the following command
to download and execute the script, then answer the questions it
asks.</p>
<blockquote>
<p>You probably shouldn't blindly paste command lines you get off
some random blog, but should always <a href="https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/i2samp.sh" rel="external nofollow noopener">check the script</a>
<strong>before</strong> running it.</p>
</blockquote>
<figure class="lang-bash highlight"><figcaption><span>bash</span></figcaption><pre class="code">
curl -sS https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/i2samp.sh | bash
</pre>
</figure>
<h2 id="bit-woes">64bit woes</h2>
<p>So I did say in theory. As I had bought an 8GB Pi, and a <a href="https://www.raspberrypi.org/forums/viewtopic.php?f=117&amp;t=275370" rel="external nofollow noopener">beta
version of 64bit Raspberry Pi OS</a> had been released, I had
made that my daily driver when trying (and, it has to be said,
mostly failing) to perform development activities on the Pi. It
might be beta but up until now I've had surprisingly few issues,
I think the worst I've seen is screen tearing when watching
films via VLC Player.</p>
<p>However, this script isn't compatible with the 64bit OS - it
throws an unsupported hardware error. After examining the script
and then tinkering it slightly to skip the hardware check, I ran
the script again but the speaker tests failed and I couldn't
play audio.</p>
<p>I next followed the manual instructions but still had a severe
case of no audio.</p>
<h2 id="try-again-with-32bit">Try again, with 32bit</h2>
<p>One of the great advantages of the Pi is you can just swap the
SD card and have a completely different operating system or
configuration. I flashed a fresh install of 32bit Raspberry Pi
OS onto a spare SD card and booted it up.</p>
<p>This time the script ran without a hitch, although it claims
that Buster is experimental. This is yet another cause for concern
to me - the script hasn't been touched for 15 months, yet the
board is still being sold. In fact, when I checked the source,
it classes <em>Stretch</em> is experimental and that was released in
<em>2017.</em> If you are actively selling a product, then you should
damn well be keeping your install up to date.</p>
<p>Digression aside, everything went smoothly on the 32bit OS.
There's a slight oddity in that you need to reboot after setting
up I2S, play audio, then reboot again in order for tools such as
<code>alsamixer</code> to work, but you only need to do that the once.</p>
<p>However, I did quite quickly note that the default volume
control that appears in the taskbar (Volume Control (ALSA/BT))
didn't work properly. When you clicked the volume icon, it would
display a <em>No volume control on this device tooltip</em> instead of
presenting a volume slider. Right clicking and choosing Output
Device Options shows a dialogue that includes a volume slider,
but that is not entirely optimal for quickly changing the
volume. Happily, I knew from previous poking around that there
are two volume controls available for the taskbar and so I added
the other one (Volume Control) and this worked absolutely fine,
presenting the slider without issue.</p>
<h2 id="speaker-quality">Speaker quality</h2>
<p>Clearly the bonnet and the speakers, were working fine, but
what about the quality? I tried playing a film via VLC Player
and immediately noticed there was no real depth to the audio, a
distinct lack of bass.</p>
<p>But, there was no feedback at all... when I wasn't playing
audio, nothing was issued from the speakers.</p>
<p>The speakers aren't the best quality but they work.</p>
<h2 id="using-full-speakers">Using full speakers</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/bonnet-full-speaker-test.jpg" class="gallery" title="Testing a bookshelf speaker. This speaker provides very nice sound, far better than the other set in this article" ><img src="https://images.cyotek.com/image/thumbnail/devblog/bonnet-full-speaker-test.jpg" alt="Testing a bookshelf speaker. This speaker provides very nice sound, far better than the other set in this article" decoding="async" loading="lazy" /></a><figcaption>Testing a bookshelf speaker. This speaker provides very nice sound, far better than the other set in this article</figcaption></figure>
<p>Although I will be using those small speakers for the
foreseeable future, I wanted to verify that the quality issues
were due to them and not the board itself. I plugged in one of
the speakers I use with my Pi Zero music centre using one of the
optional speaker headers (fortunately I could hold it in place
to test). As I mostly expected, the audio coming out of the
speaker was deep and clear.</p>
<h2 id="bit-redux-or-they-werent-kidding-about-the-pop">64bit, redux, or, they weren't kidding about the pop</h2>
<p>I was curious as to why 64bit hadn't worked - it didn't really
make a huge amount of sense. I'd checked the script and it was
just changing configuration files, not installing software or
anything like that.</p>
<p>So, I restored my 64bit image and tried again. First, I followed
the manual procedure. No audio. But when I right clicked the
speaker icon in the taskbar, I realised that the
<em>snd_rpi_hifiberry_dac</em> entry wasn't ticked the way it was for
32bit. I have no idea what device it was trying to use, but
clearly it wasn't that one!</p>
<p>On running the speaker test after checking the dac option,
everything was fine. So I tried a video and that played fine
too. Success?</p>
<p>Yes and no. Each time audio stopped or started there was a
popping noise... that would get annoying just as fast as the
feedback on the USB powered speakers and explains why the script
tried to set up a service. So...</p>
<p>I decided to restore my backup image again and retry using the
script now that I've seemingly discovered that a default device
wasn't being set up. I commented out lines 145 and 147-150 and
executed the script. After the first boot, I set the default
output device to be <em>snd_rpi_hifiberry_dac,</em> ran the script
again (opting to install the little service) and rebooted again.</p>
<p>And... no sound. But interestingly, VLC Player displayed an
error dialog stating that the audio was in use. The most obvious
cause was the helper service, so I disabled that and rebooted.
And now I have audio again, complete with pop.</p>
<p>I also noted that I couldn't change the volume at all. Sigh.</p>
<h3 id="heat-issues">Heat Issues</h3>
<p>I was typing this article up in VS Code on my desktop computer,
the Pi with the 64bit OS was playing Tim Burton's Batman. I had
paused it while working on this draft when after around 20
minutes or so the speakers started squealing. When I touched
one of them I was shocked at the temperature - they were
<em>really</em> hot. I immediately powered down the Pi.</p>
<p>Now I've watched several films on this Pi over the last few days
using the fresh 32bit install, including leaving films paused
for hours while working and I have never had any squealing
issues or problems with heat. In fact, after swapping the cards
around again, booting into 32bit, and then waiting until the
speakers had cooled down, I tested the same scenario without
issue.</p>
<p>Is this a 64bit issue? A side effect of not running the always
on service? Some random conflict with other software I've installed? I don't know. But I am definitely concerned enough that I won't
be using it with the 64bit Raspberry Pi OS again.</p>
<h2 id="in-closing">In closing</h2>
<p>I would rather not have had to purchase this board, but the
feedback from the USB powered speakers meant I needed another
solution. Software issues aside, I'm quite happy with this
bonnet.</p>
<p>I suspect I'll probably want to look into changing cases though
as I don't like the aesthetics of having this board poking out
like the spoiler of a car, not to mention the shorting out if the pin slots on the board come into contact with the metal case.</p>
<p>And the speakers, while serviceable, aren't great for watching
films. I most likely will try and replace them with a smaller
set of second-hand traditional hi-fi speakers from eBay which I
can wire directly into the board.</p>
<p>Finally, I clearly need to rethink if I'm going to stick with
the 64bit OS. I've spent a lot of time customising it and
installing various bits of software, none of which I documented
after the first day. But there seems to be too many issues with
this board when used on the 64bit OS for me to want to continue with it.</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/initial-thoughts-on-the-adafruit-i2s-3w-stereo-speaker-bonnet-for-raspberry-pi .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCommitting to a Git repository using custom datesurn:uuid:8f1eb875-1ba1-4ebe-9d23-27e5cedefd572020-09-05T10:05:32Z2020-09-01T08:21:49Z<p>Occasionally I want to create a Git repository for some ancient
piece of code in my SVN repository. As I haven't had any luck in
exporting parts of my giant mono repository, I tend to just grab
the lowest folder, initialise a new git repository and commit.
But if I do that, then everything is tagged with the current
timestamp, which is definitely not what I want for code that I
haven't touched in years.</p>
<p>Fortunately Git allows you to force the dates when you commit.
Not so fortunately, the way of doing it isn't obvious (not to
mention Git has two different date systems) and so whenever I
want to do it, I've forgotten the specifics and need to look it up. This post
serves as a reminder for myself rather than a more informative
post for general use.</p>
<p>Before committing, set the <code>GIT_COMMITTER_DATE</code> and
<code>GIT_AUTHOR_DATE</code> environment variables with the timestamp you
want to use. I usually use ISO 8601 format but you can also use
RFC 2822.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
<span class="keyword">set</span> GIT_COMMITTER_DATE=<span class="string">&quot;2011-12-02T23:37:52&quot;</span>

<span class="keyword">set</span> GIT_AUTHOR_DATE=<span class="string">&quot;2011-12-02T23:37:52&quot;</span>
</pre>
</figure>
<blockquote>
<p>If using Linux or Git Bash on Windows, use <code>export</code> instead of
<code>set</code></p>
</blockquote>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/git-commit-setup.png" class="gallery" title="Defining the environment variables that our commit will use" ><img src="https://images.cyotek.com/image/thumbnail/devblog/git-commit-setup.png" alt="Defining the environment variables that our commit will use" decoding="async" loading="lazy" /></a><figcaption>Defining the environment variables that our commit will use</figcaption></figure>
<p>Now I can <code>git commit</code> and it will use the custom dates provided by the environment variables. If I do <code>git log</code> I can see it shows the correct date. Or one of them anyway.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/git-commit-log.png" class="gallery" title="The output of `git-log`, showing that at least one date date was used" ><img src="https://images.cyotek.com/image/thumbnail/devblog/git-commit-log.png" alt="The output of `git-log`, showing that at least one date date was used" decoding="async" loading="lazy" /></a><figcaption>The output of `git-log`, showing that at least one date date was used</figcaption></figure>
<p>Alternatively, to be sure I've got the pair of dates correct, I
can use custom formatting to print both dates</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
git log --pretty=<span class="string">&quot;Author date: %ad, Committer date: %cd&quot;</span>
</pre>
</figure>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/git-commit-pretty-log.png" class="gallery" title="The output of `git-log` with a custom format string, showing that both the committer and author dates were used" ><img src="https://images.cyotek.com/image/thumbnail/devblog/git-commit-pretty-log.png" alt="The output of `git-log` with a custom format string, showing that both the committer and author dates were used" decoding="async" loading="lazy" /></a><figcaption>The output of `git-log` with a custom format string, showing that both the committer and author dates were used</figcaption></figure>
<p>One word of caution - as these are environment variables, if you
make another commit from the same session, it will re-use the
variables which may not be what you wanted.</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/committing-to-a-git-repository-using-custom-dates .
</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.comWriting DOOM WAD filesurn:uuid:54879631-44df-42e2-9eee-96318bc5e3342020-07-31T08:23:25Z2020-07-30T07:34:16Z<p>In a <a href="/post/reading-doom-wad-files">prior post</a>, I described id's WAD format used by
classic games such as DOOM and how to read them. This post
covers how to write them. As with my first post, this only
covers the original WAD format, not the enhanced ones which
followed.</p>
<h2 id="the-format">The Format</h2>
<p>A brief recap on the format. There is a 12 byte header which
details the wad type, the number of lumps of data it contains,
and an offset where the directory index is located.</p>
<table>
<thead>
<tr>
<th>Range</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>0</code> - <code>3</code></td>
<td>Either the string <code>IWAD</code> or <code>PWAD</code></td>
</tr>
<tr>
<td><code>4</code> - <code>7</code></td>
<td>The number of entries in the directory</td>
</tr>
<tr>
<td><code>8</code> - <code>11</code></td>
<td>The location of the directory</td>
</tr>
</tbody>
</table>
<p>The directory index is comprised of (16 * number of lumps) bytes
which describe the lumps. Each 16 byte header details the size,
the position in the data and the lump name.</p>
<table>
<thead>
<tr>
<th>Range</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>0</code> - <code>3</code></td>
<td>The location of the lump</td>
</tr>
<tr>
<td><code>4</code> - <code>7</code></td>
<td>The size of the lump</td>
</tr>
<tr>
<td><code>8</code> - <code>15</code></td>
<td>The name of the lump, padded with <code>NUL</code> bytes</td>
</tr>
</tbody>
</table>
<p>All integer values are in <a href="https://en.wikipedia.org/wiki/Endianness" rel="external nofollow noopener">little-endian</a> format.</p>
<h2 id="considerations">Considerations</h2>
<p>The nature of the WAD file means that in theory that you should
be able to make changes to it without having to rewrite the
entire file. For example, adding a new lump of data could simply
be added to the end of the existing data, overwriting the existing
directory index, and then a new index appended. Replacing a lump
with data the same size or small could overwrite the existing
lump, and adjusting the directory index entry. Even when
removing a lump you could opt to leave the data behind (or zero it out!) and
simply remove the meta data from the directory index.</p>
<p>However, the simplest means (albeit most inefficient) is to
rewrite the whole WAD. At this point I am simply exploring the
format (and its subsequent iterations) so therefore I'm not
going to go out of my way to complicate things and so this
demonstration program will recreate the WAD each time it is
changed.</p>
<h2 id="creating-a-wad-from-scratch">Creating a WAD From Scratch</h2>
<p>The previous post introduced the <code>WadReader</code> class, a way of
quickly enumerating a WAD file. Here, I introduce the
<code>WadOutputStream</code> class as a counterpart. This class can be used
to easy create a WAD file by calling <code>PutNextLump</code> with the name
of the lump to add, then write the lump data via the usual
<code>Stream</code> methods. Once done, flushing or closing the stream will
automatically write the directory entry. I borrowed this pattern
from <a href="https://github.com/haf/DotNetZip.Semverd" rel="external nofollow noopener">DotNetZip</a> as I find it a convenient way of creating
Zip files. In fact, I'll likely refactor <code>WadReader</code> at some
point to act more like <code>ZipInputStream</code> as well.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> <span class="symbol">(</span>Stream output <span class="symbol">=</span> File<span class="symbol">.</span>Create<span class="symbol">(</span><span class="string">&quot;test.wad&quot;</span><span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>WadOutputStream target <span class="symbol">=</span> <span class="keyword">new</span> WadOutputStream<span class="symbol">(</span>output<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>target<span class="symbol">,</span> Encoding<span class="symbol">.</span>UTF<span class="number">8</span><span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 target<span class="symbol">.</span>PutNextLump<span class="symbol">(</span><span class="string">&quot;PHOTO1&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 writer<span class="symbol">.</span>Write<span class="symbol">(</span>File<span class="symbol">.</span>ReadAllBytes<span class="symbol">(</span><span class="string">&quot;photo1.jpg&quot;</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 target<span class="symbol">.</span>PutNextLump<span class="symbol">(</span><span class="string">&quot;PHOTO2&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 writer<span class="symbol">.</span>Write<span class="symbol">(</span>File<span class="symbol">.</span>ReadAllBytes<span class="symbol">(</span><span class="string">&quot;photo2.jpg&quot;</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 target<span class="symbol">.</span>PutNextLump<span class="symbol">(</span><span class="string">&quot;PHOTO5&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 writer<span class="symbol">.</span>Write<span class="symbol">(</span>File<span class="symbol">.</span>ReadAllBytes<span class="symbol">(</span><span class="string">&quot;photo5.jpg&quot;</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>When creating an instance of <code>WadOutputStream</code>, it will immediately
write a placeholder header to the stream. This will have the correct WAD type, but the count and directory position will all be defaults until filled in at the end.</p>
<blockquote>
<p>As with the previous article, error handling, parameter
validation and non-essential code has been elided from these
snippets.</p>
</blockquote>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">class</span> WadOutputStream <span class="symbol">:</span> Stream
<span class="symbol">{</span>
 <span class="keyword">private</span> <span class="keyword">readonly</span> List<span class="symbol">&lt;</span>WadLump<span class="symbol">&gt;</span> _lumps<span class="symbol">;</span>
 <span class="keyword">private</span> <span class="keyword">readonly</span> Stream _output<span class="symbol">;</span>
 <span class="keyword">private</span> <span class="keyword">readonly</span> <span class="keyword">long</span> _start<span class="symbol">;</span>
 <span class="keyword">private</span> <span class="keyword">bool</span> _writtenDirectory<span class="symbol">;</span>

 <span class="keyword">public</span> WadOutputStream<span class="symbol">(</span>Stream output<span class="symbol">,</span> WadType type<span class="symbol">)</span>
 <span class="symbol">{</span>
 _output <span class="symbol">=</span> output<span class="symbol">;</span>
 _start <span class="symbol">=</span> output<span class="symbol">.</span>Position<span class="symbol">;</span>
 _lumps <span class="symbol">=</span> <span class="keyword">new</span> List<span class="symbol">&lt;</span>WadLump<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>WriteWadHeader<span class="symbol">(</span>type<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">void</span> WriteWadHeader<span class="symbol">(</span>WadType type<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>WadConstants<span class="symbol">.</span>WadHeaderLength<span class="symbol">]</span><span class="symbol">;</span>

 buffer<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span> <span class="symbol">=</span> type <span class="symbol">==</span> WadType<span class="symbol">.</span>Internal <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> <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">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;W&#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;A&#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;D&#39;</span><span class="symbol">;</span>
 <span class="comment">// positions 4 - 11 left at zero for now</span>

 _output<span class="symbol">.</span>Write<span class="symbol">(</span>buffer<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> WadConstants<span class="symbol">.</span>WadHeaderLength<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>Although the <code>WadOutputStream</code> inherits from <code>Stream</code>, you can't
just randomly write to it. Prior to writing a lump, you need to
call the <code>PutNextLump</code> method. This method both finalises the
previous lump, if applicable, and prepares the new lump. This is
required so that the class can keep track of the lumps in order
to write the directory entry at the end.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">public</span> <span class="keyword">void</span> PutNextLump<span class="symbol">(</span><span class="keyword">string</span> name<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>FinaliseLump<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 _lumps<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> WadLump
 <span class="symbol">{</span>
 Name <span class="symbol">=</span> name<span class="symbol">,</span>
 Offset <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>_output<span class="symbol">.</span>Position
 <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> FinaliseLump<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_lumps<span class="symbol">.</span>Count <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 WadLump lump<span class="symbol">;</span>

 lump <span class="symbol">=</span> _lumps<span class="symbol">[</span>_lumps<span class="symbol">.</span>Count <span class="symbol">-</span> <span class="number">1</span><span class="symbol">]</span><span class="symbol">;</span>
 lump<span class="symbol">.</span>Size <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>_output<span class="symbol">.</span>Position <span class="symbol">-</span> lump<span class="symbol">.</span>Offset<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>Finally, when we're done writing, we need to finish off the WAD
by writing the directory index and updating the header. We'll do
this by overriding both <code>Flush</code> and <code>Dispose</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> Dispose<span class="symbol">(</span><span class="keyword">bool</span> disposing<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>disposing <span class="symbol">&amp;&amp;</span> <span class="symbol">!</span>_writtenDirectory<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>Flush<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>Dispose<span class="symbol">(</span>disposing<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">void</span> Flush<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>_writtenDirectory<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>FinaliseLump<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>WriteDirectory<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 _output<span class="symbol">.</span>Flush<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>When preparing for writing the directory, we again check to see
if there are any lumps and finalise the last one as we did when
adding a new lump.</p>
<p>Now we can finalise the file header. We do this by creating a
new 8 byte array and place the lump count in the first four
bytes, the current stream position into the latter four,
representing where the directory index will be written. We then
write these 8 bytes at the start of the stream, overwriting the
zero block written earlier.</p>
<p>To write an integer into the byte array I'm using a custom
<code>PutInt32Le</code> method. While the <a href="https://docs.microsoft.com/en-us/dotnet/api/system.bitconverter" rel="external nofollow noopener"><code>BitConverter</code></a> class has a
<code>GetBytes</code> method, firstly this will result in repeated
allocations from byte array creation, and secondly I'd then have
to copy the contents in the destination array. Finally,
<code>BitConverter</code> will return the results based on the endian-ness
of the system and we need to ensure that <a href="https://en.wikipedia.org/wiki/Endianness" rel="external nofollow noopener">little-endian</a> is
used.</p>
<p>Once the WAD header is updated we enumerate each of our lumps
and build the 16 byte directory header containing the lump
offset, size and the padded name and then write those at the end
of the file.</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> PutInt<span class="number">32</span>Le<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">private</span> <span class="keyword">void</span> WriteDirectory<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">long</span> position<span class="symbol">;</span>

 buffer <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">byte</span><span class="symbol">[</span>WadConstants<span class="symbol">.</span>DirectoryHeaderLength<span class="symbol">]</span><span class="symbol">;</span>
 position <span class="symbol">=</span> _output<span class="symbol">.</span>Position<span class="symbol">;</span>

 <span class="comment">// first update the header</span>
 WordHelpers<span class="symbol">.</span>PutInt<span class="number">32</span>Le<span class="symbol">(</span>_lumps<span class="symbol">.</span>Count<span class="symbol">,</span> buffer<span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span>
 WordHelpers<span class="symbol">.</span>PutInt<span class="number">32</span>Le<span class="symbol">(</span><span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>position<span class="symbol">,</span> buffer<span class="symbol">,</span> <span class="number">4</span><span class="symbol">)</span><span class="symbol">;</span>

 _output<span class="symbol">.</span>Position <span class="symbol">=</span> _start <span class="symbol">+</span> <span class="number">4</span><span class="symbol">;</span>
 _output<span class="symbol">.</span>Write<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>

 _output<span class="symbol">.</span>Position <span class="symbol">=</span> position<span class="symbol">;</span>

 <span class="comment">// now the directory entries</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> _lumps<span class="symbol">.</span>Count<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 WadLump lump<span class="symbol">;</span>

 lump <span class="symbol">=</span> _lumps<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">;</span>

 WordHelpers<span class="symbol">.</span>PutInt<span class="number">32</span>Le<span class="symbol">(</span>lump<span class="symbol">.</span>Offset<span class="symbol">,</span> buffer<span class="symbol">,</span> WadConstants<span class="symbol">.</span>LumpStartOffset<span class="symbol">)</span><span class="symbol">;</span>
 WordHelpers<span class="symbol">.</span>PutInt<span class="number">32</span>Le<span class="symbol">(</span>lump<span class="symbol">.</span>Size<span class="symbol">,</span> buffer<span class="symbol">,</span> WadConstants<span class="symbol">.</span>LumpSizeOffset<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> lump<span class="symbol">.</span>Name<span class="symbol">.</span>Length<span class="symbol">;</span> j<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 buffer<span class="symbol">[</span>WadConstants<span class="symbol">.</span>LumpNameOffset <span class="symbol">+</span> j<span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">)</span>lump<span class="symbol">.</span>Name<span class="symbol">[</span>j<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> j <span class="symbol">=</span> lump<span class="symbol">.</span>Name<span class="symbol">.</span>Length<span class="symbol">;</span> j <span class="symbol">&lt;</span> WadConstants<span class="symbol">.</span>LumpNameLength<span class="symbol">;</span> j<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 buffer<span class="symbol">[</span>WadConstants<span class="symbol">.</span>LumpNameOffset <span class="symbol">+</span> j<span class="symbol">]</span> <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 _output<span class="symbol">.</span>Write<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="symbol">}</span>

 _writtenDirectory <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="rewriting-an-existing-wad">Rewriting an existing WAD</h2>
<p>This example, taken from the <code>WadFile</code> class, enumerates all
existing lumps and then writes them into a new stream. Although
not demonstrated here, it assumes the <code>GetInputStream</code> for a
given <code>WadLump</code> will return either the original data for
existing lumps, the modified data for existing lumps that have
been altered, or the data for new lumps.</p>
<p>As it can't write to the source stream whilst also reading from
it, it does all this to a temporary stream, and then, when done,
copies the contents of the temporary stream over the original
stream.</p>
<p>This isn't exactly the most efficient approach, but does avoid
all of the complexity of determining which parts of the file to
update, which parts to clear, keeping a list of changed items
for batch saving, etc. This is most likely something I will
investigate further in a future topic.</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">using</span> <span class="symbol">(</span>Stream temp <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetTemporaryStream<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>WadOutputStream output <span class="symbol">=</span> <span class="keyword">new</span> WadOutputStream<span class="symbol">(</span>temp<span class="symbol">,</span> _type<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> _lumps<span class="symbol">.</span>Count<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 WadLump lump<span class="symbol">;</span>

 lump <span class="symbol">=</span> _lumps<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">;</span>
 output<span class="symbol">.</span>PutNextLump<span class="symbol">(</span>lump<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Stream input <span class="symbol">=</span> lump<span class="symbol">.</span>GetInputStream<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 input<span class="symbol">.</span>CopyTo<span class="symbol">(</span>output<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 output<span class="symbol">.</span>Flush<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 stream<span class="symbol">.</span>Position <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>SetLength<span class="symbol">(</span><span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span>

 temp<span class="symbol">.</span>Position <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 temp<span class="symbol">.</span>CopyTo<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="to-pad-or-not-to-pad">To Pad, Or Not To Pad</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/wad-writer-padding.png" class="gallery" title="An example of padding between lumps in DOOM.WAD, running under Mono on a 64bit Raspberry Pi" ><img src="https://images.cyotek.com/image/thumbnail/devblog/wad-writer-padding.png" alt="An example of padding between lumps in DOOM.WAD, running under Mono on a 64bit Raspberry Pi" decoding="async" loading="lazy" /></a><figcaption>An example of padding between lumps in DOOM.WAD, running under Mono on a 64bit Raspberry Pi</figcaption></figure>
<p>After I discovered that the data in the <a href="/post/decoding-doom-picture-files#padding">DOOM picture lumps were
padded</a>, I was curious if the WAD file itself was. I copied
the hex viewer project from that solution and used it to
highlight the different ranges in a WAD. To my surprise, it
seemed that in even though picture lumps already had their own
padding, the lumps themselves were also padded to always have an
even number of bytes. Interestingly, sometimes if a lump started
on an even number two padding bytes were still included. I
suppose there is a reason but I didn't dig further info it and
so didn't build padding support into the writer classes.</p>
<p>Also possibly worthy of note, I checked the <code>DARKWAR.WAD</code> file and
this didn't use padding at all.</p>
<h2 id="does-it-work">Does it work?</h2>
<p>In a word, yes. I tested dumping <code>DOOM.WAD</code> into separate data
files using the <code>waddemo</code> program, then repacking them into a
brand new WAD. I then ran DOOM using the new wad and played
through the first level. Everything seemed to be running fine.</p>
<h2 id="getting-the-source-code">Getting the source code</h2>
<p>As noted in the first article in this series, there isn't a
single download available per post as I've done a
larger-than-usual demonstration solution. The full project is
available from our <a href="https://github.com/cyotek/WadDemo" rel="external nofollow noopener">GitHub</a> page.</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/writing-doom-wad-files .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comDecoding DOOM Picture Filesurn:uuid:f71ecf59-13b8-42b2-82a7-d1c0e83a67882020-07-30T07:33:42Z2020-07-30T07:33:42Z<p>In my <a href="/post/reading-doom-wad-files">previous post</a>, I described id's WAD format used by
classic games such as DOOM and how to read them. While
researching the format though, I wasn't 100% sure that I was
extracting lumps properly - the only readable file I'd
discovered was <code>DMXGUS</code> in <code>DOOM1.WAD</code>, and also <code>LICENSE</code> in
<code>DARKWAR.WAD</code>... hardly conclusive.</p>
<p>Armed with the specification from the <a href="http://www.gamers.org/docs/FAQ/DOOM.FAQ.Specs.Chapters.5.html" rel="external nofollow noopener">DOOM FAQ</a>, I decided
to take a brief segue into decoding the pictures to verify the
lumps I was extracting were valid.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/decoding-doom-pictures-title.png" class="gallery" title="The title screen from shareware doom" ><img src="https://images.cyotek.com/image/thumbnail/devblog/decoding-doom-pictures-title.png" alt="The title screen from shareware doom" decoding="async" loading="lazy" /></a><figcaption>The title screen from shareware doom</figcaption></figure><h2 id="the-format">The Format</h2>
<p>Like the WAD format, id's picture format is also reasonably
straightforward. It is comprised of 3 parts - a header which
describes the image size and also positional information used by
the DOOM engine. Then there is a column index which points to
where the data for a particular column is located. The remainder
of the file is comprised of the column data.</p>
<p>Just like a WAD, integer values are in <a href="https://en.wikipedia.org/wiki/Endianness" rel="external nofollow noopener">little-endian</a>
format.</p>
<h3 id="header">Header</h3>
<table>
<thead>
<tr>
<th>Range</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>0</code> - <code>1</code></td>
<td>16-bit integer containing the image width</td>
</tr>
<tr>
<td><code>2</code> - <code>3</code></td>
<td>16-bit integer containing the image height</td>
</tr>
<tr>
<td><code>4</code> - <code>5</code></td>
<td>16-bit integer describing the X offset</td>
</tr>
<tr>
<td><code>6</code> - <code>7</code></td>
<td>16-bit integer describing the Y offset</td>
</tr>
</tbody>
</table>
<p>Note that the X and Y offsets may be negative, this is used to
absolutely position the image by the DOOM engine.</p>
<h3 id="column-index">Column Index</h3>
<p>The column index follows on immediately from the header and is a
simple list of 32-bit integers, one entry for each column.</p>
<h3 id="column-data">Column Data</h3>
<p>Column data is the most tricky part of the file. Each column is
divided into &quot;posts&quot; of up to 128 bytes each. Each post starts
of with a byte indicating which row drawing will commence with,
followed by the height of the post. This is then followed by an
dummy byte, a sequence of bytes equal to the post height which
represent indexes in a palette, followed by another dummy byte.</p>
<p>If the next byte after this is <code>255</code>, then that is the end of
the column. Otherwise, it is the start of a new post for the
same column.</p>
<p>The following diagram shows example data for a column comprised
of a single post. The row is <code>00</code>, so drawing will commence with
the first row. The height is <code>03</code>, so there are three pixels to
render. After the dummy byte are the 3 pixels values, all <code>BF</code>
in this example. DOOM pictures are 8-bit indexed bitmaps, so
these point to the palette index to use. After another dummy
byte is the end of column marker <code>FF</code>. If there were multiple
posts for this column, then the <code>FF</code> would instead be the new
row index.</p>
<p><img src="https://images.cyotek.com/image/devblog/decoding-doom-pictures-column.png" decoding="async" loading="lazy" alt="The data for the image" /></p>
<p>For backdrop images, multiple posts seem to be used as DOOM's
native size is 320x200 and no post seems to be greater than 128
bytes. For sprite images, multiple posts are used to allow for
transparency, ending a post at the start of a transparent
region, and creating a new post when a solid colour resumes.</p>
<h3 id="a-visual-example">A Visual Example</h3>
<p>As I don't think I described the format very well above, I'll
try a visual example.</p>
<p>This is picture <code>STCFN037</code> blown up 1000% given the original
image is only 9x7. I've highlighted column 8 which is comprised
of 5 pixels of one colour, one pixel of another, plus a single
transparent pixel.</p>
<p><img src="https://images.cyotek.com/image/devblog/decoding-doom-pictures-format-example.png" decoding="async" loading="lazy" alt="An example picture, blown up 100%" /></p>
<p>And here we have the raw data for this picture, highlighted and
annotated.</p>
<p><img src="https://images.cyotek.com/image/devblog/decoding-doom-pictures-format.png" decoding="async" loading="lazy" alt="The data for the image" /></p>
<table>
<thead>
<tr>
<th>Key</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>The 8 byte file header</td>
</tr>
<tr>
<td>2</td>
<td>The column index, with the pointer for column 8 highlighted</td>
</tr>
<tr>
<td>3</td>
<td>The data for column 8, comprised of two posts of 3 pixels each. This allows one pixel in the middle of the column to be transparent</td>
</tr>
<tr>
<td>4</td>
<td>A sub post for column 8</td>
</tr>
</tbody>
</table>
<h2 id="padding">Padding</h2>
<p>I noted when examining the data of some files that padding bytes
are added to the end of some images. At least one padding byte
is always added if the total size of the data is an odd number,
but sometimes extra bytes are added to even sizes as well (but
still ensuring the final size is even).</p>
<h2 id="getting-the-palette">Getting the Palette</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/decoding-doom-pictures-wip.png" class="gallery" title="The first decode test. I hadn't hooked up palettes at this point, nor was I loading sub posts" ><img src="https://images.cyotek.com/image/thumbnail/devblog/decoding-doom-pictures-wip.png" alt="The first decode test. I hadn't hooked up palettes at this point, nor was I loading sub posts" decoding="async" loading="lazy" /></a><figcaption>The first decode test. I hadn't hooked up palettes at this point, nor was I loading sub posts</figcaption></figure>
<p>First things first - remember that DOOM pictures are indexed
bitmaps so you need a palette. As all DOOM pictures share the
same palette (with numerous variations), they aren't included in
the picture data and need to be supplied externally.</p>
<p>The attached demonstration program includes an appropriate
palette, but you can also pull one out directly from a WAD file.
There is a lump named <code>PLAYPAL</code> which contains multiple palettes
in simple RGB triplets. Each palette contains 256 colours and
therefore each palette is 768 bytes in length. You can use the
<code>waddemo</code> tool from the <a href="/post/reading-doom-wad-files">first article</a> to each manually
extract the first 768 bytes of the <code>PLAYPAL</code> data, or use the
<strong>Extract Palettes</strong> command to easily get them all.</p>
<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> LoadPalette<span class="symbol">(</span><span class="keyword">string</span> fileName<span class="symbol">)</span>
<span class="symbol">{</span>
 Color<span class="symbol">[</span><span class="symbol">]</span> palette<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> size<span class="symbol">;</span>

 buffer <span class="symbol">=</span> File<span class="symbol">.</span>ReadAllBytes<span class="symbol">(</span>fileName<span class="symbol">)</span><span class="symbol">;</span>
 size <span class="symbol">=</span> buffer<span class="symbol">.</span>Length <span class="symbol">/</span> <span class="number">3</span><span class="symbol">;</span>
 palette <span class="symbol">=</span> <span class="keyword">new</span> Color<span class="symbol">[</span>size<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> size<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> offset<span class="symbol">;</span>

 offset <span class="symbol">=</span> i <span class="symbol">*</span> <span class="number">3</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>buffer<span class="symbol">[</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> 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="symbol">}</span>

 <span class="keyword">return</span> palette<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Although this is a 24-bit palette, it is similar to the <a href="/post/reading-and-writing-18-bit-rgb-vga-palette-pal-files-with-csharp">18-bit
format I have written about earlier</a>.</p>
<h2 id="decoding-the-picture">Decoding the Picture</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/decoding-doom-pictures-wip-colour.png" class="gallery" title="The second decode test, this time with palettes" ><img src="https://images.cyotek.com/image/thumbnail/devblog/decoding-doom-pictures-wip-colour.png" alt="The second decode test, this time with palettes" decoding="async" loading="lazy" /></a><figcaption>The second decode test, this time with palettes</figcaption></figure>
<p>With a palette in hand, we can read the data. First we need to
get the width and the height of the image. As with the previous
article, I am eschewing the <code>BitConverter</code> class in favour of
something that won't decide to reverse the bytes on a big-endian
system.</p>
<p>As the X and Y offset are used to position the rendered image
in the DOOM engine, I'm ignoring them.</p>
<p>Next, I initialize a byte array which will represent our pixel
data. I set all the values of this to <code>255</code> as this is the
colour used for transparency.</p>
<p>Now it's time to read the column data. For each column I set up
a loop to read a post - first, get the row to render. If this is
<code>255</code>, I know I'm done for this column so I exit the loop.
Otherwise, I get the height, skip a byte, then read the bytes
for the post height, which I assign to my pixel data. Read one
final byte to account for the second dummy value and back to the
start of the loop.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> Bitmap Read<span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">]</span> data<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">byte</span><span class="symbol">[</span><span class="symbol">]</span> pixelData<span class="symbol">;</span>

 width <span class="symbol">=</span> WordHelpers<span class="symbol">.</span>GetInt<span class="number">16</span>Le<span class="symbol">(</span>data<span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span>
 height <span class="symbol">=</span> WordHelpers<span class="symbol">.</span>GetInt<span class="number">16</span>Le<span class="symbol">(</span>data<span class="symbol">,</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">;</span>

 pixelData <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">byte</span><span class="symbol">[</span>width <span class="symbol">*</span> height<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> pixelData<span class="symbol">.</span>Length<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 pixelData<span class="symbol">[</span>i<span class="symbol">]</span> <span class="symbol">=</span> <span class="number">255</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">for</span> <span class="symbol">(</span><span class="keyword">int</span> column <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span> column <span class="symbol">&lt;</span> width<span class="symbol">;</span> column<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> pointer<span class="symbol">;</span>

 pointer <span class="symbol">=</span> WordHelpers<span class="symbol">.</span>GetInt<span class="number">32</span>Le<span class="symbol">(</span>data<span class="symbol">,</span> <span class="symbol">(</span>column <span class="symbol">*</span> <span class="number">4</span><span class="symbol">)</span> <span class="symbol">+</span> <span class="number">8</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">do</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> row<span class="symbol">;</span>
 <span class="keyword">int</span> postHeight<span class="symbol">;</span>

 row <span class="symbol">=</span> data<span class="symbol">[</span>pointer<span class="symbol">]</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>row <span class="symbol">!=</span> <span class="number">255</span> <span class="symbol">&amp;&amp;</span> <span class="symbol">(</span>postHeight <span class="symbol">=</span> data<span class="symbol">[</span><span class="symbol">++</span>pointer<span class="symbol">]</span><span class="symbol">)</span> <span class="symbol">!=</span> <span class="number">255</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 pointer<span class="symbol">++</span><span class="symbol">;</span> <span class="comment">// unused value</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> postHeight<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>row <span class="symbol">+</span> i <span class="symbol">&lt;</span> height <span class="symbol">&amp;&amp;</span> pointer <span class="symbol">&lt;</span> data<span class="symbol">.</span>Length <span class="symbol">-</span> <span class="number">1</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 pixelData<span class="symbol">[</span><span class="symbol">(</span><span class="symbol">(</span>row <span class="symbol">+</span> i<span class="symbol">)</span> <span class="symbol">*</span> width<span class="symbol">)</span> <span class="symbol">+</span> column<span class="symbol">]</span> <span class="symbol">=</span> data<span class="symbol">[</span><span class="symbol">++</span>pointer<span class="symbol">]</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 pointer<span class="symbol">++</span><span class="symbol">;</span> <span class="comment">// unused value</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> <span class="keyword">while</span> <span class="symbol">(</span>pointer <span class="symbol">&lt;</span> data<span class="symbol">.</span>Length <span class="symbol">-</span> <span class="number">1</span> <span class="symbol">&amp;&amp;</span> data<span class="symbol">[</span><span class="symbol">++</span>pointer<span class="symbol">]</span> <span class="symbol">!=</span> <span class="number">255</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>CreateIndexedBitmap<span class="symbol">(</span>width<span class="symbol">,</span> height<span class="symbol">,</span> pixelData<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Decoding the picture is slightly complicated due to the multiple
posts feature, but this means the more transparency is used by a
given picture, the less data that picture requires.</p>
<h2 id="getting-the-bitmap">Getting the Bitmap</h2>
<p>I have spoken before about the <code>GetPixel</code> and <code>SetPixel</code> methods
of the <code>Bitmap</code> class being not fit for purpose, and so I had no
intention of using them with this project either. Instead, I'm
going to restort to using unsafe code to directly manipulate the
bitmap pixels... this is slightly simplified by the fact that as
it is an indexed image, each pixel is only a byte.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> Bitmap CreateIndexedBitmap<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">byte</span><span class="symbol">[</span><span class="symbol">]</span> pixelData<span class="symbol">)</span>
<span class="symbol">{</span>
 Bitmap bitmap<span class="symbol">;</span>
 BitmapData bitmapData<span class="symbol">;</span>
 ColorPalette palette<span class="symbol">;</span>
 <span class="keyword">int</span> index<span class="symbol">;</span>
 <span class="keyword">int</span> stride<span class="symbol">;</span>

 bitmap <span class="symbol">=</span> <span class="keyword">new</span> Bitmap<span class="symbol">(</span>width<span class="symbol">,</span> height<span class="symbol">,</span> PixelFormat<span class="symbol">.</span>Format<span class="number">8</span>bppIndexed<span class="symbol">)</span><span class="symbol">;</span>
 bitmapData <span class="symbol">=</span> bitmap<span class="symbol">.</span>LockBits<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> ImageLockMode<span class="symbol">.</span>WriteOnly<span class="symbol">,</span> PixelFormat<span class="symbol">.</span>Format<span class="number">8</span>bppIndexed<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// can&#39;t create brand new palettes</span>
 <span class="comment">// so need to rework the existing one</span>
 palette <span class="symbol">=</span> bitmap<span class="symbol">.</span>Palette<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> <span class="number">256</span><span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 palette<span class="symbol">.</span>Entries<span class="symbol">[</span>i<span class="symbol">]</span> <span class="symbol">=</span> _palette<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 bitmap<span class="symbol">.</span>Palette <span class="symbol">=</span> palette<span class="symbol">;</span>

 <span class="comment">// apply palette indexes to the bitmap</span>
 index <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 stride <span class="symbol">=</span> bitmapData<span class="symbol">.</span>Stride <span class="symbol">&lt;</span> <span class="number">0</span> <span class="symbol">?</span> <span class="symbol">-</span>bitmapData<span class="symbol">.</span>Stride <span class="symbol">:</span> bitmapData<span class="symbol">.</span>Stride<span class="symbol">;</span>

 <span class="keyword">unsafe</span>
 <span class="symbol">{</span>
 <span class="keyword">byte</span><span class="symbol">*</span> row<span class="symbol">;</span>

 row <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">*</span><span class="symbol">)</span>bitmapData<span class="symbol">.</span>Scan<span class="number">0</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> 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> width<span class="symbol">;</span> x<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 row<span class="symbol">[</span>x<span class="symbol">]</span> <span class="symbol">=</span> pixelData<span class="symbol">[</span>index<span class="symbol">++</span><span class="symbol">]</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 row <span class="symbol">+=</span> stride<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 bitmap<span class="symbol">.</span>UnlockBits<span class="symbol">(</span>bitmapData<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> bitmap<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="syntax-highlighting">Syntax Highlighting</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/decoding-doom-pictures-hex.png" class="gallery" title="This turned out to be more helpful than I was expecting" ><img src="https://images.cyotek.com/image/thumbnail/devblog/decoding-doom-pictures-hex.png" alt="This turned out to be more helpful than I was expecting" decoding="async" loading="lazy" /></a><figcaption>This turned out to be more helpful than I was expecting</figcaption></figure>
<p>After that initial test I was having a spot of bother where some
images crashed when loading, and some didn't decode properly. As
staring at a bunch of bytes doesn't really help with context, I
took the hex viewing code I wrote when ironing out issues
<a href="/post/writing-adobe-swatch-exchange-ase-files-using-csharp">writing Adobe Swatch Exchange files</a>, souped it up some and
used it to do a syntax highlighted view of picture files. This
helped me iron out where I was going wrong.</p>
<p>I'm starting to think that this sort of tool is a actually a
good idea so I'll keep refining this in future samples.</p>
<h2 id="efficiency">Efficiency</h2>
<p>Interestingly, in terms of storage at least, DOOM's picture
format stands up quite well against modern formats - as long as
transparency is involved and even taking into account the
palette being stored externally. When not involved, it doesn't
hold against formats that involve compression such as PNG (which
hadn't been invented yet) or even GIF (which had).</p>
<p>With that said, it's a simple enough format to decode and the
others are decidedly less so.</p>
<p><img src="https://images.cyotek.com/image/devblog/decoding-doom-pictures-possessed.jpg" decoding="async" loading="lazy" alt="Possessed human. 52x53, transparency" /></p>
<table>
<thead>
<tr>
<th>Format</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<tr>
<td>DOOM</td>
<td>1,644 bytes</td>
</tr>
<tr>
<td>BMP</td>
<td>3,834 bytes</td>
</tr>
<tr>
<td>GIF</td>
<td>1,840 bytes</td>
</tr>
<tr>
<td>JPG</td>
<td>2,613 bytes</td>
</tr>
<tr>
<td>PNG</td>
<td>1,426 bytes</td>
</tr>
<tr>
<td>PNG (Transparent)</td>
<td>2,148 bytes</td>
</tr>
</tbody>
</table>
<p><img src="https://images.cyotek.com/image/devblog/decoding-doom-pictures-titlescreen.jpg" decoding="async" loading="lazy" alt="Title screen. 320x200, no transparency" /></p>
<table>
<thead>
<tr>
<th>Format</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<tr>
<td>DOOM</td>
<td>68,168 bytes</td>
</tr>
<tr>
<td>BMP</td>
<td>65,078 bytes</td>
</tr>
<tr>
<td>GIF</td>
<td>41,051 bytes</td>
</tr>
<tr>
<td>JPG</td>
<td>37,223 bytes</td>
</tr>
<tr>
<td>PNG</td>
<td>36,498 bytes</td>
</tr>
</tbody>
</table>
<h2 id="other-formats">Other formats</h2>
<p>From my limited testing, this format was only used for DOOM and
DOOM II. I tested the shareware WADs for Heretic and Hexen and
the full WAD for Rise of the Triad but all three appear to be
using a different image format.</p>
<h2 id="download">Download</h2>
<p>The sample application can be downloaded from our <a href="https://github.com/cyotek/DoomPictureViewer/" rel="external nofollow noopener">GitHub</a> page.</p>
<h2 id="more-images">More Images</h2>
<p>I'll end this post with a few more images.</p>
<div class="article-gallery">
<a href="https://images.cyotek.com/image/devblog/decoding-doom-pictures-credits-wip.png" class="gallery" title="Work in progress loading the credits - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/decoding-doom-pictures-credits-wip.png" alt="Work in progress loading the credits" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/decoding-doom-pictures-help-wip-colour.png" class="gallery" title="More work in progress - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/decoding-doom-pictures-help-wip-colour.png" alt="More work in progress" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/decoding-doom-pictures-transparency.png" class="gallery" title="The fully transparent column in this sprite caused assorted issues with the first iteration of this tool - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/decoding-doom-pictures-transparency.png" alt="The fully transparent column in this sprite caused assorted issues with the first iteration of this tool" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/decoding-doom-pictures-custom-palette.png" class="gallery" title="The same image, this time with a different palette applied - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/decoding-doom-pictures-custom-palette.png" alt="The same image, this time with a different palette applied" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/decoding-doom-pictures-spider.png" class="gallery" title="Stuff of nightmares - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/decoding-doom-pictures-spider.png" alt="Stuff of nightmares" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/decoding-doom-pictures-cyborg.png" class="gallery" title="And more nightmares - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/decoding-doom-pictures-cyborg.png" alt="And more nightmares" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/decoding-doom-pictures-angry.png" class="gallery" title="I don't think pinky is very happy - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/decoding-doom-pictures-angry.png" alt="I don't think pinky is very happy" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/decoding-doom-pictures-doom2-title.png" class="gallery" title="DOOM II title screen - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/decoding-doom-pictures-doom2-title.png" alt="DOOM II title screen" decoding="async" loading="lazy" /></a></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/decoding-doom-picture-files .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comReading DOOM WAD Filesurn:uuid:d3b426c8-4073-46e7-a5f9-0deb5c3386152020-07-30T07:32:57Z2020-07-30T07:32:57Z<p>WAD &quot;Where's All the Data&quot; files used by DOOM and various other
games are simple containers, similar to zip and other archive
formats, without additional complexity (such as compression) and
data-centric rather than file. This article describes how to
read the WAD files used by DOOM, DOOM II, Rise of the Triad and
similar games of that era. Yes, I'm talking DOS and 1993, not
the more modern reboots.</p>
<p>The article only covers reading of a WAD and extracting its
contents, it does not cover the format of the individual data
within given that the data is application dependent. With that
said, I'll be covering the DOOM picture format in the next
article.</p>
<p>In 2018 I looked into the MIX format used by the Command &amp;
Conquer games which is very similar to WAD but for reasons I
don't recall I didn't end up writing a post about the format.
Recently I finished reading Jimmy Maher's excellent <a href="https://www.filfre.net/tag/doom/" rel="external nofollow noopener">series on
DOOM</a> and that reminded me I had wanted to look into WAD and
other container formats for my own future use. As I have been
completely unable to finish a single draft blog post I currently
have, I decided something fresh and new (to me anyway!) was a
good idea.</p>
<p>Although I don't normally plug other sites, Jimmy's blog <a href="https://www.filfre.net/" rel="external nofollow noopener">The
Digital Antiquarian</a> is a fantastic blog of the games of
yesteryear and I wish I could write half as well as him.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/wad-reader-triad.png" class="gallery" title="An example of reading a WAD file and examining one of the contained data lumps" ><img src="https://images.cyotek.com/image/thumbnail/devblog/wad-reader-triad.png" alt="An example of reading a WAD file and examining one of the contained data lumps" decoding="async" loading="lazy" /></a><figcaption>An example of reading a WAD file and examining one of the contained data lumps</figcaption></figure><h2 id="about-wad-formats">About WAD Formats</h2>
<p>There are various formats of WAD file available, each building
on the previous. This initial series of articles only covers the
original version first introduced in DOOM. At the time of
writing, I haven't looked the other versions but I plan to look
at some of them in future articles.</p>
<p>I have tested the code presented in this article with WAD files
from Shareware DOOM, ULTIMATE DOOM, DOOM II and Rise of the
Triad: Dark War.</p>
<h2 id="the-format">The Format</h2>
<p>The format is simple enough. There is a 12 byte header which
details the wad type, the number of lumps of data it contains,
and an offset where the directory index is located.</p>
<table>
<thead>
<tr>
<th>Range</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>0</code> - <code>3</code></td>
<td>Either the string <code>IWAD</code> or <code>PWAD</code></td>
</tr>
<tr>
<td><code>4</code> - <code>7</code></td>
<td>The number of entries in the directory</td>
</tr>
<tr>
<td><code>8</code> - <code>11</code></td>
<td>The location of the directory</td>
</tr>
</tbody>
</table>
<p>The directory index is comprised of (16 * number of lumps) bytes
which describe the lumps. Each 16 byte header details the size,
the position in the data and the lump name.</p>
<table>
<thead>
<tr>
<th>Range</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>0</code> - <code>3</code></td>
<td>The location of the lump</td>
</tr>
<tr>
<td><code>4</code> - <code>7</code></td>
<td>The size of the lump</td>
</tr>
<tr>
<td><code>8</code> - <code>15</code></td>
<td>The name of the lump, padded with <code>NUL</code> bytes</td>
</tr>
</tbody>
</table>
<p>As far as I know, the directory can be located anywhere in a WAD
file, or at least anywhere after the header. All of the WADs I
have examined have the directory at the end of the file which
makes perfect sense from a serialisation standpoint, but there's
no reason why it couldn't be elsewhere. The only rule is that
all elements in the directory index must be contiguous.</p>
<p>All integer values are in <a href="https://en.wikipedia.org/wiki/Endianness" rel="external nofollow noopener">little-endian</a> format.</p>
<h3 id="wad-types">WAD Types</h3>
<p>The first four bytes of the file header are either <code>IWAD</code> or
<code>PWAD</code>, and this denotes the type of the WAD. The <code>I</code> prefix
means this is an &quot;internal&quot; WAD, which is the main WAD for a
game. The <code>P</code> prefix denotes a &quot;patch&quot; WAD, which allows a WAD
to override the lumps from the main internal WAD, e.g. for
providing custom levels, skins or other data.</p>
<h2 id="reading-the-header">Reading the Header</h2>
<p>Reading the header is quite straightforward - first read in the
12 bytes into a buffer and define the WAD type based on the
first byte. Next, we extract 32bit integers from each set of 4
bytes in the remainder of the header that contain the number of
data lumps and then the start of the directory listing.</p>
<blockquote>
<p>Note: In the interests of clarity, parameter and data
validation have been omitted from the snippets in this
article.</p>
</blockquote>
<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">byte</span> _wadHeaderLength <span class="symbol">=</span> <span class="number">12</span><span class="symbol">;</span>
<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">byte</span> _lumpCountOffset <span class="symbol">=</span> <span class="number">4</span><span class="symbol">;</span>
<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">byte</span> _directoryStartOffset <span class="symbol">=</span> <span class="number">8</span><span class="symbol">;</span>

<span class="keyword">private</span> WadType _type<span class="symbol">;</span>
<span class="keyword">private</span> <span class="keyword">int</span> _lumpCount<span class="symbol">;</span>
<span class="keyword">private</span> <span class="keyword">int</span> _directoryStart<span class="symbol">;</span>

<span class="keyword">private</span> <span class="keyword">void</span> ReadWadHeader<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>

 buffer <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">byte</span><span class="symbol">[</span>_wadHeaderLength<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> _wadHeaderLength<span class="symbol">)</span><span class="symbol">;</span>

 _type <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;I&#39;</span> <span class="symbol">?</span> WadType<span class="symbol">.</span>Internal <span class="symbol">:</span> WadType<span class="symbol">.</span>Patch<span class="symbol">;</span>

 _lumpCount <span class="symbol">=</span> GetInt<span class="number">32</span>Le<span class="symbol">(</span>_buffer<span class="symbol">,</span> _lumpCountOffset<span class="symbol">)</span><span class="symbol">;</span>
 _directoryStart <span class="symbol">=</span> GetInt<span class="number">32</span>Le<span class="symbol">(</span>_buffer<span class="symbol">,</span> _directoryStartOffset<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">int</span> GetInt<span class="number">32</span>Le<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>
 <span class="keyword">return</span> buffer<span class="symbol">[</span>offset <span class="symbol">+</span> <span class="number">3</span><span class="symbol">]</span> <span class="symbol">&lt;&lt;</span> <span class="number">24</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">&lt;&lt;</span> <span class="number">16</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">&lt;&lt;</span> <span class="number">8</span> <span class="symbol">|</span> buffer<span class="symbol">[</span>offset<span class="symbol">]</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>You could use the <code>BitConverter.ToInt32</code> method, but then if
this code was ran on a big-endian system, the
<a href="https://docs.microsoft.com/en-us/dotnet/api/system.bitconverter" rel="external nofollow noopener">BitConverter</a> class would automatically reverse the bytes,
returning values that would be <em>very</em> wrong and so this set of
articles will use their own code which ignores the endian-ness
of the system and will always read and write as little-endian.</p>
</blockquote>
<h2 id="reading-the-directory">Reading the Directory</h2>
<p>Now that we know where the directory index is located in the
file, we can read out the individual lump details. As with the
WAD header, we declare a buffer big enough to fill the directory
header, then read in the bytes. Using the same <code>GetInt32Le</code>
method described earlier, we extract the size of the lump and
its position in the file.</p>
<p>Next, we find the real length of the lump name, by starting at
the end of the array and working back until we find a non-zero
value. Once we have this length we call
<code>Encoding.ASCII.GetString</code> to extract the name. Unfortunately,
if we called this API without defining the true length, the
returned string would include any<code>NUL</code> padding bytes.</p>
<figure class="lang-c# highlight"><figcaption><span>c#</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">byte</span> _directoryHeaderLength <span class="symbol">=</span> <span class="number">16</span><span class="symbol">;</span>
<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">byte</span> _lumpStartOffset <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">byte</span> _lumpSizeOffset <span class="symbol">=</span> <span class="number">4</span><span class="symbol">;</span>
<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">byte</span> _lumpNameOffset <span class="symbol">=</span> <span class="number">8</span><span class="symbol">;</span>

<span class="keyword">private</span> <span class="keyword">void</span> LoadDirectory<span class="symbol">(</span>Stream stream<span class="symbol">,</span> <span class="keyword">int</span> lumpCount<span class="symbol">,</span> <span class="keyword">int</span> directoryStart<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>

 stream<span class="symbol">.</span>Seek<span class="symbol">(</span>directoryStart<span class="symbol">,</span> SeekOrigin<span class="symbol">.</span>Begin<span class="symbol">)</span><span class="symbol">;</span>

 buffer <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">byte</span><span class="symbol">[</span>_directoryHeaderLength<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> lumpCount<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> offset<span class="symbol">;</span>
 <span class="keyword">int</span> size<span class="symbol">;</span>
 <span class="keyword">string</span> name<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> _directoryHeaderLength<span class="symbol">)</span><span class="symbol">;</span>

 offset <span class="symbol">=</span> GetInt<span class="number">32</span>Le<span class="symbol">(</span>buffer<span class="symbol">,</span> _lumpStartOffset<span class="symbol">)</span><span class="symbol">;</span>
 size <span class="symbol">=</span> GetInt<span class="number">32</span>Le<span class="symbol">(</span>buffer<span class="symbol">,</span> _lumpSizeOffset<span class="symbol">)</span><span class="symbol">;</span>
 name <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetSafeLumpName<span class="symbol">(</span>buffer<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// Do something with the 3 values</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">string</span> GetSafeLumpName<span class="symbol">(</span><span class="keyword">byte</span><span class="symbol">[</span><span class="symbol">]</span> buffer<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="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> _directoryHeaderLength<span class="symbol">;</span> i <span class="symbol">&gt;</span> _lumpNameOffset<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>entry<span class="symbol">[</span>i <span class="symbol">-</span> <span class="number">1</span><span class="symbol">]</span> <span class="symbol">!=</span> <span class="string">&#39;\0&#39;</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 length <span class="symbol">=</span> i <span class="symbol">-</span> _lumpNameOffset<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> length <span class="symbol">&gt;</span> <span class="number">0</span>
 <span class="symbol">?</span> Encoding<span class="symbol">.</span>ASCII<span class="symbol">.</span>GetString<span class="symbol">(</span>entry<span class="symbol">,</span> _lumpNameOffset<span class="symbol">,</span> length<span class="symbol">)</span>
 <span class="symbol">:</span> <span class="keyword">null</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="about-names-and-empty-data">About Names and Empty Data</h3>
<p>Lump names may not be unique and can appear multiple times. For
example, every DOOM map that I've looked at so far has a lump
named <code>THINGS</code>, another named <code>LINEDEFS</code> and several more.</p>
<p>As a result, DOOM seems to make use of a uniquely named lump
(e.g. <code>E1M1</code>) that serve no purpose other than to be a bookmark
to a contiguous set of lumps that make up a feature (and
sometimes another placeholder at the end if the lumps are
dynamic). For placeholders, the lump size is set to zero, and
the lump offset is either set to the offset of the next valid
lump or again zero. This also means that, depending on the
application using the WAD, lump order is important.</p>
<h2 id="reading-lump-data">Reading Lump Data</h2>
<p>To read the actual data for a given lump, we would set the
<code>Position</code> of our backing <code>Stream</code> to the lump offset and then
only read data up to the length of the lump.</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>
 <span class="keyword">using</span> <span class="symbol">(</span>WadReader reader <span class="symbol">=</span> <span class="keyword">new</span> WadReader<span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 WadLump lump<span class="symbol">;</span>

 <span class="keyword">while</span> <span class="symbol">(</span><span class="symbol">(</span>lump <span class="symbol">=</span> reader<span class="symbol">.</span>GetNextLump<span class="symbol">(</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>
 <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>lump<span class="symbol">.</span>Size<span class="symbol">]</span><span class="symbol">;</span>

 stream<span class="symbol">.</span>Position <span class="symbol">=</span> lump<span class="symbol">.</span>Offset<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="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
</pre>
</figure>
<p>This sounds error prone and means you have to know this
information up front instead of being able to pass a <code>Stream</code> to
another method. So for this case, I created an <code>OffsetStream</code>
class which basically acts as a window into another stream
without being to read data it shouldn't or the caller needing to
explicitly know about source boundaries.</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> OffsetStream <span class="symbol">:</span> Stream
<span class="symbol">{</span>
 <span class="keyword">private</span> <span class="keyword">readonly</span> <span class="keyword">int</span> _length<span class="symbol">;</span>
 <span class="keyword">private</span> <span class="keyword">readonly</span> <span class="keyword">int</span> _start<span class="symbol">;</span>
 <span class="keyword">private</span> <span class="keyword">readonly</span> Stream _stream<span class="symbol">;</span>
 <span class="keyword">private</span> <span class="keyword">long</span> _position<span class="symbol">;</span>

 <span class="keyword">public</span> OffsetStream<span class="symbol">(</span>Stream source<span class="symbol">,</span> <span class="keyword">int</span> start<span class="symbol">,</span> <span class="keyword">int</span> length<span class="symbol">)</span>
 <span class="symbol">{</span>
 _stream <span class="symbol">=</span> source<span class="symbol">;</span>
 _start <span class="symbol">=</span> start<span class="symbol">;</span>
 _length <span class="symbol">=</span> length<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">bool</span> CanRead
 <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="keyword">public</span> <span class="keyword">override</span> <span class="keyword">bool</span> CanSeek
 <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="keyword">public</span> <span class="keyword">override</span> <span class="keyword">bool</span> CanWrite
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</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">override</span> <span class="keyword">long</span> Length
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _length<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">long</span> Position
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _position<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> value <span class="symbol">&gt;</span> _length<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentOutOfRangeException<span class="symbol">(</span>nameof<span class="symbol">(</span>value<span class="symbol">)</span><span class="symbol">,</span> value<span class="symbol">,</span> <span class="string">&quot;Value outside of stream range.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 _position <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">override</span> <span class="keyword">int</span> Read<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="keyword">int</span> count<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_position <span class="symbol">+</span> count <span class="symbol">&gt;</span> _length<span class="symbol">)</span>
 <span class="symbol">{</span>
 count <span class="symbol">=</span> _length <span class="symbol">-</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>_position<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>count <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _stream<span class="symbol">.</span>Position <span class="symbol">=</span> _start <span class="symbol">+</span> _position<span class="symbol">;</span>
 _stream<span class="symbol">.</span>Read<span class="symbol">(</span>buffer<span class="symbol">,</span> offset<span class="symbol">,</span> count<span class="symbol">)</span><span class="symbol">;</span>
 _position <span class="symbol">+=</span> count<span class="symbol">;</span>
 <span class="symbol">}</span>

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

 <span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">long</span> Seek<span class="symbol">(</span><span class="keyword">long</span> offset<span class="symbol">,</span> SeekOrigin origin<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">long</span> value<span class="symbol">;</span>

 <span class="keyword">switch</span> <span class="symbol">(</span>origin<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> SeekOrigin<span class="symbol">.</span>Begin<span class="symbol">:</span>
 value <span class="symbol">=</span> offset<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>

 <span class="keyword">case</span> SeekOrigin<span class="symbol">.</span>Current<span class="symbol">:</span>
 value <span class="symbol">=</span> _position <span class="symbol">+</span> offset<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>

 <span class="keyword">case</span> SeekOrigin<span class="symbol">.</span>End<span class="symbol">:</span>
 value <span class="symbol">=</span> _length <span class="symbol">-</span> offset<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>nameof<span class="symbol">(</span>origin<span class="symbol">)</span><span class="symbol">,</span> origin<span class="symbol">,</span> <span class="string">&quot;Invalid origin value.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">this</span><span class="symbol">.</span>Position <span class="symbol">=</span> value<span class="symbol">;</span>

 <span class="keyword">return</span> value<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>With this class in place, I can now get a <code>Stream</code> that only
provides access to the a specific lumps data with a call similar
to the below.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> Stream GetInputStream<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> <span class="keyword">new</span> OffsetStream<span class="symbol">(</span>_container<span class="symbol">,</span> _offset<span class="symbol">,</span> _size<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>I can then dispose of this stream or pass it to another method
(for example <code>ImageFile.FromStream</code>) without needing to know or
care that this is part of something bigger or affecting that.</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>lump <span class="symbol">=</span> reader<span class="symbol">.</span>GetNextLump<span class="symbol">(</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>
 Image image <span class="symbol">=</span> Image<span class="symbol">.</span>FromStream<span class="symbol">(</span>lump<span class="symbol">.</span>GetInputStream<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="putting-it-all-together">Putting it all together</h2>
<p>For this example, I created the <code>WadReader</code> class, which is a
forward reading class for quickly enumerating the contents of a
WAD. I also added a <code>WadFile</code> class which will load all the lump
meta data into a collection for further use.</p>
<h3 id="using-the-wadreader">Using the WadReader</h3>
<p>The <code>WadReader</code> is designed for quickly enumerating the contents
of a WAD. It maintains enough state to know where it is in the
WAD, but nothing else, expecting the consumer to take care of
storing whatever information is required. This would be useful,
for example, if you wanted to pull out one or more lumps for
load on demand.</p>
<p>The <code>WadReader</code> class exposes a <code>Type</code> and <code>Count</code> property, and
a <code>GetNextLump</code> method which can be used to enumerate.
<code>GetNextLump</code> will return a valid object as long as there are
items remaining, and <code>null</code> once it reaches the end of the file.</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> WriteWadInfo<span class="symbol">(</span><span class="keyword">string</span> fileName<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>WadReader reader <span class="symbol">=</span> <span class="keyword">new</span> WadReader<span class="symbol">(</span>stream<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 WadLump lump<span class="symbol">;</span>

 Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span><span class="string">&quot;File: {0}&quot;</span><span class="symbol">,</span> fileName<span class="symbol">)</span><span class="symbol">;</span>
 Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span><span class="string">&quot;Type: {0}&quot;</span><span class="symbol">,</span> reader<span class="symbol">.</span>Type<span class="symbol">)</span><span class="symbol">;</span>
 Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span><span class="string">&quot;Lump Count: {0}&quot;</span><span class="symbol">,</span> reader<span class="symbol">.</span>Count<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">while</span> <span class="symbol">(</span><span class="symbol">(</span>lump <span class="symbol">=</span> reader<span class="symbol">.</span>GetNextLump<span class="symbol">(</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>
 Console<span class="symbol">.</span>WriteLine<span class="symbol">(</span><span class="string">&quot;{0}: Offset {1}, Size {2}&quot;</span><span class="symbol">,</span> lump<span class="symbol">.</span>Name<span class="symbol">,</span> lump<span class="symbol">.</span>Offset<span class="symbol">,</span> lump<span class="symbol">.</span>Size<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// stream.Position is also automatically set to the</span>
 <span class="comment">// start of the lump data, allowing me to do</span>
 <span class="comment">// stream.Read if required, or call lump.GetInputStream()</span>
 <span class="comment">// to get a stream to pass to other methods</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="using-the-wadfile-class">Using the WadFile class</h3>
<p>The <code>WadFile</code> class loads all the lumps (but not the actual
data) into a collection so that it is always available. You can
then pull out lump data at any point without having to re-read
the directory and provides convenience methods for more easily
pulling out WAD data. It isn't as efficient as <code>WadReader</code>, but
easier to use. It also supports write operations whilst
<code>WadReader</code> does not.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> FillItems<span class="symbol">(</span><span class="keyword">string</span> fileName<span class="symbol">)</span>
<span class="symbol">{</span>
 WadFile wadFile<span class="symbol">;</span>

 wadFile <span class="symbol">=</span> WadFile<span class="symbol">.</span>LoadFrom<span class="symbol">(</span>fileName<span class="symbol">)</span><span class="symbol">;</span>

 namesListBox<span class="symbol">.</span>BeginUpdate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 namesListBox<span class="symbol">.</span>Items<span class="symbol">.</span>Clear<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 namesListBox<span class="symbol">.</span>Sorted <span class="symbol">=</span> <span class="keyword">false</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> wadFile<span class="symbol">.</span>Lumps<span class="symbol">.</span>Count<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 namesListBox<span class="symbol">.</span>Items<span class="symbol">.</span>Add<span class="symbol">(</span>wadFile<span class="symbol">.</span>Lumps<span class="symbol">[</span>i<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>_useNameSort<span class="symbol">)</span>
 <span class="symbol">{</span>
 namesListBox<span class="symbol">.</span>Sorted <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 namesListBox<span class="symbol">.</span>EndUpdate<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="wheres-all-the-source-code">Where's All The Source Code</h2>
<p>There is no single download available for this sample as rather
than doing a simple demo as I do for most blog posts, it is a
slightly more complex solution covering reading, writing and
various other features too. The full project is available from
our <a href="https://github.com/cyotek/WadDemo" rel="external nofollow noopener">GitHub</a> page.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>The WAD format has no real features and so is simple to read and
write. The linked GitHub page includes a demonstration program
which allows WAD files to be opened and contents extracted.</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-doom-wad-files .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comA review of the Argon ONE Raspberry Pi 4 Caseurn:uuid:9214cff7-61d9-4182-be79-201fe2965a3e2020-08-17T09:07:55Z2020-06-16T07:48:15Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ag1-completed-unit.jpg" class="gallery" title="The assembled case" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ag1-completed-unit.jpg" alt="The assembled case" decoding="async" loading="lazy" /></a><figcaption>The assembled case</figcaption></figure>
<blockquote>
<p>This is my own review using hardware I researched and bought
myself. There are no affiliated links in this article.</p>
</blockquote>
<p>When the Raspberry Pi was originally announced in 2012 I rapidly
pre-ordered the Model B and tried my best to use it as a normal
computer. Regretfully, the hardware was just not suitable - I
couldn't even use it for the most basic of web-browsing, let
alone any work. I tried again when the Pi 2 was released and was
just as disappointed by that. With that said, 8 years later that
original Model B is still in use - I have multiple Pi's running
specific applications (network security, media centre, <a href="/post/installing-mopidy-and-phat-beat-on-a-raspberry-pi">music
player</a> and retro console). But I never quite gave up on
having a tiny <em>viable</em> Linux desktop computer.</p>
<p>Although I mostly ignored the Raspberry Pi 4 on release, I
recently decided to get one and see if I could get the
experience I wanted. First though, I wanted to look into cases
as one of the real annoyances I have with the Raspberry Pi is
the lack of a power button for headless devices. While looking
through the list of cases on <a href="https://thepihut.com/collections/raspberry-pi-cases" rel="external nofollow noopener">The Pi Hut</a> I saw the <a href="https://thepihut.com/collections/raspberry-pi-cases/products/argon-one-raspberry-pi-4-case" rel="external nofollow noopener">Argon
ONE Raspberry Pi 4 case</a> which looked very appealing at first
glance and even more so with the feature list</p>
<ul>
<li>Multi function power button (reboot, safe shut down, shut down)</li>
<li>Aluminium case acts as a heat sink for the Raspberry Pi</li>
<li>Built in fan</li>
<li>Re-routes HDMI, audio jack and USB-C power connector to the
back of the case with the rest of the ports</li>
<li>Provides easy access to colour coded and labelled GPIO ports
via a magnetic cover</li>
</ul>
<p>Naturally, the case costs more than many other cases (£25 at
time of writing) but the feature set would seem to justify it.</p>
<p>Unfortunately (or so I thought) the case was out of stock, so I
added an in-stock notification and got on with other things. As
it turned out, I was somewhat lucky as in the interim an <a href="https://www.raspberrypi.org/blog/8gb-raspberry-pi-4-on-sale-now-at-75/" rel="external nofollow noopener">8GB
Raspberry Pi</a> was released and so I bought the newer model
rather than the 4GB I had originally planned.</p>
<h2 id="unpacking">Unpacking</h2>
<p>On unpacking the case, I was pleasantly surprised. It is double
the width of a normal Pi due to the expansion board, and
probably to make sure the LED's are centred. Although the base
is made of plastic, the bulk of it is aluminium which has a nice
&quot;premium&quot; feel to it. The only annoyance I found was a tiny nick
or flaw on the magnetic cover which I can feel if I run my
fingers over it, but which is scarcely visible. It was also
gratifying to note the case is bare - no manufacturers logos,
&quot;go faster stripes&quot;, or anything else that would mar the clean
appearance.</p>
<p>There are a pair of grills on the top of the case, and another
on the plastic underside. There are also small rubber feet to
lift the case from the surface and stop it slipping as readily.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ag1-box-contents.jpg" class="gallery" title="The contents of the box" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ag1-box-contents.jpg" alt="The contents of the box" decoding="async" loading="lazy" /></a><figcaption>The contents of the box</figcaption></figure>
<p>Note the two protrusions on the right hand side of the fan -
with the application of two provided thermal pads, these will be
directly touching the chips on the Pi to draw off heat.</p>
<p>Also note the round circle of metal in the upper right - this a
a magnet. There is also one on the left hand side underneath the
pre-fitted board as I found out the hard way.</p>
<p>Inside the box I found</p>
<ul>
<li>An instruction book</li>
<li>The aluminium case upper with a pre-assembled board containing
the fan, power button and GPIO connections</li>
<li>The plastic base for the case, ever so slightly transparent to
allow the Pi's LEDs to shine through</li>
<li>An expansion board to plug into the Pi's micro-HDMI ports and
audio jack</li>
<li>Two silicon thermal pads for placing between the case and the
Pi for maximum contact</li>
<li>Screws for attaching the Pi + board to the aluminium body</li>
<li>Screws for attaching the two case parts together</li>
<li>Rubber feet</li>
</ul>
<h2 id="hardware-installation">Hardware Installation</h2>
<p>For my first test, I opted not to install the thermal pads as I
assumed these were sticky pads such and I didn't want to do
anything permanent until I was happy everything was working.</p>
<p>Pushing the expansion boards into the sockets running along the
edge of the Pi was a tight fit - I was really worried about
breaking something.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ag1-pi-and-expansion-board.jpg" class="gallery" title="A slightly out of focus shot of the Raspberry Pi attached to the expansion board" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ag1-pi-and-expansion-board.jpg" alt="A slightly out of focus shot of the Raspberry Pi attached to the expansion board" decoding="async" loading="lazy" /></a><figcaption>A slightly out of focus shot of the Raspberry Pi attached to the expansion board</figcaption></figure>
<p>A not very well focused photo of the Pi joined to the expansion
board. Note the ribbon connectors for display and camera <del>- you
won't be able to use these with this case</del>. Update: Commentor <a href="#comment31600">Brian</a> notes that you can slide the ribbon cables up through a slot underneath the magnetic cover (you can actually see the slot in the last photo of the article, bit I hadn't joined the dots).</p>
<p>Pushing the combined boards into the case itself was an even
tighter fit. I was less worried about breaking anything this
time than by the concern I wouldn't be able to pull it apart
again. Even though I hadn't applied the pads I screwed
everything together.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ag1-inside-with-pi.jpg" class="gallery" title="The Pi snuggly fitted into the case" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ag1-inside-with-pi.jpg" alt="The Pi snuggly fitted into the case" decoding="async" loading="lazy" /></a><figcaption>The Pi snuggly fitted into the case</figcaption></figure>
<p>The SD card slides in through a tiny gap at the bottom of the
case, meaning you can swap cards without having to dismantle it.
Unlike my official cases though, the gap is just the right size;
I've lost count of the number of times I've tried to put a card
into my Pi 2 and it slides inside the case instead of into the
reader slot.</p>
<p>On holding down the power button, I was pleased when my Pi
started to boot. I was less pleased with the <em>noise</em> coming from
the fan. It is a horrible buzzing / whiny sound. As soon as the
Pi powers on the fan switches on for a few seconds until the
boot sequence starts. Similar I suppose to switching on a rack
mounted server - those things sound like jet engines when they
start up. Fortunately the Argon fan isn't as bad but it is still
unpleasant.</p>
<p>When the Pi booted to the desktop I could see the temperature of
the Pi and it rapidly rose to 55°C and the fan kicked in again.
Still just as horribly noisy. I also noted that the case itself
was getting quite hot.</p>
<p>However, the Pi was clearly working as I had audio and video,
and so I decided to shut it down, dissemble it and apply the
thermal pads I'd originally skipped.</p>
<p>Taking it apart again was problematic for two reasons. Firstly,
when I was removing one of the screws it fell into the case and
a severe shaking wouldn't dislodge it. Secondly, as I feared the
Pi was strongly wedged into the case and was difficult to
remove.</p>
<p>Once I'd removed my Pi and the expansion board, I still couldn't
find the errant screw so I had to take off the pre-attached
board containing the fan. After this, I found the screw firmly
stuck to a magnet. I have <em>no</em> idea how the screw managed to get
underneath the board into that corner!</p>
<p>With the fan board reassembled, I went to apply the two pads. I
don't know if it matters or not, but neither are the right size
for those protrusions. In addition, they aren't sticky pads as I
thought, but almost like a firmer version of thermal paste. They
had a piece of clear plastic on each side and those were quite a
pain to get off without damaging the pad.</p>
<p>After reassembling the case and booting the Pi up, the fan no
longer screamed (except for the initial power on) and the
temperature was 30°C (rising to just under 40°C after a few
minutes).</p>
<p>Given the tight fit of both the expansion board and then the Pi
into the case (not to mention the thermal pads), I don't think
I'd recommend you pulling this apart again unless you really had
to.</p>
<h2 id="software-installation">Software Installation</h2>
<p>In order for the power button to properly do its job, you do
need to install a software component. The software also sets
the parameters from when the fan kicks in at 10%, 55% or 100%
power.</p>
<p>To save me having to dig out the instruction book each time I
flash a new card, these are the installation instructions</p>
<figure class="lang-bash highlight"><figcaption><span>bash</span></figcaption><pre class="code">
curl https://download.argon40.com/argon1.sh | bash
</pre>
</figure>
<p>The script will install some standard Python libraries and then
create some Python scripts for handling the shutdown and fan
options and for configuring the fans or uninstalling.</p>
<p>The configuration script (<code>argonone-config</code>) is straight forward
enough to use, and you can either change it so the fan is always
running, customise the 3 predefined points, or provide your own
temperature and fan speed pairs.</p>
<p>As far as I can tell, this is vanilla Python - nothing bespoke
or relying on the Argon servers still being around in future
(assuming you have a copy of the installation script and don't
mind missing shortcut images).</p>
<p>One important note - I tested this software on the 32bit
Raspberry Pi OS and also the beta 64bit version without issue. I
also tried it on an Ubuntu image I was testing but it wouldn't
install due to being unable to install some of the Python
libraries. As I was having performance issues with Ubuntu, I
ditched the image and so didn't do any further testing.</p>
<h2 id="temperature-notes">Temperature Notes</h2>
<p>I don't have any stressing utilities for the Pi and none of the
recommended ones (e.g. Stressberry) would install in the beta
64bit Raspberry Pi OS I'm currently using and so I haven't done
any dedicated heat tests. And besides, I don't have a cool
camera that can take thermal pictures.</p>
<p>I can say that when doing various tasks (including regular
browsing, watching video with VLC player, doing (or attempting
to do) a bit of .NET development with Visual Studio Code, Rider
or MonoDevelop) the temperature never went above 50°C and mostly
hovered in the low 40's. As I write this post on my Windows
desktop, the CPU is running at 15°C and the motherboard at 34°C
and that involves a (low-end) water cooler and multiple fans on
the front, rear and top of the case. (And yes, the simple hum of
these is much more bearable than the shrieking whine of the
Argon).</p>
<p>The Model B I noted in my preamble has a couple of small
heat sinks I added but it always runs at between 42 and 45°C.</p>
<h2 id="pros">Pro's</h2>
<ul>
<li>Very sleek looking, if you like that retro-vibe (and I do!)</li>
<li>Power button that can be used to reboot, safe shutdown or cut
power depending on how you press or for how long</li>
<li>Internal re-routing means all external connections go into the
back of the case</li>
<li>Easy access to GPIO via magnetic cover</li>
<li>Ribbon cables can be routed out via slot next to the GPIO connectors</li>
<li>Can access the SD card without having to dismantle the case</li>
<li>Case acts as a heat sink to draw heat away from the Pi</li>
<li>Built in fan</li>
<li>LED's are still visible</li>
<li>No logo anything else to mar the appearance</li>
<li>Sturdy feel, well built</li>
</ul>
<h2 id="cons">Con's</h2>
<ul>
<li>Due to the case acting as a heat sink, the case can get very
hot</li>
<li>The noise the fan makes is unpleasant / unbearable</li>
<li>In order to make effective use of the power button and fan,
you need to install a service. This works out of the box on
Raspbian / Raspberry Pi OS, but not on Ubuntu</li>
<li><del>No access to ribbon connectors</del></li>
<li>I'd prefer a single press to shutdown instead of having to
hold it down for several seconds (but if this bothers me
enough I could modify the python scripts)</li>
<li>Probably not recommended to pull this apart once assembled, so make sure if you are using ribbon cables, you attach these first</li>
</ul>
<h2 id="closing-thoughts">Closing Thoughts</h2>
<p>I really like this case, it is certainly the best one I have
bought so far (which in fairness is a limited selection, two
official cases, one bare-bones for a Zero and a badly fitting
one for the Model B). If there was a version for the Raspberry
Pi 2, I'd happily replace my existing case.</p>
<p>The list of pro's far outweigh the cons, although the fact that
the metal case gets very hot is probably a significant con,
especially if Pi is running hot enough to switch on the fan.</p>
<p>The fan itself is incredibly frustrating - I manually edited the
config to run it at 5% as a test and the buzz/whine is so
annoying I can't contemplate having it running. I don't know if
I have a faulty fan or if this is just the way it is.</p>
<p>Although I'm still not quite there yet with having a tiny Linux
computer, I'm getting closer. Perhaps the Raspberry Pi 5 will be
&quot;the one&quot;, although as most of the problems I'm having with this
generation aren't speed related per se but the incompatibility
of software, maybe vendors such as JetBrains and Microsoft will
pay more attention to their partially-working &quot;cross platform&quot;
products in the future.</p>
<h2 id="more-images">More Images</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ag1-front.jpg" class="gallery" title="The front of the case, showing the SD card access" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ag1-front.jpg" alt="The front of the case, showing the SD card access" decoding="async" loading="lazy" /></a><figcaption>The front of the case, showing the SD card access</figcaption></figure>
<p>The front of the case, showing the SD card access. The large
circles on the base are where the rubber feet stick (I hadn't
applied that the point of taking this photo), the smaller
circles on the edge are the screw holes.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ag1-rear.jpg" class="gallery" title="The rear of the case with all the ports on one side" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ag1-rear.jpg" alt="The rear of the case with all the ports on one side" decoding="async" loading="lazy" /></a><figcaption>The rear of the case with all the ports on one side</figcaption></figure>
<p>The rear of the case, showing the USB and Ethernet port of the
Pi itself, plus the re-routed micro-HDMI, audio jack and USB-C
power. Plus that all important power button!</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ag1-rear-with-cables.jpg" class="gallery" title="Another rear shot, this time with cables attached" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ag1-rear-with-cables.jpg" alt="Another rear shot, this time with cables attached" decoding="async" loading="lazy" /></a><figcaption>Another rear shot, this time with cables attached</figcaption></figure>
<p>The rear of the case again, this time populated with assorted
cables.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ag1-cover-removed.jpg" class="gallery" title="The case with the magnetic cover removed exposing the GPIO ports" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ag1-cover-removed.jpg" alt="The case with the magnetic cover removed exposing the GPIO ports" decoding="async" loading="lazy" /></a><figcaption>The case with the magnetic cover removed exposing the GPIO ports</figcaption></figure>
<p>The case with the magnetic cover removed exposing the GPIO
ports and also a slot for routing ribbon cables. Notice how the the ports are colour coded and have a pin diagram.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ag1-base-with-feet.jpg" class="gallery" title="The base of the case with the feet attached and SD card" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ag1-base-with-feet.jpg" alt="The base of the case with the feet attached and SD card" decoding="async" loading="lazy" /></a><figcaption>The base of the case with the feet attached and SD card</figcaption></figure>
<p>This image shows the base of the case with the feet attached, along with an inserted SD card and was taken after two months of use.</p>
<h2 id="updates">Updates</h2>
<ul>
<li>16Jun2020 - Initial posting</li>
<li>12Aug2020 - Updated to note ribbon cable access</li>
<li>17Aug2020 - Added photo of base with feet attached</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/a-review-of-the-argon-one-raspberry-pi-4-case .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comInstalling Mopidy and pHAT Beat on a Raspberry Piurn:uuid:9f81d53c-dcad-412a-83ec-0995f5237ab12020-06-16T07:43:23Z2020-04-10T14:15:41Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/phatbeat-pi-zero-uncovered.jpg" class="gallery" title="A Raspberry Pi Zero W with the pHAT Beat audio HAT attached and wired to old-school hi-fi speakers" ><img src="https://images.cyotek.com/image/thumbnail/devblog/phatbeat-pi-zero-uncovered.jpg" alt="A Raspberry Pi Zero W with the pHAT Beat audio HAT attached and wired to old-school hi-fi speakers" decoding="async" loading="lazy" /></a><figcaption>A Raspberry Pi Zero W with the pHAT Beat audio HAT attached and wired to old-school hi-fi speakers</figcaption></figure>
<p>I've been running Mopidy for a few weeks now on a Raspberry Pi 3
Model A using a spare set of computer speakers. As I also have a
spare Raspberry Pi Zero W, I decided to buy a HAT for this that
would allow me to use a set of proper hi-fi speakers that were
powered by the Pi instead of external power.</p>
<p>This article describes how to set up the Mopidy music server
with selected extensions using the <a href="https://shop.pimoroni.com/products/phat-beat" rel="external nofollow noopener">Pimoroni pHAT Beat</a>.
However, you could follow most of this guide to create a Mopidy
music server using another model of Raspberry Pi via the built
in audio chip and 3.5mm audio jack. In this case, just ignore
the instructions for installing the pHAT Beat software and
follow everything else.</p>
<h2 id="required-hardware">Required Hardware</h2>
<p>For my setup, I used the equipment below. The links point to
<a href="https://thepihut.com/" rel="external nofollow noopener">The Pi Hut</a> but if you aren't based in the UK you could try
<a href="https://www.adafruit.com/" rel="external nofollow noopener">Adafruit</a> or your own electronics retailer.</p>
<ul>
<li><a href="https://thepihut.com/products/raspberry-pi-zero-w" rel="external nofollow noopener">A Raspberry Pi Zero W</a>. Mine was an older model 1.1
version and was bare</li>
<li>GPIO headers (both male and female, male to fit to the bare
board and female for connecting to the HAT). As I don't
currently have a soldering iron, I choose to use <a href="https://thepihut.com/products/gpio-hammer-header-solderless?variant=28950933073" rel="external nofollow noopener">this kit</a>
that included solderless versions of both headers along with a
nifty installation jig for hammering them into the boards
without breaking anything</li>
<li><a href="https://thepihut.com/products/phat-beat" rel="external nofollow noopener">pHAT Beat</a> audio board. This has two pairs of connectors
for wiring a pair of speakers, two rows of 8 leds and some
hardware buttons</li>
<li>Speakers. I picked up a pair of 40W 8Ω speakers from eBay
(TEAC LS-X8 for the curious). I naively assumed any speakers
would work but this isn't correct</li>
<li>Speaker wire. Again, I didn't really do my research and just
grabbed a coil of 1.5mm pure copper speaker wire, which does
work but is probably overkill</li>
<li>A 64GB class 10 U1 A1 SD card</li>
<li>A monitor for at least the initial set up</li>
<li>A keyboard for at least the initial set up</li>
</ul>
<h2 id="preparing-the-operating-system">Preparing the operating system</h2>
<p>I'm going to assume that if you're using a Raspberry Pi you
already know how to flash operating systems onto the SD card, so
I won't provide instructions. Previously I used <a href="https://rufus.ie/" rel="external nofollow noopener">Rufus</a> for
this sort of task, but the recently introduced <a href="https://www.raspberrypi.org/blog/raspberry-pi-imager-imaging-utility/" rel="external nofollow noopener">Raspberry Pi
Imager</a> could scarcely make this easier if it tried and is my
new go-to tool for imaging Raspberry Pi cards. As I don't need a
desktop environment for this project, I installed Raspbian
Buster Lite.</p>
<p>This guide is assuming you are starting from a completely fresh
setup.</p>
<h2 id="caveat-emptor">Caveat Emptor</h2>
<p>As is sometimes the case, this guide comes with a caveat or two.</p>
<p>Firstly, I'm not a Linux expert. I know enough to be dangerous
as they say. So I may not be using the most efficient commands
or may not be following best practices. Suggestions on
improvements are definitely welcome!</p>
<p>Secondly, I know even less about electronics that I do about
Linux. My level is basically plugging LED's and resistors into a
breadboard and getting overly excited when they light up.
Therefore I am not making any recommendations on what speakers
are compatible or what type of speaker wire to use... it's more
than likely I'm using the wrong ones now, even though so far
everything has been running fine.</p>
<p>Finally, this being Linux all the software I will be installing
is open source by various individuals. At the time of writing
(April 4th 2020) this guide works fine (I have done a fresh run
run from start to end four times during the course of writing
this article) but that is no guarantee it will work in a months
time. On the bright side, at least it isn't using Node so more
than likely it <em>will</em> work in a months time.</p>
<h2 id="installation">Installation</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/phatbeat-pi-zero-first-test.jpg" class="gallery" title="The first test of pHAT Beat on a Raspberry Pi Model 3 A" ><img src="https://images.cyotek.com/image/thumbnail/devblog/phatbeat-pi-zero-first-test.jpg" alt="The first test of pHAT Beat on a Raspberry Pi Model 3 A" decoding="async" loading="lazy" /></a><figcaption>The first test of pHAT Beat on a Raspberry Pi Model 3 A</figcaption></figure><h3 id="first-steps">First Steps</h3>
<ul>
<li>Connect the pHAT to the Pi</li>
<li>Connect a keyboard and monitor to the Pi</li>
<li>Insert the flash card</li>
<li>Power up the Pi and wait for initial setup to complete (this
may involve an automatic reboot) and for a login prompt to
appear</li>
<li>Log in. For Raspbian, the default credentials is user <code>pi</code> and
password <code>raspberry</code></li>
</ul>
<h3 id="optional-changing-the-password">(Optional) Changing the password</h3>
<ul>
<li>Execute <code>passwd</code> and follow the prompts to change the password
of the <code>pi</code> user.</li>
</ul>
<h3 id="changing-the-memory-split">Changing the memory split</h3>
<p>As this is fully headless, there's no point allocating precious
memory to the GPU, so the first thing we'll do is reduce this to
the minimum value (16MB).</p>
<ul>
<li>Execute <code>sudo nano /boot/config.txt</code></li>
<li>Add the line <code>gpu_mem=16</code> to the start of the file</li>
<li>Press <code>Control+X</code>, then <code>y</code> and press <code>Enter</code> to save the
changes and exit Nano (this guide requires editing a few files
but I won't be repeating this line)</li>
</ul>
<h3 id="connecting-to-wireless">Connecting to wireless</h3>
<p>We'll be controlling music playback using a web browser, plus it
is much more convenient to manage a Raspberry Pi remotely using
SSH rather than having to plug monitors and keyboards in, so the
next step is to configure the wireless adapter.</p>
<ul>
<li>Execute <code>sudo nano /etc/wpa_supplicant/wpa_supplicant.conf</code>
(you can press <code>tab</code> while entering filenames and the shell
will try to autocomplete them for you)</li>
<li>Add the line <code>country=GB</code>, replacing <code>GB</code> with the <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements" rel="external nofollow noopener">ISO
code</a> of your own country as appropriate</li>
<li>Add at least one network definition similar to the template
below</li>
</ul>
<figure class="lang-ini highlight"><figcaption><span>ini</span></figcaption><pre class="code">
network <span class="symbol">=</span> <span class="symbol">{</span>
 ssid<span class="symbol">=</span><span class="string">&quot;&lt;name&gt;&quot;</span>
 psk<span class="symbol">=</span><span class="string">&quot;&lt;password&gt;&quot;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="optional-enabling-ssh">(Optional) Enabling ssh</h3>
<p>The Raspberry Pi boot loader will automatically enable SSH if a
file named <code>/boot/ssh</code> is present. We can use <code>touch</code> to create
this file. SSH makes it easy to remote onto your Pi for
maintenance tasks, but if you always intend to plug into a
keyboard and monitor then you don't need to do this step.</p>
<ul>
<li>Execute <code>sudo touch /boot/ssh</code></li>
</ul>
<h3 id="optional-changing-the-host-name">(Optional) Changing the host name</h3>
<p>The default host name is <code>raspberrypi</code>. If you have multiple
Raspberry Pi's, you definitely want to change the host name and
I recommend you give it a custom name regardless.</p>
<ul>
<li>Execute <code>sudo nano /etc/hostname</code></li>
<li>Delete the existing value and enter your own using letters
a-z, digits 0-9, or a dash</li>
</ul>
<h3 id="reboot">Reboot</h3>
<p>The changes made to enable SSH (and if you changed your host
name) won't take effect until the Pi is rebooted.</p>
<ul>
<li>Execute <code>sudo reboot</code> to reboot</li>
</ul>
<p>When the Raspberry Pi reboots, just before the login prompt is
displayed, check for a message stating <strong>My IP address is
x.x.x.x</strong>. If this message doesn't appear, your Pi didn't
connect to the wireless network successfully.</p>
<h3 id="optional-continue-with-ssh">(Optional) Continue with SSH</h3>
<p>After I have SSH enabled and the Pi connected to the network, I
disconnect the monitor and keyboard and remotely access it via
SSH. I used to use <a href="https://putty.org/" rel="external nofollow noopener">Putty</a> for this, but one of the rare
things in Windows 10 I actually like is that it now includes a
SSH client which is much more convenient to use than Putty.</p>
<p>However, if you don't want to do this log into your Pi and
continue with the next section.</p>
<ul>
<li>Open a Windows command line (good old <code>cmd.exe</code>, PowerShell or
the new Windows Terminal)</li>
<li>Execute <code>ssh pi@&lt;hostname&gt;</code>, where <code>&lt;hostname&gt;</code> is the name of
your Pi</li>
<li>When prompted, type <code>yes</code> and press <code>Enter</code> to store the
fingerprint of your device (if in future you try to connect to
<code>&lt;hostname&gt;</code> and the fingerprint doesn't match, SSH will
prompt to continue as it is extremely likely you aren't
connecting to the machine you think you are)</li>
</ul>
<h3 id="creating-a-directory-to-store-music">Creating a directory to store music</h3>
<p>For our music server, we need two different directories, one to
hold the actual music and another to store playlists. You can
stick these where ever you like, but for simplicities sake I
decided to put playlists as a subdirectory of music.</p>
<p>Later on, I'll be installing Samba so that I can access these
directories via SMB shares. The first time I set all this up on
my Model A, getting the share to work was a complete <em>pain</em>. I
ended up creating user accounts on the Pi that mirrored my
Windows user accounts and all sorts of other nonsense trying to
get it to work. This time around, I followed <a href="https://askubuntu.com/a/781971/1062219" rel="external nofollow noopener">this answer</a>
on Ask Ubuntu which suggested making &quot;nobody&quot; have full control
over these directories. This worked first time for me and so
I'll be presenting the same instructions here.</p>
<ul>
<li>Execute <code>sudo mkdir /srv/music</code></li>
<li>Execute <code>sudo mkdir /srv/music/playlists</code></li>
<li>Execute <code>sudo chown -R nobody.nogroup /srv/music</code></li>
<li>Execute <code>sudo chmod -R 777 /srv/music</code></li>
</ul>
<h3 id="updating-the-system">Updating the system</h3>
<p>As this is a fresh install, it's likely to be out of date.
Before installing any other software I like to make sure that
everything else is up to date first.</p>
<ul>
<li>Execute <code>sudo apt update</code> (this looks for updates but doesn't
actually install anything)</li>
<li>Execute <code>sudo apt upgrade -y</code> (this installs all available
updates. The <code>-y</code> flag means it will install without prompting
to continue first)</li>
</ul>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/phatbeat-pi-zero-second-test.jpg" class="gallery" title="The second test, temporarily located on a hastly cleared shelf" ><img src="https://images.cyotek.com/image/thumbnail/devblog/phatbeat-pi-zero-second-test.jpg" alt="The second test, temporarily located on a hastly cleared shelf" decoding="async" loading="lazy" /></a><figcaption>The second test, temporarily located on a hastly cleared shelf</figcaption></figure><h3 id="installing-mopidy">Installing Mopidy</h3>
<p>Before we can install Mopidy, we need to add it to our package
resources as per their <a href="https://docs.mopidy.com/en/latest/installation/debian/" rel="external nofollow noopener">installation guilde</a>.</p>
<ul>
<li>Execute <code>wget -q -O - https://apt.mopidy.com/mopidy.gpg | sudo apt-key add -</code></li>
<li>Execute <code>sudo wget -q -O /etc/apt/sources.list.d/mopidy.list https://apt.mopidy.com/buster.list</code></li>
</ul>
<p>Now we need to check for updates again and then install Mopidy</p>
<ul>
<li><code>sudo apt update &amp;&amp; sudo apt install mopidy -y</code> (the &amp;&amp;
operator executes multiple commands sequentially, but only if
the proceeding command(s) were successful)</li>
</ul>
<h3 id="installing-python-pip">Installing Python Pip</h3>
<p>Mopidy is written using Python and some of its extensions aren't
installed via <code>apt</code>, but instead with Python's own package
installer, Pip.</p>
<ul>
<li>Execute <code>sudo apt install python3-pip -y</code></li>
</ul>
<h3 id="installing-mopidy-iris">Installing Mopidy-Iris</h3>
<p>Mopidy includes a few basic plugins, but doesn't actually
include anything for front end playback. There are a number of
web based front ends I've tried and I don't like any of them.
<a href="https://github.com/jaedb/Iris" rel="external nofollow noopener">Mopidy-Iris</a> is probably the best of the bunch, it looks
good and provides basic playlist management. I don't like it
because it keeps trying to load Spotify (which I don't use) when
I'm working with playlists and because it appears to be more
focused towards making playlists out of individual tracks,
whereas I am a touch old school and my playlists are made out of
full albums and shuffle is definitely banned!</p>
<ul>
<li>Execute <code>sudo python3 -m pip install Mopidy-Iris</code></li>
<li>Execute <code>sudo sh -c 'echo &quot;mopidy ALL=NOPASSWD: /usr/local/ lib/python3.7/dist-packages/mopidy_iris/system.sh&quot; &gt;&gt; /etc/ sudoers'</code> (this is for giving Mopidy permission to update
Iris)</li>
</ul>
<h3 id="installing-mopidy-local">Installing Mopidy-Local</h3>
<p>Although Mopidy ships with a files plugin for playing local
media, Mopidy-Local is a better alternative - it caches all the
media in a database, so is able to quickly access media
information. It also provides album art to other plugins, for
example Iris above.</p>
<ul>
<li>Execute <code>sudo python3 -m pip install Mopidy-Local</code></li>
</ul>
<h3 id="configuring-mopidy">Configuring Mopidy</h3>
<p>With Mopidy and extensions installed, we need to update the
configuration to tell it where and how to process our media
files. I also disable the file plugin given we are using local
instead.</p>
<ul>
<li>Execute <code>sudo nano /etc/mopidy/mopidy.conf</code></li>
</ul>
<p>Add the following content, replacing paths as appropriate.</p>
<figure class="lang-ini highlight"><figcaption><span>ini</span></figcaption><pre class="code">
[<span class="section">file</span>]
enabled <span class="symbol">=</span> <span class="keyword">false</span>

[<span class="section">m3u</span>]
playlists_dir <span class="symbol">=</span> <span class="symbol">/</span>srv<span class="symbol">/</span>music<span class="symbol">/</span>playlists

[<span class="section">http</span>]
hostname <span class="symbol">=</span> <span class="symbol">::</span>

[<span class="section">local</span>]
media_dir <span class="symbol">=</span> <span class="symbol">/</span>srv<span class="symbol">/</span>music
scan_timeout <span class="symbol">=</span> <span class="number">10000</span>
scan_flush_threshold <span class="symbol">=</span> <span class="number">100</span>
</pre>
</figure>
<p>By default, Mopidy's HTTP browser is only accessible from local
host - not great for a headless device. The <code>hostname = ::</code>
opens it up fully to any computer on your network. Consider
viewing the warning in the <a href="https://docs.mopidy.com/en/latest/ext/http/" rel="external nofollow noopener">Mopidy-HTTP documentation</a>.</p>
<p>The <code>scan_timeout</code> and <code>scan_flush_threshold</code> values are a
matter of personal preference. I have just under 5000 MP3 files
and on both the Model 3A and Zero initial scans have timed out
with the default values, or completely stalled, loosing
progress. By increasing the <code>scan_timeout</code> to <code>10000</code> and
decreasing <code>scan_flush_threshold</code> to <code>100</code> I have avoided some
of these issues.</p>
<h3 id="enable-mopidy-to-run-at-start-up">Enable Mopidy to run at start up</h3>
<p>Although Mopidy installs a system service, this is isn't
enabled.</p>
<ul>
<li>Execute <code>sudo systemctl enable mopidy</code> to force Mopidy to
start at boot time</li>
<li>Execute<code>sudo systemctl start mopidy</code> to start Mopidy now</li>
</ul>
<h3 id="checking-the-mopidy-configuration">Checking the Mopidy configuration</h3>
<p>To check the complete Mopidy configuration, enter the following
command. This will output a configuration similar to the file
you edited above, but also including all other parameters and
their values.</p>
<ul>
<li>Execute <code>sudo mopidyctl config</code></li>
</ul>
<h3 id="installing-samba">Installing Samba</h3>
<p>I mentioned at the start of the guide I'd be installing Samba
and we're now at that point. This will make it easy to connect
to the music directory via a file share to copy new tunes over.</p>
<ul>
<li>Execute <code>sudo apt install samba -y</code></li>
<li>If you are asked any questions by the install, just accept the
default value</li>
</ul>
<h3 id="configuring-samba">Configuring Samba</h3>
<p>I have found the default Samba configuration works out of the
box, but you still need to configure at least one share. As with
the directory permissions at the start of this guide, the share
configuration below comes from the same <a href="https://askubuntu.com/a/781971/1062219" rel="external nofollow noopener">Ask Ubuntu</a> answer.</p>
<ul>
<li>Execute <code>sudo nano /etc/samba/smb.conf</code></li>
</ul>
<p>Add the following lines to the end of the file, replacing the
path as appropriate.</p>
<figure class="lang-ini highlight"><figcaption><span>ini</span></figcaption><pre class="code">
[<span class="section">Music</span>]
comment <span class="symbol">=</span> Music
path <span class="symbol">=</span> <span class="symbol">/</span>srv<span class="symbol">/</span>music
browseable <span class="symbol">=</span> <span class="keyword">yes</span>
guest ok <span class="symbol">=</span> <span class="keyword">yes</span>
read only <span class="symbol">=</span> <span class="keyword">no</span>
create mask <span class="symbol">=</span> <span class="number">777</span>
</pre>
</figure>
<h3 id="restarting-samba">Restarting Samba</h3>
<p>As we've changed the configuration, we need to restart the
service.</p>
<ul>
<li>Execute <code>sudo service smbd restart</code></li>
</ul>
<h3 id="copying-music">Copying Music</h3>
<p>You should now be able to access your music share over the
network. As copying files to a Raspberry Pi Zero seems to be
very slow (it consistently took four hours to copy 25GB of music
in my tests) you may wish to start with a single album until you
have tested everything, and then copy your full library.</p>
<h3 id="updating-the-music-library">Updating the music library</h3>
<p>As we've installed Mopidy-Local which stores media information
in a database, we need to tell Mopidy to scan the file system to
update that database.</p>
<p>The documentation recommends setting up a cron job for this, but
this seems overkill unless you are constantly modifying your
media library. I prefer to run it on demand when I add music.</p>
<ul>
<li>Execute <code>sudo mopidyctl local scan</code></li>
</ul>
<p>Note that this may take some time to run. If any file's time out
you can always run the command again when it completes and it
will try again with the skipped files.</p>
<h3 id="installing-phat-beat">Installing pHAT Beat</h3>
<p>(Skip this section if you are using the built-in soundcard and
onboard audio connector.)</p>
<blockquote>
<p>Installing this software will disable the onboard sound.
However, I found that it also had another impact for if I
undid the settings it made to <code>/boot/config.txt</code> and
re-enabled onboard sound, audio no longer played properly
through it.</p>
</blockquote>
<ul>
<li>Execute <code>curl https://get.pimoroni.com/phatbeat | bash</code></li>
<li>When prompted to continue enter <code>y</code></li>
<li>(Optional) When prompted to install examples and
documentation, enter <code>y</code>. If you answer <code>n</code>, the VU meter
won't automatically work. It was much easier to answer <code>y</code>
than to <a href="https://github.com/pimoroni/pivumeter" rel="external nofollow noopener">install it from source</a></li>
<li>Once the installation has completed, answer <code>y</code> to reboot</li>
</ul>
<h3 id="testing-the-speakers">Testing the speakers</h3>
<p>To test your audio is working and you have the speakers the
right way around, you can use the speaker-test program.</p>
<ul>
<li>Execute <code>speaker-test -c2 -t wav</code></li>
<li>Press <code>Ctrl+C</code> to terminate</li>
</ul>
<h3 id="running-iris">Running Iris</h3>
<p>You should now have a headless music server - last step is to
open Iris and play!</p>
<ul>
<li>Open a web browser pointing to <code>http://&lt;hostname&gt;:6680/iris/</code>
(don't forget the port)</li>
<li>The first time you access Iris you'll be prompted for a few
settings, update these as appropriate and click Save</li>
<li>Click Albums and verify music is loaded</li>
<li>Right click an Album and choose Play</li>
</ul>
<h3 id="optional-adding-a-start-up-sound">(Optional) Adding a start up sound</h3>
<p>As this is a headless device, it is hard to know when it is
ready to accept commands. For this reason I took to adding a
start up sound to play a brief tone when the Pi has finished
booting. As the Pi Zero is much slower to boot than the others,
this is definitely something I appreciate.</p>
<ul>
<li>Copy a short wave file to your music share</li>
<li>Execute <code>sudo nano /etc/rc.local</code></li>
</ul>
<p>Add the following line, changing the path and filename as
appropriate, towards the end of the file but <strong>before</strong> the
<code>exit 0</code> line.</p>
<figure class="lang-sh highlight"><figcaption><span>sh</span></figcaption><pre class="code">
aplay /srv/music/sounds/tada.wav
</pre>
</figure>
<h3 id="optional-reducing-the-master-volume">(Optional) Reducing the master volume</h3>
<p>I find that the default volume is pretty loud and as I usually
want the music to be background noise, I set the volume in Iris
to be a low single digit. I also tweak the master volume so that
when the machine starts it is already quieter, even though
Mopidy still thinks it is 100%</p>
<ul>
<li>Execute <code>sudo nano /etc/rc.local</code></li>
</ul>
<p>Add the following line, changing the percentage according to
your own tastes. If you added a start up sound, it might be a
good idea to put this line before the call to <code>aplay</code>.</p>
<figure class="lang-sh highlight"><figcaption><span>sh</span></figcaption><pre class="code">
amixer sset Master -- 70%
</pre>
</figure>
<h2 id="closing-thoughts">Closing Thoughts</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/phatbeat-pi-zero-bokeh.jpg" class="gallery" title="The final unit, along with a frosted acrylic shield which produces a nice bokeh effect" ><img src="https://images.cyotek.com/image/thumbnail/devblog/phatbeat-pi-zero-bokeh.jpg" alt="The final unit, along with a frosted acrylic shield which produces a nice bokeh effect" decoding="async" loading="lazy" /></a><figcaption>The final unit, along with a frosted acrylic shield which produces a nice bokeh effect</figcaption></figure>
<p>Ever so slowly I'm becoming a fan of Linux, one of these days I
might try and use a full blown desktop (although that'll
probably be difficult due to Visual Studio and WinForms).</p>
<p>I'm not really a fan of the pHAT Beat. My original Mopidy setup
on the 3A didn't use a HAT, so the GPIO pins were free. I'd
added a couple of wires and a switch to safely shutdown the Pi
when pressed, and then start it up again if pressed again. The
pHAT Beat takes away that option, and even though it has a
hardware button, if you manage to get it working it only seems
to shutdown the device, not to start it up again. I haven't yet
looked into if there is another way I can add a power switch
(having a switch on a USB cable isn't fully sufficient as I
still want to safely halt the Pi, not just do the equivalent of
yanking out the power cable).</p>
<p>In addition, the VU meter doesn't scale to the current volume,
meaning that as I have everything turned quite low, only the
first pair of LED's light up - and barely at that. On the 3A, I
changed the source code and recompiled based on some advice on
the Pimoroni forums but I haven't done that on the Zero yet. It
is a shame it doesn't just scale nicely for you.</p>
<p>I also wish the speaker connectors on the HAT were screw based.
Instead, you just push the wires until they lock. I don't find
that to a be a particularly strong mechanism though.</p>
<p>However, there is no denying that quality of audio wise, using
this HAT is far better than my previous set up. Whether that's
just to the speakers or the hardware on the expansion board is
another question but it is definitely better. And at the end of
the day, a VU meter is just a nicety, it's not like I sit and
stare at it!</p>
<p>While I don't entirely like playlist management on Iris, I
really only need to set play lists up once. What I don't like is
having to reload from scratch each reboot and set the repeat
flag. Previously I played music on my desktop computer using
<a href="https://www.foobar2000.org/" rel="external nofollow noopener">foobar2000</a> and what I really liked about that was it would
continue where it left off. I'm sure WinAmp used to do that too
back in the day and one of the reasons I didn't like Windows
Media Player either. I think the only way to get that behaviour
back is to write something myself to control Mopidy directly via
the <a href="https://www.musicpd.org/doc/html/protocol.html" rel="external nofollow noopener">Music Player Daemon protocol</a>.</p>
<p>But first I definitely need to figure out a new power switch!</p>
<h3 id="updates">Updates</h3>
<ul>
<li>12Apr2020 - Added some images of the device</li>
<li>10Apr2020 - First published</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/installing-mopidy-and-phat-beat-on-a-raspberry-pi .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comTools we use - 2019 editionurn:uuid:8d9d8729-d64c-4c0e-bbfa-0709aa6fe9f02020-01-06T19:05:47Z2020-01-01T12:03:24Z<p>Happy New Year! The past few months I've been feeling quite
burned out and haven't updated this blog much - I have half
a dozen half-finished blog posts and our product updates have
suffered too. I didn't want to break the tradition though and
fortunately the number of updates were small, so here is the
2019 list of &quot;Tools We Use&quot;.</p>
<p>As far as personal goals go, 2019 was mostly a dud. I didn't
resume game development, I'm still stuck with prioritising
WebCopy to the detriment of everything else, and I still
haven't tried anything really new. This nonsense needs to stop.</p>
<p>I did make a start replacing this creaky website with some
ASP.NET Core goodness though - the preview version of the
dev blog running on .NET Core can be found at
<a href="https://devblog.cyotek.com/blog">https://devblog.cyotek.com/</a>. It still needs some layout
work and some missing RSS features but otherwise is mostly
complete and will hopefully be made &quot;live&quot; early this year.</p>
<h2 id="operating-systems">Operating Systems</h2>
<ul>
<li>Windows Home Server 2011 - file server, SVN repository,
backup host, CI server - <em>If it ain't broke, don't fix it</em></li>
<li>Windows 10 Professional - development machines</li>
<li><del>Windows 10 (virtualized)</del> - I tried using a pair of
32bit and 64bit Windows 10 VMs for software testing but the
performance is so dire I gave up</li>
<li>Windows 7 (virtualized) - testing</li>
</ul>
<h2 id="development-tools">Development Tools</h2>
<ul>
<li><a href="https://www.getpostman.com/" rel="external nofollow noopener">Postman</a> is a absolutely
brilliant client for testing REST services</li>
<li><a href="http://www.microsoft.com/visualstudio/" rel="external nofollow noopener">Visual Studio 2019</a> - The performance improvements made
to Visual Studio 2019 are quite staggering</li>
<li><ins>New!</ins> <a href="https://code.visualstudio.com/" rel="external nofollow noopener">Visual Studio Code</a> - a frankly amazing
editor / IDE. I use it for most non-.NET tasks, such as PHP
or editing markdown. Workspaces that can include multiple
folders are incredibly useful. A great tool, once you install
enough extensions to configure it &quot;your way&quot;</li>
<li><ins>New!</ins> <a href="https://github.com/0xd4d/dnSpy" rel="external nofollow noopener">dnSpy</a> - speedy .NET assembly debugger and editor</li>
<li><del><a href="https://www.jetbrains.com/decompiler/" rel="external nofollow noopener">DotPeek</a> - this program is just <em>too</em> slow to start
so I replaced it with dnSpy</del></li>
<li><a href="https://www.ndepend.com/" rel="external nofollow noopener">NDepend</a> - static code analysis. I have quite a love /
hate relationship with this application; so much so that I
barely use the user interface at all and rely on reports
published to Jenkins as part of a common build pipeline</li>
</ul>
<h2 id="visual-studio-extensions">Visual Studio Extensions</h2>
<ul>
<li><a href="http://www.oz-code.com/" rel="external nofollow noopener">OzCode</a> - an exceptional debugging aid. Things like
exception predication, condition visualisation, reveal, and
a data tip that doesn't suck really should be part of the core
Visual Studio experience</li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=RichardJMoss.AddExistingProjects-19444" rel="external nofollow noopener">Cyotek Add Projects</a> - a simple extension for easily
adding multiple projects to your solutions. Although I use
it far less now that most of my projects are packages, it is
still useful and I recently updated it to <a href="https://github.com/cyotek/Cyotek.AddProjects/releases" rel="external nofollow noopener">support Visual
Studio 2019</a></li>
<li><a href="http://editorconfig.org/" rel="external nofollow noopener">EditorConfig</a> - useful for OSS projects to avoid
space-vs-tab wars or to configure code style rules</li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=MadsKristensen.FileNesting" rel="external nofollow noopener">File Nesting</a> - allows you to easily nest (or un-nest!)
files, great for TypeScript or T4 templates</li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=MadsKristensen.OpenCommandLine" rel="external nofollow noopener">Open Command Line</a> - easily open command prompts,
PowerShell prompts, or other tools to your project /
solution directories</li>
<li><a href="http://mike-ward.net/vscoloroutput/" rel="external nofollow noopener">VSColorOutput</a> - add colour coding to Visual Studio's
Output window</li>
<li><del><a href="https://marketplace.visualstudio.com/items?itemName=SteveDowerMSFT.IndentGuides" rel="external nofollow noopener">Indent Guides</a> - easily see where you are in nested
code.</del> I forgot to install this when I switched to
VS2019 and didn't miss it... maybe I'm writing more
readable code now!</li>
<li><del><a href="http://www.jetbrains.com/resharper/" rel="external nofollow noopener">ReSharper</a></del> 2019 was the year I gave up on
JetBrains ever doing something about the performance issues
that ReSharper causes. When I moved to VS2019 I deliberately
chose not to install it and while there are many things about
it I miss, an IDE that runs faster than the human controlling
is worth more</li>
<li><a href="http://www.ncrunch.net/" rel="external nofollow noopener">NCrunch for Visual Studio</a> - frequently updated automated
parallel continuous testing tool (there's a mouthful).
Works with NUnit, MSTest, SpecFlow and a variety of other
test systems. This is by far the best continuous testing tool
on the market</li>
<li><ins>New!</ins> <a href="https://specflow.org/" rel="external nofollow noopener">SpecFlow</a> - I only used this for one
project (my implementation of <a href="https://www.cyotek.com/blog/book-review-the-ray-tracer-challenge">The Ray Tracer Challenge</a>)
and after I a while I really found this way of implementing
tests a bit of a game changer. However, I feel that I would
quickly loose my sanity if I had to write all these
specifications up front and so this is still sitting in my
&quot;todo&quot; pile to look into further.</li>
<li><ins>New!</ins> <a href="https://github.com/JosefPihrt/Roslynator" rel="external nofollow noopener">Roslynator 2019</a> - C# code analyzers,
refactoring and fixes. I use this to replace some of the more
critical functionality I previously enjoyed in ReSharper</li>
<li><ins>New!</ins> <a href="https://ewsoftware.github.io/VSSpellChecker/html/027d2fbc-7bfb-4dc3-b4f5-85f95fcf7629.htm" rel="external nofollow noopener">Visual Studio Spell Checker</a> - after I
found one too many spelling errors in comments and GUI text</li>
<li><ins>New!</ins> <a href="http://www.codemaid.net/" rel="external nofollow noopener">CodeMaid</a> - code formatting and
organising. Lets be fair to ReSharper, there's <em>nothing</em> else
available which does a better job, but CodeMaid is an
acceptable substitute</li>
<li><ins>New!</ins> <a href="https://github.com/Tim-Maes/T4Editor" rel="external nofollow noopener">T4Editor</a> - T4 template editor implements.
I use this as a replacement for the ReSharper ForTea extension
and I'm quite happy with it - it does a great job of showing
me the T4 specific aspects of my templates</li>
</ul>
<h2 id="analytics">Analytics</h2>
<ul>
<li>Unnamed Analytics. After dropping Luminitix, I replaced the
data collection with a home grown solution using RavenDB,
although I've yet to write a front end to look at the data
effectively</li>
<li><a href="https://matomo.org/" rel="external nofollow noopener">Matomo</a> - currently trialling this web based analytics
software to gain anonymous insights into cyotek.com usage</li>
</ul>
<h2 id="profiling">Profiling</h2>
<ul>
<li><a href="https://www.jetbrains.com/profiler/" rel="external nofollow noopener">dotTrace</a> - although I prefer the ANTS profiler, dotTrace
is a very usable profiler and given it is included in my
ReSharper Ultimate subscription, it's a no-brainer to use</li>
<li><a href="https://www.jetbrains.com/dotmemory/" rel="external nofollow noopener">dotMemory</a> - .NET memory profiler. As with dotTrace it
is probably time to explore alternatives if I let the
ReSharper subscription lapse (yet another reason why perpetual
licenses are better than the modern trend of renting
software)</li>
</ul>
<h2 id="documentation-tools">Documentation Tools</h2>
<ul>
<li>HelpWrite - the first application offered by Ariad in the
mists of time, now reincarnated and producing no-frills
documentation from simple markdown and YAML</li>
<li><a href="http://www.atomineerutils.com/" rel="external nofollow noopener">Atomineer Pro Documentation</a> - automatically generate
XML comment documentation in your source code (Visual
Studio extension)</li>
<li><del><a href="http://markdownedit.com/" rel="external nofollow noopener">MarkdownEdit</a> - a no frills minimalist markdown
editor.</del> Thoroughly replaced by Visual Studio Code</li>
<li><a href="http://notepad-plus-plus.org/" rel="external nofollow noopener">Notepad++</a> - a excellent text editor</li>
</ul>
<h2 id="continuous-integration">Continuous Integration</h2>
<ul>
<li><a href="https://jenkins.io/index.html" rel="external nofollow noopener">Jenkins</a> + <a href="http://afonsof.com/jenkins-material-theme/" rel="external nofollow noopener">Jenkins Material Theme</a> is easy to
install, doesn't need a database server and has a rich plugin
ecosystem, even for .NET developers. I use this to build,
test and deploy all our products and libraries</li>
</ul>
<h2 id="testing">Testing</h2>
<ul>
<li><a href="http://nunit.org/" rel="external nofollow noopener">NUnit</a> is our test framework of choice, for no
particular reason other than it was the first one we tried
after getting fed up of MSTest's limitations</li>
</ul>
<h2 id="graphics-tools">Graphics Tools</h2>
<ul>
<li><a href="http://www.getpaint.net/" rel="external nofollow noopener">Paint.NET</a> - brilliant bitmap editor with extensive
plugins</li>
<li><a href="http://www.axialis.com/iconworkshop/" rel="external nofollow noopener">Axialis IconWorkshop</a> - very nice icon editor, I have
been using this for untold years now since Microangelo was
abandoned</li>
<li><a href="http://cyotek.com/cyotek-spriter">Cyotek Spriter</a> - sprite / image map generation software
that is still in sore need of optimisation and TLC</li>
<li><a href="http://cyotek.com/cyotek-gif-animator/">Cyotek Gif Animator</a> - gif animation creator that was
shaping up nicely, although it is another application I really
want to spend more time improving</li>
<li><a href="http://www.angelcode.com/products/bmfont/" rel="external nofollow noopener">AngelCode BMFont</a> - although I haven't had a chance to
continue with game development for some years now
(something else I'd like to change in <del>2019</del> 2020), for bitmap font
creation I use BMFont along with our own <a href="https://github.com/cyotek/Cyotek.Drawing.BitmapFont" rel="external nofollow noopener">parser</a></li>
</ul>
<h2 id="virtualization">Virtualization</h2>
<ul>
<li><a href="https://www.virtualbox.org/" rel="external nofollow noopener">Oracle VM VirtualBox</a> - for creating guest OS's for
testing purposes</li>
</ul>
<h2 id="version-control">Version Control</h2>
<ul>
<li><a href="http://tortoisesvn.net/" rel="external nofollow noopener">TortoiseSVN</a> - Windows Explorer integration for SVN</li>
<li><a href="https://www.visualsvn.com/visualsvn/" rel="external nofollow noopener">VisualSVN</a> - Subversion support for Visual Studio. Unlike
AnhkSVN, VisualSVN uses TortoiseSVN under the hood, meaning
that Explorer and Visual Studio are always in the same state
no matter where I commit from, something which used to
frustrate me no end with AnhkSVN</li>
<li><a href="http://www.visualsvn.com/server/" rel="external nofollow noopener">VisualSVN Server</a> - Subversion Server for Windows</li>
<li><a href="https://github.com/" rel="external nofollow noopener">GitHub</a> / <a href="https://desktop.github.com/" rel="external nofollow noopener">GitHub Desktop</a> - for providing and
working with the open source code we publish</li>
<li><ins>New!</ins> <a href="https://gitea.io/en-us/" rel="external nofollow noopener">Gitea</a> - self hosted GitHub clone. As
I'm trying to switch from SVN to Git, some new projects
are now using Git, with Gitea as the origin.</li>
</ul>
<h2 id="filedirectory-tools">File/directory tools</h2>
<ul>
<li><a href="http://winmerge.org/" rel="external nofollow noopener">WinMerge</a> - excellent file or directory comparison
utility</li>
<li><a href="http://stefanstools.sourceforge.net/grepWin.html" rel="external nofollow noopener">grepWin</a> - another excellent tool for swiftly searching
directories for files containing specific strings or
expressions</li>
<li><a href="https://filezilla-project.org/" rel="external nofollow noopener">FileZilla</a> - simple FTP client that has served my needs
for years now</li>
</ul>
<h2 id="backups">Backups</h2>
<ul>
<li><a href="http://cyotek.com/cyotek-copytools">Cyotek CopyTools</a> - we use this for offline backups of
source code, assets and resources, documents, actually pretty
much anything we generate; including backing up the backups!</li>
<li><a href="http://www.code42.com/crashplan/" rel="external nofollow noopener">CrashPlan</a> - CrashPlan creates an online backup of the
different offline backups that CopyTools does. If you've ever
lost a hard-disk before with critical data on it that's
nowhere else, you'll have backups squirrelled away everywhere too!</li>
</ul>
<h2 id="security">Security</h2>
<ul>
<li><a href="https://www.comodo.com/" rel="external nofollow noopener">Comodo</a> - code signing certificates, and domain SSL if
a particular host doesn't support Let's Encrypt</li>
<li><a href="https://letsencrypt.org/" rel="external nofollow noopener">Let's Encrypt</a> provide short term SSL certificates for
free. If you (or your host) are able to automate the process,
this is an exceptional way to get basic SSL for your sites</li>
<li><a href="http://someonewhocares.org/hosts/" rel="external nofollow noopener">Dan Pollocks hosts file</a> blocks your computer from
connecting to many thousands of dubious internet hosts and is
continuously updated</li>
<li><ins>New!</ins> <a href="https://www.virustotal.com/" rel="external nofollow noopener">VirusTotal</a> - analyze files for malware.
This isn't new per-se as I have been using this in our build
processes for some time now but I forgot to mention it
earlier. It is a helpful tool, except for when you find that
one given engine will flag all your submissions as malicious
and then when that finally clears up another one decides to
join in the &quot;fun&quot; instead</li>
<li><ins>New!</ins> <a href="https://keepass.info/" rel="external nofollow noopener">KeePass Password Safe</a> / <a href="https://bitwarden.com/" rel="external nofollow noopener">BitWarden</a>
I finally switched from LastPass and use both of these
programs for different purposes</li>
</ul>
<h2 id="issue-tracking">Issue Tracking</h2>
<ul>
<li><a href="https://www.mantisbt.org/" rel="external nofollow noopener">Mantis Bug Tracker</a> - open source issue tracker</li>
<li><a href="https://github.com/cyotek/MantisSharp" rel="external nofollow noopener">MantisSharp</a> - I use our MantisSharp library to add
integration between various applications and our MantisBT
instance, notable for raising new issues from our automated
error monitor, and for creating road-maps on cyotek.com
product pages although as usual I haven't had much time to
maintain it</li>
</ul>
<h2 id="cms">CMS</h2>
<ul>
<li><del><a href="https://getkirby.com/" rel="external nofollow noopener">Kirby</a> - although cyotek.com uses a custom home
built CMS, I had been looking a Kirby as an alternative for
some aspects such as the Knowledge Base</del> Again I chose
to use a home built solution, this time using .NET Core</li>
</ul>
<h2 id="help-desk">Help Desk</h2>
<ul>
<li><a href="https://www.maiansupport.com/" rel="external nofollow noopener">Maian Support</a> - instead of trying to keep track of
emails, I've been using the commercial version of Maian
support to manage user support requests and feedback
submissions</li>
</ul>
<h2 id="other">Other</h2>
<ul>
<li><a href="https://ravendb.net/" rel="external nofollow noopener">RavenDB</a> - still not using this for much as I can't seem
to effectively query the data from Raven Studio, and at heart
I still think NoSQL is a fad</li>
<li><a href="https://kodi.tv/" rel="external nofollow noopener">Kodi</a> - I've used this for years now to watch video on
various generations of Raspberry Pi. I found the Films and TV
(or Movies and TV) application that ships with Windows 10 to
be absolute rubbish and was very glad when Kodi became
available on the Microsoft Store</li>
<li><a href="https://rufus.akeo.ie/" rel="external nofollow noopener">Rufus</a> - I use this utility for writing ISO images to
USB, useful for setting up new physical machines in an age
where CD drives are fairly obsolete</li>
<li><a href="https://sourceforge.net/projects/win32diskimager/" rel="external nofollow noopener">Win32 Disk Imager</a> - useful for burning ISO images to
SD cards which I do for Raspberry Pi distributions. I used to
use this for USB as well but now I prefer Rufus for that</li>
<li><a href="https://justgetflux.com/" rel="external nofollow noopener">f.lux</a> - I've been using this utterly fantastic software
for years. It adapts your monitor to the time of day,
removing blue light as evening approaches and helps reduce eye
strain when coding at night</li>
<li><a href="https://www.mozilla.org/firefox/" rel="external nofollow noopener">Firefox</a> - I switched to this as my primary browser in
2018 as my own protest against Chrome's dominance (and don't
get me started on Microsoft's recent ill advised capitulation)</li>
<li><a href="https://duckduckgo.com/" rel="external nofollow noopener">DuckDuckGo</a> <em>the search engine that doesn't track you</em> -
I can't remember when I made the switch to DuckDuckGo as it
was several years ago, but it does a great job and I rarely
have to fall back to &quot;another&quot; search engine</li>
<li><a href="https://calibre-ebook.com/" rel="external nofollow noopener">Calibre</a> - ebook management. Although I still prefer
paper books, I don't buy them often. I tend to read on e-ink
devices and Calibre makes it simple to update these</li>
</ul>
<p>What tools do you find useful? I'd love to know... maybe I'll find a new gem myself!</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/tools-we-use-2019-edition .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comResolving compile error "Interop type cannot be embedded. Use the applicable interface instead"urn:uuid:9ac86394-79f1-46dc-bedb-131cd6f291c02020-04-07T18:47:16Z2019-09-07T09:39:31Z<p>Recently I've been experimenting with the Microsoft Windows
Image Acquisition Library (WIA), a COM library that therefore
requires the use of interop in .NET. In the course of testing
some of the different features of this library I triggered a
compile error I haven't come across for a long time. As it isn't
often I work with COM interop I thought I'd write a quick post
detailing how to resolve or work around the compile errors to
more firmly cement it in my head for the next time.</p>
<h2 id="about-interop-embedding">About interop embedding</h2>
<p>In older versions of .NET (or Visual Studio), when you
referenced a COM library an interop DLL was generated - you
could always tell these from their filenames as they would start
with <code>Interop.</code>. I also seem to remember that back in the days
of .NET 1.1 I would manually run a utility program to generate
the interop DLL's, to avoid some form of naming prefix.</p>
<p>More recent versions of Visual Studio still do this, but will
also try and avoid having to include the interop DLL by
packaging up all the interfaces and embedding them directly in
your application, just like any other class you'd written.</p>
<p>This is a neat feature as it reduces application size and
deployment complexity - there's no need to ship an extra DLL,
and only interfaces relating to functionality you're directly
using is embedded.</p>
<p>However, this only works if you use the original interfaces, and
not the classes generated by the interop importer. Trying to use
any non-interface will produce a compile error, similar to</p>
<blockquote>
<p>Interop type 'CommonDialogClass' cannot be embedded. Use the
applicable interface instead</p>
</blockquote>
<h2 id="fixing-it-the-simplest-way">Fixing it the simplest way</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/embedded-interop-1a.png" class="gallery" title="With embedding disabled, you have to include an additional DLL that may include code you aren't using" ><img src="https://images.cyotek.com/image/thumbnail/devblog/embedded-interop-1a.png" alt="With embedding disabled, you have to include an additional DLL that may include code you aren't using" decoding="async" loading="lazy" /></a><figcaption>With embedding disabled, you have to include an additional DLL that may include code you aren't using</figcaption></figure>
<blockquote>
<p>Personally, I would go with the second option and so I don't
recommend you use this approach</p>
</blockquote>
<p>The easiest way of resolve this is to disable embedding the
interop library. To do this, expand the <strong>References</strong> node in
the <strong>Solution Explorer</strong> and select the COM reference you
added. In the <strong>Properties</strong> window, set the value of the
<strong>Embed Interop Types</strong> property to <code>false</code>.</p>
<h2 id="fixing-it-properly">Fixing it properly</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/embedded-interop-1b.png" class="gallery" title="With embedding enabled, there's no extra DLL - just a slight increase in your application size" ><img src="https://images.cyotek.com/image/thumbnail/devblog/embedded-interop-1b.png" alt="With embedding enabled, there's no extra DLL - just a slight increase in your application size" decoding="async" loading="lazy" /></a><figcaption>With embedding enabled, there's no extra DLL - just a slight increase in your application size</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/embedded-interop-1c.png" class="gallery" title="The meta data for a sample application showing that only the interfaces that were in use were embedded into the application" ><img src="https://images.cyotek.com/image/thumbnail/devblog/embedded-interop-1c.png" alt="The meta data for a sample application showing that only the interfaces that were in use were embedded into the application" decoding="async" loading="lazy" /></a><figcaption>The meta data for a sample application showing that only the interfaces that were in use were embedded into the application</figcaption></figure>
<p>The error message is fairly clear in how to resolve the problem</p>
<ul>
<li>&quot;Use the applicable interface instead&quot;. However, it is bit of
a puzzle as well as it requires breaking the normal rules.</li>
</ul>
<h3 id="classes-versus-interfaces">Classes versus Interfaces</h3>
<p>The WIA library has an interface named <code>ICommonDialog</code>. This
interface provides access to a variety of user interfaces for
connecting to a scanner or camera.</p>
<p>The interop library has this interface, but also generates a
further interface - <code>CommonDialog</code> (that implements
<code>ICommonDialog</code>) and a concrete class <code>CommonDialogClass</code> that
implements both interfaces.</p>
<p>As a seasoned developer, you know fine well that you can't
create a new instance of an interface, you have to create an
instance of the class which implements it. So naturally you
might write code similar to</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
WIA<span class="symbol">.</span>CommonDialogClass dialog<span class="symbol">;</span> <span class="comment">// error</span>

dialog <span class="symbol">=</span> <span class="keyword">new</span> WIA<span class="symbol">.</span>CommonDialogClass<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// error</span>
</pre>
</figure>
<p>Unfortunately, this will give you two different compile errors,
once for each <code>CommonDialogClass</code>. The secret is to use the
non-<code>I</code> prefixed interface, e.g. <code>CommonDialog</code> (if you try to
use <code>ICommonDialog</code> you'll get normal compile error you would
trying to new up any other interface.). Even though this is
completely against the rules as you might know them, this is
valid code - it is just part of the magic done under the scenes
to allow managed access to COM objects.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
WIA<span class="symbol">.</span>ICommonDialog dialog<span class="symbol">;</span> <span class="comment">// or CommonDialog</span>

dialog <span class="symbol">=</span> <span class="keyword">new</span> WIA<span class="symbol">.</span>CommonDialog<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// no error</span>
</pre>
</figure>
<h3 id="constants">Constants</h3>
<p>There is one caveat and that is constants. Some COM libraries
might define global constants and while the interop library will
include these constants, they are placed inside classes and so
if you try to access them you'll get the same compile error.</p>
<p>There's no easy workaround this time as far as I know - you need
to copy the constant values into your own code. An easy way is
just to type the class name in your code editor and press
<kbd>F12</kbd>. Visual Studio will then open the meta data for
the interop class which will show the constants and their values
- you can then just copy and paste these into your own
application.</p>
<h2 id="closing-thoughts">Closing thoughts</h2>
<p>As I noted in my preamble, I don't often work with COM so the
article above may not present the whole picture. Clarifications
or comments welcome!</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2019-09-07 - 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/resolving-compile-error-interop-type-cannot-be-embedded-use-the-applicable-interface-instead .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCanoScan LiDE 100 Windows 10 and Windows 11 Compatibilityurn:uuid:3a06c3ed-71bb-44c8-b1b5-a348bfc7a46e2022-06-23T20:27:22Z2019-09-04T19:50:05Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/canoscan-lide-100-windows-11-1g.png" class="gallery" title="Scanning on Windows 11 with my trusty CanoScan LiDE 100" ><img src="https://images.cyotek.com/image/thumbnail/devblog/canoscan-lide-100-windows-11-1g.png" alt="Scanning on Windows 11 with my trusty CanoScan LiDE 100" decoding="async" loading="lazy" /></a><figcaption>Scanning on Windows 11 with my trusty CanoScan LiDE 100</figcaption></figure>
<p>Although officially the Canon CanoScan LiDE 100 scanner is not
supported on Windows 10 or on Windows 11, I have found that it
is fully functional. This article describes the versions of
Windows I have tested the scanner with along with any
observations.</p>
<h2 id="quick-summary">Quick Summary</h2>
<table>
<thead>
<tr>
<th>Windows Version</th>
<th>Bitness</th>
<th>Supported?</th>
</tr>
</thead>
<tbody>
<tr>
<td>Windows 11 (21H2)</td>
<td>64bit</td>
<td>Yes<sup>1</sup></td>
</tr>
<tr>
<td>Windows 10 (21H2)</td>
<td>64bit</td>
<td>Yes<sup>1</sup></td>
</tr>
<tr>
<td>Windows 10 (21H1)</td>
<td>64bit</td>
<td>Yes<sup>1</sup></td>
</tr>
<tr>
<td>Windows 10 (20H2)</td>
<td>64bit</td>
<td>Yes<sup>1</sup></td>
</tr>
<tr>
<td>Windows 10 (2004)</td>
<td>64bit</td>
<td>Yes<sup>1</sup></td>
</tr>
<tr>
<td>Windows 10 (1909)</td>
<td>64bit</td>
<td>Yes</td>
</tr>
<tr>
<td>Windows 10 (1903)</td>
<td>64bit</td>
<td>Yes</td>
</tr>
<tr>
<td>Windows 10 (1809)</td>
<td>64bit</td>
<td>Yes</td>
</tr>
<tr>
<td>Windows 7 Home N</td>
<td>32bit</td>
<td>Yes</td>
</tr>
</tbody>
</table>
<ol>
<li>A recent Windows Update disables Device Manager from being
able to install drivers, see <strong>Installing on Windows 10 2004
and above</strong> for information on using Windows Update to
install the driver</li>
</ol>
<h3 id="notes">Notes</h3>
<ul>
<li>The scanner is fully supported via WIA without needing any
additional software installation, bar the driver</li>
<li>In order for the scanner hardware buttons to work (Copy, Scan,
Pdf, E-mail) you will need to install the <a href="https://www.canon.co.uk/support/consumer_products/products/scanners/lide_series/canoscan_lide_100.html?type=download&amp;softwaredetailid=tcm:14-1189487&amp;os=windows%208.1%20%2864-bit%29&amp;language=en" rel="external nofollow noopener">MP Navigator EX</a>
software (the Windows 8.1 version works fine on Windows 10 and
is bloat free). With that said, I've only tested the PDF
functionality and not anything else. Thank you to commentator
<a href="#comment29332">Gerard</a> for notifying me this software worked. Note I have
currently only tested this software on Windows 10 1909 and
Windows 10 20H2</li>
<li>I have done limited testing with the TWAIN API using Windows
10 20H2</li>
</ul>
<h2 id="installing-on-windows-11">Installing on Windows 11</h2>
<p>I have tested the below process using a fresh installation of
Windows 11 Pro (21H2) on a Surface Pro 6. These steps also work
for the CanoScan LiDE 220, something which <em>is</em> supported in
Windows 11 but again, no automatic driver installation.</p>
<ul>
<li>Make sure the scanner is plugged in</li>
<li>Open the <strong>Settings</strong> application</li>
<li>Click <strong>Windows Update</strong> from the sidebar</li>
<li>Click <strong>Check for updates</strong></li>
<li>When the check completes, click <strong>Advanced Options</strong></li>
<li>Scroll down the <strong>Additional Options</strong> section and click
<strong>Optional Updates</strong></li>
<li>Expand <strong>Driver Updates</strong></li>
<li>Check <strong>Canon - Scanner - CanoScan LiDE 100</strong></li>
<li>Click <strong>Download and Install</strong></li>
</ul>
<p>The end of the article shows some screenshots of the
installation process.</p>
<h2 id="installing-on-windows-10-2004-and-above">Installing on Windows 10 2004 and above</h2>
<p>I recently picked up another Surface Pro 2 from eBay and thought
I would test the scanner on this with a fresh install of
Windows. I installed 21H1 and plugged the scanner in but noted
that it did not initialise and checking Device Manager showed
the device was detected, but no driver was installed. The
following steps let me use the scanner again</p>
<ul>
<li>Make sure the scanner is plugged in</li>
<li>Open the <strong>Settings</strong> application</li>
<li>Click <strong>Update &amp; Security</strong></li>
<li>Click <strong>Check for updates</strong></li>
<li>When the check completes, a new <strong>View optional updates</strong> link
should be displayed - click this</li>
<li>Expand the <strong>Driver updates</strong> node</li>
<li>Check the <strong>Canon - Scanner - CanoScan LiDE 100</strong> option</li>
<li>Click <strong>Download and install</strong></li>
</ul>
<p>After a few minutes your scanner should whirr into life. I
tested using <a href="https://www.getpaint.net/" rel="external nofollow noopener">paint.net</a> and had no trouble scanning an
image. So although it is not as easy as it used to be, thank
you Microsoft for introducing such an unnecessary step, it is
still usable on the latest versions.</p>
<h2 id="updates">Updates</h2>
<table>
<thead>
<tr>
<th>Date</th>
<th>Change</th>
</tr>
</thead>
<tbody>
<tr>
<td>2022-06-23</td>
<td>Installed Windows 11 21H2 on a Surface Pro 6. After installing driver, scanner worked fine</td>
</tr>
<tr>
<td>2022-05-14</td>
<td>Installed 21H2 as a fresh install on a Surface Pro 2. Scanner is working fine, but driver did not automatically install</td>
</tr>
<tr>
<td>2021-05-25</td>
<td>Installed 21H1 as a fresh install on a Surface Pro 2. Scanner is working fine, but driver did not automatically install</td>
</tr>
<tr>
<td>2021-04-17</td>
<td>Upgraded my Surface Pro 2 from 2004 to 20H2 and scanner is working fine</td>
</tr>
<tr>
<td>2020-11-22</td>
<td>Updated formatting</td>
</tr>
<tr>
<td>2020-08-15</td>
<td>Upgraded my Surface Pro 2 from 1909 to 2004 and scanner is working fine</td>
</tr>
<tr>
<td>2020-03-29</td>
<td>Updated to cover the MP Navigator EX software</td>
</tr>
<tr>
<td>2019-11-14</td>
<td>I just updated my Surface Pro 2 from 1903 to 1909 and the scanner is working fine</td>
</tr>
<tr>
<td>2019-11-06</td>
<td>The 2019-09-26 update erroneously listed the version as 1703 but it was 1903 I tested with</td>
</tr>
<tr>
<td>2019-09-26</td>
<td>Added Windows 10 1903 after trying the scanner in my Surface 2 Pro</td>
</tr>
<tr>
<td>2019-09-04</td>
<td>Initial release of article</td>
</tr>
</tbody>
</table>
<h2 id="background-original-from-article-published-in-2019">Background (original from article published in 2019)</h2>
<p>The <a href="https://www.canon.co.uk/support/consumer_products/products/scanners/lide_series/canoscan_lide_100.html" rel="external nofollow noopener">drivers page</a> for the Canon CanoScan LiDE 100 scanner
states that the scanner is not supported in Windows 10 (either
32 or 64 bit). As far as Canon is concerned, it is officially
supported from Windows 2000 to Windows 8.1, and also on OS X
10.5 - 10.10. Despite this, it works fine on Windows 10.</p>
<p>Even though no drivers are available to download, I recently
plugged one of these scanners into my Windows 10 x64 desktop
(version 1809). To my surprise, Windows Update kicked in and
download a set of drivers and then the scanner was partially
operational.</p>
<p>What do I mean by partial? I was able to scan using the Windows
Image Acquisition framework (WIA) from several applications.
However, none of the buttons on the front of the scanner are
operational - when viewing the properties of the device the
properties page states that no applications are registered that
can use the buttons. For me, this isn't a problem as I generally
only scan pictures and so far haven't needed any OCR facilities.</p>
<p>I'm posting this as I wasn't actually expecting the scanner to
work. This is the second flatbed scanner I've bought over the
long years, the first one was in the Windows 9x era (also a
Canon) and, if memory serves, the scanner simply didn't work
with Windows NT and so once I'd moved onto Windows 2000 it was
an unusable brick.</p>
<p>I bought this scanner second hand from eBay and based on Canon's
website and my personal experience of that previous scanner, I
assumed that it wouldn't work with my Windows 10 machines. While
waiting for it to be delivered I dug out an old and frankly not
very good netbook and stuck Windows 7 on it. While the scanner
worked absolutely fine with this, I decided that I wanted to
write a quick tool for performing chain scanning with as few
user actions as possible. Given that trying to do real work with
that netbook is not feasible, I plugged the scanner into my
desktop in the slim hope it would work so I could develop my
tool. Happily for me, it did!</p>
<p>As long as I have this scanner and Windows 10 I'll test it again
with each new Windows 10 upgrade and will update this post if
the compatibility status changes.</p>
<div class="article-gallery">
<a href="https://images.cyotek.com/image/devblog/canoscan-lide-100-windows-10-1a.png" class="gallery" title="Printers & scanners listing the LiDE 100 - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/canoscan-lide-100-windows-10-1a.png" alt="Printers & scanners listing the LiDE 100" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/canoscan-lide-100-windows-10-1b.png" class="gallery" title="The Scanners and Cameras dialog listing the LiDE 100 - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/canoscan-lide-100-windows-10-1b.png" alt="The Scanners and Cameras dialog listing the LiDE 100" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/canoscan-lide-100-windows-10-1c.png" class="gallery" title="The WIA CanoScan LiDE 100 scanner properties dialog - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/canoscan-lide-100-windows-10-1c.png" alt="The WIA CanoScan LiDE 100 scanner properties dialog" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/canoscan-lide-100-windows-11-1a.png" class="gallery" title="After plugging in the scanner, Windows 11 recognised the scanner but didn't install it - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/canoscan-lide-100-windows-11-1a.png" alt="After plugging in the scanner, Windows 11 recognised the scanner but didn't install it" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/canoscan-lide-100-windows-11-1b.png" class="gallery" title="After checking for updates an update was found - but hidden - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/canoscan-lide-100-windows-11-1b.png" alt="After checking for updates an update was found - but hidden" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/canoscan-lide-100-windows-11-1c.png" class="gallery" title="When clicking Advanced Options, we can now see an optional update is available - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/canoscan-lide-100-windows-11-1c.png" alt="When clicking Advanced Options, we can now see an optional update is available" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/canoscan-lide-100-windows-11-1d.png" class="gallery" title="Finally when drilling into Optional Updates, the scanner driver is ready to install - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/canoscan-lide-100-windows-11-1d.png" alt="Finally when drilling into Optional Updates, the scanner driver is ready to install" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/canoscan-lide-100-windows-11-1e.png" class="gallery" title="Driver installation - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/canoscan-lide-100-windows-11-1e.png" alt="Driver installation" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/canoscan-lide-100-windows-11-1f.png" class="gallery" title="Driver installation complete - and Device Manager is showing the scanner properly - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/canoscan-lide-100-windows-11-1f.png" alt="Driver installation complete - and Device Manager is showing the scanner properly" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/canoscan-lide-100-windows-11-1g.png" class="gallery" title="Testing the scanner using WIA - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/canoscan-lide-100-windows-11-1g.png" alt="Testing the scanner using WIA" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/canoscan-lide-100-windows-11-1h.png" class="gallery" title="The scanned image - " ><img src="https://images.cyotek.com/image/thumbnail/devblog/canoscan-lide-100-windows-11-1h.png" alt="The scanned image" decoding="async" loading="lazy" /></a></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/canoscan-lide-100-windows-10-compatibility .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comBook Review: The Ray Tracer Challengeurn:uuid:c43d258a-faf9-4c0d-be84-6278b76b8f322019-08-11T09:37:26Z2019-08-11T09:37:26Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/jbtracer.jpg" class="gallery" title="The Ray Tracer Challenge Book Cover" ><img src="https://images.cyotek.com/image/thumbnail/devblog/jbtracer.jpg" alt="The Ray Tracer Challenge Book Cover" decoding="async" loading="lazy" /></a><figcaption>The Ray Tracer Challenge Book Cover</figcaption></figure>
<p>A few months ago I heard about a book named <a href="http://www.raytracerchallenge.com/" rel="external nofollow noopener">The Ray Tracer
Challenge</a> by Jamis Buck. The premise of the book is that it
would teach you how to write a ray tracer from scratch. The
unique approach of the book (and one I have never come across in
any other book) is that it would do this via a test driven
approach - the book would provide the test cases and their
inputs and outputs, and you fill in the blanks in your language
of choice.</p>
<p>I think I've noted in the past that if a book comes with
exercises or practice questions, I skip them, either due to time
constraints or not being in front of a computer when I read (I
prefer my tech books to be old school paper). This was a bit of
a concern as clearly I was going to have to do such exercises, a
fear that turned out to be unfounded. Being fun and having a
goal makes all the difference!</p>
<p>I bought the book on the 31st May and it sat on my desk for a
few weeks whilst I tried to wrap up the next build of WebCopy.
On the 2nd July I made my first first commit and I've spent
almost all my free time up until the start of August working
through it. I originally intended to blog a sort of mini-series
as I progressed, but that went out of the window after the first
commit - I was utterly <em>enthralled</em> by the challenge and didn't
stop for such mundane tasks as writing blog posts! Even tearing
away enough free time to write this review has been challenging,
although I mostly wrote it whilst waiting for scenes to render
and animations build.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/rotate-shape-low.gif" class="gallery" title="A low quality animation of a hollow CSG shape rotating" ><img src="https://images.cyotek.com/image/devblog/rotate-shape-low.gif" alt="A low quality animation of a hollow CSG shape rotating" decoding="async" loading="lazy" /></a><figcaption>A low quality animation of a hollow CSG shape rotating</figcaption></figure><h3 id="how-the-book-is-presented">How the book is presented</h3>
<p>As I noted, The Ray Tracer Challenge is unique to all other
books I've read in the past. Like others, it is broken up into
chapters and sections, each devoted to teaching you how to do
something, be it calculate a shadow or rotate a point in space.
Where it diverges is that for each feature there are a number of
tests, provided in <a href="https://cucumber.io/docs/gherkin/" rel="external nofollow noopener">Gherkin</a> syntax. You implement those
tests, they pass, and bit by bit your ray tracer takes shape,
almost without you realising.</p>
<p>However, Jamis doesn't just throw test cases at you and expect
you to do all the work, he describes how the topic works and
provides pseudo code so you can implement the tricky aspects of
your tracer. Being well versed in algebra and maths is probably
a bonus but is <em>by no means required.</em> I got through the book
mostly without issue and when I did have a problem, Jamis has a
<a href="http://forum.raytracerchallenge.com/" rel="external nofollow noopener">forum</a> where you can ask questions which I found to be very
helpful.</p>
<p>The book is printed in full colour with lots of images and
diagrams. A download containing the Cucumber specification files
is also available so you don't even need to manually type in all
the specifications. All in all, it is an impressive package.</p>
<h3 id="cucumber">Cucumber?</h3>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ray-tracer-16.png" class="gallery" title="A low-ploy model along with a patterned plane" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ray-tracer-16.png" alt="A low-ploy model along with a patterned plane" decoding="async" loading="lazy" /></a><figcaption>A low-ploy model along with a patterned plane</figcaption></figure>
<p>I've spent many years writing tests using either MSTest or nUnit
and other <em>similar</em> test frameworks. <a href="https://cucumber.io/" rel="external nofollow noopener">Cucumber</a> is <strong>not</strong>
your average test framework. I freely admit I spent the first
two weeks fighting how <a href="https://specflow.org/" rel="external nofollow noopener">SpecFlow</a> (a .NET Core implementation
of Cucumber) works but once I started working with it instead of
against it I found it is <em>perfect</em> for this type of book.</p>
<h3 id="stepping-through">Stepping through</h3>
<p>I mentioned that Jamis takes you through the things you need
step by step, firstly by introducing you to points, vectors and
colour, before moving onto writing to a PPM file (a text based
image format) so you can visualise your output. Although the
book recommends you then use an external tool for viewing the
files, I swiftly made my own before switching to a GUI so I
could tinker scenes as I go. After this he describes matrices,
those magical grids of numbers which can make a shape rotate on
any axis, grow or shrink, move and more. I have to confess it
took me a while to think of 3D objects as existing at 0, 0, 0
and having a matrix define where they are positioned in world
space. It definitely makes the maths easier!</p>
<p>With the data structures out of the way, Jamis takes you though
ray intersection with spheres, leading to your first rendering
of a flat sphere, immediately followed by realistic lighting and
shading. After this he shows you how to construct a scene using
nothing but spheres, before then adding shadows to make it more
believable. And, as you won't get very far with just spheres, he
introduces planes as the second primitive shape.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ray-tracer-15.png" class="gallery" title="Hand editing matrix transforms is not great so I ended up creating various UI editors to speed things along" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ray-tracer-15.png" alt="Hand editing matrix transforms is not great so I ended up creating various UI editors to speed things along" decoding="async" loading="lazy" /></a><figcaption>Hand editing matrix transforms is not great so I ended up creating various UI editors to speed things along</figcaption></figure>
<p>Next up is patterns, implementing a variety of basic patterns
such as stripes, checkers, gradient and rings. Then he
introduces reflection and refraction. With this in place you can
generate some really impressive visuals, although it takes a
good understanding of light so you can set up materials
appropriately.</p>
<p>More than half the book is covered now, and that is the last of
the rendering techniques. The remainder of the book adds more
primitive shapes - cubes, cylinders and cones. It takes a quick
segue into into grouping shapes, something <em>incredibly</em> helpful
- I made a right mess of trying to create table by individually
positing and sizing cubes, instead of constructing a group and
then positioning it as a single action. This then leads to
triangles and how to read WaveFront OBJ files, before moving
into smooth triangles so you can really make your teapot shine!</p>
<p>The penultimate chapter covers Constructive Solid Geometry which
I found particularly impressive. Instead of making an expensive
model from thousands of triangles, you take a few primitive
shapes and combine them using set operations. This lets you
create, for example, a hollowed cube with rounded edges and
other such shapes that would otherwise require complex models. I
especially liked shifting my camera so I was rendering from
inside that shape!</p>
<p>The final chapter is all about additional ideas to try, such as
different lights, texture mapping and more. No guidance here,
you're on your own implementing these!</p>
<p>Finally, there is an appendix which describes, in YAML, the
scene used to render the book cover. Jamis provides a number of
other demonstration scenes in the same format on his forums. The
YAML format is very easy to understand and putting together your
own reader to load these scenes isn't too taxing. When I started
this book I wondered if I would manage to get to the point where
I could render that cover, and it came together so easily I'm
still amazed.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ray-tracer-17.png" class="gallery" title="Shapes can be comprised of thousands of shapes which can dramatically slow down your tracer if not optimised" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ray-tracer-17.png" alt="Shapes can be comprised of thousands of shapes which can dramatically slow down your tracer if not optimised" decoding="async" loading="lazy" /></a><figcaption>Shapes can be comprised of thousands of shapes which can dramatically slow down your tracer if not optimised</figcaption></figure>
<p>Although it seems like the book jumps from subject to subject as
you progress, it makes sense in the context of the tracer by
gradually introducing the more difficult concepts and
techniques.</p>
<h3 id="is-this-book-for-you">Is this book for you?</h3>
<p>If you're interested in rendering 3D graphics then this could
well be the book for you. Even if you aren't interested so much
in the rendering but working in a 3D space then this may be
helpful.</p>
<p>You may also enjoy the book if you simply want a fun challenge
where you don't copy and paste code, but build something fully
functional from scratch.</p>
<p>You don't need to be a maths wizard to implement the ray tracer,
Jamis provides pretty much everything you need as you go along.
With that said, I did struggle sometimes - trying to position
things in 3D space was a case of &quot;enter random values until it
looks good&quot;!</p>
<p>As the book provides all test cases as Cucumber specifications,
I think if you don't have access to this in your own development
environment this will be a significant burden - I would not have
like to try and implement these as &quot;pure&quot; MSTest or nUnit tests.</p>
<h3 id="closing-thoughts">Closing thoughts</h3>
<p>I am very glad I bought this book - as I noted I have never read
anything quite like it. The way it was presented means I was
never truly floundering, and it quietly taught me without me
realising. I've always had trouble reading linear algebra but
I've noticed that I'm now finding it easier to read some
formulae. I've still got a way to go with that sort of stuff,
but I'm definitely improving.</p>
<p>I did have some problems - several times I didn't notice that
the result of a square root was supposed to be negated and
wondering why my tests were failing. On occasion I simply didn't
read instructions properly and implemented something almost, but
not quite, right. And of course, I spent a lot of time trying to
make SpecFlow fit how I have traditionally tested. But it was
pretty much all user error... I didn't spot any mistakes in the
book or run into insurmountable issues. I do wish there were a
few more reference YAML files though, that would mean I could
compare images I generate with those in the book to make sure I
really had things working correctly, not to mentioning actually
defining correct camera and lighting properties is more
challenging that you might think.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/rotate-light-low.gif" class="gallery" title="A low quality animation showing light revolving around a scene" ><img src="https://images.cyotek.com/image/devblog/rotate-light-low.gif" alt="A low quality animation showing light revolving around a scene" decoding="async" loading="lazy" /></a><figcaption>A low quality animation showing light revolving around a scene</figcaption></figure>
<p>In short, I found this book <em>fun.</em> Really, really fun. It
reminded me why I became a programmer in the first place. It
also had the unexpected side effect of making me realise that
I've been burning out for a while now doing the same old same
old... I think I'll be making some changes around here soon.</p>
<p>I also can't stress enough how awesome the test driven format of
the book is. It was quite something to implement a few tests,
and just like that you have another amazing piece of rendering
functionality.</p>
<p>Even though I've reached the end of book, this isn't the end of
the story. At the time of writing this review, Jamis has also
published 3 bonus chapters on the forums, one for creating
bounding boxes, another for soft shadows and the third for
texture mapping. I really can't wait to get that last one
implemented! Some of the (low poly!) OBJ files I've tested also
reference external materials and supporting these should be
straightforward enough.</p>
<p>As I wrap up this blog post, I've just ironed out some issues
with my Bounding Volume Hierarchies implementation and now
render speed of a model comprised of 23000 triangles at
1920x1080 has gone from 1 hour 20 minutes (already greatly
improved by some other optimisations) to <em>12 minutes.</em> Perfect!</p>
<h4 id="source-code">Source Code?</h4>
<p>I committed all my source to a Git repository and I plan on
making this available once I've finished tiding up the code.</p>
<h4 id="comments">Comments?</h4>
<p>I'm still trying to find my voice with these reviews so if you
have any comments please let me know!</p>
<h3 id="screen-shot-gallery">Screen Shot Gallery</h3>
<p>Below are some screen shots of my journey through the book. I
wish I'd thought to take more of the earlier steps, something to
keep in mind for next time.</p>
<div class="article-gallery">
<a href="https://images.cyotek.com/image/devblog/ray-tracer-1.png" class="gallery" title="Humble Beginnings - Getting to grips with points and vectors" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ray-tracer-1.png" alt="Humble Beginnings" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/ray-tracer-2.png" class="gallery" title="Baby Steps - My first 3D render!" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ray-tracer-2.png" alt="Baby Steps" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/ray-tracer-3.png" class="gallery" title="Making a scene - I was very proud just to get this far" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ray-tracer-3.png" alt="Making a scene" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/ray-tracer-4.png" class="gallery" title="Adding Depth - Implementing shadows helps bring the scene to life" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ray-tracer-4.png" alt="Adding Depth" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/ray-tracer-5.png" class="gallery" title="Is it a bird? - No, it's a plane. A very versatile shape!" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ray-tracer-5.png" alt="Is it a bird?" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/ray-tracer-6.png" class="gallery" title="Patterns - Applying a pattern to a surface. Or in this case, two patterns blended together" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ray-tracer-6.png" alt="Patterns" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/ray-tracer-7.png" class="gallery" title="Reflection - Reflecting on the situation" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ray-tracer-7.png" alt="Reflection" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/ray-tracer-8.png" class="gallery" title="Cubes - My first attempts at this were quite disastrous. But cubes are very versatile too" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ray-tracer-8.png" alt="Cubes" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/ray-tracer-14.png" class="gallery" title="Buggy Cones - Sometimes I introduced bugs, like these artefacts when rendering cones" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ray-tracer-14.png" alt="Buggy Cones" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/ray-tracer-10.png" class="gallery" title="Triangles and Objects - Of course, one cannot render a 3D scene without 'the' teapot" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ray-tracer-10.png" alt="Triangles and Objects" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/ray-tracer-11.png" class="gallery" title="CSG - I'm still tickled pink at being able to produce this composite shape" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ray-tracer-11.png" alt="CSG" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/ray-tracer-12.png" class="gallery" title="YAML Scenes - Rendering a complex scene from a definition in YAML" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ray-tracer-12.png" alt="YAML Scenes" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/ray-tracer-9.png" class="gallery" title="Cyclinders - Another sample scene, this one of cylinders and one which helped expose a rather subtle bug in my code" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ray-tracer-9.png" alt="Cyclinders" decoding="async" loading="lazy" /></a><a href="https://images.cyotek.com/image/devblog/ray-tracer-13.png" class="gallery" title="Cover! - Rendering the book's cover. Achievement awarded!" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ray-tracer-13.png" alt="Cover!" decoding="async" loading="lazy" /></a></div>
<h2 id="update-history">Update History</h2>
<ul>
<li>2019-08-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/book-review-the-ray-tracer-challenge .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comError VSP1048 when trying to instrument assembly created using Visual Studio 2019urn:uuid:13934813-aefa-45f1-9354-f7bccf4c01692019-08-03T19:08:26Z2019-08-03T19:08:26Z<p>In short - projects created using Visual Studio 2019 by default
are compiled in a deterministic fashion, meaning if you compile
the file multiple times without any changes, you will get
identical output each time. Previous versions of Visual Studio
used to include the compilation timestamp as part of the file
header, causing the output to be different each time.</p>
<p>I'm not sure of the reasons why, but some of the tools provided
with older versions of Visual Studio are unable to process
assemblies compiled with this flag - they report them as not
being valid image files and won't continue.</p>
<p>The solution is to either compile the assemblies in the
traditional way, by deleting the flag from the <code>.csproj</code> file.
Or, use a newer version of the tool that is specifically
designed for use with these assemblies.</p>
<h2 id="background">Background</h2>
<p>Last week I was performing an automated test run of a product
suite. The tests are written using MSTest and are shelled by a
custom application that takes care of instrumenting assemblies
for code coverage, running the code coverage daemon, running the
tests, analysing the results and then producing a nice report
for QA. Originally written in 2009 using Visual Studio 2008 it
was incrementally updated to Visual Studio 2013 and has been
untouched for a few years whilst successive versions of Visual
Studio roll by.</p>
<p>The last change I made to this product suite involved the
addition of several new libraries, all of which are .NET
Framework libraries using the old style project format. As I
recently switched permanently to using Visual Studio 2109 (from
2015), the new libraries and their associated test libraries
were all created using Visual Studio 2019. Two of these new
libraries were flagged as requiring code coverage, which is
where this saga started.</p>
<p>When the test run commenced I noticed it had reported warnings
stating that instrumentation of the new assemblies had failed
and after the test run had completed, no coverage was present
for these files.</p>
<p>Naturally the first thing I tried to do was manually instrument
the files using the Visual Studio 2013 version of <code>vsintr.exe</code>,
as used by our testing tool.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
&gt; <span class="string">&quot;C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\vsinstr.exe&quot;</span> <span class="string">&quot;&lt;Company&gt;.&lt;Product&gt;.&lt;Tech&gt;.IntegrationTests.dll&quot;</span>
Microsoft (R) VSInstr Post-Link Instrumentation 12.0.31101 x86
Copyright (C) Microsoft Corp. All rights reserved.

File to Process:
 &lt;Company&gt;.&lt;Product&gt;.&lt;Tech&gt;.IntegrationTests.dll --&gt; &lt;Company&gt;.&lt;Product&gt;.&lt;Tech&gt;.IntegrationTests.dll
Original file backed up to &lt;Company&gt;.&lt;Product&gt;.&lt;Tech&gt;.IntegrationTests.dll.orig

Error VSP1048: Internal instrumentation error.
</pre>
</figure>
<p>Not a very helpful error message, and a little bit of
duckduckgo'ing didn't shed a huge amount of light.</p>
<p>Next, I tried Visual Studio 2015.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
&gt; <span class="string">&quot;C:\Program Files (x86)\Microsoft Visual Studio 14.0\Team Tools\Performance Tools\vsinstr.exe&quot;</span> <span class="string">&quot;&lt;Company&gt;.&lt;Product&gt;.&lt;Tech&gt;.IntegrationTests.dll&quot;</span>
Microsoft (R) VSInstr Post-Link Instrumentation 14.0.25420 x86
Copyright (C) Microsoft Corp. All rights reserved.

Error VSP1033: The file <span class="string">&#39;&lt;Company&gt;.&lt;Product&gt;.&lt;Tech&gt;.IntegrationTests.dll&#39;</span> does <span class="keyword">not</span> contain a recognized executable image.
</pre>
</figure>
<p>At least this time we have a better error message, although at
this point I still didn't know <em>why</em> it wasn't a valid image.</p>
<p>Finally, the last version of Visual Studio I had available on my
machine was
2019.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
&gt; <span class="string">&quot;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Team Tools\Performance Tools\vsinstr.exe&quot;</span> <span class="string">&quot;&lt;Company&gt;.&lt;Product&gt;.&lt;Tech&gt;.IntegrationTests.dll&quot;</span>
Microsoft (R) VSInstr Post-Link Instrumentation 16.1.28929.3 x86
Analyzing component...
Instrumenting component...
################### (100.00 <span class="string">%%</span>)
Successfully instrumented file &lt;Company&gt;.&lt;Product&gt;.&lt;Tech&gt;.IntegrationTests.dll.
</pre>
</figure>
<p>Success! Well, I had created the projects using Visual Studio
2019 and all the older projects (some of which were originally
created using Visual Studio 2005) were all fine, as they have
been for <em>years</em> (more than a decade for some of them!).
Logically then, it must be something to do with the new project
files.</p>
<blockquote>
<p>While this sounds obvious, in my defence I somehow mucked up
my initial testing of manual instrumentation using the VS2019
tooling so that I received the exact same error message as
from VS2015. This threw me off thinking that <em>all</em> Visual
Studio versions were affected and that there was another issue
at play.</p>
</blockquote>
<p>Eventually I opened one of the affected <code>.csproj</code> files in
Notepad++ and started looking for anomalies. Right away in the
first group, I found this innocuous line</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">PropertyGroup</span><span class="symbol">&gt;</span>
 <span class="comment">&lt;!-- snip --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">Deterministic</span><span class="symbol">&gt;</span>true<span class="symbol">&lt;/</span><span class="name">Deterministic</span><span class="symbol">&gt;</span>
 <span class="comment">&lt;!-- snip --&gt;</span>
<span class="symbol">&lt;/</span><span class="name">PropertyGroup</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>I remember reading about this a few months back when I was
looking into how you could determine the time an assembly was
compiled, and reading the file header was one option I looked
at. I also remembered that the use of this flag means that wild
card versioning no longer works. Although I don't use wildcard
versioning, the fact that this flag affects the file headers
rang a warning bell.</p>
<p>Long story short, I deleted those lines from the affected
<code>.csproj</code> files (I left the test projects untouched, MSTest
wasn't bothered about those), reran my tests with full code
coverage metrics. Problem solved!</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>This is merely an informative article on the off-chance there
are other shops with older build processes such as described
above. At some point we need to transition this product suit to
use NUnit for tests as our other, newer, products do, but at
this point I'll probably just update our venerable testing
application to use the Visual Studio 2019 versions of the tools.
As the years go by I find myself more and more on the side of
the &quot;if it's not broken, don't fix it camp&quot;!</p>
<p>As part of this exercise I also discovered that this method of
code coverage is long deprecated and I should be using the
dynamic code coverage tool instead. As long as it generates a
<code>.coverage</code> file that is compatible with the old version then I
shouldn't have too much trouble updating to this this tool
either.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2019-08-03 - 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/error-vsp1048-when-trying-to-instrument-assembly-created-using-visual-studio-2019 .
</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.comChanges to CrashPlan for Small Business that may affect developer backupsurn:uuid:9de25370-18e2-4baf-ab00-f4ff4b70a13b2019-05-18T10:12:13Z2019-05-18T10:06:52Z<p>This morning I received the following email from Code42
regarding their CrashPlan for Small Business service</p>
<blockquote>
<p>Dear Valued Customer,</p>
<p>We are resending this notification to ensure that you are
aware of recent changes we made to our application. Should you
have any questions, or need assistance, please contact our
Support team.</p>
<p>The CrashPlan ® for Small Business Code42 app is intended to
back up your business files (pictures, music, documents,
etc.), not your operating system or applications. For this
reason, we have always recommended you not include
applications or other large files in your selection as they
may not backup or restore correctly. In the past, we have
allowed these files to be backed up despite this
recommendation, however, in May 2019 we made some changes to
our file exclusions and are now disallowing the backup of
application directories, VM image files, and some backup file
types.</p>
<p>You will likely see your file selection and backup archive
size go down and benefit from faster restores, syncs, and
backups. The new excludes are listed below and a full list of
excludes can be found here.</p>
<p>New excludes for Application directories and new file types we
are excluding:
• Applications: /Users/&lt;username&gt;/Applications,
/Applications/, /Program Files/, /Program Files (x86)/<br />
• VM image files: .hdd, .hds, .pvm, .pvs, .vdi, .vfd, .vhd,
.vmc, .vmdk, .vmem, .vmsd, .vmsn, .vmss, .vmtm, .vmwarevm,
.vmx, .vmxf, .vsv, .nvram, .vud, .xva<br />
• Backup files: .bck, .bkf<br />
• Other files: .tmp, .part, .rbf, .tib, .sparseimage</p>
<p>Thank you,<br />
The CrashPlan for Small Business Team</p>
</blockquote>
<p>Despite the fact it starts off by stating this is a resend of a
previous notification, it doesn't appear to be one I've received
before myself, I certainly can't find any other emails from
CrashPlan other than status reports, billing, and the odd
support ticket.</p>
<p>For the most part, I'm not affected by this - I don't use
CrashPlan for backing up operating systems or installed
application as that clearly is nuts and not what the program is
for. As well as for business documents, source code and related
resources, I do however use it to back up the <em>sources</em> for
developer tools (e.g. installation setups, previous versions,
etc), and also half dozen or so Windows VM's that I use for
testing with. When I checked the backup set I had defined for VM
backups I could see that yes, the VirtualBox disk images were
now excluded and couldn't be added back.</p>
<p>Perhaps that isn't really what CrashPlan is supposed to be for
either, but removing them from existing backups isn't exactly a
good move. CrashPlan is my final line of backups, for when
something has gone so catastrophically wrong that the backups
in-between are no longer viable. If I didn't see this
notification and found out my VM's were missing or out of date
when I <em>really</em> needed them, I'd probably be a little unhappy!
(and why don't banks offer safety deposit boxes any more?)</p>
<p>Although it's mainly only the removal of the VirtualBox disk
images that is affecting me, according to their page it also
affects files such as <code>.ini</code>, of which I also make use of.
Again, I'm a developer backing up developer resources. I did a
spot check of a source code folders containing ini files and
they were still present and not flagged as no longer being
backed up, but this is still a cause for concern, especially if
they decide to start expanding that to include all program files
or archives.</p>
<p>I've been with CrashPlan for a few years now and they've already
pulled the rug out from under my feet once when they
discontinued their previous consumer product, again when they
discontinued their Java product and replaced it with a nigh on
unusable Electron monstrosity, and now they are doing it again.
I feel this is a little worse though as now there are holes in
my backups as they arbitrarily apply new restrictions to
existing data. Of course, it's their service and they can do
what they want with it, but that doesn't mean I have to continue
using it.</p>
<p>I remember when I first moved to CrashPlan from Carbonite, one
fantastic feature of Carbonite was that it had a shell extension
that added status overlays to all files being backed up. You
could instantly see at a glance files being backed up, files out
of date, files not included to be backed up, etc. CrashPlan
doesn't offer this and it isn't feasible to get this data (I
can't stress enough how awful the UI is), so basically I don't
have a clue if there are other holes in my backups or not.</p>
<p>If you use CrashPlan for Small Business, you should check over
your backups to ensure the things you expect to be backed up are
still being backed up. There's a full list of things that
CrashPlan won't allow you to back up on their <a href="https://support.code42.com/CrashPlan/6/Troubleshooting/What_is_not_backing_up" rel="external nofollow noopener">support
page</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/changes-to-crashplan-for-small-business-that-may-affect-developer-backups .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating an ndrules file for use with NDependurn:uuid:bf54134c-05ee-4445-95e4-bf674f1adf842019-04-17T08:23:43Z2019-04-16T16:10:34Z<p>A long time ago, I wrote of some <a href="/post/a-brief-look-at-code-analysis-with-ndepend">initial experiences</a> using
<a href="https://www.ndepend.com/" rel="external nofollow noopener">NDepend</a>. Since then, I've done little with the product,
although I did integrate it with Jenkins for both <a href="/post/integrating-ndepend-with-jenkins">pipeline</a>
and <a href="/post/integrating-ndepend-with-jenkins-freestyle-jobs">freestyle</a> jobs and so every time the CI builds it
executes NDepend. However, the NDepend configuration used by
these jobs is (probably) significantly out of date.</p>
<p>As I've just upgraded NDepend to the current version, I thought
I'd better update the configurations. Fortunately when I created
NDepend projects, I always edited them to use an external rules
file and to change all the paths to relative. Absolute paths
don't work well in CI scenarios and it always felt very wrong to
me to have every single project duplicating all the rules -
updating them would be painful experience.</p>
<p>The rules file I created dates from January 2017; I'm sure that
<em>something</em> has changed in that period so I wanted to update it.
The only problem is I didn't make any notes on <em>how</em> I created
the rules file and it wasn't an obvious task to perform. This
blog post is mainly notes for Forgetful Future Richard but I
feel the information is useful enough to warrant it being a
public post.</p>
<h2 id="reasons-to-use-an-external-file">Reasons to use an external file</h2>
<p>The main reason I think an external file should be used is
simply to make updating rules easier, and to avoid having
everything duplicated.</p>
<p>Although I never got to that point, NDepend allows you to
combine both project and external rules, so I reasoned I would
have all the stock rules in one externally referenced file and
then add application specific rules at the project level.</p>
<h2 id="creating-a-rules-file-from-the-gui">Creating a rules file from the GUI</h2>
<p>NDepend doesn't seem to ship with an external rule file and so I
need to manually create one. There also doesn't seem to be a
straight forward way of creating one either, so the approach is
a little convoluted. I will update this post if I find a better
way.</p>
<blockquote>
<p>These instructions are using the stand-alone Visual NDepend
software, I haven't tested to see if the same applies when
using the Visual Studio extension.</p>
</blockquote>
<ol>
<li>Open Visual NDepend</li>
<li>Open the <strong>File</strong> menu and select <strong>New Project</strong></li>
<li>From the <em>New NDepend Project</em> dialog, enter a dummy name and
file name and click <strong>OK</strong></li>
<li>When the <em>Project Properties</em> window appears, click <strong>Add VS
Solution or Project</strong></li>
<li>Click <strong>Browse</strong> and then choose any existing Visual Studio
solution</li>
<li>Press <kbd>F5</kbd> to run the analysis</li>
<li>When the analysis is complete, click the <strong>Rule File</strong> drop
down in the <em>Queries and Rules Explorer</em> window and then
click <strong>Create Rule File</strong></li>
<li>Enter the filename of the file and then click <strong>OK</strong> to
create an empty file (make sure the filename is unique or
you'll get an error)</li>
<li>Right click <strong>Project Rules</strong> from the <em>Queries and Rules
Explorer</em> tree and click <strong>Copy</strong></li>
<li>Find the node for the new rules file you just created, right
click it and click <strong>Paste Group </strong></li>
<li>Exit Visual NDepend and delete the dummy project</li>
</ol>
<h2 id="creating-a-rules-file-via-text-editor">Creating a rules file via text editor</h2>
<p>There's a lot of steps to jump through in order to get default
rules, you can make it slightly easier by doing some of the work
using a text editor.</p>
<ol>
<li>Follow steps 1 to 3 above to create a dummy project</li>
<li>Exit Visual NDepend</li>
<li>Open the dummy project you created in a text editor of your
choice</li>
<li>Do a search for <code>&lt;Queries&gt;</code></li>
<li>Select from the <code>&lt;Queries&gt;</code> element to to the closing
<code>&lt;/Queries&gt;</code> value - this will be almost at the end of the
file so you can just select the remainder of the document and
backtrack a few characters</li>
<li>Copy this into a new text file</li>
</ol>
<p>This approach means you don't have to run a dummy analysis, and
also means you don't end up with a superfluous <strong>Project Rules</strong>
container group.</p>
<h2 id="updating-a-ndepend-project-to-use-a-rules-file">Updating a NDepend project to use a rules file</h2>
<p>The GUI doesn't allow you to delete the default <strong>Project
Rules</strong> group so I do this directly by editing the <code>.ndproj</code>
file in a text editor, rather than having to delete each child
group one by one.</p>
<ul>
<li>Open a NDepend project in a text editor</li>
<li>Delete the all text between <code>&lt;Queries&gt;</code> and <code>&lt;/Queries&gt;</code> (I
usually delete the whole thing, NDepend doesn't seem to mind)</li>
<li>Add <code>&lt;RuleFile&gt;</code> children to the <code>&lt;RuleFiles&gt;</code> element for
each file you wish to reference</li>
<li>Save the file</li>
</ul>
<p>This is an example element taken from one of my existing
projects</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">NDepend</span> <span class="name">AppName</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">...</span><span class="symbol">&quot;</span> <span class="name">Platform</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">...</span><span class="symbol">&quot;</span> <span class="name">FileWrittenByProductVersion</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">RuleFiles</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">RuleFile</span> <span class="name">Active</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">True</span><span class="symbol">&quot;</span> <span class="name">Path</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">..\..\..\build\ndepend\v7-stock.ndrules</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">RuleFiles</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">NDepend</span><span class="symbol">&gt;</span>
</pre>
</figure>
<h2 id="closing-thoughts">Closing thoughts</h2>
<p>When I compared the &quot;new&quot; stock rules I created against my
previous one from 2017, there were over 600 differences. Some of
these are documentation changes or priority, but there were
also a bunch of brand new rules. Therefore if you are using
external files it behoves you to check once in a while to see if
NDepend has introduced stock rule changes that could be of
benefit.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2019-04-16 - 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-an-ndrules-file-for-use-with-ndepend .
</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.comFrustrations of third party softwareurn:uuid:d9558fd6-7c6c-4e4f-9dc5-f1676f67d8c02019-03-02T13:21:21Z2019-03-02T13:21:21Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/vs-resharper-warnings-1a.png" class="gallery" title="Remind me again why I still use Resharper" ><img src="https://images.cyotek.com/image/thumbnail/devblog/vs-resharper-warnings-1a.png" alt="Remind me again why I still use Resharper" decoding="async" loading="lazy" /></a><figcaption>Remind me again why I still use Resharper</figcaption></figure>
<p>No detailed blog post, just a screenshot that neatly echo's my
ongoing frustration with Resharper. It was a lot longer than 7
seconds too...</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/frustrations-of-third-party-software .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comNew option in Visual Studio 2019 keeps console window open after debuggingurn:uuid:38a1e7ca-487c-45b2-bf18-a6dcb8cc1d9e2019-03-02T13:06:52Z2019-03-02T13:06:52Z<p>For as long as I've been writing console applications, I've at
the end of <code>Main</code> I've always had a piece of code to require
user input before exiting when in debug mode, in order to review
any output before the window closes. Not an onerous task, but
one I immediately do as soon as creating a new application.</p>
<h2 id="new-features">New features?</h2>
<p>Today I created a new .NET Core console application in Visual
Studio 2019 preview and added by code without a second thought.</p>
<p>When I ran the application I was in for two surprises - the
first was the icon had changed, to a fetching purple. The second
was, after the program had ran and displayed my usual &quot;<em>(Press
any key to exit.)</em>&quot; message, the console stayed open - with
another message to press a key to exit.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/vs2019-console-1a.png" class="gallery" title="Visual Studio now automatically keeps console windows opening after the program ends" ><img src="https://images.cyotek.com/image/thumbnail/devblog/vs2019-console-1a.png" alt="Visual Studio now automatically keeps console windows opening after the program ends" decoding="async" loading="lazy" /></a><figcaption>Visual Studio now automatically keeps console windows opening after the program ends</figcaption></figure>
<p>A small change, but a welcome one! And, just in case it's not to
everyone's liking, there's even an option to disable the
behaviour if you don't like it.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/vs2019-console-1b.png" class="gallery" title="A new option for allow the console to automatically close has been added" ><img src="https://images.cyotek.com/image/thumbnail/devblog/vs2019-console-1b.png" alt="A new option for allow the console to automatically close has been added" decoding="async" loading="lazy" /></a><figcaption>A new option for allow the console to automatically close has been added</figcaption></figure><h2 id="always-a-catch">Always a catch</h2>
<p>Unfortunately, this new option doesn't seem to work for old
school console applications using the full .NET Framework, so
I'll continue adding my boilerplate blocks to any new .NET
Framework console applications I create.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2019-03-02 - 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/new-option-in-visual-studio-2019-keeps-console-window-open-after-debugging .
</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.comTools we use - 2018 editionurn:uuid:a6229105-32ef-46ee-ac41-8dbfa4cbd9b82019-07-19T06:28:45Z2019-01-01T19:35:52Z<p>Happy New Year! While once again it's that time for the list of software products and services I personally used throughout the previous year, it's also a birthday of sorts - this year will be the 10th year since Cyotek was reactivated after being shut down and also since I started blogging for the first time.</p>
<p>Very little change from previous years, this may change as I force myself to move to .NET Core.</p>
<h2 id="operating-systems">Operating Systems</h2>
<ul>
<li>Windows Home Server 2011 - file server, SVN repository, backup host, CI server - <em>If it ain't broke, don't fix it</em></li>
<li>Windows 10 Professional - development machines</li>
<li><del>Windows 10 (virtualized)</del> - I tried using a pair of 32bit and 64bit Windows 10 VM's for software testing but the performance is so dire I gave up</li>
<li>Windows 7 (virtualized) - testing</li>
</ul>
<h2 id="development-tools">Development Tools</h2>
<ul>
<li><a href="https://www.getpostman.com/" rel="external nofollow noopener">Postman</a> is a absolutely brilliant client for testing REST services</li>
<li><a href="http://www.microsoft.com/visualstudio/" rel="external nofollow noopener">Visual Studio 2015 Premium / Visual Studio 2019 Preview</a> - best IDE bar none</li>
<li><a href="https://www.jetbrains.com/decompiler/" rel="external nofollow noopener">DotPeek</a> - a decent replacement to .NET Reflector that can view things that Reflector can't, making it a worthwhile replacement despite some bugs and being chronically slow to start</li>
<li><a href="https://www.ndepend.com/" rel="external nofollow noopener">NDepend</a> - static code analysis. I have quite a love / hate relationship with this application; so much so that I barely use the user interface at all and rely on reports published to Jenkins as part of a common build pipeline</li>
</ul>
<h2 id="visual-studio-extensions">Visual Studio Extensions</h2>
<ul>
<li><a href="http://www.oz-code.com/" rel="external nofollow noopener">OzCode</a> - despite the change to a subscription model this is just barely hanging onto being my number one tool and remains an exceptional debugging aid (although Resharper is catching up)</li>
<li><del><a href="https://marketplace.visualstudio.com/items?itemName=RichardJMoss.AddExistingProjects-19444" rel="external nofollow noopener">Cyotek Add Projects</a> - a simple extension for easily adding multiple projects to your solutions.</del> I don't use this at all now since converting all common projects into NuGet packages</li>
<li><a href="http://editorconfig.org/" rel="external nofollow noopener">EditorConfig</a> - useful for OSS projects to avoid space-vs-tab wars</li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=MadsKristensen.FileNesting" rel="external nofollow noopener">File Nesting</a> - allows you to easily nest (or unnest!) files, great for TypeScript or T4 templates</li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=MadsKristensen.OpenCommandLine" rel="external nofollow noopener">Open Command Line</a> - easily open command prompts, PowerShell prompts, or other tools to your project / solution directories</li>
<li><a href="http://mike-ward.net/vscoloroutput/" rel="external nofollow noopener">VSColorOutput</a> - add colour coding to Visual Studio's Output window</li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=SteveDowerMSFT.IndentGuides" rel="external nofollow noopener">Indent Guides</a> - easily see where you are in nested code</li>
<li><a href="http://www.jetbrains.com/resharper/" rel="external nofollow noopener">Resharper</a> - this is also starting to offer OzCode features which should be interesting in future as I have a real issue with renting software</li>
<li><a href="http://www.ncrunch.net/" rel="external nofollow noopener">NCrunch for Visual Studio</a> - frequently updated automated parallel continuous testing tool (there's a mouthful). Works with NUnit, MSTest and a variety of other test systems. Great for TDD and picking up how a simple change you made to one part of your project completely destroys another part. Although Resharper has offered continuous testing options for a while now, NCrunch is the superior tool - can't wait for VS2019 support</li>
</ul>
<h2 id="analytics">Analytics</h2>
<ul>
<li>Unnamed Analytics. After dropping Luminitix, I replaced the data collection with a home grown solution using RavenDB, although I've yet to write a front end to look at the data effectively</li>
<li><a href="https://matomo.org/" rel="external nofollow noopener">Matomo</a> - currently trialling this web based analytics software to gain anonymous insights into cyotek.com usage</li>
</ul>
<h2 id="profiling">Profiling</h2>
<ul>
<li><a href="https://www.jetbrains.com/profiler/" rel="external nofollow noopener">dotTrace</a> - although I prefer the ANTS profiler, dotTrace is a very usable profiler and given it is included in my Resharper Ultimate subscription, it's a no-brainer to use</li>
<li><a href="https://www.jetbrains.com/dotmemory/" rel="external nofollow noopener">dotMemory</a> - memory profiling is hard, need all the help we can get</li>
</ul>
<h2 id="documentation-tools">Documentation Tools</h2>
<ul>
<li>HelpWrite - the first application offered by Ariad in the mists of time, now reincarnated and producing no-frills documentation from simple markdown and YAML</li>
<li><a href="http://www.atomineerutils.com/" rel="external nofollow noopener">Atomineer Pro Documentation</a> - automatically generate XML comment documentation in your source code</li>
<li><a href="http://markdownedit.com/" rel="external nofollow noopener">MarkdownEdit</a> - a no frills minimalist markdown editor</li>
<li><a href="http://notepad-plus-plus.org/" rel="external nofollow noopener">Notepad++</a> - because Notepad hasn't changed in 20 years (moving menu items around doesn't count!)</li>
</ul>
<h2 id="continuous-integration">Continuous Integration</h2>
<ul>
<li><a href="https://jenkins.io/index.html" rel="external nofollow noopener">Jenkins</a> + <a href="http://afonsof.com/jenkins-material-theme/" rel="external nofollow noopener">Jenkins Material Theme</a> is easy to install, doesn't need a database server and has a rich plugin ecosystem, even for .NET developers. I use this to build, test and deploy all our products and libraries</li>
</ul>
<h2 id="testing">Testing</h2>
<ul>
<li><a href="http://nunit.org/" rel="external nofollow noopener">NUnit</a> is our test framework of choice, for no particular reason other than it was the first one we tried after getting fed up of MSTest's limitations</li>
</ul>
<h2 id="graphics-tools">Graphics Tools</h2>
<ul>
<li><a href="http://www.getpaint.net/" rel="external nofollow noopener">Paint.NET</a> - brilliant bitmap editor with extensive plugins</li>
<li><a href="http://www.axialis.com/iconworkshop/" rel="external nofollow noopener">Axialis IconWorkshop</a> - very nice icon editor, been using this for untold years now since Microangelo was abandoned</li>
<li><a href="http://cyotek.com/cyotek-spriter">Cyotek Spriter</a> - sprite / image map generation software that is sore need of optimisation and TLC</li>
<li><a href="http://cyotek.com/cyotek-gif-animator/">Cyotek Gif Animator</a> - gif animation creator that was shaping up nicely, although it is another application I really want to spend more time improving</li>
<li><a href="http://www.angelcode.com/products/bmfont/" rel="external nofollow noopener">AngelCode BMFont</a> - although I haven't had a change to continue with game development for some years now (something else I'd like to change in 2019), for bitmap font creation I use BMFont along with our own <a href="https://github.com/cyotek/Cyotek.Drawing.BitmapFont" rel="external nofollow noopener">parser</a></li>
</ul>
<h2 id="virtualization">Virtualization</h2>
<ul>
<li><a href="https://www.virtualbox.org/" rel="external nofollow noopener">Oracle VM VirtualBox</a> - for creating guest OS's for testing purposes. Version 6 has just been released and apparently with play nice with Hyper-V so I might (finally) be able to re-enable that</li>
</ul>
<h2 id="version-control">Version Control</h2>
<ul>
<li><a href="http://tortoisesvn.net/" rel="external nofollow noopener">TortoiseSVN</a> - Windows Explorer integration for SVN</li>
<li><a href="https://www.visualsvn.com/visualsvn/" rel="external nofollow noopener">VisualSVN</a> - Subversion support for Visual Studio. Unlike AnhkSVN, VisualSVN uses TortoiseSVN under the hood, meaning that Explorer and Visual Studio are always in the same state no matter where I commit from, something which used to frustrate me no end with AnhkSVN</li>
<li><a href="http://www.visualsvn.com/server/" rel="external nofollow noopener">VisualSVN Server</a> - Subversion Server for Windows</li>
<li><a href="https://github.com/" rel="external nofollow noopener">GitHub</a> / <a href="https://desktop.github.com/" rel="external nofollow noopener">GitHub Desktop</a> - for providing and working with the open source code we publish</li>
</ul>
<h2 id="filedirectory-tools">File/directory tools</h2>
<ul>
<li><a href="http://winmerge.org/" rel="external nofollow noopener">WinMerge</a> - excellent file or directory comparison utility</li>
<li><a href="http://stefanstools.sourceforge.net/grepWin.html" rel="external nofollow noopener">WinGrep</a> - another excellent tool for swiftly searching directories for files containing specific strings or expressions</li>
<li><a href="https://filezilla-project.org/" rel="external nofollow noopener">FileZilla</a> - simple FTP client that has served my needs for years now</li>
</ul>
<h2 id="backups">Backups</h2>
<ul>
<li><a href="http://cyotek.com/cyotek-copytools">Cyotek CopyTools</a> - we use this for offline backups of source code, assets and resources, documents, actually pretty much anything we generate; including backing up the backups!</li>
<li><a href="http://www.code42.com/crashplan/" rel="external nofollow noopener">CrashPlan</a> - CrashPlan creates an online backup of the different offline backups that CopyTools does. If you've ever lost a harddisk before with critical data on it that's nowhere else, you'll have backups squirrelled away everywhere too!</li>
</ul>
<h2 id="security">Security</h2>
<ul>
<li><a href="https://www.comodo.com/" rel="external nofollow noopener">Comodo</a> Although I've had extremely poor service from Comodo in the past, I haven't had any issues over the past two years</li>
<li><a href="https://letsencrypt.org/" rel="external nofollow noopener">Let's Encrypt</a> provide short term SSL certificates for free. If you (or your host) are able to automate the process, this is an exceptional way to get basic SSL for your sites</li>
<li><a href="http://someonewhocares.org/hosts/" rel="external nofollow noopener">Dan Pollocks hosts file</a> blocks your computer from connecting to many thousands of dubious internet hosts and is continuously updated</li>
</ul>
<h2 id="issue-tracking">Issue Tracking</h2>
<ul>
<li><a href="https://www.mantisbt.org/" rel="external nofollow noopener">Mantis Bug Tracker</a> - over the past year I made a serious effort to more effectively plan software releases and keep track of issues, rather than just winging it. This is mostly working thanks to using Mantis Bug Tracker, although as usual WebCopy has had the lions share of development time to the extreme detriment of our other products</li>
<li><a href="https://github.com/cyotek/MantisSharp" rel="external nofollow noopener">MantisSharp</a> - I use our MantisSharp library to add integration between various applications and our MantisBT instance, notable for raising new issues from our automated error monitor, and for creating roadmaps on cyotek.com product pages although as usual I haven't had much time to maintain it</li>
</ul>
<h2 id="cms">CMS</h2>
<ul>
<li><del><a href="https://getkirby.com/" rel="external nofollow noopener">Kirby</a> - although cyotek.com uses a custom home built CMS, I had been looking a Kirby as an alternative for some aspects such as the Knowledge Base</del> Most likely going to focus on another home grown solution using .NET Core</li>
</ul>
<h2 id="help-desk">Help Desk</h2>
<ul>
<li><ins>New!</ins> <a href="https://www.maiansupport.com/" rel="external nofollow noopener">Maian Support</a> - instead of trying to keep track of emails, I've been using the commercial version of Maian support to manage user support requests and feedback submissions</li>
</ul>
<h2 id="other">Other</h2>
<ul>
<li><a href="https://ravendb.net/" rel="external nofollow noopener">RavenDB</a> - still not using this for much as I can't seem to effectively query the data from Raven Studio</li>
<li><a href="https://kodi.tv/" rel="external nofollow noopener">Kodi</a> - I've used this for years now to watch video on various generations of Raspberry Pi. I found the Films and TV (or Movies and TV) application that ships with Windows 10 to be absolute rubbish and was very glad when Kodi became available on the Microsoft Store</li>
<li><a href="https://rufus.akeo.ie/" rel="external nofollow noopener">Rufus</a> - I use this utility for writing ISO images to USB, useful for setting up new physical machines in an age where CD drives are fairly obsolete</li>
<li><a href="https://sourceforge.net/projects/win32diskimager/" rel="external nofollow noopener">Win32 Disk Imager</a> - useful for burning ISO images to SD cards which I do for Raspberry Pi distributions. I used to use this for USB as well but now I prefer Rufus for that</li>
<li><a href="https://justgetflux.com/" rel="external nofollow noopener">f.lux</a> - I've been using this utterly fantastic software for years. It adapts your monitor to the time of day, removing blue light as evening approaches and helps reduce eye strain when coding at night</li>
<li><a href="https://www.mozilla.org/firefox/" rel="external nofollow noopener">Firefox</a> - I switched to this as my primary browser a few months as my own protest against Chrome's dominance (and don't get me started on Microsoft's recent ill advised capitulation)</li>
<li><a href="https://duckduckgo.com/" rel="external nofollow noopener">DuckDuckGo</a> <em>the search engine that doesn't track you</em> - I can't remember when I made the switch to DuckDuckGo, probably two or three years ago and it has served me perfectly well since</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/tools-we-use-2018-edition .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comChecking if your Windows Forms applications are ready for .NET Core 3.0urn:uuid:e6a10d9f-0352-4779-b9bb-2d88dc3599dc2018-08-09T19:11:53Z2018-08-09T19:11:53Z<p>Since the recent <a href="https://blogs.msdn.microsoft.com/dotnet/2018/05/07/net-core-3-and-support-for-windows-desktop-applications/" rel="external nofollow noopener">announcement</a> that .NE Core 3.0 would
support Windows Forms, I've been cautiously optimistic. Over the
last week or so I've finally started experimenting with ASP.NET
Core 2.1 and liking what I see (mostly, I haven't made my mind
up with Razor Pages yet!).</p>
<p>A couple of days ago Microsoft made another <a href="https://blogs.msdn.microsoft.com/dotnet/2018/08/08/are-your-windows-forms-and-wpf-applications-ready-for-net-core-3-0/" rel="external nofollow noopener">announcement</a>
- a tool to scan your existing Windows Forms / WPF applications
and see if how much of the API's they use are supported by Core
3.0.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/coreapianalyzer-1a.png" class="gallery" title="The .NET Core 3.0 Desktop API Analyzer in action" ><img src="https://images.cyotek.com/image/thumbnail/devblog/coreapianalyzer-1a.png" alt="The .NET Core 3.0 Desktop API Analyzer in action" decoding="async" loading="lazy" /></a><figcaption>The .NET Core 3.0 Desktop API Analyzer in action</figcaption></figure>
<p>I just gave that tool a run through of several of our
applications and I'm really surprised to see that apparently the
majority of them should &quot;just work&quot; - most of the non-compatible
problems are to do with the <code>System.Windows.Forms.Design</code>
namespace. Given this is mostly for design-time control support
I think it will be fine, although in some layout code I do use
the designers at runtime to <a href="/post/aligning-windows-forms-custom-controls-to-text-baselines-using-csharp">align controls via their text
baselines</a>; that at least will probably need rethinking. Also
for some reason I noticed that getting ACLs for a directory
isn't supported, again something that isn't critical and that I
can workaround. Gif Animator makes use of a custom app domains
which could be slightly more complicated to resolve but as with
most of the issues I saw, not insurmountable.</p>
<p>Regretfully the &quot;Recommended changes&quot; column wasn't populated
for most of the issues I saw, bar the <code>System.AppDomain</code>
detection which rather tersely states to &quot;Remove usage.&quot;.</p>
<p>All in all, it's looking quite promising and I'm quite looking
forward to getting my hands on Core 3.0 in the future, although
I have re-writing cyotek.com in ASP.NET Core to keep me busy in
the interim!</p>
<p>If you have existing Windows Forms or WPF applications that you
are considering switching to run under .NET Core, then it might
well be worth checking out Microsoft's announcement post and
running the .NET Core 3.0 Desktop API Analyzer on your own code.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/coreapianalyzer-1b.png" class="gallery" title="An example of a report showing the coverage of supported API's" ><img src="https://images.cyotek.com/image/thumbnail/devblog/coreapianalyzer-1b.png" alt="An example of a report showing the coverage of supported API's" decoding="async" loading="lazy" /></a><figcaption>An example of a report showing the coverage of supported API's</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/coreapianalyzer-1c.png" class="gallery" title="Another part of the report, this time showing the unsupported API's" ><img src="https://images.cyotek.com/image/thumbnail/devblog/coreapianalyzer-1c.png" alt="Another part of the report, this time showing the unsupported API's" decoding="async" loading="lazy" /></a><figcaption>Another part of the report, this time showing the unsupported API's</figcaption></figure><h2 id="update-history">Update History</h2>
<ul>
<li>2018-08-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/checking-if-your-windows-forms-applications-are-ready-for-net-core-3-0 .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comUpgrading a SVN repository using VisualSVN Serverurn:uuid:f51c9650-297a-4c31-90cc-bff6a1212f4c2018-08-07T16:04:33Z2018-08-07T16:04:33Z<p>Although I use Git for anything open source we produce, all
other code is still in a SVN repository. I use <a href="https://www.visualsvn.com/server/" rel="external nofollow noopener">VisualSVN
Server</a> for the server side things. When upgrading it last
week to the latest version, it mentioned that Apache SVN 1.10
had new compression options and it occurred to me that while
I've upgraded working copy formats before, my SVN databases are
probably still using whatever format they were created at. This
article covers how to upgrade SVN repositories using the
VisualSVN Server software.</p>
<h2 id="checking-the-version-of-a-repository">Checking the version of a repository</h2>
<p>According to the <a href="https://subversion.apache.org/docs/release-notes/1.10.html" rel="external nofollow noopener">release notes</a>, the current filesystem
format version used by SVN is <code>8</code>. We can either use the
<code>svnadmin info</code> command to view the version, or via a GUI by
right clicking a repository in the <strong>Visual SVN Server
Manager</strong>, selecting <strong>Properties</strong> from the context menu then
accessing the <strong>Details</strong> tab.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svnrepoupgrade-1a.png" class="gallery" title="An example of a repository using an older file format" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svnrepoupgrade-1a.png" alt="An example of a repository using an older file format" decoding="async" loading="lazy" /></a><figcaption>An example of a repository using an older file format</figcaption></figure>
<p>In the above example, my SVN Repository is at version 4, so a
little bit out of date. This particular repository was created
in <a href="/post/migrating-from-visual-sourcesafe-to-subversion">June 2011</a> using whatever version of VisualSVN Server was
current at the time.</p>
<h2 id="upgrading-a-repository">Upgrading a repository</h2>
<p>There are two ways of upgrading the repository. The first is a
simple action that doesn't change any existing commits; they
remain in whatever format they were created with. Only new
commits will use the features of the new format.</p>
<p>The second approach essentially recreates the repository from a
backup, and rewrites everything using the new format.</p>
<blockquote>
<p>Warning! If you upgrade the repository format, then you won't
be able use this repository with older versions of SVN Server.</p>
</blockquote>
<hr />
<blockquote>
<p>Important! Make sure you have backups before following either
of the processes in this article, especially the latter</p>
</blockquote>
<h2 id="upgrading-the-easy-way">Upgrading the easy way</h2>
<p>To perform a basic upgrade:</p>
<ol>
<li><p>Right click the repository to upgrade, and select <strong>All
Tasks</strong> from the context menu, then <strong>Upgrade Repository
Format...</strong></p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svnrepoupgrade-1b.png" class="gallery" title="Initiating an upgrade via VisualSVN Server" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svnrepoupgrade-1b.png" alt="Initiating an upgrade via VisualSVN Server" decoding="async" loading="lazy" /></a><figcaption>Initiating an upgrade via VisualSVN Server</figcaption></figure></li>
<li><p>A confirmation prompt will be displayed, accept it to
continue</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svnrepoupgrade-1c.png" class="gallery" title="Take note if you need to revert to an older version of SVN" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svnrepoupgrade-1c.png" alt="Take note if you need to revert to an older version of SVN" decoding="async" loading="lazy" /></a><figcaption>Take note if you need to revert to an older version of SVN</figcaption></figure></li>
<li><p>In my case, the &quot;upgrade&quot; was instantaneous and a completion
message was quickly displayed</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svnrepoupgrade-1d.png" class="gallery" title="A simple and painless upgrade" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svnrepoupgrade-1d.png" alt="A simple and painless upgrade" decoding="async" loading="lazy" /></a><figcaption>A simple and painless upgrade</figcaption></figure></li>
<li><p>When checking the properties for the repository, we can now
see that the <em>Filesystem format</em> is <code>8</code>, and the
<em>Compression</em> is <code>lz4</code>. So the upgrade seems to have done
something.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svnrepoupgrade-1e.png" class="gallery" title="The properties of the same repository after upgrading" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svnrepoupgrade-1e.png" alt="The properties of the same repository after upgrading" decoding="async" loading="lazy" /></a><figcaption>The properties of the same repository after upgrading</figcaption></figure></li>
</ol>
<p>I was curious what the upgrade process had really done, so I
compared the upgraded repository with a copy I'd made of the
original.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svnrepoupgrade-1f.png" class="gallery" title="Comparing the changed made by the upgrade" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svnrepoupgrade-1f.png" alt="Comparing the changed made by the upgrade" decoding="async" loading="lazy" /></a><figcaption>Comparing the changed made by the upgrade</figcaption></figure>
<p>Out of the 20 thousand (with spare change!) files making up the
repository, only two had changed. The <code>format</code> file had a change
from <code>4</code> to <code>8</code>, very logical. The <code>uuid</code> file had a new entry
added to it, which I assume is for new commits.</p>
<p>After I did the compare, I checked out the repository to a clean
working copy, created a new file, then committed it. I then
repeated the compare and could see the new transaction in the
repository. No idea if it's using the new format or not though.</p>
<p>This was as far as I went with the basic upgrade. I checked that
it seemed to be working as expected, but then reverted to my
backup.</p>
<h2 id="upgrading-the-harder-way">Upgrading the harder way</h2>
<p>The second option isn't really an upgrade, or at least the
approach I took wasn't. It basically involves creating a new
repository by importing a dump of your current repository, which
I assume upgrades as it goes.</p>
<blockquote>
<p>I'm not covering how to create a dump file in this article -
I'm going to assume that you are properly backing up your SVN
repository already!</p>
</blockquote>
<ol>
<li><p>Right click the repositories root node and select <strong>Import
Existing Repository...</strong> from the context menu</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svnrepoupgrade-1n.png" class="gallery" title="Initiating a repository import" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svnrepoupgrade-1n.png" alt="Initiating a repository import" decoding="async" loading="lazy" /></a><figcaption>Initiating a repository import</figcaption></figure></li>
<li><p>When prompted for a source, select the second option, <strong>Load
repository from a dump file</strong>, and click <strong>Next</strong></p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svnrepoupgrade-1g.png" class="gallery" title="Choosing the source to import from" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svnrepoupgrade-1g.png" alt="Choosing the source to import from" decoding="async" loading="lazy" /></a><figcaption>Choosing the source to import from</figcaption></figure></li>
<li><p>Enter the name of the SVN dump to import, then click <strong>Next</strong></p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svnrepoupgrade-1h.png" class="gallery" title="Selecting a SVN dump to import" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svnrepoupgrade-1h.png" alt="Selecting a SVN dump to import" decoding="async" loading="lazy" /></a><figcaption>Selecting a SVN dump to import</figcaption></figure></li>
<li><p>Next you need to specify a name for the repository, it will
default to one based on the filename of the dump. However, it
doesn't sanitise this default name and so you might find it
it invalid</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svnrepoupgrade-1i.png" class="gallery" title="Specifying the repository name" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svnrepoupgrade-1i.png" alt="Specifying the repository name" decoding="async" loading="lazy" /></a><figcaption>Specifying the repository name</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svnrepoupgrade-1j.png" class="gallery" title="An example error if your name isn't suitable" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svnrepoupgrade-1j.png" alt="An example error if your name isn't suitable" decoding="async" loading="lazy" /></a><figcaption>An example error if your name isn't suitable</figcaption></figure></li>
<li><p>The last step is to set the permissions. The default seemed a
little restrictive and weren't appropriate for my scenario, I
went with <strong>No specific permissions (global permissions
apply)</strong> to use the NTFS permissions I have set up on the
repository folders. Make sure the permissions you set match
those of the repository you're upgrading, then click
<strong>Import</strong> to start the process</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svnrepoupgrade-1k.png" class="gallery" title="Setting the permissions for the new repository" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svnrepoupgrade-1k.png" alt="Setting the permissions for the new repository" decoding="async" loading="lazy" /></a><figcaption>Setting the permissions for the new repository</figcaption></figure></li>
<li><p>Wait for the import to complete. This make take some time.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svnrepoupgrade-1l.png" class="gallery" title="A slowly moving progress bar as the dump is imported" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svnrepoupgrade-1l.png" alt="A slowly moving progress bar as the dump is imported" decoding="async" loading="lazy" /></a><figcaption>A slowly moving progress bar as the dump is imported</figcaption></figure></li>
<li><p>Once it's finished, a summary dialog will be displayed</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svnrepoupgrade-1m.png" class="gallery" title="A summary screen for a successful import" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svnrepoupgrade-1m.png" alt="A summary screen for a successful import" decoding="async" loading="lazy" /></a><figcaption>A summary screen for a successful import</figcaption></figure></li>
<li><p>Exit the VisualSVN Manager</p>
</li>
<li><p>Stop any running VisualSVN services</p>
</li>
<li><p>Open the folder where your repositories are located</p>
</li>
<li><p>Rename your current repository</p>
</li>
<li><p>Rename the import repository to have the name of the
original repository</p>
</li>
<li><p>If you have created custom hooks, you'll need to copy these
from the <code>hooks</code> folder of your original repository. If you
have customised other settings you may need to copy these
too, however as I've always used default settings this
didn't apply to me and so I don't know what files you'd need
to modify</p>
</li>
<li><p>To stop VisualSVN Server from seeing the renamed original
repository, you may wish to move it to a different location,
until you are sure you don't need it and it is safe to
delete</p>
</li>
<li><p>Restart the VisualSVN services that stopped previously</p>
</li>
</ol>
<p>Once again, I performed a compare of the repository to try and
get a feel for what the upgrade process does. Unfortunately,
importing the dump file seems to re-order files within each
transaction and so every single file was different even without
looking for anything format specific. In all honesty, I couldn't
see anything special about any change when looking at a sample
of files.</p>
<p>One thing I did note was that the new repository was 746MB in
size, compared with 680MB previously. I wasn't really expecting
the repository to be larger given it was supposed to be
optimised, and using a different compression method which you'd
assume was chosen as it gave better returns than the older one.</p>
<h2 id="which-approach-to-take">Which approach to take?</h2>
<p>I don't really know which approach is better - the former is
certainly quicker, whilst the latter should result in a more
optimised. However, VisualSVN Server doesn't make the latter
approach particular easy at all - it would be much simpler to do
<code>svnadmin load</code> from a command window instead of jumping the
hoops of creating a new repository and then replacing the old
one with the new.</p>
<p>As always, your mileage may vary. As always this article might
have errors, proceed at your own risk and make sure you have
backups!</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2018-08-07 - 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/upgrading-a-svn-repository-using-visualsvn-server .
</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.comUsing the MantisBT REST API when hosted on IISurn:uuid:49741060-f6cc-4982-b2cb-bf12fbb9e6682018-03-30T15:55:08Z2018-03-30T15:55:08Z<p>I'm currently in the process of moving our hosting services from
one provider to another; although some parts of cyotek.com
infrastructure runs on Microsoft Azure, a fair chunk uses more
traditional hosting. Our <a href="https://mantisbt.org/" rel="external nofollow noopener">MantisBT</a> instance is one such
service that I recently migrated.</p>
<p>Previously the instance was hosted on Linux, now it's on
Windows. The initial migration seemed to have gone well and so
I'd moved onto the next sub-domain on the list.</p>
<p>This morning however I noticed the error logs were listing that
cyotek.com wasn't able to display product road maps. On testing,
I was getting <code>404</code> responses for any calls to the REST API on
the migrated MantisBT instance.</p>
<h2 id="apache-redirects">Apache Redirects</h2>
<p>The REST API in MantisBT makes use of Apache's <strong>mod_rewrite</strong>
module to rewrite any URI to <code>/api/rest/</code> to
<code>/api/rest/index.php</code> using the following rules in <code>.htaccess</code></p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
# Based <span class="keyword">on</span> Slim Framework recommendation @ http://docs.slimframework.com/routing/rewrite/
RewriteEngine <span class="keyword">On</span>
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
</pre>
</figure>
<p>Of course, IIS doesn't support <code>.htaccess</code> files and so these
rewrites never occur, hence the <code>404</code>.</p>
<h2 id="redirecting-in-iis">Redirecting in IIS</h2>
<p>IIS has its own version of <strong>mod_rewrite</strong>, the <strong>URL Rewrite</strong>
module, although it isn't installed by default. You can find how
to install this module in a <a href="/post/installing-the-url-rewrite-module-into-internet-information-services">previous post</a>.</p>
<p>Once installed, you can add rewrite rules to <code>web.config</code> either
via the IIS Manager GUI, or by directly editing the
configuration - this post will cover both approaches.</p>
<h2 id="importing.htaccess-rules">Importing .htaccess rules</h2>
<p>I briefly spoke about manually modifying <code>web.config</code> to add
rewrite rules in our post on <a href="/post/redirecting-to-https-when-using-iis-behind-a-load-balancer">Redirecting to HTTPS when using
IIS behind a load balancer</a>. This time I'm going to describe
how to import the rewrite rules of a <code>.htaccess</code> file directly
into IIS.</p>
<ul>
<li><p>Firstly, open IIS manager and then navigate to the
<strong>api/rest</strong> folder of your MantisBT installation</p>
</li>
<li><p>Next open the <strong>URL Rewrite</strong> section.</p>
</li>
<li><p>Click the <strong>Import Rules...</strong> option in the Actions sidebar.</p>
</li>
<li><p>In the <strong>Configuration file</strong> field, select the <code>.htaccess</code>
file located in the local <code>api/rest</code> folder and then click
<strong>Import</strong>.</p>
</li>
<li><p>Verify that the <strong>Rewrite rules</strong> field displays the
appropriate redirect and that <strong>Converted Rules</strong> indicates
that the rule was successfully imported.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/mantisbt-iis-rewrite-1c.png" class="gallery" title="Importing .htaccess rules into IIS" ><img src="https://images.cyotek.com/image/thumbnail/devblog/mantisbt-iis-rewrite-1c.png" alt="Importing .htaccess rules into IIS" decoding="async" loading="lazy" /></a><figcaption>Importing .htaccess rules into IIS</figcaption></figure></li>
<li><p>In the Actions sidebar, click <strong>Apply</strong> to save the changes,
then click <strong>Back to Rules</strong> to return to to the main <strong>URL
Rewrite</strong> section listing the imported rule.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/mantisbt-iis-rewrite-1d.png" class="gallery" title="The imported configuration" ><img src="https://images.cyotek.com/image/thumbnail/devblog/mantisbt-iis-rewrite-1d.png" alt="The imported configuration" decoding="async" loading="lazy" /></a><figcaption>The imported configuration</figcaption></figure></li>
</ul>
<p>That should be all that is required; the REST API calls should
now rewrite and succeed. To quickly test that all is well, open
up <code>http://yourinstantURL/api/rest/projects/</code> in your browser -
if you get a 401 error, then the redirect is working correctly.</p>
<h2 id="manually-updating-the-configuration">Manually updating the configuration</h2>
<p>If your instance is hosted remotely you might not have direct
access to IIS Manager or be able to remotely connect to it.
Below is the complete <code>web.config</code> file used to enable
redirects, simply copy it to the <code>/api/rest</code> folder of your
MantisBT instance.</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">configuration</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">system.webServer</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">rewrite</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">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">API Redirect</span><span class="symbol">&quot;</span> <span class="name">stopProcessing</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">match</span> <span class="name">url</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">^</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">conditions</span> <span class="name">logicalGrouping</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">MatchAll</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="comment">&lt;!--# Based on Slim Framework recommendation @ http://docs.slimframework.com/routing/rewrite/--&gt;</span>
 <span class="symbol">&lt;</span><span class="name">add</span> <span class="name">input</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">{REQUEST_FILENAME}</span><span class="symbol">&quot;</span> <span class="name">matchType</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">IsFile</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="name">negate</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">conditions</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">action</span> <span class="name">type</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Rewrite</span><span class="symbol">&quot;</span> <span class="name">url</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">index.php</span><span class="symbol">&quot;</span> <span class="name">appendQueryString</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span> <span class="name">logRewrittenUrl</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">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">rewrite</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">system.webServer</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">configuration</span><span class="symbol">&gt;</span>
</pre>
</figure>
<h2 id="update-history">Update History</h2>
<ul>
<li>2018-03-30 - 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-the-mantisbt-rest-api-when-hosted-on-iis .
</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.comTools we use - 2017 editionurn:uuid:f1d1da7f-2c47-4e25-ba0b-2ef6439b5a402018-01-31T17:20:02Z2018-01-01T12:27:39Z<p>Happy New Year! Once again it's that time for the list of software products I used throughout the past year. Although there's a fair few new entries, overall there's not a huge amount of change given Cyotek's current technology focus.</p>
<p>(This list has grown over time and could probably do with some form of better grouping and ordering. For now, categories are fairly ad-hoc and nothing is in any particular order)</p>
<h2 id="operating-systems">Operating Systems</h2>
<ul>
<li>Windows Home Server 2011 - file server, SVN repository, backup host, CI server</li>
<li>Windows 10 Professional - development machines</li>
<li><del>Windows Vista</del> (virtualized) - testing</li>
<li><ins>New!</ins> Windows 7 (virtualized) - testing</li>
</ul>
<h2 id="development-tools">Development Tools</h2>
<ul>
<li><a href="https://www.getpostman.com/" rel="external nofollow noopener">Postman</a> is a absolutely brilliant client for testing REST services</li>
<li><a href="http://www.microsoft.com/visualstudio/" rel="external nofollow noopener">Visual Studio 2015 Premium / Visual Studio 2017 Enterprise</a> - best IDE bar none</li>
<li><a href="https://www.jetbrains.com/decompiler/" rel="external nofollow noopener">DotPeek</a> - a decent replacement to .NET Reflector that can view things that Reflector can't, making it a worthwhile replacement despite some bugs and being chronically slow to start</li>
<li><ins>New!</ins> <a href="https://www.ndepend.com/" rel="external nofollow noopener">NDepend</a> - static code analysis. I have quite a love / hate relationship with this application; so much so that I barely use the user interface at all and rely on reports published to Jenkins as part of a common build pipeline</li>
</ul>
<h2 id="visual-studio-extensions">Visual Studio Extensions</h2>
<ul>
<li><a href="http://www.oz-code.com/" rel="external nofollow noopener">OzCocde</a> - despite the change to a subscription model this is just barely hanging onto being my number one tool and remains an exceptional debugging aid (although Resharper is catching up)</li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=RichardJMoss.AddExistingProjects-19444" rel="external nofollow noopener">Cyotek Add Projects</a> - a simple extension for easily adding multiple projects to your solutions. I don't use this as much now as I've converted most of my libraries into NuGet packages</li>
<li><a href="http://editorconfig.org/" rel="external nofollow noopener">EditorConfig</a> - useful for OSS projects to avoid space-vs-tab wars and now built into Visual Studio 2017</li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=MadsKristensen.FileNesting" rel="external nofollow noopener">File Nesting</a> - allows you to easily nest (or unnest!) files, great for TypeScript or T4 templates</li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=MadsKristensen.OpenCommandLine" rel="external nofollow noopener">Open Command Line</a> - easily open command prompts, PowerShell prompts, or other tools to your project / solution directories</li>
<li><a href="http://mike-ward.net/vscoloroutput/" rel="external nofollow noopener">VSColorOutput</a> - add colour coding to Visual Studio's Output window</li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=SteveDowerMSFT.IndentGuides" rel="external nofollow noopener">Indent Guides</a> - easily see where you are in nested code</li>
<li><a href="http://www.jetbrains.com/resharper/" rel="external nofollow noopener">Resharper</a> - this is also starting to offer OzCode features which should be interesting in future</li>
<li><a href="http://www.ncrunch.net/" rel="external nofollow noopener">NCrunch for Visual Studio</a> - frequently updated automated parallel continuous testing tool (there's a mouthful). Works with NUnit, MSTest and a variety of other test systems. Great for TDD and picking up how a simple change you made to one part of your project completely destroys another part. Although Resharper has offered continuous testing options for a while now, I still think NCrunch is the superior offering</li>
</ul>
<h2 id="analytics">Analytics</h2>
<ul>
<li><del>Innovasys Luminitix (Link Removed)</del> - unsupported and hasn't been updated in years</li>
<li><ins>New!</ins> Unnamed Analytics. After dropping Luminitix, I replaced the data collection with a home grown solution, although I've yet to write a front end to look at the data effectively</li>
<li><ins>New!</ins> <del><a href="https://piwik.org/" rel="external nofollow noopener">Piwik</a></del> <a href="https://matomo.org/" rel="external nofollow noopener">Matomo</a> - currently trialling this web based analytics software on cyotek.com</li>
</ul>
<h2 id="profiling">Profiling</h2>
<ul>
<li><a href="https://www.jetbrains.com/profiler/" rel="external nofollow noopener">dotTrace</a> - although I prefer the ANTS profiler, dotTrace is a very usable profiler and given it is included in my Resharper Ultimate subscription, it's a no-brainer to use</li>
<li><a href="https://www.jetbrains.com/dotmemory/" rel="external nofollow noopener">dotMemory</a> - memory profiling is hard, need all the help we can get</li>
</ul>
<h2 id="documentation-tools">Documentation Tools</h2>
<ul>
<li><del><a href="http://www.innovasys.com/product/dx/overview" rel="external nofollow noopener">Innovasys Document! X</a></del> - Previously we used this to produce the user manuals for our applications, however had since moved to an home built solution</li>
<li>HelpWrite - the first application offered by Ariad in the mists of time, now reincarnated and producing no-frills documentation from simple markdown and YAML</li>
<li><a href="http://www.atomineerutils.com/" rel="external nofollow noopener">Atomineer Pro Documentation</a> - automatically generate XML comment documentation in your source code</li>
<li><a href="http://markdownedit.com/" rel="external nofollow noopener">MarkdownEdit</a> - a no frills minimalist markdown editor that <del>is</del> was actively maintained and Just Works</li>
<li><a href="http://notepad-plus-plus.org/" rel="external nofollow noopener">Notepad++</a> - because Notepad hasn't changed in 20 years (moving menu items around doesn't count!)</li>
</ul>
<h2 id="continuous-integration">Continuous Integration</h2>
<ul>
<li><a href="https://jenkins.io/index.html" rel="external nofollow noopener">Jenkins</a> - although the UI is fairly horrible (<a href="http://afonsof.com/jenkins-material-theme/" rel="external nofollow noopener">Jenkins Material Theme</a> helps!), Jenkins is easy to install, doesn't need a database server and has a rich plugin ecosystem, even for .NET developers. I use this to build, test and even deploy. TeamCity <em>may</em> be more powerful, but Jenkins is easier to maintain. Although I don't use Blue Ocean (too Git-centric and also seemed to be the cause of sporadic authentication issues), I do use common pipeline scripts to build and test our internal libraries (including creating and publishing of packages) and to generate and publish product documentation. Our core applications are still built with freestyle jobs and batch files though.</li>
</ul>
<h2 id="testing">Testing</h2>
<ul>
<li><a href="http://nunit.org/" rel="external nofollow noopener">NUnit</a> is our test framework of choice. We stuck with version 2 for a very long time, but now the Jenkins reporting plugin we use supports nUnit 3 test results we're migrating projects to version 3 whenever they are modified for other reasons</li>
</ul>
<h2 id="graphics-tools">Graphics Tools</h2>
<ul>
<li><a href="http://www.getpaint.net/" rel="external nofollow noopener">Paint.NET</a> - brilliant bitmap editor with extensive plugins</li>
<li><a href="http://www.axialis.com/iconworkshop/" rel="external nofollow noopener">Axialis IconWorkshop</a> - very nice icon editor, been using this for untold years now since Microangelo decided to become the Windows Paint of icon editing</li>
<li><a href="http://cyotek.com/cyotek-spriter">Cyotek Spriter</a> - sprite / image map generation software that is sore need of optimisation and TLC</li>
<li><a href="http://cyotek.com/cyotek-gif-animator/">Cyotek Gif Animator</a> - gif animation creator that was shaping up nicely, although it is another application I really want to spend more time improving</li>
</ul>
<h2 id="virtualization">Virtualization</h2>
<ul>
<li><a href="https://www.virtualbox.org/" rel="external nofollow noopener">Oracle VM VirtualBox</a> - for creating guest OS's for testing purposes</li>
</ul>
<h2 id="version-control">Version Control</h2>
<ul>
<li><a href="http://tortoisesvn.net/" rel="external nofollow noopener">TortoiseSVN</a> - Windows Explorer integration for SVN</li>
<li><del><a href="http://ankhsvn.open.collab.net/" rel="external nofollow noopener">AnhkSVN</a></del> - Subversion support for Visual Studio</li>
<li><ins>New!</ins> <a href="https://www.visualsvn.com/visualsvn/" rel="external nofollow noopener">VisualSVN</a> - Subversion support for Visual Studio. Unlike AnhkSVN, VisualSVN uses TortoiseSVN under the hood, meaning that Explorer and Visual Studio are always in the same state no matter where I commit from, something which used to frustrate me no end with AnhkSVN</li>
<li><a href="http://www.visualsvn.com/server/" rel="external nofollow noopener">VisualSVN Server</a> - Subversion Server for Windows</li>
<li><a href="https://github.com/" rel="external nofollow noopener">GitHub</a> / <a href="https://desktop.github.com/" rel="external nofollow noopener">GitHub Desktop</a> - for providing and working with the open source code we publish</li>
</ul>
<h2 id="filedirectory-tools">File/directory tools</h2>
<ul>
<li><a href="http://winmerge.org/" rel="external nofollow noopener">WinMerge</a> - excellent file or directory comparison utility</li>
<li><a href="http://stefanstools.sourceforge.net/grepWin.html" rel="external nofollow noopener">WinGrep</a> - another excellent tool for swiftly searching directories for files containing specific strings or expressions</li>
<li><a href="https://filezilla-project.org/" rel="external nofollow noopener">FileZilla</a> - simple FTP client that has served my needs for years now</li>
</ul>
<h2 id="backups">Backups</h2>
<ul>
<li><a href="http://cyotek.com/cyotek-copytools">Cyotek CopyTools</a> - we use this for offline backups of source code, assets and resources, documents, actually pretty much anything we generate; including backing up the backups!</li>
<li><a href="http://www.code42.com/crashplan/" rel="external nofollow noopener">CrashPlan</a> - CrashPlan creates an online backup of the different offline backups that CopyTools does. If you've ever lost a harddisk before with critical data on it that's nowhere else, you'll have backups squirrelled away everywhere too!</li>
</ul>
<h2 id="security">Security</h2>
<ul>
<li><del>StartSSL</del> I'd already <a href="https://www.cyotek.com/blog/startssl-code-signing-certificates-are-crippled">written</a> last year about how StartSSL code signing certificates were crippled and essentially useless, but due to other serious breeches of trust, the company is now no more. In a way it's a shame as they were extremely cost effective, but at the same time I wouldn't have used them again regardless due to the code signing killswitch</li>
<li><ins>New!</ins> <a href="https://www.comodo.com/" rel="external nofollow noopener">Comodo</a> Although I've had extremely poor service from Comodo in the past, I've had a year of stress free SSL and Code Signing certificates from them. Lets just hope 2018 passes as smoothly</li>
<li><ins>New!</ins> <a href="https://letsencrypt.org/" rel="external nofollow noopener">Let's Encrypt</a> provide short term SSL certificates for free. If you (or your host) are able to automate the process, this is an exceptional way to get basic SSL for your sites</li>
<li><a href="http://someonewhocares.org/hosts/" rel="external nofollow noopener">Dan Pollocks hosts file</a> blocks your computer from connecting to many thousands of dubious internet hosts and is continuously updated</li>
</ul>
<h2 id="issue-tracking">Issue Tracking</h2>
<ul>
<li><ins>New!</ins> <a href="https://www.mantisbt.org/" rel="external nofollow noopener">Mantis Bug Tracker</a> - over the past year I made a serious effort to more effectively plan software releases and keep track of issues, rather than just winging it. This is mostly working thanks to using Mantis Bug Tracker, although as usual WebCopy has had the lions share of development time to the extreme detriment of our other products. I also considered <a href="https://www.atlassian.com/software/jira" rel="external nofollow noopener">Jira</a> which is more powerful, but MantisBT is simply easier to install and maintain</li>
<li><ins>New!</ins> <a href="https://github.com/cyotek/MantisSharp" rel="external nofollow noopener">MantisSharp</a> - I use our MantisSharp library to add integration between various applications and our MantisBT instance, notable for raising new issues from our automated error monitor, and for creating roadmaps on cyotek.com product pages</li>
</ul>
<h2 id="cms">CMS</h2>
<ul>
<li><ins>New!</ins> <a href="https://getkirby.com/" rel="external nofollow noopener">Kirby</a> - although cyotek.com uses a custom home built CMS, I've been looking a Kirby as an alternative for some aspects such as the Knowledge Base</li>
</ul>
<h2 id="other">Other</h2>
<ul>
<li><ins>New!</ins> <a href="https://ravendb.net/" rel="external nofollow noopener">RavenDB</a> - for <em>years</em> I have been fairly rabidly against NoSQL and have always been perfectly happy with SQL Server (at least until I had to work with a product that used the XML data type which, mildly, was a nightmare). When looking into how to replace Lumintix with a custom solution, I settled on saving the metrics as JSON and decided to use RavenDB to store it in. I have to say, I am really impressed with RavenDB Studio, the speed of the application is amazing and something to aspire to. Just as impressive is the speed of support by Hibernating Rhino's</li>
<li><ins>New!</ins> <a href="https://kodi.tv/" rel="external nofollow noopener">Kodi</a> - I've used this for years now to watch video on various generations of Raspberry Pi. I found the Films and TV (or Movies and TV) application that ships with Windows 10 to be absolute rubbish and was very glad when Kodi became available on the Microsoft Store</li>
<li><ins>New!</ins> <a href="https://rufus.akeo.ie/" rel="external nofollow noopener">Rufus</a> - I use this utility for writing ISO images to USB, useful for setting up new physical machines in an age where CD drives are fairly obsolete</li>
<li><ins>New!</ins> <a href="https://sourceforge.net/projects/win32diskimager/" rel="external nofollow noopener">Win32 Disk Imager</a> - useful for burning ISO images to SD cards which I do for Raspberry Pi distributions. I used to use this for USB as well but now I prefer Rufus for that</li>
<li><a href="https://justgetflux.com/" rel="external nofollow noopener">f.lux</a> - I've been using this utterly fantastic software for years. It adapts your monitor to the time of day, removing blue light as evening approaches and helps reduce eye strain when coding at night</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/tools-we-use-2017-edition .
</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.comRedirecting to HTTPS when using IIS behind a load balancerurn:uuid:8ceab7e1-834d-4f00-bc82-c78c6bcbaa6a2017-11-19T17:12:25Z2017-11-19T13:25:52Z<p>Cyotek has a number of different websites powering various bits
of our software and services. Some are Windows based using IIS,
and some are Linux based using Apache. Internal servers are are
directly accessed but external servers are behind load
balancers. Almost all are using HTTPS now and have redirects in
place to force the use of HTTPS over HTTP.</p>
<p>There are plenty of articles on the internet dealing how to use
<code>.htaccess</code> files to perform redirects using Apache, and various
articles on different methods of redirecting IIS or ASP.NET
applications. However, there seems to be a slight gap when it
comes to load balancers or reverse proxies. Depending on how the
load balancer / reverse proxy (referred to as just load balancer
for the rest of the article) operates, the secure connection may
terminate at the load balancer, and so the web server always
receives &quot;insecure&quot; traffic.</p>
<p>If the secure connection is terminated at the balancer, then
this can be a problem for the most common method of redirecting
traffic, which is to check if the current request's scheme is
HTTP and perform a redirect if it is. But if the web server
<em>always</em> gets HTTP traffic, then this approach will simply
result in an infinite redirect loop, something I've managed to
do inadvertently more than once.</p>
<p>Fortunately however, when a load balancer forwards traffic to
the final server, it generally includes some extra headers such
as <code>X-Forwarded-For</code> and <code>X-Forwarded-Proto</code>. These headers
provide details such as the original scheme/protocol being used
and the origin IP address.</p>
<blockquote>
<p>When researching the final details for writing this article, I
found that the non-standard <code>X-Forwarded-*</code> headers have been
replaced under <a href="https://tools.ietf.org/html/rfc7239" rel="external nofollow noopener">RFC 7239</a> by a new <code>Forwarded</code> header. It's
possible therefore that the <code>X-Forwarded-For</code> header may start
dropping out of use and you'd be expected to work with
<code>Forwarded</code> instead.</p>
</blockquote>
<p>The header I'm interested in in this case is <code>X-Forwarded-Proto</code>
which simply details the scheme of the original request. If the
value is <code>http</code>, then clearly the original request wasn't secure
and so I can issue a redirect. If it's <code>https</code>, then the
original request - up to hitting the load balancer - was secure,
and we shouldn't try and redirect, or we're back into infinite
loop territory.</p>
<blockquote>
<p>Remember that these are request headers, e.g. headers you
can't control and which could be forged or manipulated.</p>
</blockquote>
<p>When using IIS, I've mostly used code based redirects although
the very oldest bits use an ancient configuration based
re-writer named <code>Intelligencia.UrlRewriter</code>. However, for some
time IIS now has had an optional URL rewriter of its own, which
is what I'm going to use in this article.</p>
<blockquote>
<p>By default the URL rewrite module isn't installed, although
it's easy enough to add using the Web Platform Installer. I've
detailed the steps in a <a href="/post/installing-the-url-rewrite-module-into-internet-information-services">prior
post</a>.</p>
</blockquote>
<h2 id="adding-a-rule-to-force-https">Adding a rule to force HTTPS</h2>
<p>Although you can configure rules from within IIS Managers GUI
interface, I'm just going to detail how to directly modify the
<code>web.config</code> of an application.</p>
<p>In your <code>web.config</code>, find the <code>system.webServer</code> element (or
add it as a child of <code>configuration</code> if it is not already
present), and add a <code>rewrite</code> element containing the following
content.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">rewrite</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">rules</span><span class="symbol">&gt;</span>
 <span class="comment">&lt;!-- Specify a rule element. The stopProcessing attribute means no other
 rules will be processed if this rule is a match. You can also add a boolean
 attribute named enabled which you can use to temporarily disable a rule
 without deleting it from the file --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">rule</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">HTTPS Redirect</span><span class="symbol">&quot;</span> <span class="name">stopProcessing</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;!-- Specifies a regular expression used to match the incoming URL. As I
 want all URL&#39;s to be considered, I tell it to match any character. By wrapping 
 the expression in brackets I create a capturing group I can use elsewhere. --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">match</span> <span class="name">url</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="comment">&lt;!-- Each rule can have one or more conditions --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">conditions</span><span class="symbol">&gt;</span>
 <span class="comment">&lt;!-- Here I&#39;ve added a condition which looks at the X-Forwarded-Proto
 header to see if it&#39;s any value other than &quot;https&quot;. (The attribute
 negate=&quot;true&quot; means &quot;does not match&quot;) --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">add</span> <span class="name">input</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">{HTTP_X_FORWARDED_PROTO}</span><span class="symbol">&quot;</span> <span class="name">pattern</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">https</span><span class="symbol">&quot;</span> <span class="name">negate</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">conditions</span><span class="symbol">&gt;</span>
 <span class="comment">&lt;!-- The action element states what should happen if all the rule
 conditions are met. This action instructs IIS to perform a 301 redirect
 to the original host (via the HTTP_HOST variable) with the original request
 path (as captured in the match element above) via R:1 --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">action</span> <span class="name">type</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Redirect</span><span class="symbol">&quot;</span> <span class="name">url</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">https://{HTTP_HOST}/{R:1}</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <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">rewrite</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>Rather than trying to add a description outside the code block,
I've added a bunch of comments inside the XML.</p>
<p>Happily, these are the only changes you need to make to
web.config files - you don't need to define new
<code>configSections</code>, <code>httpHandlers</code>, or other elements. I assume
these have been pre-defined at a higher level, but haven't
confirmed this.</p>
<h2 id="a-note-on-using-letsencrypt-renewal-challenges">A note on using LetsEncrypt renewal challenges</h2>
<p>If you are renewing LetsEncrypt certificates via a HTTP
challenge (where a file is placed on your server to confirm
validity), then this rule will cause the challenge to fail due
to the redirect. If your processes are set to only renew the
certificates at the last minute then SSL can stop working on
your website, causing web browsers to display unwelcome danger
messages when visitors attempt to access your site. Somethign
<em>else</em> that has happened more than once to me, and the main
reason why cyotek.com is now running off a 3 year traditional
certificate after lasts months mishap.</p>
<p>As the file is always in a static location
(<code>.well-known/acme-challenge</code>) we can expand our original rule
to add a new condition which prevents the redirect from
triggering if that path is present.</p>
<p>Using the example rule above, I added a new child of the
<code>conditions</code> element as follows.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">add</span> <span class="name">input</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">{PATH_INFO}</span><span class="symbol">&quot;</span> <span class="name">pattern</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">^/\.well-known/acme-challenge/.*</span><span class="symbol">&quot;</span> <span class="name">negate</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>
</pre>
</figure>
<p>When testing a HTML located in this path, the request was not
forcibly redirected to HTTPS and was displayed via unsecure
HTTP, which should allow LetsEncrypt challenges to succeed. Of
course, the certificates on the domains where I've implemented
these rules aren't due to expire for another two months so time
will tell if this is sufficient.</p>
<h2 id="closing-notes">Closing notes</h2>
<p>Forcing a redirect from HTTP to HTTPS is only one part - you
should also consider using a Content Security Policy (CSP) that
instructs browsers to automatically use HTTPS and setting up
HTTP Strict Transport Security (HSTS). You should also ensure
the redirects you put in place are 301 to guide search engines
to update references (the example above uses a 301 redirect).
Lots of things to try and remember! <a href="https://www.troyhunt.com/" rel="external nofollow noopener">Troy Hunt</a> has a helpful
post on <a href="https://www.troyhunt.com/the-6-step-happy-path-to-https/" rel="external nofollow noopener">getting started with HTTPS</a>.</p>
<p>There is also lots more you can do using rewrite rules - you can
view the <a href="https://www.iis.net/learn/extensions/url-rewrite-module/url-rewrite-module-configuration-reference?amp;clcid=0x409" rel="external nofollow noopener">documentation</a> for more information and examples.
And, although in this article I only covered modifying
<code>web.config</code> directly, you can use the GUI tools in IIS Manager
to explore all the supported function and experiment with rule
creation.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-11-19 - 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/redirecting-to-https-when-using-iis-behind-a-load-balancer .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comInstalling the URL Rewrite module into Internet Information Servicesurn:uuid:04e9ede1-8629-49c0-be74-9033941363a12017-11-19T13:25:46Z2017-11-19T13:25:46Z<p>For quite some time now Internet Information Services (IIS) has
had an URL Rewriter component that you can optionally install.
As one of my upcoming shorter posts mentions the use of it, I
thought I'd better provide installation instructions separately
rather than cluttering up that post.</p>
<h2 id="a-note-on-iis-versions">A note on IIS versions</h2>
<p>Some versions of Internet Information Services Manager (IIS
Manager) have a Start Page, as demonstrated in the below
screenshot. All instructions on this post assume that on opening
IIS Manager the user has selected the root server node in the
tree if it wasn't already selected.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/install-url-rewriter-1a.png" class="gallery" title="An example of a Start Page on older versions of IIS" ><img src="https://images.cyotek.com/image/thumbnail/devblog/install-url-rewriter-1a.png" alt="An example of a Start Page on older versions of IIS" decoding="async" loading="lazy" /></a><figcaption>An example of a Start Page on older versions of IIS</figcaption></figure><h2 id="checking-if-the-url-rewrite-module-is-installed">Checking if the URL Rewrite module is installed</h2>
<p>To see if the URL Rewrite module is installed, open IIS Manager
and look in the <strong>IIS</strong> group - if the module is installed, an
icon named <strong>URL Rewrite</strong> will be present. The screenshot below
shows an example of a server when the module is installed.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/install-url-rewriter-1b.png" class="gallery" title="An example of an IIS instance containing the URL Rewrite module" ><img src="https://images.cyotek.com/image/thumbnail/devblog/install-url-rewriter-1b.png" alt="An example of an IIS instance containing the URL Rewrite module" decoding="async" loading="lazy" /></a><figcaption>An example of an IIS instance containing the URL Rewrite module</figcaption></figure><h2 id="installing-the-module">Installing the module</h2>
<p>You can download the module from Microsoft's URL Rewrite
<a href="https://www.iis.net/downloads/microsoft/url-rewrite" rel="external nofollow noopener">homepage</a>, but as IIS Manager includes a helpful tool for
installing new features called the Web Platform Installer I'm
going to detail using that.</p>
<blockquote>
<p>I can't remember if the Web Platform Installer itself is
included with IIS or if you need to download and install that
first too. If it's not installed then you could always use the
link above to download the URL Rewrite module directly.</p>
</blockquote>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/install-url-rewriter-1c.png" class="gallery" title="An example of an IIS instance where the URL Rewrite module is not installed" ><img src="https://images.cyotek.com/image/thumbnail/devblog/install-url-rewriter-1c.png" alt="An example of an IIS instance where the URL Rewrite module is not installed" decoding="async" loading="lazy" /></a><figcaption>An example of an IIS instance where the URL Rewrite module is not installed</figcaption></figure>
<p>In the <strong>Management</strong> group, double click the <strong>Web Platform
Installer</strong> icon to start the tool.</p>
<blockquote>
<p>I'm sure this tool used to open as a separate application, but
when testing today it was embedded within the IIS Manager
window so the behaviour may differ depending on which version
of the Web Platform Installer is installed.</p>
</blockquote>
<p>Once the Web Platform Installer has loaded and displayed the
Spotlight product list, type <kbd>rewrite</kbd> into the search
box and press <kbd>Enter</kbd>.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/install-url-rewriter-1d.png" class="gallery" title="Search results listing matching applications" ><img src="https://images.cyotek.com/image/thumbnail/devblog/install-url-rewriter-1d.png" alt="Search results listing matching applications" decoding="async" loading="lazy" /></a><figcaption>Search results listing matching applications</figcaption></figure>
<p>Find the entry for the URL Rewrite module then click the <strong>Add</strong>
button for the entry. Now click the <strong>Install</strong> button.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/install-url-rewriter-1e.png" class="gallery" title="Web Platform Installer prerequisites dialog" ><img src="https://images.cyotek.com/image/thumbnail/devblog/install-url-rewriter-1e.png" alt="Web Platform Installer prerequisites dialog" decoding="async" loading="lazy" /></a><figcaption>Web Platform Installer prerequisites dialog</figcaption></figure>
<p>Sometimes a component might need additional features which will
be automatically included and listed in this dialog, although in
this case the module has no additional dependencies.</p>
<p>Click <strong>I Accept</strong> to begin the installation.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/install-url-rewriter-1f.png" class="gallery" title="Web Platform Installer progress dialog" ><img src="https://images.cyotek.com/image/thumbnail/devblog/install-url-rewriter-1f.png" alt="Web Platform Installer progress dialog" decoding="async" loading="lazy" /></a><figcaption>Web Platform Installer progress dialog</figcaption></figure>
<p>After a few moments the installation should be completed.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/install-url-rewriter-1g.png" class="gallery" title="Web Platform Installer installation complete dialog" ><img src="https://images.cyotek.com/image/thumbnail/devblog/install-url-rewriter-1g.png" alt="Web Platform Installer installation complete dialog" decoding="async" loading="lazy" /></a><figcaption>Web Platform Installer installation complete dialog</figcaption></figure>
<p>Click <strong>Finish</strong> to dismiss the Web Platform Installer window.</p>
<p>Even though the module has been installed, it still won't appear
in the running instance of IIS Manager - you need to exit and
restart the application.</p>
<p>On restarting IIS Manager, you should now see the <strong>URL
Rewrite</strong> icon present in the <strong>IIS</strong> group.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/install-url-rewriter-1h.png" class="gallery" title="Another example of an IIS instance where the URL Rewrite module is installed" ><img src="https://images.cyotek.com/image/thumbnail/devblog/install-url-rewriter-1h.png" alt="Another example of an IIS instance where the URL Rewrite module is installed" decoding="async" loading="lazy" /></a><figcaption>Another example of an IIS instance where the URL Rewrite module is installed</figcaption></figure><h2 id="update-history">Update History</h2>
<ul>
<li>2017-11-19 - 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/installing-the-url-rewrite-module-into-internet-information-services .
</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.comRestoring missing Authorization header when using PHP with Apacheurn:uuid:7e299d17-ceda-4abb-b436-c2fd20c15f1f2017-07-06T20:15:37Z2017-07-06T17:50:03Z<p>I was recently looking into using our <a href="https://mantisbt.org/" rel="external nofollow noopener">Mantis Bug Tracker</a>
instance to automatically generate product road-maps - now that
we are actually starting to properly plan product updates and as
keeping them up to date manually isn't really working.</p>
<p>I spent a fair amount of fruitless time sending requests to
Mantis via <a href="https://www.getpostman.com/" rel="external nofollow noopener">Postman</a> only for every single request to fail
with <code>401 API Token required</code> - despite the fact I'd created a
limited access user and generated an API token associated with
that.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/php-authorize-header-1a.png" class="gallery" title="An error I swiftly got tired of seeing..." ><img src="https://images.cyotek.com/image/thumbnail/devblog/php-authorize-header-1a.png" alt="An error I swiftly got tired of seeing..." decoding="async" loading="lazy" /></a><figcaption>An error I swiftly got tired of seeing...</figcaption></figure>
<p>In the end after looking at the Mantis source files, I resorted
to editing <code>AuthMiddleware.php</code> directly on the server to start
spitting out output as a crude way of attempting to identify the
issue. This showed that the <code>Authorization</code> header just wasn't
present - any other header I sent was there, just that one in
particular was missing.</p>
<p>The documentation for <a href="http://php.net/manual/en/function.apache-request-headers.php" rel="external nofollow noopener"><code>apache_request_headers</code></a> doesn't
mention anything about authorisation, nor does
<a href="http://php.net/manual/en/function.getallheaders.php" rel="external nofollow noopener"><code>getallheaders</code></a>. <a href="http://php.net/manual/en/reserved.variables.server.php" rel="external nofollow noopener"><code>$_SERVER</code></a> on the other hand mentions
that new values may be created based on the contents of the
<code>Authorization</code> header but it too doesn't state anything about
the header being removed.</p>
<p>Fortunately, I found an answer in a user comment for the <a href="http://docs.php.net/manual/en/features.http-auth.php#114877" rel="external nofollow noopener">HTTP
authentication with PHP</a> documentation topic which is to
alter your <code>.htaccess</code> file to include the following line</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0
</pre>
</figure>
<p>I made this change to the <code>.htaccess</code> file located in the Mantis
REST API client folders (I didn't do it at the root level), and
now the API is working. Baby steps...</p>
<blockquote>
<p>Please note however that I'm not a PHP developer, and when it
comes to hosting, I'm an IIS guy and have very little
familiarity with Apache. So while this tweak works for me, I
can't state for certain it is the correct approach or if it
should have been handled another way. Nor do I know what the
cause is - seems odd that if this was official PHP behaviour
that it isn't documented anywhere that I could find. If you
know of a better way please <a href="https://cyotek.com/contact">let me know</a>!</p>
</blockquote>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/php-authorize-header-1b.png" class="gallery" title="Content! Glorious glorious content!" ><img src="https://images.cyotek.com/image/thumbnail/devblog/php-authorize-header-1b.png" alt="Content! Glorious glorious content!" decoding="async" loading="lazy" /></a><figcaption>Content! Glorious glorious content!</figcaption></figure><h2 id="update-history">Update History</h2>
<ul>
<li>2017-07-06 - 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/restoring-missing-authorization-header-when-using-php .
</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.comIntegrating NDepend with Jenkins Freestyle Jobsurn:uuid:33d70964-b969-4d9a-9dd6-84200b3eaaaf2017-02-11T19:28:49Z2017-02-11T19:16:26Z<p>Previously, I've <a href="/post/integrating-ndepend-with-jenkins">described</a> on this blog how to do a basic
integration of <a href="http://www.ndepend.com/" rel="external nofollow noopener">NDepend</a> with <a href="https://jenkins.io/" rel="external nofollow noopener">Jenkins</a> pipeline jobs. The
disadvantages of the previous post was it was essentially part
of a series tailored to our build process and so not easy to
view as a stand-alone article and it only covered pipelines.</p>
<p>As I result, I've added this complementary post to cover how to
perform the same level of integration with a freestyle project.
I don't normally like duplicating content on this blog but I
think this version is easier to read, not to mention the post I
did have planned for this week is delayed due to stubborn
mathematical issues.</p>
<h2 id="prerequisites-and-notes">Prerequisites and notes</h2>
<ul>
<li>This guide is written on the assumption you are familiar with
Jenkins and configuring freestyle projects</li>
<li>In order to publish reports for viewing within Jenkins, you
need to have installed the <a href="https://wiki.jenkins-ci.org/display/JENKINS/HTML+Publisher+Plugin" rel="external nofollow noopener">HTML Publisher</a> plugin</li>
<li>The NDepend GUI has been used to create a NDepend project file
for later analysis</li>
<li>An existing freestyle project is available which already
successfully checks out and compiles the assemblies the
NDepend project will process</li>
<li>NDepend must be installed on any computers used as a Jenkins
build agent. I have tested versions 6 and 7 without issue</li>
<li>The default Jenkins content security policy prevents embedded
NDepend reports from being viewed correctly. <a href="/post/adjusting-the-jenkins-content-security-policy">This guide</a>
contains instructions on reconfiguring the policy</li>
</ul>
<h2 id="calling-ndepend">Calling NDepend</h2>
<p>At present, there isn't a dedicated Jenkins plugin available for
NDepend, so we're slightly limited in how much integration we
can do.</p>
<p>The first step is to call NDepend. We can do this by adding a
<strong>Execute Windows batch command</strong> step to our project and then
set the <strong>Command</strong> field to call <code>NDepend.Console.exe</code> passing
in the filename of our project.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
NDepend.Console.exe <span class="string">&quot;%WORKSPACE%\cyotek\source\Applications\WebCopy\WebCopy.ndproj&quot;</span>
</pre>
</figure>
<blockquote>
<p><code>NDepend.Console.exe</code> requires the project filename to be
fully qualified. You can use the <code>%WORKSPACE%</code> environment
variable to get the fully qualified path of the agent
workspace and append your project filename to that.</p>
</blockquote>
<p>I have NDepend and other similar tools used by builds checked
into SVN so that they automatically get checked out into a build
agents workspace, so in my case I can use a relative path
pointing to <code>NDepend.Console.exe</code>. Alternatively, if the
location of NDepend's binaries are part of the OS path, you can
omit the path completely.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-jenkins-2a.png" class="gallery" title="An example of a command for executing NDepend via Jenkins" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-jenkins-2a.png" alt="An example of a command for executing NDepend via Jenkins" decoding="async" loading="lazy" /></a><figcaption>An example of a command for executing NDepend via Jenkins</figcaption></figure><h2 id="using-absolute-or-relative-paths-in-a-ndepend-project">Using absolute or relative paths in a NDepend project</h2>
<p>By default, all paths and filenames inside the NDepend project
are absolute. Jenkins builds take place in a temporary
workspace, the location of which could be different for each
agent. In such scenarios, the use of absolute paths could result
in either absolute failure, or out-dated / unexpected results.</p>
<p>There are two ways we can work around this - the first is to use
command line switches to override the paths in the project, and
the second is to make them relative.</p>
<h3 id="overriding-the-absolute-paths">Overriding the absolute paths</h3>
<p>The <code>InDirs</code> and <code>OutDir</code> arguments can be used to specify
override paths. <code>InDirs</code> controls where all the source files to
analyse are located and <code>OutDir</code> specifies where the report will
be written. Note that <code>InDirs</code> allows you to specify multiple
paths if required.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
NDepend.Console.exe <span class="string">&quot;%WORKSPACE%\cyotek\source\Applications\WebCopy\WebCopy.ndproj&quot;</span> /InDirs <span class="string">&quot;%WORKSPACE%\cyotek\source\Applications\WebCopy&quot;</span> /OutDir <span class="string">&quot;%WORKSPACE%\cyotek\source\Applications\WebCopy\NDependOut&quot;</span>
</pre>
</figure>
<h3 id="configuring-ndepend-to-use-relative-paths">Configuring NDepend to use relative paths</h3>
<p>These instructions apply to the stand alone tool, but should
also work from the Visual Studio extension.</p>
<ul>
<li>Open the <strong>Project Properties</strong> editor</li>
<li>Select the <strong>Paths Referenced</strong> tab</li>
<li>In the path list, select each path you want to make relative</li>
<li>Right click and select <strong>Set as Path Relative (to the NDepend
Project File Location)</strong></li>
<li>Save your changes</li>
</ul>
<p>Personally, I don't like absolute paths being stored in
documents, so I reconfigure my NDepend projects to use relative
paths.</p>
<h2 id="failing-the-build">Failing the build</h2>
<p>If the <strong>Execute Windows batch command</strong> returns non-zero, by
default Jenkins will fail the build. When NDepend runs
successfully and doesn't find any critical violations then it
will return the expected zero. However, even if it has otherwise
ran successfully, it will return non-zero in the event of
critical violations.</p>
<p>Depending on your needs, you might want to disable this
behaviour. For example, if you are using NDepend with an
existing code base, then potentially you're going to have
genuine violations to investigate, or false positives.</p>
<p>We can handle this either by marking the build as <em>unstable</em> or
suppressing the error.</p>
<h3 id="marking-the-build-as-unstable">Marking the build as unstable</h3>
<p>The <strong>Execute Windows batch command</strong> has an additional
parameter (hidden behind the <strong>Advanced</strong> button) named
<strong>ERRORLEVEL to set build unstable</strong>. By setting the value of
this field to <code>1</code> (the exit code NDepend will return when
critical violations are encountered), then the build result will
be Unstable, but it will continue executing.</p>
<h3 id="suppressing-the-error">Suppressing the error</h3>
<p>As the name of the step, <strong>Execute Windows batch command</strong>,
suggests, this isn't the execution of a single command. Jenkins
will create a batch file based on the contents of the
<strong>Command</strong> field which it then executes. Therefore, to suppress
the error we simply need to exit the batch file ourselves,
allowing us to supply our own exit code.</p>
<p>To do this, just add <code>exit /b 0</code> as a new line in the
<strong>Command</strong> field and any existing error code will be ignored.</p>
<blockquote>
<p>If you do this and NDepend fails to run for any other reason,
you won't know about it unless you check the log files. I'd
probably just go with marking the build as unstable, but you
could also change the command to start checking <code>ERRORLEVEL</code>
values manually and acting accordingly.</p>
</blockquote>
<h2 id="publishing-the-html">Publishing the HTML</h2>
<p>After the NDepend analysis has completed, a HTML report will be
generated. While this report doesn't offer the level of
functionality that viewing the results in NDepend's GUI does, it
provides a lot of useful data.</p>
<p>To embed this into Jenkins, we need to add a <strong>Publish HTML
reports</strong> post-build step.</p>
<ul>
<li>Set the <strong>HTML directory to archive</strong> field to point to the
location where the NDepend report has been saved - by default
this is a folder named <code>NDependOut</code> located in the same folder
as the NDepend project.</li>
<li>Set the <strong>Index page[s]</strong> field to be the name of the report,
which is always <code>NDependReport.html</code></li>
<li>Finally, set the <strong>Report title</strong> field to be whatever you
want - this will be used to label the navigation links in
Jenkins</li>
</ul>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-jenkins-2b.png" class="gallery" title="An example of a command for publishing the NDepend report into Jenkins" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-jenkins-2b.png" alt="An example of a command for publishing the NDepend report into Jenkins" decoding="async" loading="lazy" /></a><figcaption>An example of a command for publishing the NDepend report into Jenkins</figcaption></figure><h2 id="viewing-the-report">Viewing the report</h2>
<blockquote>
<p>As I mentioned at the start of this article, Jenkins has a
security policy that restricts embedded resources. This policy
cripples the NDepend report, and so you will need to loosen
the restrictions slightly as per my <a href="/post/adjusting-the-jenkins-content-security-policy">previous
post</a>.</p>
</blockquote>
<p>Once your Jenkins job has been ran and completed successfully
new navigation options for viewing the reports should appear in
the sidebar for the job, and the status overview providing
access to the NDepend report.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-jenkins-2c.png" class="gallery" title="Jenkins dashboard showing links to the published NDepend report, along with an unstable build due to rule violations" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-jenkins-2c.png" alt="Jenkins dashboard showing links to the published NDepend report, along with an unstable build due to rule violations" decoding="async" loading="lazy" /></a><figcaption>Jenkins dashboard showing links to the published NDepend report, along with an unstable build due to rule violations</figcaption></figure><h2 id="using-external-resource-files-to-avoid-duplication">Using external resource files to avoid duplication</h2>
<p>Another NDepend default is to save all the rules in the project
file. These stock rules substantially increase the size of the
NDepend project and if you don't modify them it doesn't really
make sense to have them duplicated over and over again.</p>
<p>Fortunately, NDepend allows rules to be stored in external
files, and so I used the NDepend GUI to create a rules file. Now
each project has had all the built in rules deleted and just
references the external file. Handy!</p>
<p>If your projects are using absolute paths, you can just the
<code>/RuleFiles</code> and <code>/KeepProjectRuleFiles</code> parameters with
<code>NDepend.Console.exe</code> for overriding the absolute versions if
required.</p>
<p>Similarly, NDepend version 7 introduces new settings for project
debt, but also provides the ability to store these in an
external file. Regretfully there doesn't seem to be override
switches available in <code>NDepend.Console.exe</code>, but as I've yet to
test the technical debt features yet I don't know if this will
be an issue or not.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-02-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/integrating-ndepend-with-jenkins-freestyle-jobs .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comAdjusting the Jenkins Content Security Policyurn:uuid:8e29f8ba-3547-40a9-8142-49611d31e72f2017-02-03T19:35:46Z2017-02-03T19:35:46Z<p>One of the security features of Jenkins is to send Content
Security Policy (CSP) headers which describes how certain
resources can behave. The <a href="https://wiki.jenkins-ci.org/display/JENKINS/Configuring+Content+Security+Policy" rel="external nofollow noopener">default policy</a> blocks pretty much
everything - no JavaScript, inline CSS, or even CSS from
external websites. This can cause problems with content added to
Jenkins via build processes, typically using the <a href="https://wiki.jenkins-ci.org/display/JENKINS/HTML+Publisher+Plugin" rel="external nofollow noopener">HTML Publisher
Plugin</a>.</p>
<p>While turning this policy off completely is not recommended it
can be beneficial to adjust the policy to be less restrictive,
allowing the user of external reports without compromising
security.</p>
<p>Although I described modifying the CSP in an <a href="/post/integrating-ndepend-with-jenkins#security-breach">earlier post</a>,
I didn't realise at the time of publishing that the method I
mentioned was only temporarily, and as soon as Jenkins was
restarted, the defaults were reapplied. This post both covers
that original method, and how to do it permanently, serving as a
stand-alone reference.</p>
<h2 id="the-hudson.model.directorybrowsersupport.csp-setting">The hudson.model.DirectoryBrowserSupport.CSP setting</h2>
<p>The contents of the CSP headers are defined by the
<code>hudson.model.DirectoryBrowserSupport.CSP</code> setting and supports
a wide range of values - the CSP specification is very flexible
and allows you to control how pretty much any type of resource
is loaded, or what JavaScript features are permitted.</p>
<p>The specific configuration values are beyond the scope of this
post, but you can learn more about CSP settings at <a href="https://content-security-policy.com/" rel="external nofollow noopener">a reference
site</a>.</p>
<p>In my own Jenkins instance, in order to get published NDepend
analysis reports working, I ended up using the following policy
which may be a good starting point for similar reports - it
allows the use of JavaScript and line CSS, but leaves everything
else blocked unless referenced directly from the Jenkins origin.</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
sandbox allow-scripts; default-src &#39;self&#39;; style-src &#39;self&#39; &#39;unsafe-inline&#39;;
</pre>
</figure>
<h2 id="temporarily-reconfiguring-the-content-security-policy">Temporarily reconfiguring the Content Security Policy</h2>
<p>To change the CSP, open the <strong>Script Console</strong> in Jenkins
administration section, and run the following command</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
System<span class="symbol">.</span>setProperty<span class="symbol">(</span><span class="string">&quot;hudson.model.DirectoryBrowserSupport.CSP&quot;</span><span class="symbol">,</span> <span class="string">&quot;sandbox allow-scripts; default-src &#39;self&#39;; style-src &#39;self&#39; &#39;unsafe-inline&#39;;&quot;</span><span class="symbol">)</span>
</pre>
</figure>
<p>Important! Changing the policy via the <strong>Script Console</strong> is not
permanent and will revert back to the default values when
Jenkins is restarted.</p>
<h2 id="permanently-changing-the-content-security-policy-when-running-jenkins-via-the-command-line">Permanently changing the Content Security Policy when running Jenkins via the command line</h2>
<p>If you run Jenkins via the command line, e.g. by calling
<code>java.exe</code>, you can add an extra argument to set the value of
the CSP setting.</p>
<p>The Java <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/java.html" rel="external nofollow noopener">documentation</a> states you can use the <code>-D</code> argument
to set a &quot;system property value&quot;, allowing us to add the
following to the command line</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
-Dhudson.model.DirectoryBrowserSupport.CSP=&quot;sandbox allow-scripts; default-src &#39;self&#39;; style-src &#39;self&#39; &#39;unsafe-inline&#39;;&quot;
</pre>
</figure>
<blockquote>
<p>Remember to add double quotes around the CSP value, otherwise
it will not be parsed correctly</p>
</blockquote>
<h3 id="a-note-on-order-of-arguments">A note on order of arguments</h3>
<p>There's one important caveat though - the ordering of the
parameters is important. I run Jenkins as a Windows service
(more on that in the next section) and the command line I use
looks similar to the following</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
-Xrs -Xmx256m -Dfile.encoding=UTF8 -Dhudson.lifecycle=hudson.lifecycle.WindowsServiceLifecycle -jar &quot;%BASE%\jenkins.war&quot; --httpPort=&lt;PORTNUM&gt; --webroot=&quot;%BASE%\war&quot;
</pre>
</figure>
<p>Initially I added the new argument to the end of that string,
but this never had any effect - each time Jenkins started the
value was missing. When checking the <strong>System Information</strong> view
I noted the parameter wasn't listed in the main <em>System
Properties</em> table but was instead appended to the value of
another property - <code>sun.java.command</code>. That was the point I
realized ordering mattered and moved the argument to before the
<code>--http</code> argument. After that, the property setting was
correctly applied.</p>
<h2 id="permanently-changing-the-content-security-policy-when-jenkins-is-running-as-a-windows-service">Permanently changing the Content Security Policy when Jenkins is running as a Windows Service</h2>
<p>If you run Jenkins as a Windows Service, the command parameters
are read from <code>jenkins.xml</code>, which is located in your main
Jenkins installation. If you open this file in the text editor
of your choice, you should see XML 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">service</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">id</span><span class="symbol">&gt;</span>jenkins<span class="symbol">&lt;/</span><span class="name">id</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">name</span><span class="symbol">&gt;</span>Jenkins<span class="symbol">&lt;/</span><span class="name">name</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">description</span><span class="symbol">&gt;</span>This service runs Jenkins continuous integration system.<span class="symbol">&lt;/</span><span class="name">description</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">env</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">JENKINS_HOME</span><span class="symbol">&quot;</span> <span class="name">value</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">%BASE%</span><span class="symbol">&quot;</span><span class="symbol">/&gt;</span>
 <span class="symbol">&lt;</span><span class="name">executable</span><span class="symbol">&gt;</span>%BASE%\jre\bin\java<span class="symbol">&lt;/</span><span class="name">executable</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">arguments</span><span class="symbol">&gt;</span>-Xrs -Xmx256m -Dfile.encoding=UTF8 -Dhudson.lifecycle=hudson.lifecycle.WindowsServiceLifecycle -jar &quot;%BASE%\jenkins.war&quot; --httpPort=<span class="symbol">&lt;</span><span class="name">PORTNUM</span><span class="symbol">&gt;</span> --webroot=&quot;%BASE%\war&quot;<span class="symbol">&lt;/</span><span class="name">arguments</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">logmode</span><span class="symbol">&gt;</span>rotate<span class="symbol">&lt;/</span><span class="name">logmode</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">onfailure</span> <span class="name">action</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">restart</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
<span class="symbol">&lt;/</span><span class="name">service</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>Simply add your new argument to the <code>arguments</code> element (with
the same caveat that parameter ordering is important), save the
file and restart Jenkins.</p>
<blockquote>
<p>Note: The contents of the <code>arguments</code> element must be a single
line, if line breaks are present the service will terminate on
startup</p>
</blockquote>
<h2 id="checking-the-value-of-the-content-security-policy-setting">Checking the value of the Content Security Policy setting</h2>
<p>An easy way to check the CSP policy (regardless of if you set it
via the command line or the <strong>Script Console</strong>) is to use
Jenkins' <strong>System Information</strong> view. This view includes a
<strong>System Properties</strong> table, if a custom CSP is in use, this
will be displayed in the table.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/jenkins-disable-csp-1a.png" class="gallery" title="Verifying that the CSP settings have been correctly recognised by Jenkins" ><img src="https://images.cyotek.com/image/thumbnail/devblog/jenkins-disable-csp-1a.png" alt="Verifying that the CSP settings have been correctly recognised by Jenkins" decoding="async" loading="lazy" /></a><figcaption>Verifying that the CSP settings have been correctly recognised by Jenkins</figcaption></figure><h2 id="update-history">Update History</h2>
<ul>
<li>2017-02-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/adjusting-the-jenkins-content-security-policy .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comIntegrating NDepend with Jenkinsurn:uuid:8dc14a71-f495-4422-ad1d-f8c16886ac332017-02-03T19:38:50Z2017-01-27T16:52:18Z<p>Apparently it's National Jenkins Month here at Cyotek as we seem
to be writing about it quite a lot recently. Previously I
explained how I got fed up of manually building and publishing
NuGet package projects, and got our Jenkins CI server to build
and publish them for me.</p>
<p>This got me thinking - some time ago I received a license for
<a href="http://www.ndepend.com/" rel="external nofollow noopener">NDepend</a> and even wrote a <a href="/post/a-brief-look-at-code-analysis-with-ndepend">post</a> briefly covering some of
its features.</p>
<p>Unfortunately while NDepend is a powerful tool, I have serious
issues with it's UI, both in terms of accessibility (it's very
keyboard unfriendly) and the way the UI operates (such as huge
floating tool&quot;tips&quot;). Add to that having to manually run the
tool meant a simple outcome - the tool was never used.</p>
<blockquote>
<p>Note: The version I have is 6.3 which is currently 9 months
out of date - while I was writing this post I discovered a new
2017 version is now available which I hope may have addressed
some of the issues I previously raised</p>
</blockquote>
<p>Despite the fact I wasn't hugely enamoured with NDepend, a
static analysis tool of some sort is a good thing to have in
your tool-belt for detecting issues you might miss or not be
aware of. And as I've been spending so much time with Jenkins
automation recently, I wondered how much of NDepend I could
automate away.</p>
<h2 id="pipeline-vs-freestyle">Pipeline vs Freestyle</h2>
<p>I'm going to be adding the NDepend integration to the Jenkins
pipeline script that I covered in two articles available
<a href="/post/using-a-jenkins-pipeline-to-build-and-publish-nuget-packages">here</a> and <a href="/post/using-parameters-with-jenkins-pipeline-builds">here</a>, but if you're not using pipelines you
can still do this with Freestyle jobs.</p>
<h2 id="tinkering-the-script">Tinkering the script</h2>
<p>Once again I'm going to declare some variables at the top of my
script so I can easily adjust them if need be. To avoid adding
any more parameters, I'm going to infer the existence of a
NDepend project <code>*.ndproj</code> by assuming it is named after the
project being compiled, and located in the same directory as the
solution.</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
def nDependProjectName <span class="symbol">=</span> <span class="string">&quot;${libName}.ndproj&quot;</span>
def nDependProject <span class="symbol">=</span> slnPath <span class="symbol">+</span> nDependProjectName
def nDependRunner <span class="symbol">=</span> <span class="string">&quot;\&quot;</span><span class="symbol">$</span><span class="symbol">{</span>WORKSPACE<span class="symbol">}</span><span class="symbol">\\</span>tools<span class="symbol">\\</span>ndepend<span class="symbol">\\</span>NDepend<span class="symbol">.</span>Console<span class="symbol">.</span>exe<span class="symbol">\</span><span class="string">&quot;&quot;</span>
</pre>
</figure>
<p>I have NDepend checked into version control in a tools directory
so it is available on build agents without needing a dedicated
installation. You'll need to adjust the path above to where the
executable is located (or define a Jenkins tool reference to
use)</p>
<h2 id="calling-ndepend">Calling NDepend</h2>
<p>As with test execution, I'm going to have a separate stage for
code analysis that will only appear and execute if a NDepend
project is detected. To perform the auto-detection I can make
use of the built-in <code>fileExists</code> command</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
<span class="keyword">if</span><span class="symbol">(</span>fileExists<span class="symbol">(</span>slnPathRel <span class="symbol">+</span> nDependProjectName<span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 stage<span class="symbol">(</span><span class="string">&#39;Analyse&#39;</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 bat<span class="symbol">(</span><span class="string">&quot;${nDependRunner} \&quot;</span><span class="symbol">$</span><span class="symbol">{</span>nDependProject<span class="symbol">}</span><span class="symbol">\</span><span class="string">&quot;&quot;</span><span class="symbol">)</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>The path specified in <code>fileExists</code> must be relative to the
current directory. Conversely, <code>NDepend.Console.exe</code> requires
the project filename to be fully qualified.</p>
</blockquote>
<p>I decided to place this new stage between the <strong>Build</strong> and
<strong>Tests</strong> stages in my pipeline script, as there isn't much
point running tests if an analysis finds critical errors.</p>
<h2 id="using-absolute-or-relative-paths-in-a-ndepend-project">Using absolute or relative paths in a NDepend project</h2>
<p>By default, all paths and filenames inside the NDepend project
are absolute. As Jenkins builds in temporary workspaces that
could be different for each build agent it's usually preferable
to use relative paths.</p>
<p>There are two ways we can work around this - the first is to use
command line switches to override the paths in the project, and
the second is to make them relative.</p>
<h3 id="overriding-the-absolute-paths">Overriding the absolute paths</h3>
<p>The <code>InDirs</code> and <code>OutDir</code> arguments can be used to specify
override paths - you'll need to specify both of these, as
<code>InDirs</code> controls where all the source files to analyse are
located, and <code>OutDir</code> specifies where the report will be
written. Note that <code>InDirs</code> allows you to specify multiple paths
if required.</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
bat<span class="symbol">(</span><span class="string">&quot;${nDependRunner} \&quot;</span><span class="symbol">$</span><span class="symbol">{</span>nDependProject<span class="symbol">}</span><span class="symbol">\</span><span class="string">&quot; /InDirs ${WORKSPACE}\\${binPath} /OutDir \&quot;</span><span class="symbol">$</span><span class="symbol">{</span>slnPath<span class="symbol">}</span>NDependOut<span class="symbol">\</span><span class="string">&quot;&quot;</span><span class="symbol">)</span>
</pre>
</figure>
<blockquote>
<p>Normally I always quote paths so that file names with spaces
don't cause parsing errors. In this case the <code>InDirs</code>
parameter is not quoted due to the path ending in a <code>\</code>
character. If I leave it quoted, NDepend seems to be treating
as an escape for the quote, thus causing a different set of
parsing errors</p>
</blockquote>
<h3 id="configuring-ndepend-to-use-relative-paths">Configuring NDepend to use relative paths</h3>
<p>These instructions apply to the stand alone tool, but should
also work from the Visual Studio extension.</p>
<ul>
<li>Open the <strong>Project Properties</strong> editor</li>
<li>Select the <strong>Paths Referenced</strong> tab</li>
<li>In the path list, select each path you want to make relative</li>
<li>Right click and select <strong>Set as Path Relative (to the NDepend
Project File Location)</strong></li>
<li>Save your changes</li>
</ul>
<p>As I don't really want absolute paths in these files, I'm going
to go with this option, although it would be better if I could
configure the default behaviour of NDepend in regards to paths.
As I already have some NDepend projects, I'm going to leave
<code>InDirs</code> and <code>OutDir</code> arguments in the script until I have time
to correct these existing projects with absolute paths.</p>
<h2 id="to-fail-or-not-to-fail-that-is-the-question">To fail or not to fail, that is the question</h2>
<p>Jenkins normally fails the build when a <code>bat</code> statement returns
a non-zero exit code, which is usually the expected behaviour.
If NDepend runs successfully and doesn't find any critical
violations then it will return the expected zero. However, even
if it has otherwise ran successfully, it will return non-zero in
the event of critical violations.</p>
<p>It's possibly a good idea to leave this behaviour alone, but for
the time being I don't want NDepend to be capable of failing my
builds. Firstly because I'm attaching these projects to code
that often has been in use for years and I need time to go
through any violations, and secondly because I know from
previous experience that NDepend reports false positives.</p>
<p>The <code>bat</code> command has an optional <code>returnStatus</code> argument. Set
this to <code>true</code> and Jenkins will return the exit code for your
script to check, but won't fail the build if it's non-zero.</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
bat<span class="symbol">(</span>returnStatus<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span> script<span class="symbol">:</span> <span class="string">&quot;${nDependRunner} \&quot;</span><span class="symbol">$</span><span class="symbol">{</span>nDependProject<span class="symbol">}</span><span class="symbol">\</span><span class="string">&quot; /InDirs ${WORKSPACE}\\${binPath} /OutDir \&quot;</span><span class="symbol">$</span><span class="symbol">{</span>slnPath<span class="symbol">}</span>NDependOut<span class="symbol">\</span><span class="string">&quot;&quot;</span><span class="symbol">)</span>
</pre>
</figure>
<h2 id="publishing-the-html">Publishing the HTML</h2>
<p>Once NDepend has created the report, we need to get this into
Jenkins. Unsurprisingly, Jenkins has a <a href="https://wiki.jenkins-ci.org/display/JENKINS/HTML+Publisher+Plugin" rel="external nofollow noopener">HTML
Publisher</a>
plugin for just this purpose - we only have to specify the
location of the report files, the default filename and the
report name.</p>
<p>The location is whatever we set the <code>OutDir</code> argument to when we
executed NDepend. The default filename will always be
<code>NDependReport.html</code>, and we can call it whatever we want!</p>
<p>Adding the following <code>publishHTML</code> command to the anaylse stage
will do the job nicely</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
publishHTML<span class="symbol">(</span><span class="symbol">[</span>allowMissing<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span> alwaysLinkToLastBuild<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span> keepAll<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span> reportDir<span class="symbol">:</span> slnPathRel <span class="symbol">+</span> <span class="string">&#39;NDependOut&#39;</span><span class="symbol">,</span> reportFiles<span class="symbol">:</span> <span class="string">&#39;NDependReport.html&#39;</span><span class="symbol">,</span> reportName<span class="symbol">:</span> <span class="string">&#39;NDepend&#39;</span><span class="symbol">]</span><span class="symbol">)</span>
</pre>
</figure>
<h2 id="security-breach">Security Breach!</h2>
<p>Once the HTML has been published, it will appear in the sidebar
menu for the job. On trying to view the report you might be in
for a surprise though.</p>
<blockquote>
<p>If you're using Blue Ocean, then the first part of the
statement above is incorrect - the Blue Ocean UI doesn't show
the HTML reports at all, to view the reports you need to use
the Classic interface</p>
</blockquote>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-jenkins-1a.png" class="gallery" title="That's... a lot of errors" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-jenkins-1a.png" alt="That's... a lot of errors" decoding="async" loading="lazy" /></a><figcaption>That's... a lot of errors</figcaption></figure>
<p>Jenkins wraps the report in a frame so that you can get back to
the original job page. The request that loads the document into
the frame has the <code>Content-Security-Policy:</code>,
<code>X-Content-Security-Policy</code> and <code>X-WebKit-CSP</code> headers set,
which effectively lock the page down to external resources and
script execution.</p>
<p>The NDepend report makes use of script and in-line CSS and so
the policy headers completely break it, unless you're using an
older version of Internet Explorer that doesn't process those
headers.</p>
<p>As I'm much happier pretending that IE doesn't exist clearly
that's not a solution for me. I did test it just to check
though, and setting IE to an emulated mode worked after a
fashion - the page was very unresponsive and several times
stopped painting. Go IE!</p>
<h2 id="reconfiguring-the-jenkins-content-security-policy">Reconfiguring the Jenkins Content Security Policy</h2>
<blockquote>
<p><strong>Update 03Feb2017</strong>. The instructions below only temporarily
change the CSP and will be reverted when Jenkins is restarted.
<a href="/post/adjusting-the-jenkins-content-security-policy">This follow-up post</a> describes how to permanently change
the CSP.</p>
</blockquote>
<p>I don't want to be disabling security features without good
cause and so although the Jenkins <a href="https://wiki.jenkins-ci.org/display/JENKINS/Configuring+Content+Security+Policy" rel="external nofollow noopener">documentation</a> does state
how to disable the CSP (along with a warning of why you
shouldn't!), I'm going to try adjusting it instead.</p>
<p>After some testing, the following policy would allow the report
to work correctly</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
sandbox allow-scripts; default-src <span class="string">&#39;self&#39;</span>; style-src <span class="string">&#39;self&#39;</span> <span class="string">&#39;unsafe-inline&#39;</span>;
</pre>
</figure>
<blockquote>
<p>I'm not a security expert. I tinkered the CSP policy enough to
allow it to work without turning it off fully, but that
doesn't mean the settings I have chosen are either optimal or
safe (for example, I didn't try using file hashes).</p>
</blockquote>
<p>To change the CSP, open the <strong>Script Console</strong> in Jenkins
administration section, and run the following command</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
System<span class="symbol">.</span>setProperty<span class="symbol">(</span><span class="string">&quot;hudson.model.DirectoryBrowserSupport.CSP&quot;</span><span class="symbol">,</span> <span class="string">&quot;sandbox allow-scripts; default-src &#39;self&#39;; style-src &#39;self&#39; &#39;unsafe-inline&#39;;&quot;</span><span class="symbol">)</span>
</pre>
</figure>
<p>With this policy in place, refreshing the report (after clearing
the browser cache) would display a fully functional report. I
still have some errors regarding fonts the CSS is referencing,
but as they don't even exist it seemed a little pointless adding
a rule for them.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-jenkins-1b.png" class="gallery" title="Much better, a functional report" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-jenkins-1b.png" alt="Much better, a functional report" decoding="async" loading="lazy" /></a><figcaption>Much better, a functional report</figcaption></figure><h2 id="another-alternative-to-changing-the-csp">Another alternative to changing the CSP</h2>
<p>One other possible alternative to avoid changing the CSP would
be to replace the NDepend report - it's actually a feature of
NDepend that you can specify a custom XSLT file used to generate
the report. Assuming this is straight forward enough to do, that
would actually be a pretty cool feature of NDepend and would
mean a static report could be generated that would comply with a
default CSP, not to mention trimming the report down a bit to
just essentials.</p>
<h2 id="creating-a-rules-file">Creating a rules file</h2>
<p>Another NDepend default is to save all the rules in the project
file. However, just like this Jenkins pipeline script I keep
adapting, I don't want to keep dozens of copies of stock rules.</p>
<p>And NDepend delivers here too - it allows rules to be stored in
external files, and so I used the NDepend GUI to create a rules
file before deleting all the rules embedded in the project.</p>
<p>As none of my previous NDepend projects use rule files, I didn't
add any overrides in the <code>NDepend.Console.exe</code> call above, but
you can use the <code>/RuleFiles</code> and <code>/KeepProjectRuleFiles</code>
parameters for overriding them if required.</p>
<h2 id="comparing-previous-results-a-work-in-progress">Comparing previous results, a work in progress</h2>
<p>One interesting feature of NDepend is that can automatically
compare the current analysis with previous ones, allowing to you
judge if code quality is improving (or not).</p>
<p>Of course, that will only work if the previous report data exist
- which it won't if it's only stored in a temporary workspace. I
also don't want that data in version control. I tried adding a
public share on our server, but when ran via Jenkins, both
NDepend and the HTML Publish claimed the directory didn't exist.
I tried pasting the command line from the Jenkins log into a new
console window which executed perfectly, so it's more than
likely a permissions issue for the service the Jenkins agent
runs under.</p>
<p>As the HTML Publisher plugin doesn't support exclusions, and as
we probably don't want all that historical data being uploaded
into Jenkins either, that would also mean copying the bits of
the report we wanted to publish to another folder for the plugin
to process.</p>
<p>All in all, for the time being I'll just stick with the current
analysis report - at least it is a starting point for
investigating my code.</p>
<h2 id="done-for-now">Done, for now</h2>
<p>And with this new addition my little script has become that much
more powerful. While I still have to do a little more tinkering
to the script by removing some of the parameters I've added and
making more use of auto detection, I think the script is
finished for the time being (at least until I revisit historical
NDepend analyses, or find something else to plug into it!)</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-01-27 - First published</li>
<li>2017-02-03 - Added note that CSP policy changes are temporary</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/integrating-ndepend-with-jenkins .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comUsing parameters with Jenkins pipeline buildsurn:uuid:20c34e0b-60e3-4816-a003-e088d81f381e2017-01-26T16:52:50Z2017-01-20T20:23:17Z<p>After my <a href="/post/using-a-jenkins-pipeline-to-build-and-publish-nuget-packages">first experiment</a> in building and publishing our
Nuget packages using Jenkins, I wasn't actually anticipating
writing a follow up post. As it transpires however, I was
unhappy with the level of duplication - at the moment I have 19
packages for our internal libraries, and there are around 70
other non-product libraries that could be turned into packages.
I don't really want 90+ copies of that script!</p>
<p>As I did mention originally, Jenkins does recommend that the
build script is placed into source control, so I started looking
at doing that. I wanted to have a single version that was
capable of handling different configurations that some projects
have and that would receive any required parameters directly
from the Jenkins job.</p>
<p>Fortunately this is both possible and easy to do as you can add
custom properties to a Jenkins job which the Groovy scripts can
then access. This article will detail how I took my original
script, and adapted it to handle 19 (and counting!) package
compile and publish jobs.</p>
<h2 id="defining-parameters">Defining parameters</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/pipeline-2b.png" class="gallery" title="An example of a parameterised build" ><img src="https://images.cyotek.com/image/thumbnail/devblog/pipeline-2b.png" alt="An example of a parameterised build" decoding="async" loading="lazy" /></a><figcaption>An example of a parameterised build</figcaption></figure>
<p>Parameters are switched off and hidden by default, but it's easy
enough to enable them. In the <strong>General</strong> properties for your
job, find and tick the option marked <strong>This project is
parameterised</strong>.</p>
<p>This will then show a button marked <strong>Add Parameter</strong> which,
when clicked, will show a drop-down of the different parameter
types available. For my script, I'm going to use single line
string, multi-line string and boolean parameters.</p>
<blockquote>
<p>The parameter name is used as environment variables in batch
jobs, therefore you should try and avoid common parameter
names such as <code>PATH</code> and also ensure that the name doesn't
include special characters such as spaces.</p>
</blockquote>
<p>By the time I'd added 19 pipeline projects (including converting
the four I'd created earlier) into parameterised builds running
from the same source script, I'd ended up with the following
parameters</p>
<table>
<thead>
<tr>
<th>Type</th>
<th>Name</th>
<th>Example Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>String</td>
<td>LIBNAME</td>
<td>Cyotek.Core</td>
</tr>
<tr>
<td>String</td>
<td>TESTLIBNAME</td>
<td>Cyotek.Core.Tests</td>
</tr>
<tr>
<td>String</td>
<td>LIBFOLDERNAME</td>
<td>src</td>
</tr>
<tr>
<td>String</td>
<td>TESTLIBFOLDERNAME</td>
<td>tests</td>
</tr>
<tr>
<td>Multi-line</td>
<td>EXTRACHECKOUTREMOTE</td>
<td>/source/Libraries/Cyotek.Win32</td>
</tr>
<tr>
<td>Multi-line</td>
<td>EXTRACHECKOUTLOCAL</td>
<td>.\source\Libraries\Cyotek.Win32</td>
</tr>
<tr>
<td>Boolean</td>
<td>SIGNONLY</td>
<td>false</td>
</tr>
</tbody>
</table>
<p>More parameters than I really wanted, but it covers the
different scenarios I need. Note that with the exception of
<code>LIBNAME</code>, all other parameters are optional and the build
should still run even if they aren't actually defined.</p>
<h2 id="accessing-parameters">Accessing parameters</h2>
<p>There are at least 3 ways that I know of accessing the
parameters from your script</p>
<ul>
<li><code>env.&lt;ParameterName&gt;</code> - returns the string parameter from
environment variables. (You can also use <code>env.</code> to get other
environment variables, for example <code>env.ProgramFiles</code>)</li>
<li><code>params.&lt;ParameterName&gt;</code> - returns the strongly typed
parameter</li>
<li><code>&quot;${&lt;ParameterName&gt;}&quot;</code> - returns the value via interpolation</li>
</ul>
<p>Of the three types above, the first two return <code>null</code> if you
request a parameter which doesn't exist - very helpful for when
you decide to add a new parameter later and don't want to update
all the existing projects!</p>
<p>The third however, will crash the build. It'll be easy to
diagnose if this happens as the output log for the build will
contain lines similar to the following</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
groovy.lang.MissingPropertyException: No such property: LIBFOLDERNAME for class: groovy.lang.Binding
 at groovy.lang.Binding.getVariable(Binding.java:63)
 at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onGetProperty(SandboxInterceptor.java:224)
 at org.kohsuke.groovy.sandbox.impl.Checker$4.call(Checker.java:241)
 at org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:238)
 at org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:221)
 at org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:221)
 at com.cloudbees.groovy.cps.sandbox.SandboxInvoker.getProperty(SandboxInvoker.java:28)
 at com.cloudbees.groovy.cps.impl.PropertyAccessBlock.rawGet(PropertyAccessBlock.java:20)
 at WorkflowScript.run(WorkflowScript:84)
 ... and much more!
</pre>
</figure>
<p>So my advice is to only use the interpolation versions when you
can guarantee the parameters will exist.</p>
<h2 id="adapting-the-previous-script">Adapting the previous script</h2>
<p>In my first attempt at creating the pipeline job, I had a block
of variables defined at the top of the script so I could easily
edit them when creating the next pipeline. I'm now going to
adapt that block to use parameters.</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
def libName <span class="symbol">=</span> params<span class="symbol">.</span>LIBNAME
def testLibName <span class="symbol">=</span> params<span class="symbol">.</span>TESTLIBNAME

def sourceRoot <span class="symbol">=</span> <span class="string">&#39;source\\Libraries\\&#39;</span>

def slnPath <span class="symbol">=</span> <span class="string">&quot;${WORKSPACE}\\${sourceRoot}${libName}\\&quot;</span>
def slnName <span class="symbol">=</span> <span class="string">&quot;${slnPath}${libName}.sln&quot;</span>
def projPath <span class="symbol">=</span> combinePath<span class="symbol">(</span>slnPath<span class="symbol">,</span> params<span class="symbol">.</span>LIBFOLDERNAME<span class="symbol">)</span>
def projName <span class="symbol">=</span> <span class="string">&quot;${projPath}${libName}.csproj&quot;</span>
def testsPath <span class="symbol">=</span> combinePath<span class="symbol">(</span>slnPath<span class="symbol">,</span> params<span class="symbol">.</span>TESTLIBFOLDERNAME<span class="symbol">)</span>

def hasTests <span class="symbol">=</span> testLibName <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> testLibName<span class="symbol">.</span>length<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">&gt;</span> <span class="number">0</span>
</pre>
</figure>
<p>I'm using <code>params</code> to access the parameters to avoid any
interpolation crashes. As it's possible the path parameters
could be missing or empty, I'm also using a <code>combinePath</code> helper
function. This is a <em>very</em> naive implementation and should
probably be made a little more robust. Although Java has a
<code>File</code> object which we could use, it is blocked by default as
Jenkins runs scripts in a sandbox. As I don't think turning off
security features is particularly beneficial, this simple
implementation will serve the requirements of my build jobs
easily enough.</p>
<figure class="lang-groovydef highlight"><figcaption><span>groovydef</span></figcaption>{
 def result;
 
 // This is a somewhat naive implementation, but it's sandbox safe
 
 if(path2 == null || path2.length() == 0)
 {
 result = path1
 }
 else
 {
 result = path1 + path2
 }
 
 if(result.charAt(result.length() - 1) != '\\')
 {
 result += '\\'
 }
 
 return result
}
</figure>
<blockquote>
<p>Note: The helper function must be placed <strong>outside</strong> <code>node</code>
statements</p>
</blockquote>
<h2 id="using-multi-line-string-parameters">Using multi-line string parameters</h2>
<p>The multi-line string parameter is exactly the same as a normal
string parameter, the difference simply seems to be the type of
editor they use. So if you want to treat them as an array of
values, you will need to build this yourself using the <code>split</code>
function.</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
<span class="keyword">if</span><span class="symbol">(</span>additionalCheckoutRemote <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> additionalCheckoutRemote<span class="symbol">.</span>length<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">)</span>
<span class="symbol">{</span>
 def additionalCheckoutRemotes <span class="symbol">=</span> additionalCheckoutRemote<span class="symbol">.</span>split<span class="symbol">(</span><span class="string">&quot;\\r?\\n&quot;</span><span class="symbol">)</span>

 <span class="comment">// do stuff with the string array created above </span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="performing-multiple-checkouts">Performing multiple checkouts</h2>
<p>Some of my projects are slightly naughty and pull code files
from outside their respective library folders. The previous
version of the script had these extra checkout locations
hard-coded, but that clearly will no longer suffice. Instead, by
leveraging the multi-line string parameters, I have let each job
define zero or more locations and check them out that way.</p>
<blockquote>
<p>I chose to use two parameters, one for the remote source and
one for the local destination even though this complicates
things slightly - but I felt it was better than trying to
munge both values into a single line</p>
</blockquote>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
<span class="keyword">if</span><span class="symbol">(</span>additionalCheckoutRemote <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> additionalCheckoutRemote<span class="symbol">.</span>length<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">)</span>
<span class="symbol">{</span>
 def additionalCheckoutRemotes <span class="symbol">=</span> additionalCheckoutRemote<span class="symbol">.</span>split<span class="symbol">(</span><span class="string">&quot;\\r?\\n&quot;</span><span class="symbol">)</span>
 def additionalCheckoutLocals <span class="symbol">=</span> params<span class="symbol">.</span>EXTRACHECKOUTLOCAL<span class="symbol">.</span>split<span class="symbol">(</span><span class="string">&quot;\\r?\\n&quot;</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> additionalCheckoutRemotes<span class="symbol">.</span>size<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span> 
 <span class="symbol">{</span>
 checkout<span class="symbol">(</span>changelog<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span> poll<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span> scm<span class="symbol">:</span> 
 <span class="symbol">[</span>
 <span class="symbol">$</span><span class="keyword">class</span><span class="symbol">:</span> <span class="string">&#39;SubversionSCM&#39;</span><span class="symbol">,</span> 
 additionalCredentials<span class="symbol">:</span> <span class="symbol">[</span><span class="symbol">]</span><span class="symbol">,</span> 
 excludedCommitMessages<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span> 
 excludedRegions<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span> 
 excludedRevprop<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span> 
 excludedUsers<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span> 
 filterChangelog<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span> 
 ignoreDirPropChanges<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span> 
 includedRegions<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span> 
 locations<span class="symbol">:</span> <span class="symbol">[</span><span class="symbol">[</span>credentialsId<span class="symbol">:</span> <span class="string">&#39;&lt;SVNCREDENTIALSID&gt;&#39;</span><span class="symbol">,</span> depthOption<span class="symbol">:</span> <span class="string">&#39;infinity&#39;</span><span class="symbol">,</span> ignoreExternalsOption<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span> local<span class="symbol">:</span> additionalCheckoutLocals<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">,</span> remote<span class="symbol">:</span> svnRoot <span class="symbol">+</span> additionalCheckoutRemotes<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">]</span><span class="symbol">]</span><span class="symbol">,</span> 
 workspaceUpdater<span class="symbol">:</span> <span class="symbol">[</span><span class="symbol">$</span><span class="keyword">class</span><span class="symbol">:</span> <span class="string">&#39;UpdateWithCleanUpdater&#39;</span><span class="symbol">]</span>
 <span class="symbol">]</span>
 <span class="symbol">)</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>I simply parse the two parameters, and issue a <code>checkout</code>
command for each pair. It would possibly make more sense to do
only a single <code>checkout</code> command with multiple <code>locations</code>, but
this way got the command up and running with minimum fuss.</p>
<h2 id="running-the-tests">Running the tests</h2>
<p>As not all my libraries have dedicated tests yet, I had defined
a <code>hasTests</code> variable at the top of the script which will be
true if the <code>TESTLIBNAME</code> parameter has a value. I could then
use this to exclude the NUnit execution and publish steps from
my earlier script, but that would still mean a <strong>Test</strong> stage
would be present. Somewhat to my surprise, I found wrapping the
<code>stage</code> statement in an <code>if</code> block works absolutely fine,
although it has a bit of an odour. It does mean that empty test
stages won't be display though.</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
<span class="keyword">if</span><span class="symbol">(</span>hasTests<span class="symbol">)</span>
<span class="symbol">{</span>
 stage<span class="symbol">(</span><span class="string">&#39;Test&#39;</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">try</span>
 <span class="symbol">{</span>
 <span class="comment">// call nunit2</span>
 <span class="comment">// can&#39;t use version 3 as the results plugin doesn&#39;t support the v3 output XML format</span>
 bat<span class="symbol">(</span><span class="string">&quot;${nunitRunner} \&quot;</span><span class="symbol">$</span><span class="symbol">{</span>testsPath<span class="symbol">}</span>bin<span class="symbol">/$</span><span class="symbol">{</span>config<span class="symbol">}</span><span class="symbol">/$</span><span class="symbol">{</span>testLibName<span class="symbol">}</span><span class="symbol">.</span>dll<span class="symbol">\</span><span class="string">&quot; /xml=\&quot;</span><span class="symbol">./</span>testresults<span class="symbol">/$</span><span class="symbol">{</span>testLibName<span class="symbol">}</span><span class="symbol">.</span>xml<span class="symbol">\</span><span class="string">&quot; /nologo /nodots /framework:net-4.5&quot;</span><span class="symbol">)</span>
 <span class="symbol">}</span>
 <span class="keyword">finally</span>
 <span class="symbol">{</span>
 <span class="comment">// as no subsequent stage will be ran if the tests fail, make sure we publish the results regardless of outcome</span>
 <span class="comment">// http://stackoverflow.com/a/40609116/148962</span>
 step<span class="symbol">(</span><span class="symbol">[</span><span class="symbol">$</span><span class="keyword">class</span><span class="symbol">:</span> <span class="string">&#39;NUnitPublisher&#39;</span><span class="symbol">,</span> testResultsPattern<span class="symbol">:</span><span class="string">&#39;testresults/*.xml&#39;</span><span class="symbol">,</span> debug<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span> keepJUnitReports<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span> skipJUnitArchiver<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span> failIfNoResults<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>
<p>Those were pretty much the only modifications I made to the
existing script to convert it from something bound to a specific
project to something I could use in multiple projects.</p>
<h2 id="archiving-the-artefacts">Archiving the artefacts</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/pipeline-2c.png" class="gallery" title="Build artefacts published to Jenkins" ><img src="https://images.cyotek.com/image/thumbnail/devblog/pipeline-2c.png" alt="Build artefacts published to Jenkins" decoding="async" loading="lazy" /></a><figcaption>Build artefacts published to Jenkins</figcaption></figure>
<p>In my original article, I briefly mentioned one of the things I
wanted the script to do was to archive the build artefacts but
then never mentioned it again. That was simply because I
couldn't get the command to work and I forgot to state that in
the post. As it happens, I realised what was wrong while working
on the improved version - I'd made all the paths in the script
absolute, but this command requires them to be relative to the
workspace.</p>
<p>The following command will archive the contents of the libraries
output folder along with the generated Nuget package.</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
archiveArtifacts artifacts<span class="symbol">:</span> <span class="string">&quot;${sourceRoot}${libName}\\${LIBFOLDERNAME}\\bin\\${config}\\*,nuget\\*.nupkg&quot;</span><span class="symbol">,</span> caseSensitive<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span> onlyIfSuccessful<span class="symbol">:</span> <span class="keyword">true</span>
</pre>
</figure>
<h2 id="updating-the-pipeline-to-use-a-jenkinsfile">Updating the pipeline to use a &quot;Jenkinsfile&quot;</h2>
<p>Now that I've got a (for the moment!) final version of the
script, it's time to add it to SVN and then tell Jenkins where
to find it. This way, all pipeline jobs can use the one script
and automatically inherit any changes to it.</p>
<p>The steps below will configure an existing pipeline job to use a
script file taken from SVN.</p>
<ul>
<li>In the <strong>Pipeline</strong> section of your jobs properties, set the
<strong>Definition</strong> field to be <strong>Pipeline script from SCM</strong></li>
<li>Select <strong>Subversion</strong> from the <strong>SCM</strong> field</li>
<li>Set the <strong>Repository URL</strong> to the location where the script is
located</li>
<li>Specify credentials as appropriate</li>
<li>Click <strong>Advanced</strong> to show advanced settings</li>
<li>Check the <strong>Ignore Property Changes on directories</strong> option</li>
<li>Enter <code>.*</code> in the <strong>Excluded Regions</strong> field</li>
<li>Set the <strong>Script Path</strong> field to match the filename of your
groovy script</li>
<li>Click <strong>Save</strong> to save the job details</li>
</ul>
<p>Now instead of using an in-line script, the pipeline will pull
the script right out of version control.</p>
<p>There are a couple of things to note however</p>
<ul>
<li>This repository becomes part of the polling of the job (if
polling is configured). Changing the <strong>Ignore Property Changes
on directories</strong> and <strong>Excluded Regions</strong> settings will
prevent changes to the script for triggering unnecessary
rebuilds</li>
<li>The specified repository is checked out into a sub-folder of
the job data named <code>workspace@script</code>. In other-words, <strong>it is
checked out directly into your Jenkins installation</strong>.
Originally I located the script in my <code>\build</code> folder along
with all other build files, until I noted all the files were
being checked out into multiple server paths, not the
temporary work spaces. My advice therefore is to stick the
script by itself in a folder so that it is the only file that
is checked out, and perhaps change the <strong>Repository depth</strong>
field to <strong>files</strong>.</li>
</ul>
<blockquote>
<p>It is worth reiterating the point, the contents of this folder
will be checked out onto the server where you have installed
Jenkins, not slave work-spaces</p>
</blockquote>
<h2 id="cloning-the-pipeline">Cloning the pipeline</h2>
<p>As it got a little tiresome creating the jobs manually over and
over again, I ended up creating a dummy pipeline for testing. I
created a new pipeline project, defined all the variables and
then populated these based on the requirements of one of my
libraries. Then I'd try and build the project.</p>
<p>If (or once) the build was successful I'd clone that template
project as the &quot;official&quot; pipeline, then update the template
pipeline for the next project. Rinse and repeat!</p>
<p>To create a new pipeline based on an existing job</p>
<ul>
<li>From the Jenkins dashboard choose <strong>New Item</strong> from Jenkins</li>
<li>Enter a unique name</li>
<li>Scroll to the bottom of the page, and in <strong>Copy from</strong> field,
start typing the name of your template job - when the
autocomplete lists your job, click it or press <kbd>Tab</kbd></li>
<li>Click <strong>OK</strong> to create the template</li>
</ul>
<p>Using this approach saved me a ton of work setting up quite a
few pipeline jobs.</p>
<h2 id="are-we-done-yet">Are we done yet?</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/pipeline-2a.png" class="gallery" title="My Jenkins dashboard showing 19 parameterised pipeline jobs running from one script" ><img src="https://images.cyotek.com/image/thumbnail/devblog/pipeline-2a.png" alt="My Jenkins dashboard showing 19 parameterised pipeline jobs running from one script" decoding="async" loading="lazy" /></a><figcaption>My Jenkins dashboard showing 19 parameterised pipeline jobs running from one script</figcaption></figure>
<p>Of course, as I was finalising the draft of this this post it
occurred to me that with a bit more work I could actually get
rid of virtually all the parameters I'd just added.</p>
<ul>
<li>All my pipeline projects are named after the library, so I
could discard the <code>LIBNAME</code> parameter in favour of the built
in <code>JOB_BASE_NAME</code> parameter</li>
<li>Given the relevant test projects are all named
<code>&lt;ProjectName&gt;.Tests</code>, I could auto generate that value and
use the <code>fileExists</code> command to detect if a test project was
present</li>
<li>The <code>LIBFOLDERNAME</code> and <code>TESTLIBFOLDERNAME</code> parameters are
required because not all my libraries are consistent with
their paths - some are directly in <code>/src</code>, some are in
<code>/src/&lt;ProjectName&gt;</code> and so on. Spending a little time
reworking the file system to be consistent means I could drop
another two parameters</li>
</ul>
<p>Happily thanks to having all the builds running from one script,
this means when I get around to making these improvements
there's only one script to update (excluding deleting the
obsolete parameters of course).</p>
<p>And this concludes my second articles on Jenkins pipelines, as
always comments welcome.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-01-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/using-parameters-with-jenkins-pipeline-builds .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comUsing a Jenkins Pipeline to build and publish Nuget packagesurn:uuid:576d0a08-fe2f-482d-92f2-b989686df0272017-01-26T16:52:36Z2017-01-14T13:41:51Z<p>I've mentioned elsewhere on this blog that our core products are
built using standard batch files, which are part of the products
source so they can be either build manually or from
<a href="https://jenkins.io/" rel="external nofollow noopener">Jenkins</a>. Over the last year I've been
gradually converting our internal libraries onto Nuget packages,
hosted on private servers. These packages are also built with a
simple batch file, although they currently aren't part of the CI
processes and also usually need editing before they can be ran
again.</p>
<p>After recently discovering that my StartSSL code signing
certificate was <a href="/post/startssl-code-signing-certificates-are-crippled">utterly useless</a>, I spent the better part of
a day rebuilding and publishing all the different packages with
a new non-crippled certificate. After that work was done, I
decided it was high time I built the packages using the CI
server.</p>
<p>Rather than continue with the semi-manual batch files, I decided
to make use of the <a href="https://jenkins.io/doc/book/pipeline/" rel="external nofollow noopener">pipeline</a> functionality that was added to
Jenkins, which to date I hadn't looked at.</p>
<h2 id="what-we-are-replacing">What we are replacing</h2>
<p>I suppose to start with it would be helpful to see an existing
build file for one of our libraries and then show how I created
a pipeline to replace this file. The library in question is
named <strong>Cyotek.Core</strong> and has nothing to do with .NET Core, but
has been the backbone of our common functionality since 2009.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
@ECHO <span class="keyword">OFF</span>

<span class="keyword">SETLOCAL</span>

<span class="keyword">CALL</span> ..\..\..\build\initbuild.bat

<span class="comment">REM Build and sign the file</span>
<span class="string">%msbuildexe%</span> Cyotek.Core.sln /p:Configuration=Release /verbosity:minimal /nologo /t:Clean,Build
<span class="keyword">CALL</span> signcmd src\bin\Release\Cyotek.Core.dll

<span class="comment">REM Create the package</span>
<span class="keyword">PUSHD</span> <span class="string">%CD%</span>
<span class="keyword">IF</span> <span class="keyword">NOT</span> <span class="keyword">EXIST</span> nuget MKDIR nuget
<span class="keyword">CD</span> nuget
<span class="string">%nugetexe%</span> pack ..\src\Cyotek.Core.csproj -Prop Configuration=Release
<span class="keyword">POPD</span>

<span class="comment">REM Publish</span>
<span class="string">%nugetexe%</span> push nuget\Cyotek.Core.1.3.0.nupkg -s &lt;YOURPACKAGEURI&gt; &lt;YOURAPIKEY&gt;

<span class="keyword">ENDLOCAL</span>
</pre>
</figure>
<p>These are the steps involved for building one of our Nuget
packages</p>
<ul>
<li>Get the source out of SVN (manual)</li>
<li>Edit the <code>AssemblyInfo.cs</code> file with a new version (manual)</li>
<li>Edit the batch file to mirror the version change (manual)</li>
<li>Restore NuGet packages (manual, if required)</li>
<li>Build the project in release mode</li>
<li>Run the associated testing library if present (manual)</li>
<li>Apply a digital signature to the release binary</li>
<li>Create a new Nuget package</li>
<li>Publish the package</li>
</ul>
<p>A few inconvenient manual steps there, lets see how Jenkins will
help.</p>
<h3 id="about-cyotek.cores-project-structure">About Cyotek.Core's Project Structure</h3>
<p>As it turns out, due to the way my environment is set up and how
projects are built, my scenario is a little bit more complicated
that it might otherwise be.</p>
<p>Our SVN repository is laid out as follows</p>
<ul>
<li><code>/</code> - Contains a <code>nuget.config</code> file so that all projects
share a single package folder, and also contains the strong
name key used by internal libraries</li>
<li><code>/build</code> - Numerous batch scripts for performing build actions
and InnoSetup includes for product deployment</li>
<li><code>/lib</code> - Native libraries for which a Nuget package isn't (or
wasn't) available</li>
<li><code>/resources</code> - Graphics and other media that can be linked by
individual projects without having multiple copies of common
images scattered everywhere</li>
<li><code>/source</code> - Source code</li>
<li><code>/tools</code> - Binaries for tools such as NUnit and internal
deployment tools so build agents have the resources they need
to work correctly</li>
</ul>
<p>Our full products check out a full copy of the entire repository
and while that means there is generally no issues about missing
files, it also means that new workspaces take a very long time
to checkout a large amount of data.</p>
<p>All of our public libraries (such as <strong>ImageBox</strong>) are self
contained. For the most part the internal ones are too, except
for the build processes and/or media resources. There are the
odd exceptions however with one being Cyotek.Core - we use a
number of Win32 API calls in our applications, normally defined
in a single interop library. However, there's a couple of key
libraries which I want dependency free and Cyotek.Core is one of
them. That doesn't mean I want to duplicate the interop
declarations though. Our interop library groups calls by type
(GDI, Resources, Find etc) and has separate partial code files
for each one. The libraries I want dependency free can then just
link the necessary files, meaning no dependencies, no publicly
exposed interop API, and no code duplication.</p>
<h2 id="what-is-a-pipeline">What is a pipeline?</h2>
<p>At the simplest level, a pipeline breaks your build down into a
series of discrete tasks, which are then executed sequentially.
If you've used Gulp or Grunt then the pattern should be
familiar.</p>
<p>A pipeline is normally comprised of one or more <strong>nodes</strong>. Each
node represents a build agent, and you can customise which
agents are used (for example to limit some actions to being only
performed on a Windows machine).</p>
<p>Nodes then contain one or more <strong>stages</strong>. A stage is a
collection of actions to perform. If all actions in the stage
complete successfully, the next stage in the current node is
then executed. The Jenkins dashboard will show how long each
stage took to execute and if the execution of the stage was
successful. Jenkins will also break the log down into sections
based on the stages, so when you click a stage in the dashboard,
you can view only the log entries related to that stage, which
can make it easier to diagnose some build failures (the full
output log is of course still available).</p>
<p>The screenshot below shows a pipeline comprised of 3 stages.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/pipeline-1a.png" class="gallery" title="A pipeline comprised of three stages showing two successful runs plus test results" ><img src="https://images.cyotek.com/image/thumbnail/devblog/pipeline-1a.png" alt="A pipeline comprised of three stages showing two successful runs plus test results" decoding="async" loading="lazy" /></a><figcaption>A pipeline comprised of three stages showing two successful runs plus test results</figcaption></figure>
<p>Pipelines are written in custom DSL based on a language named
<a href="http://www.groovy-lang.org/" rel="external nofollow noopener">Groovy</a>, which should be familiar
to anyone used to C-family programming languages. The following
snippet shows a sample job that does nothing but print out a
message into the log.</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
node <span class="symbol">{</span>
 stage<span class="symbol">(</span><span class="string">&#39;Message&#39;</span><span class="symbol">)</span> <span class="symbol">{</span>
 echo <span class="string">&#39;Hello World&#39;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Jenkins offers a number of built in commands but the real power
of the pipeline (as with freestyle jobs) is the ability to call
any installed plugin, even if they haven't been explicitly
designed with pipelines in mind.</p>
<h2 id="creating-a-pipeline">Creating a pipeline</h2>
<p>To create a new pipeline, choose <strong>New Item</strong> from Jenkins,
enter a name then select the Pipeline option. Click <strong>OK</strong> to
create the pipeline ready for editing.</p>
<p>Compared to traditional freestyle jobs, there's very few
configuration options as you will be writing script to do most
of the work.</p>
<p>Ignore all the options for now and scroll to the bottom of the
page where you'll find the pipeline editor.</p>
<h2 id="defining-our-pipeline">Defining our pipeline</h2>
<p>As the screenshot above shows, I divided the pipeline into 3
stages, each of which will perform some tasks</p>
<ul>
<li>Build
<ul>
<li>Get the source and required resources from SVN</li>
<li>Setup the workspace (creating required directories, cleaning
up old artefacts)</li>
<li>Update <code>AssemblyInfo.cs</code></li>
<li>Restore Nuget packages</li>
<li>Build the project</li>
</ul>
</li>
<li>Test
<ul>
<li>Run the tests for the library using NUnit 2</li>
<li>Publish the test results</li>
</ul>
</li>
<li>Deploy
<ul>
<li>Digitally sign the release binary</li>
<li>Create a Nuget package</li>
<li>Publish the package</li>
<li>Archive artefacts</li>
</ul>
</li>
</ul>
<p>Quite a list! Lets get started.</p>
<blockquote>
<p>Jenkins recommends you create the pipeline script in a
separate <code>Jenkinsfile</code> and check this into version control.
This might be a good idea once you have finalised your script,
but while developing it is probably a better idea to save it
in-line.</p>
<p>With that said, I still recommend developing the script in a
separate editor and then copying and pasting it into Jenkins.
I don't know if it is the <a href="https://github.com/afonsof/jenkins-material-theme" rel="external nofollow noopener">custom theme</a> I use or something
else, but the editor is really buggy and the cursor doesn't
appear in the right place, making deleting or updating
characters an interesting game of chance.</p>
</blockquote>
<p>I want all the actions to occur in the same workspace / agent,
so I'll define a single node containing my three stages. As a
lot of my packages will be compiled the same way, I'm going to
try and make it easier to copy and paste the script and adjust
things in one place at the top of the file, so I'll declare some
variables with these values.</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
node 
<span class="symbol">{</span>
 def libName <span class="symbol">=</span> <span class="string">&#39;Cyotek.Core&#39;</span>
 def testLibName <span class="symbol">=</span> <span class="string">&#39;Cyotek.Core.Tests&#39;</span>

 def slnPath <span class="symbol">=</span> <span class="string">&quot;${WORKSPACE}\\source\\Libraries\\${libName}\\&quot;</span>
 def slnName <span class="symbol">=</span> <span class="string">&quot;${slnPath}${libName}.sln&quot;</span>
 def projPath <span class="symbol">=</span> <span class="string">&quot;${slnPath}src\\&quot;</span>
 def projName <span class="symbol">=</span> <span class="string">&quot;${projPath}${libName}.csproj&quot;</span>
 def testsPath <span class="symbol">=</span> <span class="string">&quot;${slnPath}tests\\&quot;</span>

 def svnRoot <span class="symbol">=</span> <span class="string">&#39;&lt;YOURSVNTRUNKURI&gt;&#39;</span>
 def nugetApiKey <span class="symbol">=</span> <span class="string">&#39;&lt;YOURNUGETAPIKEY&gt;&#39;</span>
 def nugetServer <span class="symbol">=</span> <span class="string">&#39;&lt;YOURNUGETSERVERURI&gt;&#39;</span>

 def config <span class="symbol">=</span> <span class="string">&#39;Release&#39;</span>
 
 def nunitRunner <span class="symbol">=</span> <span class="string">&quot;\&quot;</span><span class="symbol">$</span><span class="symbol">{</span>WORKSPACE<span class="symbol">}</span><span class="symbol">\\</span>tools<span class="symbol">\\</span>nunit<span class="number">2</span><span class="symbol">\\</span>bin<span class="symbol">\\</span>nunit<span class="symbol">-</span>console<span class="symbol">-</span>x<span class="number">86</span><span class="symbol">.</span>exe<span class="symbol">\</span><span class="string">&quot;&quot;</span>
 def nuget <span class="symbol">=</span> <span class="string">&quot;\&quot;</span><span class="symbol">$</span><span class="symbol">{</span>WORKSPACE<span class="symbol">}</span><span class="symbol">\\</span>tools<span class="symbol">\\</span>nuget<span class="symbol">\\</span>nuget<span class="symbol">.</span>exe<span class="symbol">\</span><span class="string">&quot;&quot;</span>

 stage<span class="symbol">(</span><span class="string">&#39;Build&#39;</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// todo</span>
 <span class="symbol">}</span>
 
 stage<span class="symbol">(</span><span class="string">&#39;Test&#39;</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// todo</span>
 <span class="symbol">}</span>
 
 stage<span class="symbol">(</span><span class="string">&#39;Deploy&#39;</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// todo</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>In the above snippet, you may note I used a combination of
single and double quoting for strings. Similar to PowerShell,
Groovy does different things with strings depending on if they
are single or double quoted. Single quoted strings are treated
as-is, whereas double quoted strings will be interpolated -
the <code>${TOKEN}</code> patterns will be automatically replaced with
appropriate value. In the example above, I'm interpolating
both variables I've defined in the script and also standard
Jenkins environment variables.</p>
<p>You'll also note the use of escape characters as if you're
using backslashes you need to escape them. You also need to
escape single/double quotes if they match the quote the string
itself is using.</p>
</blockquote>
<h2 id="checking-out-the-repository">Checking out the repository</h2>
<p>I hadn't noticed this previously given that I was always
checking out the entire repository, but the <code>checkout</code> command
lets you specify multiple locations, customising both the remote
source and the local destination. This is perfect, as it means I
can now grab the bits I need. I add a <code>checkout</code> command to the
<strong>Build</strong> stage as follows</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
checkout<span class="symbol">(</span>
 <span class="symbol">[</span>
 <span class="symbol">$</span><span class="keyword">class</span><span class="symbol">:</span> <span class="string">&#39;SubversionSCM&#39;</span><span class="symbol">,</span>
 additionalCredentials<span class="symbol">:</span> <span class="symbol">[</span><span class="symbol">]</span><span class="symbol">,</span>
 excludedCommitMessages<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span>
 excludedRegions<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span>
 excludedRevprop<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span>
 excludedUsers<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span>
 filterChangelog<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span>
 ignoreDirPropChanges<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span>
 includedRegions<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span>
 locations<span class="symbol">:</span>
 <span class="symbol">[</span>
 <span class="symbol">[</span>credentialsId<span class="symbol">:</span> <span class="string">&#39;&lt;SVNCREDENTIALSID&gt;&#39;</span><span class="symbol">,</span> depthOption<span class="symbol">:</span> <span class="string">&#39;files&#39;</span> <span class="symbol">,</span> ignoreExternalsOption<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span> local<span class="symbol">:</span> <span class="string">&#39;.&#39;</span> <span class="symbol">,</span> remote<span class="symbol">:</span> <span class="string">&quot;${svnRoot}&quot;</span><span class="symbol">]</span><span class="symbol">,</span>
 <span class="symbol">[</span>credentialsId<span class="symbol">:</span> <span class="string">&#39;&lt;SVNCREDENTIALSID&gt;&#39;</span><span class="symbol">,</span> depthOption<span class="symbol">:</span> <span class="string">&#39;infinity&#39;</span><span class="symbol">,</span> ignoreExternalsOption<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span> local<span class="symbol">:</span> <span class="string">&#39;./build&#39;</span> <span class="symbol">,</span> remote<span class="symbol">:</span> <span class="string">&quot;${svnRoot}/build&quot;</span><span class="symbol">]</span><span class="symbol">,</span> 
 <span class="symbol">[</span>credentialsId<span class="symbol">:</span> <span class="string">&#39;&lt;SVNCREDENTIALSID&gt;&#39;</span><span class="symbol">,</span> depthOption<span class="symbol">:</span> <span class="string">&#39;infinity&#39;</span><span class="symbol">,</span> ignoreExternalsOption<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span> local<span class="symbol">:</span> <span class="string">&#39;./tools&#39;</span> <span class="symbol">,</span> remote<span class="symbol">:</span> <span class="string">&quot;${svnRoot}/tools&quot;</span><span class="symbol">]</span><span class="symbol">,</span> 
 <span class="symbol">[</span>credentialsId<span class="symbol">:</span> <span class="string">&#39;&lt;SVNCREDENTIALSID&gt;&#39;</span><span class="symbol">,</span> depthOption<span class="symbol">:</span> <span class="string">&#39;infinity&#39;</span><span class="symbol">,</span> ignoreExternalsOption<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span> local<span class="symbol">:</span> <span class="string">&#39;./source/Libraries/Cyotek.Win32&#39;</span><span class="symbol">,</span> remote<span class="symbol">:</span> <span class="string">&quot;${svnRoot}/source/Libraries/Cyotek.Win32&quot;</span><span class="symbol">]</span>
 <span class="symbol">[</span>credentialsId<span class="symbol">:</span> <span class="string">&#39;&lt;SVNCREDENTIALSID&gt;&#39;</span><span class="symbol">,</span> depthOption<span class="symbol">:</span> <span class="string">&#39;infinity&#39;</span><span class="symbol">,</span> ignoreExternalsOption<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span> local<span class="symbol">:</span> <span class="string">&quot;./source/Libraries/${libName}&quot;</span> <span class="symbol">,</span> remote<span class="symbol">:</span> <span class="string">&quot;${svnRoot}/source/Libraries/${libName}&quot;</span><span class="symbol">]</span>
 <span class="symbol">]</span><span class="symbol">,</span>

 workspaceUpdater<span class="symbol">:</span> <span class="symbol">[</span><span class="symbol">$</span><span class="keyword">class</span><span class="symbol">:</span> <span class="string">&#39;UpdateUpdater&#39;</span><span class="symbol">]</span>
 <span class="symbol">]</span>
<span class="symbol">)</span>
</pre>
</figure>
<blockquote>
<p>I didn't write the bulk of the <code>checkout</code> commands by hand,
instead I used Jenkins built in Snippet Generator to set all
the parameters using the familiar GUI and generate the
required script from that, at which point I could start adding
extra locations, tinkering formatting etc.</p>
</blockquote>
<p>As you can see, I can have configured different <code>local</code> and
<code>remote</code> attributes for each location to mimic the full repo.
I've also set the root location to only get the files at the
root level using the <code>depthOption</code> - otherwise it would check
out the entire repository anyway!</p>
<p>If I now run the build, everything is swiftly checked out to the
correct locations. Excellent start!</p>
<h3 id="preventing-polling-for-triggering-builds-for-satellite-folders">Preventing polling for triggering builds for satellite folders</h3>
<p>Well actually, it wasn't. While I was testing this pipeline, I
was also checking in files elsewhere to the repository. And as
I'd enabled polling for the pipeline, it kept triggering builds
without need due to the fact I'd included the repository root
for the strong name key. (After this blog post is complete I
think I'll do a little spring cleaning on the repository!)</p>
<p>In freestyle projects, I configure patterns so that builds are
only triggered when the changes made to the folders that
actually contain the application files. However, I could not get
the <code>checkout</code> command to honour either the <code>includedRegions</code> or
<code>excludedRegions</code> properties. Fortunately, when I took another
look at the built-in Snippet Generator, I noticed the command
supported two new properties - <code>changelog</code> and <code>poll</code>, the
latter of which controls if polling is enabled. So the solution
seemed simple - break the <code>checkout</code> command into two different
commands, one to do the main project checkout and another (with
<code>poll</code> set to <code>false</code>) to checkout supporting files.</p>
<p>The <strong>Build</strong> stage now looks as follows. Note that I had to put
the &quot;support&quot; checkout first, otherwise it would delete the
results of the previous checkout (again, probably due to the
root level location... sigh). You can always check the
Subversion Polling Log for your job to see what SVN URI's its
looking for.</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
checkout<span class="symbol">(</span>changelog<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span> poll<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span> scm<span class="symbol">:</span>
 <span class="symbol">[</span>
 <span class="symbol">$</span><span class="keyword">class</span><span class="symbol">:</span> <span class="string">&#39;SubversionSCM&#39;</span><span class="symbol">,</span>
 additionalCredentials<span class="symbol">:</span> <span class="symbol">[</span><span class="symbol">]</span><span class="symbol">,</span>
 excludedCommitMessages<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span>
 excludedRegions<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span>
 excludedRevprop<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span>
 excludedUsers<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span>
 filterChangelog<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span>
 ignoreDirPropChanges<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span>
 includedRegions<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span>
 locations<span class="symbol">:</span>
 <span class="symbol">[</span>
 <span class="symbol">[</span>credentialsId<span class="symbol">:</span> <span class="string">&#39;&lt;SVNCREDENTIALSID&gt;&#39;</span><span class="symbol">,</span> depthOption<span class="symbol">:</span> <span class="string">&#39;files&#39;</span> <span class="symbol">,</span> ignoreExternalsOption<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span> local<span class="symbol">:</span> <span class="string">&#39;.&#39;</span> <span class="symbol">,</span> remote<span class="symbol">:</span> <span class="string">&quot;${svnRoot}&quot;</span><span class="symbol">]</span><span class="symbol">,</span>
 <span class="symbol">[</span>credentialsId<span class="symbol">:</span> <span class="string">&#39;&lt;SVNCREDENTIALSID&gt;&#39;</span><span class="symbol">,</span> depthOption<span class="symbol">:</span> <span class="string">&#39;infinity&#39;</span><span class="symbol">,</span> ignoreExternalsOption<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span> local<span class="symbol">:</span> <span class="string">&#39;./build&#39;</span> <span class="symbol">,</span> remote<span class="symbol">:</span> <span class="string">&quot;${svnRoot}/build&quot;</span><span class="symbol">]</span><span class="symbol">,</span> 
 <span class="symbol">[</span>credentialsId<span class="symbol">:</span> <span class="string">&#39;&lt;SVNCREDENTIALSID&gt;&#39;</span><span class="symbol">,</span> depthOption<span class="symbol">:</span> <span class="string">&#39;infinity&#39;</span><span class="symbol">,</span> ignoreExternalsOption<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span> local<span class="symbol">:</span> <span class="string">&#39;./tools&#39;</span> <span class="symbol">,</span> remote<span class="symbol">:</span> <span class="string">&quot;${svnRoot}/tools&quot;</span><span class="symbol">]</span><span class="symbol">,</span> 
 <span class="symbol">[</span>credentialsId<span class="symbol">:</span> <span class="string">&#39;&lt;SVNCREDENTIALSID&gt;&#39;</span><span class="symbol">,</span> depthOption<span class="symbol">:</span> <span class="string">&#39;infinity&#39;</span><span class="symbol">,</span> ignoreExternalsOption<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span> local<span class="symbol">:</span> <span class="string">&#39;./source/Libraries/Cyotek.Win32&#39;</span><span class="symbol">,</span> remote<span class="symbol">:</span> <span class="string">&quot;${svnRoot}/source/Libraries/Cyotek.Win32&quot;</span><span class="symbol">]</span>
 <span class="symbol">]</span><span class="symbol">,</span>
 workspaceUpdater<span class="symbol">:</span> <span class="symbol">[</span><span class="symbol">$</span><span class="keyword">class</span><span class="symbol">:</span> <span class="string">&#39;UpdateUpdater&#39;</span><span class="symbol">]</span>
 <span class="symbol">]</span>
<span class="symbol">)</span>

checkout<span class="symbol">(</span>
 <span class="symbol">[</span>
 <span class="symbol">$</span><span class="keyword">class</span><span class="symbol">:</span> <span class="string">&#39;SubversionSCM&#39;</span><span class="symbol">,</span>
 additionalCredentials<span class="symbol">:</span> <span class="symbol">[</span><span class="symbol">]</span><span class="symbol">,</span>
 excludedCommitMessages<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span>
 excludedRegions<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span>
 excludedRevprop<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span>
 excludedUsers<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span>
 filterChangelog<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span>
 ignoreDirPropChanges<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span>
 includedRegions<span class="symbol">:</span> <span class="string">&#39;&#39;</span><span class="symbol">,</span>
 locations<span class="symbol">:</span> <span class="symbol">[</span><span class="symbol">[</span>credentialsId<span class="symbol">:</span> <span class="string">&#39;&lt;SVNCREDENTIALSID&gt;&#39;</span><span class="symbol">,</span> depthOption<span class="symbol">:</span> <span class="string">&#39;infinity&#39;</span><span class="symbol">,</span> ignoreExternalsOption<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span> local<span class="symbol">:</span> <span class="string">&quot;./source/Libraries/${libName}&quot;</span><span class="symbol">,</span> remote<span class="symbol">:</span> <span class="string">&quot;${svnRoot}/source/Libraries/${libName}&quot;</span><span class="symbol">]</span><span class="symbol">]</span><span class="symbol">,</span> 
 workspaceUpdater<span class="symbol">:</span> <span class="symbol">[</span><span class="symbol">$</span><span class="keyword">class</span><span class="symbol">:</span> <span class="string">&#39;UpdateUpdater&#39;</span><span class="symbol">]</span>
 <span class="symbol">]</span>
<span class="symbol">)</span>
</pre>
</figure>
<blockquote>
<p>A few minutes later I checked something else in... and wham,
the pipeline built itself again (it behaved fine after that
though). I had a theory that it was because Jenkins stored the
repository poll data separately and only parsed it from the
DSL when the pipeline was actually ran rather than saved, but
on checking the raw XML for the job there wasn't anything
extra. So that will have to remain a mystery for now.</p>
</blockquote>
<h2 id="deleting-and-creating-directories">Deleting and creating directories</h2>
<p>As I'm going to be generating Nuget packages and running tests,
I'll need some folders to put the output into. I already know
that NUnit won't run if the specified test results folder
doesn't exist, and I don't want to clutter the root of the
workspace with artefacts even if it is a temporary location.</p>
<p>For all its apparent power, the pipeline DSL also seems quite
limiting at times. It provides a (semi useless) remove directory
command, but doesn't have a command for actually creating
directories. Not to worry though as it does have <code>bat</code> and <code>sh</code>
commands for invoking either Windows batch or Unix shell files.
As I'm writing this blog post from a Windows perspective, I'll
be using ye-olde DOS commands.</p>
<p>But, before I create the directories, I'd better delete any
existing ones to make sure any previous artefacts are removed.
There's a built-in <code>deleteDir</code> command which recursively deletes
a directory. The <em>current</em> directory, which I why I referred to
it as semi-useless above - I would prefer to delete a directory
by name.</p>
<p>Another built-in command is <code>dir</code>. Not synonymous with the DOS
command, this helpful command changes directory, performs
whatever actions you define, then restores the original
directory - the equivalent of the <code>PUSHD</code>, <code>CD</code> and <code>POPD</code>
commands in my batch file at the top of this post.</p>
<p>The following snippets will delete the <code>nuget</code> and <code>testresults</code>
directories if they exist. If they don't then nothing will
happen. I found this a bit surprising - I would have expected it
to crash given I told it to delete a directory that doesn't
exist.</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
dir<span class="symbol">(</span><span class="string">&#39;nuget&#39;</span><span class="symbol">)</span>
<span class="symbol">{</span>
 deleteDir<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">}</span>
dir<span class="symbol">(</span><span class="string">&#39;testresults&#39;</span><span class="symbol">)</span>
<span class="symbol">{</span>
 deleteDir<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>We can then issue commands to create the directories. Normally
I'd use <code>IF NOT EXIST &lt;NAME&gt; MKDIR &lt;NAME&gt;</code>, but as we have
already deleted the folders we can just issue create commands.</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
bat<span class="symbol">(</span><span class="string">&#39;MKDIR testresults&#39;</span><span class="symbol">)</span>
bat<span class="symbol">(</span><span class="string">&#39;MKDIR nuget&#39;</span><span class="symbol">)</span>
</pre>
</figure>
<p>And now our environment is ready - time to build.</p>
<h2 id="building-a-project">Building a project</h2>
<p>First thing to do is to restore packages by calling <code>nuget restore</code> along with the filename of our solution</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
bat<span class="symbol">(</span><span class="string">&quot;${nuget} restore \&quot;</span><span class="symbol">$</span><span class="symbol">{</span>slnName<span class="symbol">}</span><span class="symbol">\</span><span class="string">&quot;&quot;</span><span class="symbol">)</span>
</pre>
</figure>
<p>Earlier I mentioned that I usually had to edit the projects
before building a Nuget package - this is due to needing to
update the version of the package as by default Nuget servers
don't allow you to overwrite packages with the same version
number. Our <code>.nuspec</code> files are mostly set up to use the
<code>$version$</code> token, which then pulls the true version from the
<code>AssemblyInformationVersion</code> attribute in the source project.
The core products run a batch command called
<code>updateversioninfo3</code> will will replace part of that version with
the contents of the Jenkins <code>BUILD_NUMBER</code> environment variable,
so I'm going to call that here.</p>
<blockquote>
<p>I don't want to get sidetracked as this post is already quite
long, so I'll probably cover this command in a different blog
post.</p>
</blockquote>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
bat<span class="symbol">(</span><span class="string">&quot;&quot;</span>&quot;
CALL <span class="symbol">.\\</span>build<span class="symbol">\\</span>initbuild
CALL updateversioninfo<span class="number">3</span> <span class="symbol">\</span><span class="string">&quot;${projPath}Properties\\AssemblyInfo.cs\&quot;</span>
<span class="string">&quot;&quot;</span>&quot;<span class="symbol">)</span>
</pre>
</figure>
<p>If you're paying attention, you'll see the string above looks
different from previous commands. To make it easy to specify
tool locations and other useful values our command scripts may
need, we have a file named <code>initbuild.bat</code> that sets up these
values in a single place.</p>
<p>However, each Jenkins <code>bat</code> call is a separate environment.
Therefore if I call <code>initbuild</code> from one <code>bat</code>, the values will
be lost in the second. Fortunately Groovy supports multi-line
strings, denoted by wrapping them in triple quotes (single or
double). As I'm using interpolation in the string as well, I
need to use double.</p>
<p>All preparation is completed and it's now time to build the
project. Although my <code>initbuild</code> script sets up a <code>msbuildexe</code>
variable, I wanted to test Jenkins tool commands and so I
defined a <strong>MSBuild</strong> tool named <code>MSBuild14</code>. The <code>tool</code> command
returns that value, so I can then use it to execute a release
build</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
def msbHome <span class="symbol">=</span> tool name<span class="symbol">:</span> <span class="string">&#39;MSBuild14&#39;</span><span class="symbol">,</span> type<span class="symbol">:</span> <span class="string">&#39;hudson.plugins.msbuild.MsBuildInstallation&#39;</span>
bat<span class="symbol">(</span><span class="string">&quot;\&quot;</span><span class="symbol">$</span><span class="symbol">{</span>msbHome<span class="symbol">}</span><span class="symbol">\</span><span class="string">&quot; \&quot;</span><span class="symbol">$</span><span class="symbol">{</span>slnName<span class="symbol">}</span><span class="symbol">\</span><span class="string">&quot; /p:Configuration=${config} /verbosity:minimal /nologo /t:Clean,Build&quot;</span><span class="symbol">)</span>
</pre>
</figure>
<h2 id="running-tests">Running tests</h2>
<p>With our <strong>Build</strong> stage complete, we can now move onto the
<strong>Test</strong> stage - which is a lot shorter and simpler.</p>
<p>I use NUnit to perform all of the testing of our library code.
By combining that with the <a href="https://wiki.jenkins-ci.org/display/JENKINS/NUnit+Plugin" rel="external nofollow noopener">NUnit
Plugin</a>
it means the rest results are directly visible in the Jenkins
dashboard, and I can see new tests, failed tests, or if the
number of tests suddenly drops.</p>
<blockquote>
<p>Note that the NUnit plugin hasn't been updated to support
reports generated by NUnit version 3, so I am currently
restricted to using NUnit 2</p>
</blockquote>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
bat<span class="symbol">(</span><span class="string">&quot;${nunitRunner} \&quot;</span><span class="symbol">$</span><span class="symbol">{</span>testsPath<span class="symbol">}</span>bin<span class="symbol">/$</span><span class="symbol">{</span>config<span class="symbol">}</span><span class="symbol">/$</span><span class="symbol">{</span>testLibName<span class="symbol">}</span><span class="symbol">.</span>dll<span class="symbol">\</span><span class="string">&quot; /xml=\&quot;</span><span class="symbol">./</span>testresults<span class="symbol">/$</span><span class="symbol">{</span>testLibName<span class="symbol">}</span><span class="symbol">.</span>xml<span class="symbol">\</span><span class="string">&quot; /nologo /nodots /framework:net-4.5&quot;</span><span class="symbol">)</span>
</pre>
</figure>
<p>After that's ran, I call the publish. Note that this plugin
doesn't participate with the Jenkins pipeline API and so it
doesn't have a dedicated command. Instead, you can use the
<code>step</code> command to execute the plugin.</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
step<span class="symbol">(</span><span class="symbol">[</span><span class="symbol">$</span><span class="keyword">class</span><span class="symbol">:</span> <span class="string">&#39;NUnitPublisher&#39;</span><span class="symbol">,</span> testResultsPattern<span class="symbol">:</span> <span class="string">&#39;testresults/*.xml&#39;</span><span class="symbol">,</span> debug<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span> keepJUnitReports<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span> skipJUnitArchiver<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span> failIfNoResults<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">]</span><span class="symbol">)</span>
</pre>
</figure>
<blockquote>
<p>Rather unfortunately the Snippet Editor wouldn't work
correctly for me when trying to generating the above step. It
would always generate the code <code>&lt;object of type hudson.plugins.nunit.NUnitPublisher&gt;</code>. Fortunately Ola Eldøy
had <a href="https://stackoverflow.com/a/40609116/148962" rel="external nofollow noopener">the answer</a>.</p>
</blockquote>
<p>However, there's actually a flaw with this sequence - if the
<code>bat</code> command that executes NUnit returns a non-zero exit code
(for example if the test run fails), the rest of the pipeline is
skipped and you won't actually see the failed tests appear in
the dashboard.</p>
<p>The solution is to wrap the <code>bat</code> call in <code>try ... finally</code>
block. If you aren't familiar with the try...catch pattern,
basically you <em>try</em> an operation, <em>catch</em> any problems, and
<em>finally</em> perform an action even if the initial operation
failed. In our case, we don't care if any problems occur, but we
do want to publish any available results.</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
ry
<span class="symbol">{</span>
 bat<span class="symbol">(</span><span class="string">&quot;${nunitRunner} \&quot;</span><span class="symbol">$</span><span class="symbol">{</span>testsPath<span class="symbol">}</span>bin<span class="symbol">/$</span><span class="symbol">{</span>config<span class="symbol">}</span><span class="symbol">/$</span><span class="symbol">{</span>testLibName<span class="symbol">}</span><span class="symbol">.</span>dll<span class="symbol">\</span><span class="string">&quot; /xml=\&quot;</span><span class="symbol">./</span>testresults<span class="symbol">/$</span><span class="symbol">{</span>testLibName<span class="symbol">}</span><span class="symbol">.</span>xml<span class="symbol">\</span><span class="string">&quot; /nologo /nodots /framework:net-4.5&quot;</span><span class="symbol">)</span>
<span class="symbol">}</span>
<span class="keyword">finally</span>
<span class="symbol">{</span>
 step<span class="symbol">(</span><span class="symbol">[</span><span class="symbol">$</span><span class="keyword">class</span><span class="symbol">:</span> <span class="string">&#39;NUnitPublisher&#39;</span><span class="symbol">,</span> testResultsPattern<span class="symbol">:</span> <span class="string">&#39;testresults/*.xml&#39;</span><span class="symbol">,</span> debug<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span> keepJUnitReports<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">,</span> skipJUnitArchiver<span class="symbol">:</span> <span class="keyword">false</span><span class="symbol">,</span> failIfNoResults<span class="symbol">:</span> <span class="keyword">true</span><span class="symbol">]</span><span class="symbol">)</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Now even if tests fail, the publish step will still attempt to
execute.</p>
<h2 id="building-the-package">Building the package</h2>
<p>With building and testing out of the way, it's time to create
the Nuget package. As all our libraries that are destined for
packages have <code>.nuspec</code> files, then we just call <code>nuget pack</code>
with the C# project filename.</p>
<p>Optionally, if you have an authenticode code signing
certificate, now would be a good time to apply it.</p>
<p>I create a <strong>Deploy</strong> stage containing the appropriate commands
for signing and packaging, as follows</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
bat<span class="symbol">(</span><span class="string">&quot;&quot;</span>&quot;
CALL <span class="symbol">.\\</span>build<span class="symbol">\\</span>initbuild
CALL <span class="symbol">.\\</span>build<span class="symbol">\\</span>signcmd <span class="symbol">$</span><span class="symbol">{</span>projPath<span class="symbol">}</span>bin<span class="symbol">\\$</span><span class="symbol">{</span>config<span class="symbol">}</span><span class="symbol">\\$</span><span class="symbol">{</span>libName<span class="symbol">}</span><span class="symbol">.</span>dll
<span class="string">&quot;&quot;</span>&quot;<span class="symbol">)</span>

dir<span class="symbol">(</span><span class="string">&#39;nuget&#39;</span><span class="symbol">)</span>
<span class="symbol">{</span>
 bat<span class="symbol">(</span><span class="string">&quot;${nuget} pack \&quot;</span><span class="symbol">$</span><span class="symbol">{</span>projName<span class="symbol">}</span><span class="symbol">\</span><span class="string">&quot; -Prop Configuration=${config}&quot;</span><span class="symbol">)</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="publishing-the-package">Publishing the package</h2>
<p>Once the package has been built, then we can publish it. In my
original batch files, I have to manually update the file to
change the version. However, <code>NUGET.EXE</code> actually supports
wildcards - and given that the first stage in our pipeline
deletes previous artefacts from the build folder, then there
can't be any existing packages. Therefore, assuming our
<code>updateversioninfo3</code> did its job properly, and our <code>.nuspec</code>
files use <code>$version$</code>, we shouldn't be creating packages with
duplicate names and have no need to hard-code filenames.</p>
<figure class="lang-groovy highlight"><figcaption><span>groovy</span></figcaption><pre class="code">
stage<span class="symbol">(</span><span class="string">&#39;Deploy&#39;</span><span class="symbol">)</span>
<span class="symbol">{</span>
 dir<span class="symbol">(</span><span class="string">&#39;nuget&#39;</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 bat<span class="symbol">(</span><span class="string">&quot;${nuget} push *.nupkg -s ${nugetServer} ${nugetApiKey}&quot;</span><span class="symbol">)</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="all-done">All Done?</h2>
<p>And that seems to be it. With the above script in place, I can
now build and publish Nuget packages for our common libraries
automatically. Which should serve as a good incentive to get as
much of our library code into packages as possible!</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/pipeline-1b.png" class="gallery" title="My Jenkins dashboard showing four pipeline projects using variations of the above script" ><img src="https://images.cyotek.com/image/thumbnail/devblog/pipeline-1b.png" alt="My Jenkins dashboard showing four pipeline projects using variations of the above script" decoding="async" loading="lazy" /></a><figcaption>My Jenkins dashboard showing four pipeline projects using variations of the above script</figcaption></figure>
<p>During the course of writing this post, I have tinkered and
adapted the original build script multiple times. After
finalising both the script and this blog post, I used the source
script to create a further 3 pipelines. In each case all I had
to do was change the <code>libName</code> and <code>testsName</code> variables, remove
the unnecessary <code>Cyotek.Win32</code> checkout location, and in one
case add a new checkout location for the <code>libs</code> folder. There
are now four pipelines happily building packages, so I'm going
to class this as a success and continue migrating my Nuget
builds into Jenkins.</p>
<p>My freestyle jobs have a step to email individuals when the
builds are broken, but I haven't added this to the pipeline jobs
yet. As subsequent stages don't execute if the previous stage
has failed, that implies I'd need to add a <code>mail</code> command to
each stage in another <code>try ... finally</code> block - something to
investigate another day.</p>
<p>The complete script can be downloaded from a link at the end of
this post.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-01-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/using-a-jenkins-pipeline-to-build-and-publish-nuget-packages .
</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.comStartSSL code signing certificates are crippledurn:uuid:1ded22bc-668b-4343-a68c-74a1e2a62fa42017-01-08T05:12:31Z2017-01-05T20:31:48Z<blockquote>
<p>TL;DR: StartSSL code signing certificates are crippled and
your binaries no longer trusted once they have expired, even
if they have been counter signed.</p>
</blockquote>
<p>Two years ago I purchase a code signing certificate from
StartSSL which was extremely smooth - I originally documented
the process in <a href="/post/creating-a-code-signing-certificate-with-startssl">a blog post</a>.</p>
<p>Fast forward two years of happily signing binaries and the
certificate was due to expire - time to renew. StartSSL has
recently had some trouble and their root certificates were going
to be distrusted by some of the major browsers. Although this
was a concern, I still probably would have purchased a new code
signing certificate from them, except for a &quot;lucky&quot; incident.</p>
<h2 id="what-is-the-problem-with-the-certificates">What is the problem with the certificates</h2>
<p>This blog post had quite long introduction as we haven't had the
best of luck with code certificates, but I decided against
publishing it. Suffice to say, I delayed purchasing a new
certificate until after it expired while I tried to determine if
we were going to go with another CA. By chance, I was testing
one of our signed setup programs in a virtual machine while
looking at an unrelated deployment issue. The binaries had been
countersigned before the expiry and by rights should have been
perfectly fine. Should have.</p>
<p>Instead of the usual Windows Vista UAC dialog (we <a href="/post/tools-we-use-2016-edition">use</a> Vista
VM's for testing) I was expecting, I got the following instead</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/startssl-1a.png" class="gallery" title="Why would this dialog be displayed for digitally signed software?" ><img src="https://images.cyotek.com/image/thumbnail/devblog/startssl-1a.png" alt="Why would this dialog be displayed for digitally signed software?" decoding="async" loading="lazy" /></a><figcaption>Why would this dialog be displayed for digitally signed software?</figcaption></figure>
<p>As I noted that binary was signed before the certificate expired
and the certificate hadn't been revoked, so what was the
problem? After all, signed software doesn't normally stop being
trusted after the natural lifetime of a certificate. (I tested
using a decade old copy of the Office 2003 setup to confirm).</p>
<p>On checking the signed programs properties and viewing the
signature, I was greeted with this</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/startssl-1b.png" class="gallery" title="I swore a lot when I saw this" ><img src="https://images.cyotek.com/image/thumbnail/devblog/startssl-1b.png" alt="I swore a lot when I saw this" decoding="async" loading="lazy" /></a><figcaption>I swore a lot when I saw this</figcaption></figure>
<p>Now fortunately, this is a) after the fact and b) I try to keep
my writing professional given that anything you write on the
internet has a habit of hanging around. But there was
substantial amounts of swearing going on when I saw this. (And a
wry chuckle that at least I'd removed the validation checks so I
wouldn't have a repeat of all software breaking <strong>again</strong>.
(Something else I subsequently verified as our build process
checks our binaries to make sure they are signed, now any Cyotek
binary which came from a NuGet package failed the deployment
check)).</p>
<p>Not being a security expert and unable to find answers with
searching, I took to <a href="https://stackoverflow.com/a/41421347/148962" rel="external nofollow noopener">Stack Overflow</a> and got a helpful
response</p>
<blockquote>
<p>Not all publisher certificates are enabled to permit
timestamping to provide indefinite lifetime. If the
publisher’s signing certificate contains the lifetime signer
OID (OID_KP_LIFETIME_SIGNING 1.3.6.1.4.1.311.10.3.13), the
signature becomes invalid when the publisher’s signing
certificate expires, even if the signature is timestamped.
This is to free a Certificate Authority from the burden of
maintaining Revocation lists (CRL, OCSP) in perpetuity.</p>
</blockquote>
<p>That sounded easy enough to verify, so I checked the certificate
properties, and there it was</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/startssl-1c.png" class="gallery" title="Oh look, a kill switch" ><img src="https://images.cyotek.com/image/thumbnail/devblog/startssl-1c.png" alt="Oh look, a kill switch" decoding="async" loading="lazy" /></a><figcaption>Oh look, a kill switch</figcaption></figure>
<p>That innocuous looking <em>Lifetime Signing</em> value is anything but
- it's like a hidden kill switch, and is the reason that the
binaries are now untrusted. Except this time around instead of 9
months of affected files, I've got <strong>two years</strong> worth of
untrusted files.</p>
<p>Checking other certificates (such as that Office 2003 setup)
just had the <em>Code Signing</em> entry, including my original two
Comodo certificates.</p>
<h2 id="the-solution">The solution?</h2>
<p>Maybe StartSSL stopped doing this in the past two years, but
somehow it seems unlikely. It may also be that only some classes
of certificates are affected by this (the first two I had from
Comodo and the one from StartSSL were class 2. I can say that
the Comodo certificates weren't crippled however.)</p>
<p>Regardless of whether class 3 aren't affected, or if they don't
do this anymore, I'm not using them in future. There wasn't even
the hint of a suggestion that the certificate I'd bought in good
faith was time bombed - clearly I would never have bought it if
I knew this would happen.</p>
<p>Add that and the fact that StartSSL are now owned by WoSign (a
Chinese CA I'd never heard of before), and are being distrusted
due to certain practices, it doesn't seem like a good idea for
me personally to use their services.</p>
<p>Against my better judgement I went back to Comodo as I couldn't
justify the price of other CA's. However, bar an initial hiccup,
the validation process is complete and we have our new company
certificate - I can switch the CI server back on now that the
builds aren't going to fail! And in fact, the process this time
was even easier and just involved the web browser.</p>
<p>And best of all, no kill switch in the certificate...</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/startssl-1d.png" class="gallery" title="Our new certificate with not a kill switch in sight" ><img src="https://images.cyotek.com/image/thumbnail/devblog/startssl-1d.png" alt="Our new certificate with not a kill switch in sight" decoding="async" loading="lazy" /></a><figcaption>Our new certificate with not a kill switch in sight</figcaption></figure>
<p>I wonder what will go wrong with code signing next? Hopefully
nothing and I won't be writing another post bemoaning
authenticode in future.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-01-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/startssl-code-signing-certificates-are-crippled .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comTools we use - 2016 editionurn:uuid:692bd5c0-c8f4-491f-bc5c-5b414e5dc4c42017-01-06T06:48:10Z2017-01-01T13:23:28Z<p>Happy New Year! Once again it's that time for the list of software products I use throughout the year. Not much change again overall, but given what I see happening in the web developer world when even your package manager needs a package manager I find the stability refreshing.</p>
<h2 id="operating-systems">Operating Systems</h2>
<ul>
<li>Windows Home Server 2011 - file server, SVN repository, backup host, CI server</li>
<li>Windows 10 Professional - development machines</li>
<li><del>Windows XP (virtualized) - testing</del> - We don't support XP anymore</li>
<li>Windows Vista (virtualized) - testing. Windows updates are broken on every single Vista snapshot we have which is annoying</li>
</ul>
<h2 id="development-tools">Development Tools</h2>
<ul>
<li><a href="https://www.getpostman.com/" rel="external nofollow noopener">Postman</a> is a absolutely brilliant client for testing REST services.</li>
<li><a href="http://www.microsoft.com/visualstudio/" rel="external nofollow noopener">Visual Studio 2015 Premium</a> - best IDE bar none</li>
<li><a href="https://www.jetbrains.com/decompiler/" rel="external nofollow noopener">DotPeek</a> - a decent replacement to .NET Reflector that can view things that Reflector can't, making it a worthwhile replacement despite some bugs and being chronically slow to start</li>
</ul>
<h2 id="visual-studio-extensions">Visual Studio Extensions</h2>
<ul>
<li><a href="http://www.oz-code.com/" rel="external nofollow noopener">OzCocde</a> - still my number one tool and one I'd be lost without, I can no longer abide debugging on machines without this beauty</li>
<li><a href="http://cyotek.com/blog/visual-studio-extension-for-adding-multiple-projects-to-a-solution">Cyotek Add Projects</a> - a simple extension I created that I use pretty much any time I create a new solution to add references to my standard source code libraries (at least until I finish converting them into Nuget packages)</li>
<li><a href="http://editorconfig.org/" rel="external nofollow noopener">EditorConfig</a> - useful for OSS projects to avoid space-vs-tab wars and now built into Visual Studio 2017</li>
<li><a href="https://visualstudiogallery.msdn.microsoft.com/3ebde8fb-26d8-4374-a0eb-1e4e2665070c" rel="external nofollow noopener">File Nesting</a> - allows you to easily nest (or unnest!) files, great for TypeScript or T4 templates</li>
<li><a href="https://visualstudiogallery.msdn.microsoft.com/4e84e2cf-2d6b-472a-b1e2-b84932511379" rel="external nofollow noopener">Open Command Line</a> - easily open command prompts, PowerShell prompts, or other tools to your project / solution directories</li>
<li><a href="http://mike-ward.net/vscoloroutput/" rel="external nofollow noopener">VSColorOutput</a> - add colour coding to Visual Studio's Output window</li>
<li><a href="http://visualstudiogallery.msdn.microsoft.com/e792686d-542b-474a-8c55-630980e72c30" rel="external nofollow noopener">Indent Guides</a> - easily see where you are in nested code</li>
<li><a href="http://www.jetbrains.com/resharper/" rel="external nofollow noopener">Resharper</a> - originally as a replacement for Regionerate, this swiftly became a firm favourite every time it told me I was doing something stupid.</li>
<li><a href="http://www.ncrunch.net/" rel="external nofollow noopener">NCrunch for Visual Studio</a> - (version 3!) automated parallel continuous testing tool. Works with NUnit, MSTest and a variety of other test systems. Great for TDD and picking up how a simple change you made to one part of your project completely destroys another part. We've all been there!</li>
</ul>
<h2 id="analytics">Analytics</h2>
<ul>
<li><del>Innovasys Luminitix (Link Removed)</del> - seeking alternatives</li>
</ul>
<h2 id="profiling">Profiling</h2>
<ul>
<li><ins>New!</ins> <a href="https://www.jetbrains.com/profiler/" rel="external nofollow noopener">dotTrace</a> - although I prefer the ANTS profiler, dotTrace is a very usable profiler and given it is included in my Resharper subscription, it's a no-brainer to use</li>
<li><ins>New!</ins> <a href="https://www.jetbrains.com/dotmemory/" rel="external nofollow noopener">dotMemory</a> - memory profiling is hard, need all the help we can get</li>
</ul>
<h2 id="documentation-tools">Documentation Tools</h2>
<ul>
<li><a href="http://www.innovasys.com/product/dx/overview" rel="external nofollow noopener">Innovasys Document! X</a> - Currently we use this to produce the user manuals for our applications</li>
<li><a href="http://www.atomineerutils.com/" rel="external nofollow noopener">Atomineer Pro Documentation</a> - automatically generate XML comment documentation in your source code</li>
<li><a href="http://markdownedit.com/" rel="external nofollow noopener">MarkdownEdit</a> - a no frills minimalist markdown editor that is actively maintained and Just Works</li>
<li><a href="http://notepad-plus-plus.org/" rel="external nofollow noopener">Notepad++</a> - because Notepad hasn't changed in 20 years (moving menu items around doesn't count!)</li>
</ul>
<h2 id="continuous-integration">Continuous Integration</h2>
<ul>
<li><ins>New!</ins> <a href="https://jenkins.io/index.html" rel="external nofollow noopener">Jenkins</a> - although the UI is fairly horrible (<a href="http://afonsof.com/jenkins-material-theme/" rel="external nofollow noopener">Jenkins Material Theme</a> helps!), Jenkins is easy to install, doesn't need a database server and has a rich plugin ecosystem, even for .NET developers. I use this to build, test and even deploy. TeamCity may be more powerful, but Jenkins is easier to maintain</li>
</ul>
<h2 id="graphics-tools">Graphics Tools</h2>
<ul>
<li><a href="http://www.getpaint.net/" rel="external nofollow noopener">Paint.NET</a> - brilliant bitmap editor with extensive plugins</li>
<li><a href="http://www.axialis.com/iconworkshop/" rel="external nofollow noopener">Axialis IconWorkshop</a> - very nice icon editor, been using this for untold years now since Microangelo decided to become the Windows Paint of icon editing</li>
<li><a href="http://cyotek.com/cyotek-spriter">Cyotek Spriter</a> - sprite / image map generation software</li>
<li><a href="http://cyotek.com/cyotek-gif-animator/">Cyotek Gif Animator</a> - gif animation creator that is shaping up nicely, when I have time to work on it</li>
</ul>
<h2 id="virtualization">Virtualization</h2>
<ul>
<li><a href="https://www.virtualbox.org/" rel="external nofollow noopener">Oracle VM VirtualBox</a> - for creating guest OS's for testing purposes</li>
</ul>
<h2 id="version-control">Version Control</h2>
<ul>
<li><a href="http://tortoisesvn.net/" rel="external nofollow noopener">TortoiseSVN</a> - Windows Explorer integration for SVN</li>
<li><a href="http://ankhsvn.open.collab.net/" rel="external nofollow noopener">AnhkSVN</a> - Subversion support for Visual Studio</li>
<li><a href="http://www.visualsvn.com/server/" rel="external nofollow noopener">VisualSVN Server</a> - Subversion Server for Windows</li>
<li><a href="https://github.com/" rel="external nofollow noopener">GitHub</a> / <a href="http://windows.github.com/" rel="external nofollow noopener">GitHub for Windows</a> - for the public facing aspects of our source code.</li>
</ul>
<h2 id="filedirectory-tools">File/directory tools</h2>
<ul>
<li><a href="http://winmerge.org/" rel="external nofollow noopener">WinMerge</a> - excellant file or directory comparison utility</li>
<li><a href="http://stefanstools.sourceforge.net/grepWin.html" rel="external nofollow noopener">WinGrep</a> - another excellent tool for swiftly searching directories for filters containing specified strings or expressions</li>
</ul>
<h2 id="backups">Backups</h2>
<ul>
<li><a href="http://cyotek.com/cyotek-copytools">Cyotek CopyTools</a> - we use this for offline backups of source code, assets and resources, documents, actually pretty much anything we generate; including backing up the backups!</li>
<li><a href="http://www.code42.com/crashplan/" rel="external nofollow noopener">CrashPlan</a> - CrashPlan creates an online backup of the different offline backups that CopyTools does. If you've ever lost a harddisk before with critical data on it that's nowhere else, you'll have backups squirrelled away everywhere too!</li>
</ul>
<h2 id="security">Security</h2>
<ul>
<li><del><a href="https://startssl.com/" rel="external nofollow noopener">StartSSL</a></del> / <del><a href="https://www.comodo.com/" rel="external nofollow noopener">Comodo</a></del> / ??? - <del>my code signing certificate just expired and rather unfortunately our previous vendor of choice, StartSSL, is having a few trust issues (to put it mildly) in addition to having been bought out by a Chinese CA. I've used Comodo in the past, but they have have the distinction of having the absolute worst customer service I have ever had the displeasure of experiencing. And the rest cost far to much for such a small studio as Cyotek. A conundrum...</del> <ins>Update 05Jan2015</ins> I went with Comodo after discovering the StartSSL was <a href="/post/startssl-code-signing-certificates-are-crippled">crippled</a> and this time the process was mostly smoth and stress free</li>
<li><ins>New!</ins> <a href="http://someonewhocares.org/hosts/" rel="external nofollow noopener">Dan Pollocks hosts file</a> blocks your computer from connecting to many thousands of dubious internet hosts and is continuously updated</li>
</ul>
<h2 id="other">Other</h2>
<ul>
<li><a href="https://justgetflux.com/" rel="external nofollow noopener">f.lux</a> - not really sure why I haven't mentioned this before, I've been using this utterly fantastic software for years. It adapts your monitor to the time of day, removing blue light as evening approaches and helps reduce eye strain when coding at night</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/tools-we-use-2016-edition .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comFTP Server Easter Eggsurn:uuid:322d8cc3-871b-4423-990c-97ecf96a83f62016-11-05T19:32:57Z2016-11-05T09:23:21Z<p>I've recently being working on integrating FTP into our
<a href="https://cyotek.com/cyotek-copytools">CopyTools</a> application. As a result of this, I have been
staring at quite a lot at FTP logs as the various tests and
processes do their work.</p>
<p>This morning I was running the CopyTools GUI client watching the
progress bar climb upwards as I was putting the support through
it's final paces. At the same time, the output from the FTP
commands were being printed to the debug log. I was idly
watching that too, when all of a sudden the following entries
appeared</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
PASV
227 Entering Passive Mode (91,208,99,4,171,236)
RETR /cyowcopy/images/regexedit_thumb.png
150-Accepted data connection
150-The computer is your friend. Trust the computer
150 58.2 kbytes to download
226-File successfully transferred
226 0.060 seconds (measured here), 0.95 Mbytes per second
</pre>
</figure>
<p>At first glance, that might appear to be perfectly normal FTP
input/output, but have a look at line 5</p>
<blockquote>
<p>150-The computer is your friend. Trust the computer</p>
</blockquote>
<p>That was... unexpected, I haven't seen a message like that
appear before. The FTP server I've been testing with identifies
itself as PureFTP; I have no idea if it's an egg only in that
particular server or if other servers do it too. While I haven't
read the FTP RFC's in great details, I'm fairly sure they don't
make mention of that!</p>
<p>I wonder how many Easter eggs are built into software we've been
using for years without ever noticing? And while I'm probably
very late to the party for noticing this egg, it's pretty cool
that they are still out there and software can have some humour
while going about thankless dull tasks.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2016-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/ftp-easter-eggs .
</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.comCreating and restoring bacpac files without using a GUIurn:uuid:9dc74326-4f46-42d8-94c4-1b9312ae1df22016-06-18T19:02:26Z2016-06-18T19:02:26Z<p>Almost all databases I use are SQL Server databases. They are
created with hand written SQL scripts and upgraded with hand
written SQL scripts - it is very rare I'll use SQL Server
Management Studio's (SSMS) designers to work with database
objects. When backing up or restoring databases, I have various
SQL scripts to do this, which works fine when SQL Server has
access to your file system, or you theirs.</p>
<p>This isn't always the case. Last year I replaced our woefully
inadequate error logging system with something slightly more
robust and modern, and this system is hosted on Microsoft's
Azure platform using SaaS. No direct file access there!</p>
<p>Rather than using traditional database backups, for Azure hosted
databases you need to use <a href="https://msdn.microsoft.com/en-us/library/ee210546.aspx" rel="external nofollow noopener">Data-tier Applications</a>. While
these do serve more advanced purposes than traditional backups,
in my scenario I am simply treating them as a means of getting a
database from A to B.</p>
<p>SSMS allows you to work with these files, but only via GUI
commands - there's no SQL statements equivalent to <code>BACKUP DATABASE</code> or <code>RESTORE DATABASE</code>, which is a royal pain. Although
I have my Azure database backed up to blog storage once a week,
I want to make my own backups more frequently, and be able to
restore these locally for development work and performance
profiling. Doing this using SQL Server's GUI tools is not
conductive to an easy workflow.</p>
<h2 id="a-cli-for-working-with-bacpac-files">A CLI for working with BACPAC files</h2>
<p>Fortunately, as I work with Visual Studio I have the <a href="https://msdn.microsoft.com/en-us/library/mt204009.aspx" rel="external nofollow noopener">SQL Server
Data Tools (SSDT)</a> installed, which includes
<code>SqlPackage.exe</code>, a magical tool that will let me import and
export BACPAC files locally and remotely.</p>
<p>Less fortunately, it isn't part of the path and so we can't just
merrily type <code>sqlpackage</code> into a command window the same way you
can type <code>sqlcmd</code> and expect it to work; it won't. And it
doesn't seem to have a convenient version-independent way of
grabbing it from the registry either. On my machine it is
located at <code>C:\Program Files (x86)\Microsoft SQL Server\120\DAC\bin</code>, but this may change based on what version
of the tools you have installed.</p>
<h2 id="creating-a-bacpac-file-from-an-existing-database">Creating a BACPAC file from an existing database</h2>
<p>To export a database into a BACPAC file, you can run the
following command. Note that this works for databases on a
local/remote SQL Server instance or Azure SQL Database.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
sqlpackage.exe /a:Export /ssn:&lt;ServerName&gt; /sdn:&lt;DatabaseName&gt; /su:&lt;UserName&gt; /sp:&lt;Password&gt; /tf:&lt;ExportFileName&gt;
</pre>
</figure>
<p>Listed below are the arguments we're using. In my example above, I'm using the short form, you can use either long or short forms to suit your needs.</p>
<ul>
<li><code>/Action</code> (<code>a</code>) - the action to perform, in this case
<strong>Export</strong></li>
<li><code>/SourceServerName</code> (<code>ssn</code>) - the source server name. Can be
either the URI of an Azure database server, or the more
traditional ServerName\InstanceName</li>
<li><code>/SourceDatabaseName</code> (<code>sdn</code>) - the name of the database to
export</li>
<li><code>/SourceUser</code> (<code>su</code>) - the login user name</li>
<li><code>/SourcePassword</code> (<code>sp</code>) - the login password</li>
</ul>
<p>For trusted connections, you can skip the <code>su</code> and <code>sp</code>
arguments.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/sqlpackage-export.png" class="gallery" title="Exporting an Azure SQL Database to a data-tier application file via the command line" ><img src="https://images.cyotek.com/image/thumbnail/devblog/sqlpackage-export.png" alt="Exporting an Azure SQL Database to a data-tier application file via the command line" decoding="async" loading="lazy" /></a><figcaption>Exporting an Azure SQL Database to a data-tier application file via the command line</figcaption></figure>
<p>The screenshot above shows typical output.</p>
<h2 id="restoring-a-database-from-a-bacpac-file">Restoring a database from a BACPAC file</h2>
<p>Restoring a database is just as easy, just use an action of
<strong>Import</strong> instead of export, and invert <em>source</em> and <em>target</em>
in arguments.</p>
<figure class="lang-cmd highlight"><figcaption><span>cmd</span></figcaption>sqlpackage.exe /a:Import /tsn:<ServerName> /tdn:<DatabaseName> /tu:<UserName> /tp:<Password> /sf:<ExportFileName>
</figure>
<p>There are a couple of caveats however - if the target database
already exists and contains objects such as tables or views,
then the import will fail. The database must either not exist,
or be completely empty.</p>
<p>Sadly, despite the fact that you have separate source and target
arguments, it doesn't appear to be possible to do a direct copy
from the source server to the target server.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/sqlpackage-import.png" class="gallery" title="Importing a data-tier application into a local SQL Server instance from a BACPAC file via the command line" ><img src="https://images.cyotek.com/image/thumbnail/devblog/sqlpackage-import.png" alt="Importing a data-tier application into a local SQL Server instance from a BACPAC file via the command line" decoding="async" loading="lazy" /></a><figcaption>Importing a data-tier application into a local SQL Server instance from a BACPAC file via the command line</figcaption></figure><h2 id="an-automated-batch-script-for-restoring-a-database">An automated batch script for restoring a database</h2>
<p>The following batch file is a simple script I use to restore the
newest available <strong>bacpac</strong> file in a given directory. The
script also deletes any existing local database using <code>sqlcmd</code>
prior to importing the database via <code>sqlpackage</code>, resolving a
problem where non-empty SQL databases can't be restored using
the package tool.</p>
<p>It's a very simple script, and not overly robust but it does the
job I need it to do. I still tend to use batch files over
PowerShell for simple tasks, no complications about loaded
modules, slow startup, just swift execution without fuss.</p>
<figure class="lang-cmd highlight"><figcaption><span>cmd</span></figcaption>@ECHO OFF

SETLOCAL

REM This is the directory where the SQL data tools are installed
SET SQLPCKDIR=C:\Program Files (x86)\Microsoft SQL Server\120\DAC\bin\
SET SQLPCK="%SQLPCKDIR%SqlPackage.exe"

REM The directory where the bacpac files are stored
SET DBDIR=D:\Backups\azuredbbackups\

REM The name of the database to import
SET DBNAME=MyDatabase

REM The SQL Server name / instance
SET SERVERNAME=.

REM SQL statement to delete the import database as SQLPACKAGE won't import to an existing database
SET DROPDATABASESQL=IF EXISTS (SELECT * FROM [sys].[databases] WHERE [name] = '%DBNAME%') DROP DATABASE [%DBNAME%];

REM Try and find the newest BACPAC file
FOR /F "tokens=*" %%a IN ('DIR %DBDIR%*.bacpac /B /OD /A-D') DO SET PACNAME=%%a

IF "%PACNAME%"=="" GOTO :bacpacnotfound

SET DBFILE=%DBDIR%%PACNAME%

SQLCMD -S %SERVERNAME% -E -Q "%DROPDATABASESQL%" -b
IF %errorlevel% NEQ 0 GOTO :error

%SQLPCK% /a:Import /sf:%DBFILE% /tdn:%DBNAME% /tsn:%SERVERNAME%
IF %errorlevel% NEQ 0 GOTO :error

GOTO :done

:bacpacnotfound
ECHO No bacpac file found to import. 
EXIT /B 1

:error
ECHO Failed to import bacpac file.
EXIT /B 1

:done
ENDLOCAL
</figure>
<h2 id="update-history">Update History</h2>
<ul>
<li>2016-06-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/creating-and-restoring-bacpac-files-without-using-a-gui .
</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.comSQL Woes - Mismatched parameter types in stored proceduresurn:uuid:b2ffc139-f7e5-49b7-88c0-c3ad8716ba1b2016-05-06T18:43:43Z2016-05-06T18:43:43Z<p>We had a report of crashes occurring for certain users when
accessing a system. From the stack data in the production logs,
a timeout was occurring when running a specific stored
procedure. This procedure was written around 5 years ago and is
in use in many customer databases without issue. Why would the
same SQL suddenly start timing out in one particular database?</p>
<p>The stored procedure in question is called for users with
certain permissions to highlight outstanding units of work that
their access level permits them to do, and is a fairly popular
(and useful) feature of the software.</p>
<p>After obtaining session information from the crash logs, it was
time to run the procedure on a copy of the live database with
session details. The procedure only reads information, but doing
this on a copy helps ensure no ... accidents occur.</p>
<figure class="lang-sql highlight"><figcaption><span>sql</span></figcaption><pre class="code">
<span class="keyword">EXEC</span> [Data].[GetX] @strSiteId = <span class="string">&#39;XXX&#39;</span>, @strUserGroupId = <span class="string">&#39;XXX&#39;</span>, @strUserName = <span class="string">&#39;XXX&#39;</span>
</pre>
</figure>
<p>And it took... <em>27 seconds to return 13 rows</em>. Not good, not
good at all.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/sql-parameters-1a.png" class="gallery" title="An example of a warning and explanation in a query plan" ><img src="https://images.cyotek.com/image/thumbnail/devblog/sql-parameters-1a.png" alt="An example of a warning and explanation in a query plan" decoding="async" loading="lazy" /></a><figcaption>An example of a warning and explanation in a query plan</figcaption></figure>
<p>Viewing the query plan showed something interesting though - one
of the nodes was flagged with a warning symbol, and when the
mouse was hovered over it it stated</p>
<blockquote>
<p>Type conversion in expression
(CONVERT_IMPLICIT(nvarchar(50),[Pn].[SiteId],0)) may affect
&quot;CardinalityEstimate&quot; in query plan choice</p>
</blockquote>
<p>Time to check the procedure's SQL as there shouldn't actually be
any conversions being done, let alone implicit ones.</p>
<p>I can't publish the full SQL in this blog, so I've chopped out
all the table names and field names and used dummy aliases. The
important bits for the purposes of this post are present though,
although I apologize that it's less than readable now.</p>
<figure class="lang-sql highlight"><figcaption><span>sql</span></figcaption><pre class="code">
<span class="keyword">CREATE</span> <span class="keyword">PROCEDURE</span> [Data].[GetX]
 @strSiteId <span class="keyword">nvarchar</span> (50)
, @strUserGroupId <span class="keyword">varchar</span> (20)
, @strUserName <span class="keyword">nvarchar</span> (50)
<span class="keyword">AS</span>
<span class="keyword">BEGIN</span>

 <span class="keyword">SELECT</span> [Al1].[X]
 , [Al1].[X]
 , [Al1].[X]
 , [Al1].[X]
 <span class="keyword">INTO</span> [#Access]
 <span class="keyword">FROM</span> [X].[X] [Al1]
 <span class="keyword">WHERE</span> [Al1].[X] = @strUserName
 <span class="keyword">AND</span> [Al1].[X] = @strUserGroupId
 <span class="keyword">AND</span> [Al1].[X] = 1
 <span class="keyword">AND</span> [Al1].[X] = 1

 <span class="keyword">SELECT</span> <span class="keyword">DISTINCT</span> [Pn].[Id] [X]
 <span class="keyword">FROM</span> [Data].[X] [Pn]
 <span class="keyword">INNER</span> <span class="keyword">JOIN</span> [Data].[X] [Al2]
 <span class="keyword">ON</span> [Al2].[X] = [Pn].[Id]
 <span class="keyword">AND</span> [Al2].[X] = 0
 <span class="keyword">INNER</span> <span class="keyword">JOIN</span> [Data].[X] [Al3]
 <span class="keyword">ON</span> [Al3].[X] = [Al2].[Id]
 <span class="keyword">AND</span> [Al3].[X] = 0
 <span class="keyword">INNER</span> <span class="keyword">JOIN</span> [Data].[X] [Al4]
 <span class="keyword">ON</span> [Al4].[X] = [Al3].[Id]
 <span class="keyword">AND</span> [Al4].[X] = 0
 <span class="keyword">INNER</span> <span class="keyword">JOIN</span> [Data].[X] [Al5]
 <span class="keyword">ON</span> [Al5].[X] = [Al4].[Id]
 <span class="keyword">AND</span> [Al5].[X] = 0
 <span class="keyword">AND</span> [Al5].[X] = 1
 <span class="keyword">AND</span> [Al5].[X] = 0
 <span class="keyword">INNER</span> <span class="keyword">JOIN</span> [#Access]
 <span class="keyword">ON</span> [#Access].[X] = [Al5].[X]
 <span class="keyword">AND</span> [#Access].[X] = [Al2].[X]
 <span class="keyword">AND</span> [#Access].[X] = [Al3].[X]
 <span class="keyword">AND</span> [#Access].[X] = [Al4].[X]
 <span class="keyword">WHERE</span> <span class="keyword">EXISTS</span> (
 <span class="keyword">SELECT</span> [X]
 <span class="keyword">FROM</span> [X].[X] [Al6]
 <span class="keyword">WHERE</span> [Al5].[X] = [Al6].[X]
 <span class="keyword">AND</span> [Al5].[X] = [Al6].[X]
 <span class="keyword">AND</span> [Al6].[X] = 1
 )
 <span class="keyword">AND</span> [Pn].[SiteId] = @strSiteId;
 
 <span class="keyword">DROP</span> <span class="keyword">TABLE</span> [#Access]

<span class="keyword">END</span>;
</pre>
</figure>
<p>The SQL is fairly straight forward - we join a bunch of
different data tables together based on permissions, data status
and where the <code>[SiteId]</code> column matches the lookup value, return
return a unique list of core identifiers. With the exception of
<code>[SiteId]</code> all those joins on <code>[Id]</code> columns are integers.</p>
<blockquote>
<p>Yes, <code>[SiteId]</code> is the primary key in a table. Yes, I know it
isn't a good idea using string keys. It was a design decision
made over 8 years ago and I'm sure at some point these
anomalies will be changed. But it's a side issue to what this
post is about.</p>
</blockquote>
<p>As the warning from the query plan is quite explicit about the
column it's complaining about, it is now time to check the
definition of the table containing the <code>[SiteId]</code> column. Again,
I'm not at liberty to include anything other than the barest
information to show the problem.</p>
<figure class="lang-sql highlight"><figcaption><span>sql</span></figcaption><pre class="code">
<span class="keyword">CREATE</span> <span class="keyword">TABLE</span> [X].[X]
(
 [SiteId] <span class="keyword">varchar</span>(50) <span class="keyword">NOT</span> <span class="keyword">NULL</span> <span class="keyword">CONSTRAINT</span> [PK_X] <span class="keyword">PRIMARY</span> <span class="keyword">KEY</span>
 ...
);
GO
</pre>
</figure>
<p>Can you see the problem? The <em>table</em> defines <code>[SiteId]</code> as
<code>varchar(50)</code> - that is, up to 50 ASCII characters. The <em>stored
procedure</em> on the other hand defines the <code>@strSiteId</code> parameter
(that is used as a <code>WHERE</code> clause for <code>[SiteId]</code>) as
<code>nvarchar(50)</code>, i.e. up to 50 Unicode characters. And there we
go, implicit conversion from Unicode to ASCII that for some
(still unknown at this stage) reason destroyed the performance
of this particular database.</p>
<p>After changing the stored procedure (remember I'm on a copy of
the production database!) to remove that innocuous looking <code>n</code>,
I reran the procedure which completed instantly. And the warning
has disappeared from the plan.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/sql-parameters-1b.png" class="gallery" title="A plan for the same procedure after deleting a single character" ><img src="https://images.cyotek.com/image/thumbnail/devblog/sql-parameters-1b.png" alt="A plan for the same procedure after deleting a single character" decoding="async" loading="lazy" /></a><figcaption>A plan for the same procedure after deleting a single character</figcaption></figure>
<p>The error probably originally occurred as a simple oversight -
almost all character fields in the database are <code>nvarchar</code>'s.
Those that are <code>varchar</code> are ones that control definition data
that cannot be entered, changed or often even viewed by end
users. Anything that the end user can input is always <code>nvarchar</code>
due to the global nature of the software in question.</p>
<p>Luckily, it's a simple fix, although potentially easy to miss,
especially as you might immediately assume the SQL itself is to
blame and try to optimize that.</p>
<p>The take away from this story is simple - ensure that the data
types for variables you use in SQL match the data types of the
fields to avoid implicit conversions that can cause some very
unexpected and unwelcome performance issues - even years after
you originally wrote the code.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2016-05-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/sql-woes-mismatched-parameter-types-in-stored-procedures .
</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.comReading and writing farbfeld images using C#urn:uuid:f55d4c11-f78e-4292-8cdb-39fd5b0eff742017-03-04T06:29:21Z2016-01-19T17:00:44Z<p>Normally when I load textures in OpenGL, I have a PNG file which
I load into a <code>System.Drawing.Bitmap</code> and from there I pull out
the bytes and pass to <code>glTexImage2D</code>. It works, but seems a bit
silly having to create the bitmap in the first place. For this
reason, I was toying with the idea of creating a very simple
image format so I could just read the data directly without
requiring intermediate objects.</p>
<p>While mulling this idea over, I spotted an article on <a href="https://news.ycombinator.com/item?id=10890873" rel="external nofollow noopener">Hacker
News</a> describing a similar and simple image format named
<a href="http://tools.suckless.org/farbfeld/" rel="external nofollow noopener">farbfeld</a>. This format by <a href="http://suckless.org/" rel="external nofollow noopener">suckless.org</a> is described as
&quot;a lossless image format which is easy to parse, pipe and
compress&quot;.</p>
<p>Not having much else to do on a Friday night, I decided I'd
write a C# encoder and decoder for this format, along with a
basic GUI app for viewing and converting farbfeld images.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/farbfeld-viewer.png" class="gallery" title="A simple program for viewing and converting farbfeld images" ><img src="https://images.cyotek.com/image/thumbnail/devblog/farbfeld-viewer.png" alt="A simple program for viewing and converting farbfeld images" decoding="async" loading="lazy" /></a><figcaption>A simple program for viewing and converting farbfeld images</figcaption></figure><h2 id="the-format">The format</h2>
<table>
<thead>
<tr>
<th style="text-align: right;">Bytes</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: right;">8</td>
<td>&quot;farbfeld&quot; magic value</td>
</tr>
<tr>
<td style="text-align: right;">4</td>
<td>32-Bit BE unsigned integer (width)</td>
</tr>
<tr>
<td style="text-align: right;">4</td>
<td>32-Bit BE unsigned integer (height)</td>
</tr>
<tr>
<td style="text-align: right;">[2222]</td>
<td>4x16-Bit BE unsigned integers [RGBA] / pixel, row-aligned</td>
</tr>
</tbody>
</table>
<p>As you can see, it's about as simple as you can get, barring the
big-endian encoding I suppose. The main thing we have to worry
about is that farbeld stores RGBA values in the range 0-65535,
whereas in .NET-land we tend to use 0-255.</p>
<h2 id="decoding-an-image">Decoding an image</h2>
<p>Decoding an image is fairly straight forward. The difficult part
is turning those values into a .NET image in a fast manner.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">bool</span> IsFarbfeldImage<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>

 buffer <span class="symbol">=</span> <span class="keyword">new</span> <span class="keyword">byte</span><span class="symbol">[</span><span class="number">8</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> buffer<span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span> <span class="symbol">==</span> <span class="string">&#39;f&#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;r&#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;b&#39;</span> <span class="symbol">&amp;&amp;</span> buffer<span class="symbol">[</span><span class="number">4</span><span class="symbol">]</span> <span class="symbol">==</span> <span class="string">&#39;f&#39;</span> <span class="symbol">&amp;&amp;</span> buffer<span class="symbol">[</span><span class="number">5</span><span class="symbol">]</span> <span class="symbol">==</span> <span class="string">&#39;e&#39;</span> <span class="symbol">&amp;&amp;</span> buffer<span class="symbol">[</span><span class="number">6</span><span class="symbol">]</span> <span class="symbol">==</span> <span class="string">&#39;l&#39;</span> <span class="symbol">&amp;&amp;</span> buffer<span class="symbol">[</span><span class="number">7</span><span class="symbol">]</span> <span class="symbol">==</span> <span class="string">&#39;d&#39;</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> Bitmap Decode<span class="symbol">(</span>Stream stream<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">int</span> length<span class="symbol">;</span>
 ArgbColor<span class="symbol">[</span><span class="symbol">]</span> pixels<span class="symbol">;</span>

 width <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>
 height <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>
 length <span class="symbol">=</span> width <span class="symbol">*</span> height<span class="symbol">;</span>
 pixels <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ReadPixelData<span class="symbol">(</span>stream<span class="symbol">,</span> length<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>CreateBitmap<span class="symbol">(</span>width<span class="symbol">,</span> height<span class="symbol">,</span> pixels<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> ArgbColor<span class="symbol">[</span><span class="symbol">]</span> ReadPixelData<span class="symbol">(</span>Stream stream<span class="symbol">,</span> <span class="keyword">int</span> length<span class="symbol">)</span>
<span class="symbol">{</span>
 ArgbColor<span class="symbol">[</span><span class="symbol">]</span> pixels<span class="symbol">;</span>

 pixels <span class="symbol">=</span> <span class="keyword">new</span> ArgbColor<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>
 <span class="keyword">int</span> a<span class="symbol">;</span>

 r <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">257</span><span class="symbol">;</span>
 g <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">257</span><span class="symbol">;</span>
 b <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">257</span><span class="symbol">;</span>
 a <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">257</span><span class="symbol">;</span>

 pixels<span class="symbol">[</span>i<span class="symbol">]</span> <span class="symbol">=</span> <span class="keyword">new</span> ArgbColor<span class="symbol">(</span>a<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="keyword">return</span> pixels<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> Bitmap CreateBitmap<span class="symbol">(</span><span class="keyword">int</span> width<span class="symbol">,</span> <span class="keyword">int</span> height<span class="symbol">,</span> IList<span class="symbol">&lt;</span>ArgbColor<span class="symbol">&gt;</span> pixels<span class="symbol">)</span>
<span class="symbol">{</span>
 Bitmap bitmap<span class="symbol">;</span>
 BitmapData bitmapData<span class="symbol">;</span>

 bitmap <span class="symbol">=</span> <span class="keyword">new</span> Bitmap<span class="symbol">(</span>width<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>

 bitmapData <span class="symbol">=</span> bitmap<span class="symbol">.</span>LockBits<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> ImageLockMode<span class="symbol">.</span>ReadWrite<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">unsafe</span>
 <span class="symbol">{</span>
 ArgbColor<span class="symbol">*</span> pixelPtr<span class="symbol">;</span>

 pixelPtr <span class="symbol">=</span> <span class="symbol">(</span>ArgbColor<span class="symbol">*</span><span class="symbol">)</span>bitmapData<span class="symbol">.</span>Scan<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> width <span class="symbol">*</span> height<span class="symbol">;</span> i<span class="symbol">++</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="symbol">*</span>pixelPtr <span class="symbol">=</span> pixels<span class="symbol">[</span>i<span class="symbol">]</span><span class="symbol">;</span>
 pixelPtr<span class="symbol">++</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 bitmap<span class="symbol">.</span>UnlockBits<span class="symbol">(</span>bitmapData<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> bitmap<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="encoding-an-image">Encoding an image</h2>
<p>As with decoding, the difficult of encoding mainly lies in
getting the pixel data quickly. In this implementation, only
32bit RGBA images are supported. I will update it at some point
to support other colour depths (or at the very least add a hack
to convert lesser depths to 32bpp).</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">void</span> Encode<span class="symbol">(</span>Stream stream<span class="symbol">,</span> Bitmap image<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>
 ArgbColor<span class="symbol">[</span><span class="symbol">]</span> pixels<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="string">&#39;f&#39;</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="string">&#39;a&#39;</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="string">&#39;r&#39;</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="string">&#39;b&#39;</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="string">&#39;f&#39;</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="string">&#39;e&#39;</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="string">&#39;l&#39;</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="string">&#39;d&#39;</span><span class="symbol">)</span><span class="symbol">;</span>

 width <span class="symbol">=</span> image<span class="symbol">.</span>Width<span class="symbol">;</span>
 height <span class="symbol">=</span> image<span class="symbol">.</span>Height<span class="symbol">;</span>

 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span>width<span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span>height<span class="symbol">)</span><span class="symbol">;</span>

 pixels <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetPixels<span class="symbol">(</span>image<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">foreach</span> <span class="symbol">(</span>ArgbColor pixel <span class="keyword">in</span> pixels<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">ushort</span> r<span class="symbol">;</span>
 <span class="keyword">ushort</span> g<span class="symbol">;</span>
 <span class="keyword">ushort</span> b<span class="symbol">;</span>
 <span class="keyword">ushort</span> a<span class="symbol">;</span>

 r <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">ushort</span><span class="symbol">)</span><span class="symbol">(</span>pixel<span class="symbol">.</span>R <span class="symbol">*</span> <span class="number">257</span><span class="symbol">)</span><span class="symbol">;</span>
 g <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">ushort</span><span class="symbol">)</span><span class="symbol">(</span>pixel<span class="symbol">.</span>G <span class="symbol">*</span> <span class="number">257</span><span class="symbol">)</span><span class="symbol">;</span>
 b <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">ushort</span><span class="symbol">)</span><span class="symbol">(</span>pixel<span class="symbol">.</span>B <span class="symbol">*</span> <span class="number">257</span><span class="symbol">)</span><span class="symbol">;</span>
 a <span class="symbol">=</span> <span class="symbol">(</span><span class="keyword">ushort</span><span class="symbol">)</span><span class="symbol">(</span>pixel<span class="symbol">.</span>A <span class="symbol">*</span> <span class="number">257</span><span class="symbol">)</span><span class="symbol">;</span>

 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span>r<span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span>g<span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span>b<span class="symbol">)</span><span class="symbol">;</span>
 stream<span class="symbol">.</span>WriteBigEndian<span class="symbol">(</span>a<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">private</span> ArgbColor<span class="symbol">[</span><span class="symbol">]</span> GetPixels<span class="symbol">(</span>Bitmap bitmap<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>
 BitmapData bitmapData<span class="symbol">;</span>
 ArgbColor<span class="symbol">[</span><span class="symbol">]</span> results<span class="symbol">;</span>

 width <span class="symbol">=</span> bitmap<span class="symbol">.</span>Width<span class="symbol">;</span>
 height <span class="symbol">=</span> bitmap<span class="symbol">.</span>Height<span class="symbol">;</span>
 results <span class="symbol">=</span> <span class="keyword">new</span> ArgbColor<span class="symbol">[</span>width <span class="symbol">*</span> height<span class="symbol">]</span><span class="symbol">;</span>
 bitmapData <span class="symbol">=</span> bitmap<span class="symbol">.</span>LockBits<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> ImageLockMode<span class="symbol">.</span>WriteOnly<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">unsafe</span>
 <span class="symbol">{</span>
 ArgbColor<span class="symbol">*</span> pixel<span class="symbol">;</span>

 pixel <span class="symbol">=</span> <span class="symbol">(</span>ArgbColor<span class="symbol">*</span><span class="symbol">)</span>bitmapData<span class="symbol">.</span>Scan<span class="number">0</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>
 results<span class="symbol">[</span>row <span class="symbol">*</span> width <span class="symbol">+</span> col<span class="symbol">]</span> <span class="symbol">=</span> <span class="symbol">*</span>pixel<span class="symbol">;</span>

 pixel<span class="symbol">++</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 bitmap<span class="symbol">.</span>UnlockBits<span class="symbol">(</span>bitmapData<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="nothing-complicated">Nothing complicated</h2>
<p>As you can see, it's a remarkably simple format and very easy to
process. However, it does mean that images tend to be large - in
my testing a standard HD image was 16MB for example. Of course,
as you'll probably be using this for some specific process
you'll be able to handle compression yourself.</p>
<p>After further reflection, I decided I wouldn't be using this
format as it wouldn't quite fit my OpenGL scenario, as OpenGL
(or at least the bits I'm familiar with) expect an array of
bytes, one per channel, unlike farbfeld which uses two (and the
larger value range as mentioned at the start). But I took the
source I wrote for farbfeld, refactored it to use single bytes
(and little-endian encoding for the other values), and that way
I could just do something like this</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> pixels<span class="symbol">;</span>
<span class="keyword">int</span> length<span class="symbol">;</span>

width <span class="symbol">=</span> stream<span class="symbol">.</span>ReadUInt<span class="number">32</span>LittleEndian<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
height <span class="symbol">=</span> stream<span class="symbol">.</span>ReadUInt<span class="number">32</span>LittleEndian<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
length <span class="symbol">=</span> width <span class="symbol">*</span> height <span class="symbol">*</span> <span class="number">4</span><span class="symbol">;</span>
pixels <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>
stream<span class="symbol">.</span>Read<span class="symbol">(</span>pixels<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> length<span class="symbol">)</span><span class="symbol">;</span>

GL<span class="symbol">.</span>TexImage<span class="number">2</span>D<span class="symbol">(</span>TextureTarget<span class="symbol">.</span>Texture<span class="number">2</span>D<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> PixelInternalFormat<span class="symbol">.</span>Rgba<span class="symbol">,</span> width<span class="symbol">,</span> height<span class="symbol">,</span> <span class="number">0</span><span class="symbol">,</span> PixelFormat<span class="symbol">.</span>Rgba<span class="symbol">,</span> PixelType<span class="symbol">.</span>UnsignedByte<span class="symbol">,</span> pixels<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>No <code>System.Drawing.Bitmap</code>, decoder class or complicated
decoding required!</p>
<h2 id="the-full-source">The full source</h2>
<p>The source presented here is abridged, you can get the full
version from the <a href="https://github.com/cyotek/Cyotek.Drawing.Imaging.Farbfeld/" rel="external nofollow noopener">GitHub repository</a>.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2016-01-19 - First published</li>
<li>2017-03-04 - Corrected divisor</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-and-writing-farbfeld-images-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comTools we use - 2015 editionurn:uuid:fc1b8b78-2e50-4124-8c05-900981cd52482016-01-07T20:19:10Z2016-01-07T20:18:51Z<p>Happy New Year! It's almost becoming a tradition now to list all of the development tools and bits that I've been using over the past year, to see how things are changing. 2015 wasn't the best of years at a personal level, but despite it all I've been learning new things and looking into new tools and ways of working.</p>
<h2 id="operating-systems">Operating Systems</h2>
<ul>
<li>Windows Home Server 2011 - file server, SVN repository, backup host, CI server</li>
<li>Windows 10 Professional - development machine</li>
<li>Windows XP (virtualized) - testing</li>
<li>Windows Vista (virtualized) - testing</li>
</ul>
<h2 id="development-tools">Development Tools</h2>
<ul>
<li><ins>New!</ins> <a href="https://www.getpostman.com/" rel="external nofollow noopener">Postman</a> is a absolutely brilliant client for testing REST services.</li>
<li><a href="http://www.microsoft.com/visualstudio/" rel="external nofollow noopener">Visual Studio 2015 Premium</a> - not much to say</li>
<li><del><a href="http://www.reflector.net/" rel="external nofollow noopener">.NET Reflector</a> - controversy over free vs paid aside, this is still worth the modest cost for digging behind the scenes when you want to know how the BCL works.</del></li>
<li><ins>New!</ins> <a href="https://www.jetbrains.com/decompiler/" rel="external nofollow noopener">DotPeek</a> - a decent replacement to .NET Reflector that can view things that Reflector can't, making it a worthwhile replacement despite some bugs and being chronically slow to start</li>
<li><ins>New!</ins> <a href="http://gulpjs.com/" rel="external nofollow noopener">Gulp</a> - I use this to minify and combine JavaScript and CSS files</li>
<li><ins>New!</ins> <a href="http://www.typescriptlang.org/" rel="external nofollow noopener">TypeScript</a> - makes writing JavaScript just that much nicer, and the new React support is just icing on the cake</li>
</ul>
<h2 id="visual-studio-extensions">Visual Studio Extensions</h2>
<ul>
<li><a href="http://cyotek.com/blog/visual-studio-extension-for-adding-multiple-projects-to-a-solution">Cyotek Add Projects</a> - a simple extension I created that I use pretty much any time I create a new solution to add references to my standard source code libraries. Saves me time and key presses, which is good enough for me!</li>
<li><a href="http://www.oz-code.com/" rel="external nofollow noopener">OzCocde</a> - this is one of the tools you wonder why isn't in Visual Studio by default</li>
<li><del><a href="http://www.red-gate.com/products/dotnet-development/dotnet-demon/" rel="external nofollow noopener">.NET Demon</a> - yet another wonderful tool that helps speed up your development, this time by not slowing you down waiting for compiles. Unfortunately it's no longer supported by RedGate as apparently VS2015 will do this</del>. VS2015 doesn't do all of this, and I really miss build on save.</li>
<li><del><a href="http://visualstudiogallery.msdn.microsoft.com/c6d1c265-7007-405c-a68b-5606af238ece" rel="external nofollow noopener">VSCommands 2013</a></del> (not updated for VS2015)</li>
<li><ins>New!</ins> <a href="http://editorconfig.org/" rel="external nofollow noopener">EditorConfig</a> - useful for OSS projects to avoid space-vs-tab wars</li>
<li><ins>New!</ins> <a href="https://visualstudiogallery.msdn.microsoft.com/3ebde8fb-26d8-4374-a0eb-1e4e2665070c" rel="external nofollow noopener">File Nesting</a> - allows you to easily nest files, great for TypeScript</li>
<li><ins>New!</ins> <a href="https://visualstudiogallery.msdn.microsoft.com/4e84e2cf-2d6b-472a-b1e2-b84932511379" rel="external nofollow noopener">Open Command Line</a> - easily open command prompts, PowerShell prompts, or other tools to your project / solution directories</li>
<li><ins>New!</ins> <a href="http://mike-ward.net/vscoloroutput/" rel="external nofollow noopener">VSColorOutput</a> - I use this to colour my output window, means I don't miss VSCommands at all!</li>
<li><a href="http://visualstudiogallery.msdn.microsoft.com/e792686d-542b-474a-8c55-630980e72c30" rel="external nofollow noopener">Indent Guides</a></li>
<li><a href="http://www.jetbrains.com/resharper/" rel="external nofollow noopener">Resharper</a> - originally as a replacement for Regionerate, this swiftly became a firm favourite every time it told me I was doing something stupid.</li>
<li><a href="http://www.ncrunch.net/" rel="external nofollow noopener">NCrunch for Visual Studio</a> - (version 2!) automated parallel continuous testing tool. Works with NUnit, MSTest and a variety of other test systems. Great for TDD and picking up how a simple change you made to one part of your project completely destroys another part. We've all been there!</li>
</ul>
<h2 id="analytics">Analytics</h2>
<ul>
<li>Innovasys Luminitix (Link Removed) - we've been using this for years now in an effort to gain some understanding in how our products are used by end users. I keep meaning to write a blog post on this, maybe I'll get around to that in 201<del>4</del><del>5</del><ins>6</ins>!</li>
</ul>
<h2 id="profiling">Profiling</h2>
<ul>
<li><a href="http://www.red-gate.com/products/dotnet-development/ants-performance-profiler/" rel="external nofollow noopener">ANTS Performance Profiler</a> - the best profiler I've ever used. The bottlenecks and performance issues this has helped resolve with utter ease is insane. It. Just. Works.</li>
</ul>
<h2 id="documentation-tools">Documentation Tools</h2>
<ul>
<li><a href="http://www.innovasys.com/product/dx/overview" rel="external nofollow noopener">Innovasys Document! X</a> - Currently we use this to produce the user manuals for our applications.</li>
<li><del><a href="http://submain.com/products/ghostdoc.aspx" rel="external nofollow noopener">SubMain GhostDoc Pro</a> - Does a slightly better job of auto generating XML comment documentation thatn doing it fully from scratch. Actually, barley use this now, the way it litters my code folders with XML files when I don't use <em>any</em> functionality bar auto-document is starting to more than annoy me.</del></li>
<li><ins>New!</ins> <a href="http://www.atomineerutils.com/" rel="external nofollow noopener">Atomineer Pro Documentation</a> - having finally gotten fed up of GhostDoc's bloat and annoying config files, I replaced it with Atomineer, finding this tool to be much better for my needs</li>
<li><del><a href="http://markdownpad.com/" rel="external nofollow noopener">MarkdownPad Pro</a> - fairly decent Markdown editor that is currently better than our own so I use it instead!</del> Doesn't work properly with Windows 10, doesn't seem to be getting supported or updated</li>
<li><ins>New!</ins> <a href="http://markdownedit.com/" rel="external nofollow noopener">MarkdownEdit</a> - a no frills minimalist markdown editor that I have been using</li>
<li><a href="http://notepad-plus-plus.org/" rel="external nofollow noopener">Notepad++</a> - because Notepad hasn't changed in 20 years (moving menu items around doesn't count!)</li>
</ul>
<h2 id="graphics-tools">Graphics Tools</h2>
<ul>
<li><a href="http://www.getpaint.net/" rel="external nofollow noopener">Paint.NET</a> - brilliant bitmap editor with extensive plugins</li>
<li><a href="http://www.axialis.com/iconworkshop/" rel="external nofollow noopener">Axialis IconWorkshop</a> - very nice icon editor, been using this for untold years now since Microangelo decided to become the Windows Paint of icon editing</li>
<li><a href="http://cyotek.com/cyotek-spriter">Cyotek Spriter</a> - sprite / image map generation software</li>
<li><a href="http://cyotek.com/cyotek-gif-animator/">Cyotek Gif Animator</a> - gif animation creator that is shaping up nicely, although I'm obviously biased.</li>
</ul>
<h2 id="virtualization">Virtualization</h2>
<ul>
<li><a href="https://www.virtualbox.org/" rel="external nofollow noopener">Oracle VM VirtualBox</a> - for creating guest OS's for testing purposes. Cyotek software is informally smoke tested mainly on Windows XP, but occasionally Windows Vista. Visual Studio 2013 installed Hyper-V, but given as the VirtualBox VM's have been running for years with no problems, this is disabled. Still need to switch back to Hyper-V if I want to be able to do any mobile development. Which I do.</li>
</ul>
<h2 id="version-control">Version Control</h2>
<ul>
<li><a href="http://tortoisesvn.net/" rel="external nofollow noopener">TortoiseSVN</a> - Windows Explorer integration for SVN</li>
<li><a href="http://ankhsvn.open.collab.net/" rel="external nofollow noopener">AnhkSVN</a> - Subversion support for Visual Studio</li>
<li><a href="http://www.visualsvn.com/server/" rel="external nofollow noopener">VisualSVN Server</a> - Subversion Server for Windows</li>
<li><a href="https://github.com/" rel="external nofollow noopener">GitHub</a> / <a href="http://windows.github.com/" rel="external nofollow noopener">GitHub for Windows</a> - for the public facing aspects of our source code.</li>
</ul>
<h2 id="filedirectory-comparison">File/directory comparison</h2>
<ul>
<li><a href="http://winmerge.org/" rel="external nofollow noopener">WinMerge</a> - not much to say, it works and works well</li>
</ul>
<h2 id="file-searching">File searching</h2>
<ul>
<li><a href="http://stefanstools.sourceforge.net/grepWin.html" rel="external nofollow noopener">WinGrep</a> - previously I just used to use Notepad++'s search in files but... this is a touch simpler all around</li>
</ul>
<h2 id="backups">Backups</h2>
<ul>
<li><a href="http://cyotek.com/cyotek-copytools">Cyotek CopyTools</a> - we use this for offline backups of source code, assets and resources, documents, actually pretty much anything we generate; including backing up the backups!</li>
<li><a href="http://www.code42.com/crashplan/" rel="external nofollow noopener">CrashPlan</a> - CrashPlan creates an online backup of the different offline backups that CopyTools does. If you've ever lost a harddisk before with critical data on it that's nowhere else, you'll have backups squirrelled away everywhere too!</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/tools-we-use-2015-edition .
</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.comWorking around "Cannot use JSX unless the '--jsx' flag is provided." using the TypeScript 1.6 betaurn:uuid:7a9147b0-7ecc-4a59-b2e1-f0da02eff8312015-09-04T18:09:56Z2015-09-04T18:09:56Z<p>I've been using the utterly awesome <a href="http://facebook.github.io/react/" rel="external nofollow noopener">ReactJS</a> for a few weeks
now. At the same time I started using React, I also switched to
using <a href="http://www.typescriptlang.org/" rel="external nofollow noopener">TypeScript</a> to work with JavaScript, due to it's type
safety and less verbose syntax when creating modules and
classes.</p>
<p>While I loved both products, one problem was they didn't gel
together nicely. However, this is no longer the cause with the
new <a href="http://blogs.msdn.com/b/typescript/archive/2015/09/02/announcing-typescript-1-6-beta-react-jsx-better-error-checking-and-more.aspx" rel="external nofollow noopener">TypeScript 1.6 Beta</a>!</p>
<p>As soon as I got it installed, I created a new <code>tsx</code> file,
dropped in an example component, then saved the file. A standard
<code>js</code> file was generated containing the &quot;normal&quot; JavaScript
version of the React component. Awesome!</p>
<p>Then I tried to debug the project, and was greeted with this
error:</p>
<blockquote>
<p><em>Build: Cannot use JSX unless the '--jsx' flag is provided.</em></p>
</blockquote>
<p>In the <strong>Text Editor</strong> \ <strong>TypeScript</strong> \ <strong>Project</strong> <br />
<strong>General</strong> section of Visual Studio's <strong>Options</strong> dialog, I
found an option for configuring the JSX emit mode, but this
didn't seem to have any effect for the <code>tsx</code> file in my project.</p>
<p>Next, I started poking around the
<code>%ProgramFiles(x86)%\MSBuild\Microsoft\VisualStudio\v14.0\TypeScript</code>
folder. Inside <code>Microsoft.TypeScript.targets</code>, I found the
following declaration</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">TypeScriptBuildConfigurations</span> <span class="name">Condition</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">&#39;$(TypeScriptJSXEmit)&#39; != &#39;&#39; and &#39;$(TypeScriptJSXEmit)&#39; != &#39;none&#39;</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>$(TypeScriptBuildConfigurations) --jsx $(TypeScriptJSXEmit)<span class="symbol">&lt;/</span><span class="name">TypeScriptBuildConfigurations</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>Armed with that information I opened my <code>csproj</code> file in trusty
Notepad++, and added the following</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">PropertyGroup</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">TypeScriptJSXEmit</span><span class="symbol">&gt;</span>react<span class="symbol">&lt;/</span><span class="name">TypeScriptJSXEmit</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">PropertyGroup</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>On reloading the project in Visual Studio, I found the build now
completed without raising an error, and it was correctly
generating the vanilla <code>js</code> and <code>js.map</code> files.</p>
<p>Fantastic news, now I just need to convert my <code>jsx</code> files to
<code>tsx</code> files and be happy!</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2015-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/working-around-cannot-use-jsx-unless-the-jsx-flag-is-provided-using-the-typescript-1-6-beta .
</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.comA brief look at code analysis with NDependurn:uuid:794c1179-a855-47aa-a2c3-b17f98abf0bf2015-06-27T09:32:49Z2015-06-27T09:32:49Z<p>If you're a developer, you're probably familiar with various
tenets of your craft, such as &quot;<em>naming things is hard</em>&quot; and
&quot;<em>every non trivial program has at least one bug</em>&quot;. The latter
example is one of the reasons why there are ever increasing
amounts of tools designed to reduce the number of bugs in an
application, from testing, to performance profiling, to code
analysis.</p>
<p>In this article, I'm going to briefly take a look <a href="http://www.ndepend.com/" rel="external nofollow noopener">NDepend</a>,
a code analysis tool for Visual Studio. This is the point where
I'd like to quote the summary of the product from the NDepend
website, but there's no simple description - which sums up
NDepend pretty well actually. This is a complicated product
offering a lot of features.</p>
<p>So when I say &quot;a brief look&quot;, that's exactly what I mean. When
I've had a chance to explore the functionality fully I hope I'll
have enough knowledge and material to expand upon this initial
post.</p>
<blockquote>
<p>Disclaimer: I received a professional license for NDepend on
the condition I would write about my experiences.</p>
</blockquote>
<h2 id="what-is-ndepend-and-what-can-it-do-for-me">What is NDepend and what can it do for me?</h2>
<p>Simply put, NDepend will analyse your code and spit out a report
full of metrics, and violations against a large database of
rules. These might be the mundane (a method has too many lines)
to the more serious (your method is so complicated you will
never remember how it works in 6 months time).</p>
<p>This really doesn't even begin to cover it though, as it can do
so much more, from dependency graphs to trend analysis. One of
the interesting things about NDepend is it saves the results of
each analysis you do, allowing you to see if metrics such as
test coverage are improving (good) or critical violations
increased (not so good!).</p>
<h2 id="a-sample-project">A sample project</h2>
<p>For this article, I'm going to be using the <a href="https://github.com/cyotek/Dithering" rel="external nofollow noopener">Dithering</a>
project I created in previous blog posts to test some of the
functionality of NDepend. I choose this because the project was
fresh in my mind as I've been heavily working on it the last few
weeks, and because it was small enough that I assumed NDepend
wouldn't find much amiss. Here's another tenet - <em>assumptions
are the mother of all &lt;censored&gt;</em>.</p>
<p>You can use NDepend one of two ways, either via a stand alone
application, or via a Visual Studio extension. For this article,
I'm going to be using Visual Studio, but you should be able to
do everything in the stand alone tool as well. There's also a
CLI tool which I assume is for build integration but I haven't
looked at it yet.</p>
<h2 id="that-first-analysis">That first analysis</h2>
<p>If this is the first time using NDepend, you need to attach an
NDepend project to your solution.</p>
<ul>
<li>Open the <strong>NDepend</strong> menu and select the <strong>Attach New NDepend
Project to Current VS Solution</strong> menu item</li>
<li>The dialog that is displayed will list all the projects in
your solution, if there any you don't want to include in the
analysis, just right click them and choose the appropriate
option</li>
<li>Click the <strong>Analyze</strong> button to generate the project</li>
<li>Once the project has been created, a welcome dialog will be
displayed. Click the <strong>View NDepend Dashboard</strong> button to
continue</li>
</ul>
<p>This will open the dashboard, looking something similar to the
below.</p>
<blockquote>
<p>A HTML report will also be generated and opened in your
default browser, providing a helpful synopsis of the analysis.</p>
</blockquote>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-a1-dashboard.png" class="gallery" title="The initial dashboard for the Dithering project" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-a1-dashboard.png" alt="The initial dashboard for the Dithering project" decoding="async" loading="lazy" /></a><figcaption>The initial dashboard for the Dithering project</figcaption></figure>
<p>At this point, all the charts you can see are going to be
non-existent as you have to rerun the analysis at future times
in order to get additional data points for plotting.</p>
<p>The main information I'm interested in right now is contained in
the <strong>Code Rules</strong> block. And it doesn't make me happy to read
it:</p>
<ul>
<li>4 Critical Rules Violated for a total of 9 Critical Violations</li>
<li>37 Rules Violated for a total of 215 Violations</li>
</ul>
<p>Wow, that's a lot of violations for such a small project! Lets
take a look at these in detail.</p>
<h2 id="viewing-rules">Viewing Rules</h2>
<p>Clicking the blue hyper-links in the Dashboard will
automatically open a new view to drill down into the details of
the analysis. On clicking the <strong>Critical Rules Violated</strong> link,
I'm presented with the following</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-a1-criticalrules.png" class="gallery" title="Viewing rule violations" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-a1-criticalrules.png" alt="Viewing rule violations" decoding="async" loading="lazy" /></a><figcaption>Viewing rule violations</figcaption></figure>
<p>Clicking one of the rules in the list displays the code of the
rule and the execution results.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-a1-viewrule.png" class="gallery" title="Viewing the results of a violated rule" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-a1-viewrule.png" alt="Viewing the results of a violated rule" decoding="async" loading="lazy" /></a><figcaption>Viewing the results of a violated rule</figcaption></figure>
<p>Here we can see the the violation is triggered if any method has
more than eight parameters. In the dithering example project,
there is a class I that I used to generate the diagrams used on
the blog posts, and the <code>DrawString</code> method of this helper class
has 10 parameters, thus falling foul of the rule. Great start!</p>
<p>The next rule on the list is a bit more complicated, but
essentially it's trying to detect dead code. In a non-library
project, this should be fairly straight forward and true to form
it has detected that the <code>ArticleDiagrams</code> class and its methods
are dead code.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-a1-viewcomplexrule.png" class="gallery" title="A more complicated rule with a lot of conditions" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-a1-viewcomplexrule.png" alt="A more complicated rule with a lot of conditions" decoding="async" loading="lazy" /></a><figcaption>A more complicated rule with a lot of conditions</figcaption></figure>
<p>This is actually a very useful rule if your coding standards
insist that all dead code is removed. How useful depends on your
code coverage, if you also have a 100% rule then you should
already found and removed such code.</p>
<p>So far so good. Lets look at the final critical rule failure.</p>
<h2 id="when-rules-go-wrong">When rules go wrong</h2>
<p>The last critical rule violation is <strong>Don't call your method
Dispose</strong>. I imagine this makes a lot of sense, if your class
doesn't implement <code>IDisposable</code>, then having a method named
<code>Dispose</code> is going to be confusing at best.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-a1-falsepositive.png" class="gallery" title="I'm either mad, or this is a false positive" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-a1-falsepositive.png" alt="I'm either mad, or this is a false positive" decoding="async" loading="lazy" /></a><figcaption>I'm either mad, or this is a false positive</figcaption></figure>
<p>Interesting. So it somehow thinks that the <code>MainForm</code> and
<code>AboutDialog</code> classes - both of which inherit from <code>Form</code> -
shouldn't have methods named <code>Dispose</code>. Well, somewhere in its
inheritance chain <code>Form</code> does implement <code>IDisposable</code> so this
violation is completely wrong.</p>
<p>As a test, I added <code>IDisposable</code> to the signature of
<code>AboutDialog</code> and re-ran the NDepend analysis. It promptly
decided that the <code>Dispose</code> method in that class was now fine. Of
course, now Resharper is complaining <em>Base interface
'IDisposable' is redundant because
Cyotek.DitheringTest.AboutDialog inherits 'Form'</em>. Sorry
NDepend, you're definitely wrong in this instance.</p>
<blockquote>
<p>At this point, I excluded the <code>ArticleDiagrams</code> class from the
solution and reran the analysis, removing some the violations
that were valid, but not really appropriate as it was dead
code.</p>
</blockquote>
<h2 id="more-violations">More violations</h2>
<p>So far, I've looked at 4 failed rules. 3 I'm happy to accept,
and if this were production code I'd be getting rid of the dead
code and resolving all three. The fourth violation is flat out
wrong and I'm ignoring it for now.</p>
<p>However, there were lots of other (non-critical) violations, so
I'll have a look at those now. The <strong>Queries and Rules
Explorer</strong> window opened earlier has a drop down list which I
can use to filter the results, so now I choose <strong>31 Rules
Violated</strong> to look at the other warnings.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-a1-morerules.png" class="gallery" title="A bunch of important, but not critical, rule violations" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-a1-morerules.png" alt="A bunch of important, but not critical, rule violations" decoding="async" loading="lazy" /></a><figcaption>A bunch of important, but not critical, rule violations</figcaption></figure>
<p>There's plenty of other violations listed. I'll outline a tiny
handful of them below.</p>
<h3 id="override-equals-and-operator-equals-on-value-types-structures-should-be-immutable">Override equals and operator equals on value types / Structures should be immutable</h3>
<p>This pair of failures is caused by the custom <code>ArgbColor</code> struct
and is the simplest structure to handle a 32bit colour.
Actually, this struct is being called out for a few rules all of
which I agree with. If this were production code, I'd be
following a lot of the recommendations it makes (in fact, in the
&quot;real&quot; version of this class in my production libraries I do
follow most of them - a key exception being my structs are still
mutable).</p>
<h3 id="static-fields-should-be-prefixed-with-a-s_instance-fields-should-be-prefixed-with-a-m">Static fields should be prefixed with a 's_' / Instance fields should be prefixed with a 'm_'</h3>
<p>These rules vie between I disagree with them, and NDepend
shouldn't be picking them up. In the first place, I disagree
with the rule - I simply use an underscore prefix and leave it
at that.</p>
<p>However, NDepend is also picking up all of the control names in
my forms. I seriously doubt any developer is going to use <code>m_</code>
in front of their control names and so I don't think NDepend
should be looking at these - I consider them &quot;designer&quot; code of
sorts and should be excluded. There's a few more rules being
triggered by controls, and I think it's looking messier than it
should.</p>
<p>I can edit the rule to use my own conversion of the plain
underscore, but I can't do much about NDepend picking up WinForm
control names.</p>
<h3 id="non-static-classes-should-be-instantiated-or-turned-to-static">Non-static classes should be instantiated or turned to static</h3>
<p>This is an interesting one. It's basically being triggered by
the <code>LineDesigner</code> class, a designer for the <code>Line</code> control to
allow only horizontal resizing. Control designers can't be
static and so this rule doesn't apply. It is referenced by the
<code>Designer</code> attribute of the <code>Line</code> class so we probably just
need to edit the rule to support it.</p>
<h2 id="and-more">And more</h2>
<p>There's quite a few rule violations so I won't cover them all.
It's an interesting mix of rules I would find useful, and rules
subject to interpretation (an example is if I have an <code>internal</code>
class I still mark its members as <code>public</code>, NDepend think this
is incorrect).</p>
<p>But, NDepend doesn't force you to accept its view. You can
simply turn off any rule that you don't want influencing the
analysis and it will be fully disabled, including the dashboard
updating itself in real-time.</p>
<p>Assuming you have analysed the project multiple times, you can
turn on recent violations only, thus hiding any previous
violations. You may find this very useful if you are working
from a legacy code base!</p>
<h2 id="editing-rules">Editing Rules</h2>
<p>With that said, there are other options if a rule doesn't quite
fit the bill. NDepend uses LINQ with a set of custom extensions
(Code Query over LINQ (CQLinq)) as the base of its rules. So you
can put your programmer hat on and modify these rules to suit
your needs.</p>
<p>As a concrete example, I'm going to look at the <strong>Instances size
shouldn't be too big</strong> rule. This has flagged the <code>Line</code> control
as being too big, something I found curious as the control is a
simple affair that just draws a 3D line. When I look at the
details for the violation it mentions 6 fields. But the control
only has 3. Or does it?</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-a1-editrule.png" class="gallery" title="Why does this rule think a class with 3 fields really has 6?" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-a1-editrule.png" alt="Why does this rule think a class with 3 fields really has 6?" decoding="async" loading="lazy" /></a><figcaption>Why does this rule think a class with 3 fields really has 6?</figcaption></figure>
<p>The query results don't include the names of the fields, so I'm
going to adjust the code of the rule to include them. This is a
really nice aspect of NDepend - as I type in the code pane, it
continually tries to compile and run the rule, including syntax
highlighting of errors, and intellisense.</p>
<p>I added the <code>, names = ...</code> condition to the code as follows,
which allowed me to influence the output to include an extra
column</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
warnif count <span class="symbol">&gt;</span> <span class="number">0</span> <span class="keyword">from</span> t <span class="keyword">in</span> JustMyCode<span class="symbol">.</span>Types <span class="keyword">where</span> 
 t<span class="symbol">.</span>SizeOfInst <span class="symbol">&gt;</span> <span class="number">64</span> 
 <span class="keyword">orderby</span> t<span class="symbol">.</span>SizeOfInst <span class="keyword">descending</span>
<span class="keyword">select</span> <span class="keyword">new</span> <span class="symbol">{</span> t<span class="symbol">,</span> t<span class="symbol">.</span>SizeOfInst<span class="symbol">,</span> t<span class="symbol">.</span>InstanceFields<span class="symbol">,</span> names <span class="symbol">=</span> <span class="keyword">string</span><span class="symbol">.</span>Join<span class="symbol">(</span><span class="string">&quot;, &quot;</span><span class="symbol">,</span> t<span class="symbol">.</span>InstanceFields<span class="symbol">.</span>Select<span class="symbol">(</span>f <span class="symbol">=&gt;</span> f<span class="symbol">.</span>Name<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/ndepend-a1-editrule-result.png" class="gallery" title="Apparently because an event is a field!" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-a1-editrule-result.png" alt="Apparently because an event is a field!" decoding="async" loading="lazy" /></a><figcaption>Apparently because an event is a field!</figcaption></figure>
<p>The results of the modified rule show that there are 3 variables
which are backing fields for properties, and then 3 events. Is
an event a field? I don't think so, an event is an event. But
NDepend thinks it is a field. Regardless though, by editing the
rule I was easily able to add additional output from the rule,
and although not demonstrated here I've also used some of the
built in filtering options to exclude results from being
returned.</p>
<p>The ability to write your own rules could potentially be very
useful with many possibilities.</p>
<h2 id="interpretation-is-king">Interpretation is king</h2>
<p>In a way, I'm glad that NDepend doesn't have the ability to
automatically fix violations the way some other tools do. I ran
NDepend on my <a href="https://github.com/cyotek/Cyotek.Collections.Generic.CircularBuffer" rel="external nofollow noopener">CircularBuffer library</a>, and one of the
suggestions was to change the visibility of the class from
<code>public</code> to <code>internal</code>. Making the single class of a library
project inaccessible to consumers isn't the best of ideas!</p>
<p>I think what I'm leading to here, is use common sense with the
violations, do not just blindly accept anything it says as
gospel.</p>
<h2 id="viewing-dependencies">Viewing Dependencies</h2>
<p>Any application is going to have dependencies, and depending on
how tight your coupling is, this could be an evil nightmare. You
can display a visual hierarchy of the dependencies of your
project via a handy <strong>Dependency Diagram</strong> - below is the one
for the dithering project. Quite small as there are few
references, The thicker the arrow, the more dependencies from
the destination assembly you're using.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-a1-dependency-diagram.png" class="gallery" title="Easy dependency viewing" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-a1-dependency-diagram.png" alt="Easy dependency viewing" decoding="async" loading="lazy" /></a><figcaption>Easy dependency viewing</figcaption></figure>
<p>In the case where the diagram is so big as to become
meaningless, you can also view a <strong>Dependency Matrix</strong> - this
lets you plot assemblies against each other and see the usages.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-a1-matrix.png" class="gallery" title="Viewing code dependencies via a matrix" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-a1-matrix.png" alt="Viewing code dependencies via a matrix" decoding="async" loading="lazy" /></a><figcaption>Viewing code dependencies via a matrix</figcaption></figure>
<p>Clicking one of the nodes in the matrix will then open a
simplified Dependency Graph, making it a little easier to browse
than a huge spaghetti diagram.</p>
<h2 id="code-metrics">Code Metrics</h2>
<p>Many years ago, I used a small tool that displayed the size of
the different directories on my computer in a <a href="https://en.wikipedia.org/wiki/Treemapping" rel="external nofollow noopener">treemap</a> to
see which folders took up the most space. I haven't used that
tool for years (I don't need a colour graph to know my Steam
directory is huge!) but I do find that sort of display to be
oddly compelling.</p>
<p>NDepend makes use of a tree map to display code metrics - the
size of the squares defaults to the code size (useful for seeing
huge methods, although again, as the screenshot below indicates,
I really wish NDepend would exclude designer code). You can also
control the colour of the square via another metric - the
default being complexity, so the greener the square the easier
the code should be to maintain.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-a1-codemetrics.png" class="gallery" title="An easy way to gauge the health of your code" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-a1-codemetrics.png" alt="An easy way to gauge the health of your code" decoding="async" loading="lazy" /></a><figcaption>An easy way to gauge the health of your code</figcaption></figure>
<p>I couldn't see how to access this from Visual Studio, but the
HTML report also includes an <strong>Abstractness versus Instability</strong>
diagram which &quot;helps to detect which assemblies are potentially
painful to maintain (i.e concrete and stable) and which
assemblies are potentially useless (i.e abstract and instable)&quot;.
Meaning you should probably take note if anything appears in the
red zone!</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-a1-abstractness-vs-instability.png" class="gallery" title="NDepend doesn't think WebCopy's code is unstable. Well, at least that's one thing that isn't" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-a1-abstractness-vs-instability.png" alt="NDepend doesn't think WebCopy's code is unstable. Well, at least that's one thing that isn't" decoding="async" loading="lazy" /></a><figcaption>NDepend doesn't think WebCopy's code is unstable. Well, at least that's one thing that isn't</figcaption></figure><h2 id="updating-the-analysis">Updating the analysis</h2>
<p>You can trigger a manual refresh of the analysis at any time,
but also by default NDepend will perform one after each build,
meaning you can always be up to date on the metrics of your
project.</p>
<h2 id="show-me-a-big-project">Show me a big project</h2>
<p>So far I have looked at only a small demonstration project.
However, as the ultimate test of my review, I decided to scan
<a href="https://cyotek.com/cyotek-webcopy">WebCopy</a>. I was very curious to see how NDepend would handle
that solution. NDepend scanned the code base quite nicely
(despite an old version of one of my libraries getting detected
and playing havoc)</p>
<p>As an indication of the size of the project, it reports that
WebCopy has 60 thousand lines of code (translating to half a
million IL instructions), 24 thousand lines of comments, and
nearly 1800 types spread over 44 assemblies. A fair amount!</p>
<p>I had a quick look through the violations list, and noticed a
few oddities - there are <em>lots</em> of <code>Forms</code> in these projects,
yet the <strong>Don't call your method Dispose</strong> violation that so
annoyed me earlier was only recorded 4 times. One of these was
actually valid (a manager class who's children were disposable),
while the others weren't. Still, there's a curious disparity in
the way NDepend is running these rules it seems.</p>
<p>I did find some violations indicating genuine problems (or
potential problems) in the code through so at some point (sigh -
there's a lot of them) I will have to take a closer look and go
through them all in detail.</p>
<p>Just before I sign off, I shall show you the dependency diagram
(maybe I need to try and make my code simpler!) and the
complexity diagram.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-a1-dependency-diagram-big.png" class="gallery" title="You are looking at a window into Code Hell. Fear it" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-a1-dependency-diagram-big.png" alt="You are looking at a window into Code Hell. Fear it" decoding="async" loading="lazy" /></a><figcaption>You are looking at a window into Code Hell. Fear it</figcaption></figure><figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ndepend-a1-codemetrics-big.png" class="gallery" title="A bit too much red here for my liking" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ndepend-a1-codemetrics-big.png" alt="A bit too much red here for my liking" decoding="async" loading="lazy" /></a><figcaption>A bit too much red here for my liking</figcaption></figure><h2 id="thats-all-folks">That's all, folks</h2>
<p>For a &quot;brief&quot; overview, this has been quite a long article -
NDepend is such a big product, one article cannot possibly cover
it all. Just take a look at their <a href="http://www.ndepend.com/features/" rel="external nofollow noopener">feature list</a>!</p>
<p>Ideally I will try to cover more of NDepend in future articles,
as I'm still exploring the feature set, so stay tuned.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2015-06-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/a-brief-look-at-code-analysis-with-ndepend .
</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.comAn introduction to dithering imagesurn:uuid:d1a52a83-2650-49b0-a59d-3a30c327f9602015-06-06T11:00:01Z2015-06-06T11:00:01Z<p>When you reduce the number of colours in an image, it's often
hard to get a 1:1 match, and so typically you can expect to see
banding in an image - areas of unbroken solid colours where once
multiple similar colours were present. Such banding can often
ruin the look of the image, however by using dithering
algorithms you can reduce such banding and greatly improve the
appearance of the reduced image.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/dithering-original.png" class="gallery" title="The sample image our demonstration program will be using, a picture of the Tower of London" ><img src="https://images.cyotek.com/image/thumbnail/devblog/dithering-original.png" alt="The sample image our demonstration program will be using, a picture of the Tower of London" decoding="async" loading="lazy" /></a><figcaption>The sample image our demonstration program will be using, a picture of the Tower of London</figcaption></figure>
<p>Here we see a nice view of the Tower of London (Image Credit:
<a href="http://www.publicdomainpictures.net/view-image.php?image=18490" rel="external nofollow noopener">Vera Kratochvil</a>). Lets say we wanted to reduce the number
of colours in this image to 256 using the <a href="http://en.wikipedia.org/wiki/Web_colors" rel="external nofollow noopener">web safe colour
palette</a>.</p>
<p>If we simply reduce the colour depth by matching the nearest
colour in the old palette to one in the new, then we'll get
something similar to the image below. As is quite evident, the
skyline has been badly effected by banding.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/dithering-banding.png" class="gallery" title="Not exactly the best representation of the original image" ><img src="https://images.cyotek.com/image/thumbnail/devblog/dithering-banding.png" alt="Not exactly the best representation of the original image" decoding="async" loading="lazy" /></a><figcaption>Not exactly the best representation of the original image</figcaption></figure>
<p>However, by applying a technique known as dithering, we can
still reduce the colour depth using exactly the same palette,
and get something comparable to the original and more
aesthetically pleasing.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/dithering-errordiffusion.png" class="gallery" title="That looks a lot better!" ><img src="https://images.cyotek.com/image/thumbnail/devblog/dithering-errordiffusion.png" alt="That looks a lot better!" decoding="async" loading="lazy" /></a><figcaption>That looks a lot better!</figcaption></figure><h2 id="types-of-dithering">Types of dithering</h2>
<p>There are several different types of dithering, mostly falling
into <strong>Ordered</strong> or <strong>Unordered</strong> categories.</p>
<p>Ordered dithering uses a patterned matrix in order to dither the
image. An example of this is the very distinctive (and
nostalgic!) <em>Bayer</em> algorithm.</p>
<p>Unordered, or error diffusion, dithering calculates an error
value for each pixel and then propagates this to the
neighbouring pixels, often with very good results. The most well
known of these is <em>Floyd–Steinberg</em>, although there are several
more such as <em>Burkes</em>, and <em>Sierra</em>.</p>
<blockquote>
<p>You could potentially use dithering for applications other
than images. An image is simply a block of pixel data, i.e.
colours. Colours are just numbers, and so is a great deal of
other data. So in theory you can dither a lot more than &quot;just&quot;
images.</p>
</blockquote>
<h2 id="dithering-via-error-diffusion">Dithering via Error Diffusion</h2>
<p>For at least the first part of this series, I will be
concentrating on error diffusion. For this algorithm, you scan
the image from left to right, top to bottom and visit each
pixel. Then, for each pixel, you calculate a value known as the
&quot;error&quot;.</p>
<p>After calculating the error it is then applied to one or more
neighbouring values that haven't yet been processed. Generally,
this would mean adjusting at least 3 neighbouring cells, but
depending on the algorithm this could be quite a few more. I'll
go into this in more detail when I describe individual dithering
algorithms in subsequent posts.</p>
<blockquote>
<p>So how do you determine the error? Well, hopefully is clear
that you don't dither an image as a single process. There has
to be another piece in the puzzle, a process to transform a
value. The <em>error</em> therefore is the difference between the
original and new values. When it comes to images, typically
this is going to a form of colour reduction, for example 32bit
(16 million colours) to 8bit (256 colours).</p>
</blockquote>
<p>The diagram below tries to show what I mean - the grey boxes are
pixels that have been processed. The blue box is the pixel that
is currently being transformed, with the green therefore being
unprocessed pixels and candidates for the error diffusion. The
arrows simply highlight that the candidates are always forward
of the current pixel, and not behind it.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/dithering-diagram.png" class="gallery" title="A small illustration to try and demonstrate how the error diffusion works" ><img src="https://images.cyotek.com/image/devblog/dithering-diagram.png" alt="A small illustration to try and demonstrate how the error diffusion works" decoding="async" loading="lazy" /></a><figcaption>A small illustration to try and demonstrate how the error diffusion works</figcaption></figure>
<blockquote>
<p>It's worth repeating that the error is <strong>not</strong> applied to any
previously transformed value. If you do modify an already
processed value, then you would need to have some way of
reprocessing it (as the combined value+error may not be valid
for your reduction method), which could get messy fast.</p>
</blockquote>
<h2 id="next-steps">Next Steps</h2>
<p>Hopefully this article serves as at least a basic and high level
overview of dithering - additional posts will deal with the
actual implementation of dithering.</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/an-introduction-to-dithering-images .
</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.comEssential Algorithms - A Book Reviewurn:uuid:11832fc1-553b-4bfb-9bb7-7092ade6f9d32015-03-07T09:03:37Z2015-03-07T09:03:37Z<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/lrg.jpg" class="gallery" title="Essential Algorithms Book Cover" ><img src="https://images.cyotek.com/image/thumbnail/devblog/lrg.jpg" alt="Essential Algorithms Book Cover" decoding="async" loading="lazy" /></a><figcaption>Essential Algorithms Book Cover</figcaption></figure>
<p>This post is a review (or possibly some long winded rambling) of
the book <a href="http://www.wiley.com/go/essentialalgorithms" rel="external nofollow noopener">Essential Algorithms: A Practical Approach to Computer
Algorithms</a> by Rod Stephens and published by Wiley.</p>
<blockquote>
<p>Disclaimer: I received a copy of this book (with a personal
signed inscription too :)) directly from Rod with the
condition that I review the book. This has not influenced my
review except that I have tried to do a decent job rather than
just picking a star and saying I liked it.</p>
</blockquote>
<h2 id="quick-overview">Quick overview</h2>
<p>The book has quite a few chapters covering a pretty good
selection of algorithms, including</p>
<ul>
<li>Numerical Algorithms</li>
<li>Linked Lists, Arrays, Stacks and Queues, Hash Tables</li>
<li>Sorting</li>
<li>Searching</li>
<li>Recursion</li>
<li>Trees, Balanced Trees, Decision Trees</li>
<li>Basic Network Algorithms, More Network Algorithms</li>
<li>String Algorithms</li>
<li>Cryptography</li>
<li>Complexity Theory</li>
<li>Distributed Algorithms</li>
</ul>
<p>There's also a glossary as you would expect with this sort of
reference, and an appendix with the answers to all the practice
questions - you will need this!</p>
<p>Each chapter is divided into sections, and ends with a summary
and a set of practice questions, some of which are marked with
one or more <code>*</code> to indicate a tougher problems. Standard stuff!</p>
<p>There's also sample code available from the book's website.</p>
<h2 id="tell-me-about-the-book-already">Tell me about the book already</h2>
<p>I don't have a very strong maths background, and there is a
distinct lack of material on either mathematics or algorithms to
be found in my selection of programming books. I do have one
other book on the subject of algorithms/data structures - it is
so dry and filled with source code in an unfamiliar language I
haven't even attempted to read it yet.</p>
<p>Essential Algorithms on the other hand, is a book I found to be
very approachable, bar a hiccup or two.</p>
<p>When I buy computer books, they are pretty much always for a
specific language or technology, but I think Essential
Algorithms is actually language agnostic. While the accompanying
downloadable source is in C#, the code in the book is pseudo
code written as plain English (or perhaps Rod's version of
Beginners All-purpose Symbo... <em>cough</em>). I actually found this
refreshing as when trying to grasp a tenet of the algorithms Rod
was describing I didn't have to &quot;think code&quot; which is more
helpful than it sounds.</p>
<h2 id="my-head">My head!</h2>
<p>So I mentioned hiccups. What were these? Well, my initial foray
into the book was slightly bewildering. The very first chapter,
describes various performance characteristics (Big O notation)
and chapter two dives right into numerical algorithms. This
second chapter actually covers quite a lot, but I did find it
difficult to grasp. I don't find fault with Rod's writing for
this, but my lack of knowledge on mathematics. With that said,
it looked interesting enough that I am determined to get enough
knowledge to be able to read this chapter and understand it!</p>
<h2 id="when-is-an-algorithm-not-an-algorithm">When is an algorithm not an algorithm</h2>
<p>Chapters three through five cover linked lists, arrays, stacks
and queues, something I suspect any C# developer would
recognise. Even though I'm intimately familiar with these data
structures, and, (with the exception of linked lists) use them
regularly, I still discovered quite a few new things I hadn't
considered regarding implementation and advanced usage of such
structures, which never occurred to me when using black box
implementations.</p>
<p>An example of this is sentinel values to avoid having to write
code to handle special cases (such as the start or end of a
linked list). Seems obvious but I hadn't thought of it -
assuming I was aware of the special case I'd write extra code to
handle it.</p>
<h2 id="sorting-and-searching">Sorting and Searching</h2>
<p>I suppose every programmer can write a bubble sort without even
thinking about it, but Essential Algorithms covers no less than
8 different ways of performing a sort.</p>
<p>Closely tied to sorting is searching, as it is more efficient to
search sorted data. Oddly however, this is an incredibly short
chapter - barely 6 pages. However, it does include binary and
interpolation search algorithms which are much better than the
usual linear search that I would normally do.</p>
<p>With that said, the book then follows on with a detailed chapter
on hash tables which can also help you find data extremely fast.</p>
<h2 id="seeing-the-forest-for-the-trees">Seeing the forest for the trees</h2>
<p>Many people are familiar with a tree as a means of presenting
hierarchical data, but that's not what the chapters on trees
cover. Essential Algorithms describes binary trees, complete
trees, sorted trees, how to traverse trees, how to search trees,
expression evaluation, the list goes on.</p>
<p>I found this chapter engrossing as I could dimly see the light
bulb flickering of how I could make use of these techniques.</p>
<p>This is then followed by a chapter on balanced trees (AVL trees
and b-trees). I started getting a bit lost here, although it
didn't help due to my schedule I was reading through the last
chapters very piecemeal, a few pages here, a page or two there.
While I started to get glimmers of ideas from the first two
chapters on trees, the 3rd tree chapter - Decision Trees - was
another head scratcher.</p>
<h2 id="networks-are-trees-with-added-epic">Networks are trees with added epic</h2>
<p>There are two chapters which deal with networks. As with the
first couple of tree chapters, I also found these chapters quite
interesting, with the caveat I couldn't immediately see how I
can use this knowledge in my code. They cover network traversal,
short path detection, map colouring (whoever would believe that
automatically colouring a map in as few shades as possible would
be so hard!) and a bit more besides.</p>
<h2 id="my-head-is-hurting-again">My head is hurting again</h2>
<p>The last few chapters deal with cryptology (interesting, but
there's no way I'm going to try reinventing that wheel, I'll use
managed black box classes!), complexity theory (I gave up trying
to understand it) and distributed algorithms, which falls neatly
under the parallel processing banner and so again should be
somewhat familiar to C# developers. I wondered why this was the
last chapter as the complexity theory was so complicated it
should have been at the end.</p>
<h2 id="sample-code">Sample Code</h2>
<p>I haven't tried most of the exercises offered at the end of each
chapter, so I can't comment on the accuracy of these. And, as I
haven't finished them I've avoided looking in detail at the
source code examples. The ones I have browsed don't seem to be
bad, and while are not extensively commented (so you'll probably
have to have the book to hand for reference), they do include
enough comments for you to know what's going on, and the code
itself is not written in an obtuse fashion. Even from the brief
look I'd taken I could see things I could learn from so yet more
bonus.</p>
<h2 id="in-conclusion">In conclusion</h2>
<p>Unlike many programming books I have bought in the past,
Essential Algorithms is actually a book that I want to read
again, both to pick up what eluded me the first time around, and
to help me visualize ways of using what I have learned in the
code I write.</p>
<p>However, if like me you don't have a strong head for maths, you
might struggle with some of the chapters.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2015-03-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/essential-algorithms-a-book-review .
</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.comTools we use - 2014 editionurn:uuid:4672ca96-5131-4e4a-a150-eeb81cde37502015-01-17T08:10:44Z2015-01-17T08:10:44Z<p>Following on from <a href="/post/tools-we-use-2013-edition">last years</a> post, I'll list again what I'm using and seeing what (if anything) has changed.</p>
<p>tl;dr; - it's pretty much the same as last year</p>
<h2 id="operating-systems">Operating Systems</h2>
<ul>
<li>Windows Home Server 2011 - file server, SVN repository, backup host, CI server</li>
<li>Windows 8.1 Professional - development machine.</li>
<li>Windows XP (virtualized) - testing</li>
<li>Windows Vista (virtualized) - testing</li>
<li><ins>New!</ins> Windows 10 (virtualized) - testing</li>
</ul>
<h2 id="development-tools">Development Tools</h2>
<ul>
<li><a href="http://www.microsoft.com/visualstudio/" rel="external nofollow noopener">Visual Studio 2013 Premium</a> - not much to say</li>
<li><a href="http://www.oz-code.com/" rel="external nofollow noopener">OzCocde</a> - this is one of the tools you wonder why isn't in Visual Studio by default</li>
<li><a href="http://www.red-gate.com/products/dotnet-development/dotnet-demon/" rel="external nofollow noopener">.NET Demon</a> - yet another wonderful tool that helps speed up your development, this time by not slowing you down waiting for compiles. Unfortunately it's no longer supported by RedGate as apparently VS2015 will do this</li>
<li><a href="http://www.ncrunch.net/" rel="external nofollow noopener">NCrunch for Visual Studio</a> - (version 2!) automated parallel continuous testing tool. Works with NUnit, MSTest and a variety of other test systems. Great for TDD and picking up how a simple change you made to one part of your project completely destroys another part. We've all been there!</li>
<li><a href="http://www.reflector.net/" rel="external nofollow noopener">.NET Reflector</a> - controversy over free vs paid aside, this is still worth the modest cost for digging behind the scenes when you want to know how the BCL works.</li>
<li><a href="http://cyotek.com/blog/visual-studio-extension-for-adding-multiple-projects-to-a-solution">Cyotek Add Projects</a> - a simple extension I recently created that I use pretty much any time I create a new solution to add references to my standard source code libraries. Saves me time and key presses, which is good enough for me!</li>
<li><a href="http://www.jetbrains.com/resharper/" rel="external nofollow noopener">Resharper</a> - originally as a replacement for Regionerate, this swiftly became a firm favourite every time it told me I was doing something stupid.</li>
<li>Other extensions are <a href="http://visualstudiogallery.msdn.microsoft.com/c6d1c265-7007-405c-a68b-5606af238ece" rel="external nofollow noopener">VSCommands 2013</a>, <a href="http://visualstudiogallery.msdn.microsoft.com/56633663-6799-41d7-9df7-0f2a504ca361" rel="external nofollow noopener">Web Essentials 2013</a> and <a href="http://visualstudiogallery.msdn.microsoft.com/e792686d-542b-474a-8c55-630980e72c30" rel="external nofollow noopener">Indent Guides</a></li>
</ul>
<h2 id="analytics">Analytics</h2>
<ul>
<li>Innovasys Luminitix (Link Removed) - we've been using this for over 18 months now in an effort to gain some understanding in how our products are used by end users. I keep meaning to write a blog post on this, maybe I'll get around to that in 201<del>4</del><ins>5</ins>!</li>
</ul>
<h2 id="profiling">Profiling</h2>
<ul>
<li><a href="http://www.red-gate.com/products/dotnet-development/ants-performance-profiler/" rel="external nofollow noopener">ANTS Performance Profiler</a> - the best profiler I've ever used. The bottlenecks and performance issues this has helped resolve with utter ease is insane. It. Just. Works.</li>
</ul>
<h2 id="documentation-tools">Documentation Tools</h2>
<ul>
<li><a href="http://www.innovasys.com/product/dx/overview" rel="external nofollow noopener">Innovasys Document! X</a> - Currently we use this to produce the user manuals for our applications.</li>
<li><a href="http://submain.com/products/ghostdoc.aspx" rel="external nofollow noopener">SubMain GhostDoc Pro</a> - Does a slightly better job of auto generating XML comment documentation thatn doing it fully from scratch. Actually, barley use this now, the way it litters my code folders with XML files when I don't use <em>any</em> functionality bar auto-document is starting to more than annoy me.</li>
<li><a href="http://markdownpad.com/" rel="external nofollow noopener">MarkdownPad Pro</a> - fairly decent Markdown editor that is currently better than our own so I use it instead!</li>
<li><a href="http://notepad-plus-plus.org/" rel="external nofollow noopener">Notepad++</a> - because Notepad hasn't changed in 20 years (moving menu items around doesn't count!)</li>
</ul>
<h2 id="graphics-tools">Graphics Tools</h2>
<ul>
<li><a href="http://www.getpaint.net/" rel="external nofollow noopener">Paint.NET</a> - brilliant bitmap editor with extensive plugins</li>
<li><a href="http://www.axialis.com/iconworkshop/" rel="external nofollow noopener">Axialis IconWorkshop</a> - very nice icon editor, been using this for untold years now since Microangelo decided to become the Windows Paint of icon editing</li>
<li><a href="http://cyotek.com/cyotek-spriter">Cyotek Spriter</a> - sprite / image map generation software</li>
<li><a href="http://cyotek.com/cyotek-gif-animator/">Cyotek Gif Animator</a> - gif animation creator that is shaping up nicely, although I'm obviously biased.</li>
</ul>
<h2 id="virtualization">Virtualization</h2>
<ul>
<li><a href="https://www.virtualbox.org/" rel="external nofollow noopener">Oracle VM VirtualBox</a> - for creating guest OS's for testing purposes. Cyotek software is informally smoke tested mainly on Windows XP, but occasionally Windows Vista. Visual Studio 2013 installed Hyper-V, but given as the VirtualBox VM's have been running for years with no problems, this is disabled. Still need to switch back to Hyper-V if I want to be able to do any mobile development. Which I do.</li>
</ul>
<h2 id="version-control">Version Control</h2>
<ul>
<li><a href="http://tortoisesvn.net/" rel="external nofollow noopener">TortoiseSVN</a> - Windows Explorer integration for SVN</li>
<li><a href="http://ankhsvn.open.collab.net/" rel="external nofollow noopener">AnhkSVN</a> - Subversion support for Visual Studio</li>
<li><a href="http://www.visualsvn.com/server/" rel="external nofollow noopener">VisualSVN Server</a> - Subversion Server for Windows</li>
<li><a href="https://github.com/" rel="external nofollow noopener">GitHub</a> / <a href="http://windows.github.com/" rel="external nofollow noopener">GitHub for Windows</a> - for the public facing aspects of our source code.</li>
</ul>
<h2 id="filedirectory-comparison">File/directory comparison</h2>
<ul>
<li><a href="http://winmerge.org/" rel="external nofollow noopener">WinMerge</a> - not much to say, it works and works well</li>
</ul>
<h2 id="file-searching">File searching</h2>
<ul>
<li><ins>New!</ins> <a href="http://stefanstools.sourceforge.net/grepWin.html" rel="external nofollow noopener">WinGrep</a> - previously I just used to use Notepad++'s search in files but... this is a touch simpler all around</li>
</ul>
<h2 id="backups">Backups</h2>
<ul>
<li><a href="http://cyotek.com/cyotek-copytools">Cyotek CopyTools</a> - we use this for offline backups of source code, assets and resources, documents, actually pretty much anything we generate; including backing up the backups!</li>
<li><a href="http://www.code42.com/crashplan/" rel="external nofollow noopener">CrashPlan</a> - CrashPlan creates an online backup of the different offline backups that CopyTools does. If you've ever lost a harddisk before with critical data on it that's nowhere else, you'll have backups squirrelled away everywhere too!</li>
</ul>
<hr />
<p><em>So only the smallest of changes both in regards to software, and the technologies I use. All the cool kids seem to be using Node, Gulp, Bower, Grunt and who knows what else... maybe I'll finally have some time to look at some of this in the upcoming year. Maybe I'll get that CI server fixed. Maybe I'll write that mobile app I keep meaning to write. Maybe a lot of things. Maybe.</em></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/tools-we-use-2014-edition .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a code signing certificate with StartSSLurn:uuid:c6f0abd6-17f0-472d-852d-49cc098fc32b2017-01-05T20:16:23Z2014-12-31T12:21:03Z<blockquote>
<p><strong>Edit 02Jan2017</strong>: Even if you wanted to ignore the
revelations of dubious practices of StartSSL and with them now
being owned by WoSign, there is another matter to consider -
StartSSL authenticode certificates <a href="/post/startssl-code-signing-certificates-are-crippled">don't support lifetime
signing</a>.
Meaning, when your certificate has expired, your signed
binaries are no longer trusted, negating the point of signing
them in the first place. For this reason, I don't recommend
using StartSSL any further.</p>
</blockquote>
<p>The process of obtaining a code signing certificate from
<a href="https://www.startssl.com/" rel="external nofollow noopener">StartSSL</a> differs significantly from
the process I originally went through with Comodo. This blog
post serves to document how I did it for StartSSL, both as a
reference for myself and for anyone else! Personally I find this
approach easier than fiddling around exporting certificates from
a browser, and it gives you a lot more control.</p>
<p>This post assumes you have already validated a level 2
personal/organization identity with StartSSL. Free level 1
validations cannot be used to create code signing certificates.</p>
<blockquote>
<p>The usual caveat applies - I have tested this process and
signed software with the final certificate and all seems well.
However, it's entirely possible I'm missing something and
something drastic given the apparent compromising of the
previous certificate. I'll update this post should I discover
anything untoward, but your mileage may vary. <strong>I am not a
security expert</strong>.</p>
</blockquote>
<h2 id="prerequisites">Prerequisites</h2>
<p>In order to generate the certificate signing request (CSR) and
convert the certificate, you're going to need to use
<a href="https://www.openssl.org/" rel="external nofollow noopener">OpenSSL</a>. As I'm running on Windows, you'll more than likely
want to download pre-compiled Win32 binaries - you can find
these <a href="http://slproweb.com/products/Win32OpenSSL.html" rel="external nofollow noopener">here</a>. I mostly use 64bit versions of Windows, but I
found the 32bit version of OpenSSL to suffice.</p>
<h3 id="the-openssl_conf-environment-variable-or-avoiding-warning-cant-open-config-file-usrlocalsslopenssl.cnf">The OPENSSL_CONF environment variable, or avoiding &quot;WARNING: can't open config file: /usr/local/ssl/openssl.cnf&quot;</h3>
<p>When trying to use the OpenSSL command line tools, you may
receive <code>WARNING: can't open config file: /usr/local/ssl/openssl.cnf</code>. It might be a warning, but it is a
fatal one.</p>
<p>If this happens, just run the following line in your command
window (or add it to your batch script), replacing the path as
appropriate for your local installation.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
<span class="keyword">SET</span> OPENSSL_CONF=C:\OpenSSL-Win32\bin\openssl.cfg
</pre>
</figure>
<blockquote>
<p>As a side note, the OpenSSL installer does register this as a
global environment variable, but it doesn't seem to kick in
right away - I received this warning when initially testing on
a Windows 8.1 development machine, and I received it again
when installing on an XP VM for testing as I write this post.</p>
</blockquote>
<h3 id="a-note-on-paths">A note on paths</h3>
<p>By default, the OpenSSL installer linked to above will install
the binaries to your Windows system directory. If (as I did) you
choose not to do this, you will either need to include the
OpenSSL bin folder in your path, include the path in the
executable call, or set the current directory to the OpenSSL bin
folder. Whatever method you choose, the rest of this article
assumes that this has been done.</p>
<h2 id="generating-the-certificate-signing-request">Generating the certificate signing request</h2>
<p>A CSR or Certificate Signing request is a block of encrypted
text that is generated on the server that the certificate will
be used on. It contains information that will be included in
your certificate such as your organization name, common name
(domain name), locality, and country (<a href="https://www.sslshopper.com/what-is-a-csr-certificate-signing-request.html" rel="external nofollow noopener">source</a>).</p>
<p>StartSSL requires a CSR that you need to create yourself.
Although this might sound a little ominous (and something
StartSSL could improve upon, as they offer zero guidance on the
subject) it's pretty easy to do.</p>
<p>Open a command window and run the following command, replacing
<code>yournamehere</code> as appropriate.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
openssl req -out yournamehere.csr -new -newkey rsa:2048 -nodes -keyout yournamehere.key
</pre>
</figure>
<p>When you run the command, you'll be asked for a number of
different values, such as <strong>Country</strong>, <strong>State,</strong> <strong>Common
Name</strong> and so on. Make sure these values match the details you
have validated with StartSSL.</p>
<p>Once you have submitted all the requested values, OpenSSL will
generate both a certificate request and a private key with a bit
size of 2048. By default it will use the SHA1 algorithm. For a
more secure key, change <code>2048</code> to a larger value such as 4096.
Do not use a value lower that 2048 however.</p>
<blockquote>
<p>You've probably heard that Google is doing away with <a href="http://googleonlinesecurity.blogspot.co.uk/2014/09/gradually-sunsetting-sha-1.html" rel="external nofollow noopener">SHA1
SSL</a>
and while I don't think this applies to code signing
certificates, you may wish to use something newer, for example
appending to the above command line <code>-sha256</code> will use SHA2.
However, I didn't discover this option until after I'd created
the certificate so I can't say if this works or not. I also
remember Windows XP didn't support one of the .NET SHA
algorithms so this too may factor into your choice.</p>
</blockquote>
<p>Copy <code>yournamehere.key</code> somewhere <strong>secure</strong>. If you lose this
file, you won't be able to generate any more certificates from
that CSR. And you don't want anyone else getting their hands on
the key either.</p>
<blockquote>
<p>Make sure all details are correct before submitting the CSR to
StartSSL. You won't be able to create a new certificate
without revoking the previous one - and StartSSL will charge
you for this.</p>
</blockquote>
<p>Open up <code>yournamehere.csr</code> in your favourite text editor, copy
out the entire contents of the file and paste it into StartSSL's
CSR request field and submit the form.</p>
<h2 id="creating-the-certificate">Creating the certificate</h2>
<p>Once StartSSL has processed your request, you'll be able to
download the certificate. When you request your certificate from
them you'll be presented with another unfriendly form containing
a block of text. Copy this into a new file and save it somewhere
with a <code>.crt</code> extension.</p>
<p>If you double click this file from Windows Explorer, then it
should display the certificate allowing you to check all the
details.</p>
<blockquote>
<p>The certificate you have just downloaded is actually in
<strong>PEM</strong> format, but Windows doesn't recognize the <code>.pem</code>
extension in order to view it as described above.</p>
</blockquote>
<p>However, you're not quite done. Microsoft's <code>signtool.exe</code>
command requires that certificates be in the <strong>Personal
Information Exchange</strong> (PFX) format instead. So we'll need to
convert the certificate using the private key we created
earlier.</p>
<p>Open a command window and run the following, as usual replacing
file names as appropriate. If you omit the <code>-password</code> argument
then OpenSSL will prompt for one. Older versions of Windows only
support password lengths of 32 characters. Make sure the
password is as secure as you can, random characters, symbols,
the works.</p>
<blockquote>
<p>If your chosen password includes the &amp; character, surround the
password in double quotes otherwise the command parser will
get a little confused and the command will fail</p>
</blockquote>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
openssl pkcs12 -export -out yournamehere.pfx -inkey yournamehere.key -in yournamehere.crt -password pass:<span class="string">&quot;yourpasswordhere&quot;</span>
</pre>
</figure>
<p>This should then generate <code>yournamehere.pfx</code> in a format that
<code>signtool.exe</code> will understand. Don't lose the password, you'll
need it whenever you use the certificate to sign files or to
import the certificate into a store.</p>
<blockquote>
<p>Unlike with the <code>.crt</code> file, trying to open the <code>.pfx</code> file
will display a wizard for importing the certificate into your
store.</p>
</blockquote>
<h2 id="and-youre-done">And you're done</h2>
<p>With these simple steps complete you now have a certificate that
you can use to code sign your programs. Good luck!</p>
<h2 id="example-signtool.exe-usage">Example signtool.exe usage</h2>
<p>Although this article is about creating the certificate, it only
make sense to quickly outline the right parameters for signing
your files. Again, I'm assuming that <code>signtool.exe</code> is somewhere
in your path for the below command line to work.</p>
<blockquote>
<p>Edit 2016Jul27: According to Micheal Herrmann's
<a href="#comment2537">comment</a>, the time stamping URL is now
<code>http://tsa.startssl.com/rfc3161</code>, so if the example below no
longer works try this URL instad.</p>
</blockquote>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
signtool sign /f yournamehere.pfx /p yourpasswordhere /tr http://www.startssl.com/timestamp yourfilehere.exe
</pre>
</figure>
<blockquote>
<p>If you've previously use another timestamping service, you may
have used <code>/t &lt;url&gt;</code> - this won't work with StartSSL's
timestamp server, you must use the <code>/tr</code> parameter instead.</p>
</blockquote>
<h2 id="update-history">Update History</h2>
<ul>
<li>2014-12-31 - First published</li>
<li>2016-07-27 - Added note about timestamping URLs</li>
<li>2017-01-02 - Added warning about code signing certificates</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-code-signing-certificate-with-startssl .
</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.comCircularBuffer - a first-in, first-out collection of objects using a fixed bufferurn:uuid:a403716f-9426-4866-a84e-91d173810b7b2023-02-05T17:58:40Z2014-06-21T15:00:13Z<p>I haven't had much time to work on blog posts recently, but I do
have one quick post to make.</p>
<p>One of our current application prototype's stores a bunch of
data. Data continuously arrives, but I only want to store so
much of it. You could do this with something list a <code>List&lt;T&gt;</code>
and just remove items when the collection is too big, but I
wanted something a bit more efficient which didn't have to do
any sort of resizing or allocation when adding and removing
items. Enter the <a href="http://en.wikipedia.org/wiki/Circular_buffer" rel="external nofollow noopener">circular buffer</a>.</p>
<h2 id="what-is-a-circular-buffer">What is a circular buffer?</h2>
<p>To quote Wikipedia, a circular buffer, cyclic buffer or ring
buffer is a data structure that uses a single, fixed-size buffer
as if it were connected end-to-end. This structure lends itself
easily to buffering data streams.</p>
<p>Indeed. Originally I didn't want to invent the wheel, so when I
found Circular Buffer for .NET<a id="fnref:1" href="#fn:1" class="footnote-ref"><sup>1</sup></a> I thought I would use that.
Unfortunately as soon as I started using, I hit some problems,
both in the code and with what I was trying to do. So I stopped
working on what I was doing and wrote a full set of tests for
the class, fixing bugs as I went, and also adding some more
features to handle what I wanted.</p>
<p>Eventually I decided I would put the code up on GitHub as
Circular Buffer for .NET doesn't seem to be maintained any
longer.</p>
<h2 id="a-generic-circularbuffert-class-for.net">A generic CircularBuffer&lt;T&gt; class for .NET</h2>
<p>On our <a href="https://github.com/cyotek/Cyotek.Collections.Generic.CircularBuffer" rel="external nofollow noopener">GitHub</a> page you can download a modified version of
the original class. I'm not going into too many details here as
it's very straightforward to use - if you've used <code>Queue&lt;T&gt;</code> or
<code>Stack&lt;T&gt;</code> then you'll be right at home.</p>
<ul>
<li><code>Get</code> - Removes and returns one or more items from the start
of the buffer</li>
<li><code>Put</code> - Adds one or more items to the end of the buffer. If
the buffer is full, then items at the start will be
overwritten</li>
<li><code>Peek</code> - Retrieve one or more items from the start of the
buffer, without removing them</li>
<li><code>PeekLast</code> - Retrieve the last item in the buffer without
removing it</li>
<li><code>ToArray</code> - Return all items in the buffer</li>
<li><code>CopyTo</code> - An advanced version of <code>ToArray</code>, allows you to
copy items from the buffer into another array</li>
<li><code>Clear</code> - Resets the buffer</li>
</ul>
<p>There are also some properties to control behaviour or provide
state information.</p>
<ul>
<li><code>Capacity</code> - The total number of items the buffer can hold</li>
<li><code>Size</code> - The current number of items in the buffer</li>
<li><code>AllowOverwrite</code> - When <code>true</code>, new items overwrite the oldest
items when the buffer is full. Otherwise, an exception is
thrown</li>
<li><code>IsEmpty</code> - <code>true</code> if the buffer is empty</li>
<li><code>IsFull</code> - <code>true</code> if the buffer is full and <code>AllowOverwrite</code>
is <code>false</code></li>
</ul>
<h2 id="examples">Examples</h2>
<p>This first example creates a <code>CircularBuffer&lt;T&gt;</code>, adds four
items, then retrieves the first item.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
CircularBuffer<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">&gt;</span> target<span class="symbol">;</span>
<span class="keyword">string</span> firstItem<span class="symbol">;</span>

target <span class="symbol">=</span> <span class="keyword">new</span> CircularBuffer<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">&gt;</span><span class="symbol">(</span><span class="number">10</span><span class="symbol">)</span><span class="symbol">;</span>
target<span class="symbol">.</span>Put<span class="symbol">(</span><span class="string">&quot;Alpha&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
target<span class="symbol">.</span>Put<span class="symbol">(</span><span class="string">&quot;Beta&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
target<span class="symbol">.</span>Put<span class="symbol">(</span><span class="string">&quot;Gamma&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

firstItem <span class="symbol">=</span> target<span class="symbol">.</span>Get<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// Returns Alpha</span>
</pre>
</figure>
<p>This second example shows how the buffer will automatically
overwrite the oldest items when full.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
CircularBuffer<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">&gt;</span> target<span class="symbol">;</span>
<span class="keyword">string</span> firstItem<span class="symbol">;</span>

target <span class="symbol">=</span> <span class="keyword">new</span> CircularBuffer<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">&gt;</span><span class="symbol">(</span><span class="number">3</span><span class="symbol">)</span><span class="symbol">;</span>
target<span class="symbol">.</span>Put<span class="symbol">(</span><span class="string">&quot;Alpha&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
target<span class="symbol">.</span>Put<span class="symbol">(</span><span class="string">&quot;Beta&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
target<span class="symbol">.</span>Put<span class="symbol">(</span><span class="string">&quot;Gamma&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
target<span class="symbol">.</span>Put<span class="symbol">(</span><span class="string">&quot;Delta&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

firstItem <span class="symbol">=</span> target<span class="symbol">.</span>Get<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span> <span class="comment">// Returns beta</span>
</pre>
</figure>
<p>For more examples, see the test class <code>CircularBufferTests</code> as
this has tests which cover almost all the code paths.</p>
<h2 id="requirements">Requirements</h2>
<p>.NET Framework 2.0 or later.</p>
<h2 id="download">Download</h2>
<p><a href="https://github.com/cyotek/Cyotek.Collections.Generic.CircularBuffer" rel="external nofollow noopener">Cyotek.Collections.Generic.CircularBuffer</a></p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2014-06-21 - First published</li>
<li>2020-11-21 - Updated formatting</li>
<li>2023-02-05 - Fixed swapped URLs</li>
</ul>
<div class="footnotes">
<hr />
<ol>
<li id="fn:1">
<p><a href="http://circularbuffer.codeplex.com/" rel="external nofollow noopener">http://circularbuffer.codeplex.com/</a>, link no longer active<a href="#fnref:1" 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/circularbuffer-a-first-in-first-out-collection-of-objects-using-a-fixed-buffer .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comAdd Projects Extension - 1.0.1.0urn:uuid:feefb7be-680f-4d17-8370-ac2cba5456172014-10-14T16:38:40Z2014-04-18T18:29:06Z<p><em>A short and sweet post today...</em></p>
<p>I've been happily using the Add Projects extension since first
writing it several months ago, and I actually find it a real
time saver.</p>
<p>However, one thing that has been bugging me is trying to find
specific projects in an ever growing list.</p>
<p>I've just updated the extension to version 1.0.1.0 by adding a
handy filter option to the main dialog (sorry, it's still
Windows Forms as opposed to XAML, so continues to look clunky in
the VSIDE!)</p>
<p>I've also pushed the source to <a href="https://github.com/cyotek/Cyotek.AddProjects" rel="external nofollow noopener">GitHub</a>. At some point I'll
convert it to XAML and properly publish it, but for now the
(signed) package can be downloaded <a href="http://cyotek.com/downloads/get/Cyotek.VisualStudioExtensions.AddProjects.vsix">here</a>.</p>
<p>One word of caution - I doubt the source code project will open
in Visual Studio 2012 any more, as I had to install the VS2013
SDK and upgrade the project to work on this update. The compiled
extension is supported on Visual Studio 2012 and Visual Studio
2013.</p>
<h3 id="downloading">Downloading</h3>
<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>2014-04-18 - First published</li>
<li>2014-10-14 - Updated to include Visual Studio Gallery and
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/add-projects-extension-1-0-1-0 .
</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.comTools we use - 2013 editionurn:uuid:b745ca0e-828b-4ec7-87ff-f1951fb07f8e2013-12-31T19:10:00Z2013-12-31T18:26:19Z<p>As another year enters its final stages, I decided I would log the primary tools I use for my developer role, should be an interesting experiment to compare with each year, if I don't get distracted by something shiny.</p>
<p>I actually wrote this post in October last year (2012 that is) but in the end forgot to post it. <span style="font-size: 6pt; color: silver">That sounds marginally better than &quot;I lost the post until search was added to cyotek.com's admin system&quot;!</span> Surprisingly little is different in the 15 months since it was first written. Either nothing <em>has</em> changed or I'm stuck in my ways with my head in the sand. <em>I'm sure it's the latter!</em></p>
<h2 id="operating-systems">Operating Systems</h2>
<ul>
<li>Windows Home Server 2011 - file server, SVN repository, backup host. It's a shame it has been discontinued</li>
<li>Windows 8.1 Professional - development machine. Interesting, I was still using Windows 7 when I first wrote this post... thought I'd been using WIndows 8 for longer than that.</li>
<li>Windows XP (virtualized) - testing</li>
<li>Windows Vista (virtualized) - testing</li>
<li>Windows 3.1 (virtualized) - no reason other than because I can?</li>
</ul>
<h2 id="development-tools">Development Tools</h2>
<ul>
<li><a href="http://www.microsoft.com/visualstudio/" rel="external nofollow noopener">Visual Studio 2013 Premium</a> - an improvement upon 2010 in so many ways, and the second change in this update post as I was using 2012 last year</li>
<li><del>BugAid</del><a href="http://www.oz-code.com/" rel="external nofollow noopener">OzCocde</a> (the old name was better!) - this is one of the tools you wonder why isn't in Visual Studio by default</li>
<li><a href="http://www.red-gate.com/products/dotnet-development/dotnet-demon/" rel="external nofollow noopener">.NET Demon</a> - yet another wonderful tool that helps speed up your development, this time by not slowing you down waiting for compiles. I was just recently reminded of this when I was working on a machine without the Demon and I was shocked at how long I was waiting around for solution builds to complete</li>
<li><a href="http://www.ncrunch.net/" rel="external nofollow noopener">NCrunch for Visual Studio</a> - (version 2!) automated parallel continuous testing tool. Works with NUnit, MSTest and a variety of other test systems. Great for TDD and picking up how a simple change you made to one part of your project completely destroys another part. We've all been there!</li>
<li><a href="http://www.reflector.net/" rel="external nofollow noopener">.NET Reflector</a> - controversy over free vs paid aside, this is still worth the modest cost for digging behind the scenes when you want to know how the BCL works.</li>
<li><a href="http://cyotek.com/blog/visual-studio-extension-for-adding-multiple-projects-to-a-solution">Cyotek Add Projects</a> - a simple extension I recently created that I use pretty much any time I create a new solution to add references to my standard source code libraries. Saves me time and key presses, which is good enough for me!</li>
<li><a href="http://www.jetbrains.com/resharper/" rel="external nofollow noopener">Resharper</a> - originally as a replacement for Regionerate, this swiftly became a firm favourite every time it told me I was doing something stupid.</li>
<li>Other extensions are <a href="http://visualstudiogallery.msdn.microsoft.com/c6d1c265-7007-405c-a68b-5606af238ece" rel="external nofollow noopener">VSCommands 2013</a>, <a href="http://visualstudiogallery.msdn.microsoft.com/56633663-6799-41d7-9df7-0f2a504ca361" rel="external nofollow noopener">Web Essentials 2013</a> and <a href="http://visualstudiogallery.msdn.microsoft.com/e792686d-542b-474a-8c55-630980e72c30" rel="external nofollow noopener">Indent Guides</a></li>
</ul>
<h2 id="analytics">Analytics</h2>
<ul>
<li>Innovasys Luminitix (Link Removed) - we've been using this for over 18 months now in an effort to gain some understanding in how our products are used by end users. I keep meaning to write a blog post on this, maybe I'll get around to that in 2014!</li>
</ul>
<h2 id="profiling">Profiling</h2>
<ul>
<li><a href="http://www.red-gate.com/products/dotnet-development/ants-performance-profiler/" rel="external nofollow noopener">ANTS Performance Profiler</a> - the best profiler I've ever used. The bottlenecks and performance issues this has helped resolve with utter ease is insane. It. Just. Works.</li>
</ul>
<h2 id="documentation-tools">Documentation Tools</h2>
<ul>
<li><a href="http://www.innovasys.com/product/dx/overview" rel="external nofollow noopener">Innovasys Document! X</a> - Currently we use this to produce the user manuals for our applications.</li>
<li><a href="http://submain.com/products/ghostdoc.aspx" rel="external nofollow noopener">SubMain GhostDoc Pro</a> - Does a slightly better job of auto generating XML comment documentation thatn doing it fully from scratch. Actually, I use this less and less now, the way it litters my code folders with XML files when I don't use <em>any</em> functionality bar auto-document is starting to more than annoy me.</li>
<li><a href="http://markdownpad.com/" rel="external nofollow noopener">MarkdownPad Pro</a> - fairly decent Markdown editor that is currently better than our own so I use it instead!</li>
<li><a href="http://notepad-plus-plus.org/" rel="external nofollow noopener">Notepad++</a> - because Notepad hasn't changed in 20 years (moving menu items around doesn't count!)</li>
</ul>
<h2 id="graphics-tools">Graphics Tools</h2>
<ul>
<li><a href="http://www.getpaint.net/" rel="external nofollow noopener">Paint.NET</a> - brilliant bitmap editor with extensive plugins</li>
<li><a href="http://www.axialis.com/iconworkshop/" rel="external nofollow noopener">Axialis IconWorkshop</a> - very nice icon editor, been using this for untold years now since Microangelo decided to become the Windows Paint of icon editing (but without the <a href="http://www.youtube.com/watch?v=O4YC4JI-0ko" rel="external nofollow noopener">awesome</a>)</li>
<li><a href="http://cyotek.com/cyotek-spriter">Cyotek Spriter</a> - sprite / image map generation software</li>
<li><a href="http://cyotek.com/cyotek-gif-animator/">Cyotek Gif Animator</a> - gif animation creator that is shaping up nicely, although I'm obviously biased.</li>
</ul>
<h2 id="virtualization">Virtualization</h2>
<ul>
<li><a href="https://www.virtualbox.org/" rel="external nofollow noopener">Oracle VM VirtualBox</a> - for creating guest OS's for testing purposes. Cyotek software is informally smoke tested mainly on Windows XP, but occasionally Windows Vista. Visual Studio 2013 installed Hyper-V, but given as the VirtualBox VM's have been running for years with no problems, this is disabled.</li>
</ul>
<h2 id="version-control">Version Control</h2>
<ul>
<li><a href="http://tortoisesvn.net/" rel="external nofollow noopener">TortoiseSVN</a> - Windows Explorer integration for SVN</li>
<li><a href="http://ankhsvn.open.collab.net/" rel="external nofollow noopener">AnhkSVN</a> - Subversion support for Visual Studio</li>
<li><a href="http://www.visualsvn.com/server/" rel="external nofollow noopener">VisualSVN Server</a> - Subversion Server for Windows</li>
<li><a href="https://github.com/" rel="external nofollow noopener">GitHub</a> / <a href="http://windows.github.com/" rel="external nofollow noopener">GitHub for Windows</a> - for the public facing aspects of our source code.</li>
</ul>
<h2 id="filedirectory-comparison">File/directory comparison</h2>
<ul>
<li><a href="http://winmerge.org/" rel="external nofollow noopener">WinMerge</a> - not much to say, it works and works well</li>
</ul>
<h2 id="backups">Backups</h2>
<ul>
<li><a href="http://cyotek.com/cyotek-copytools">Cyotek CopyTools</a> - we use this for offline backups of source code, assets and resources, documents, actually pretty much anything we generate; including backing up the backups!</li>
<li><del>Carbonite</del><a href="http://www.code42.com/crashplan/" rel="external nofollow noopener">CrashPlan</a> - another change for this year was to put aside the apathy and do away with Carbonites dreadful software onto something better. CrashPlan creates an online backup of the different offline backups that CopyTools does. If you've ever lost a harddisk before with critical data on it that's nowhere else, you'll have backups squirrelled away everywhere too!</li>
</ul>
<h2 id="technologies">Technologies</h2>
<p>[Not a tool but sticking it here anyway so I can see if <em>this</em> changes between years]</p>
<ul>
<li>For web work, mostly MVC, although I still have legacy WebForms applications to support. For desktop development, still Windows Forms. I never took to WPF and I'm not really interested in cutting 90% of our users off by switching to Metro apps. Hmmpf. Not much to say really, I don't get to use as much cutting edge stuff as I'd like to. Been working with Azure's PaaS recently, that's pretty cool.</li>
</ul>
<hr />
<p>Slightly larger list than I was expected, but even when I wrote this I was surprised at how little was in it... a lot of tools have faded by the wayside it seems. And I haven't mentioned tools I use but rarely, that seems a bit daft.</p>
<p>I wonder what I'll be using next year?</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/tools-we-use-2013-edition .
</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.comCyotek Add Projects Extension updated for Visual Studio 2013 RTMurn:uuid:96fe63e9-d6a4-4618-9a63-ce74189d09192014-10-14T16:36:41Z2013-10-18T16:43:31Z<p>In my <a href="/post/visual-studio-extension-for-adding-multiple-projects-to-a-solution">last post</a> I introduced <strong>Cyotek Add Projects</strong>, a
simple extension for Visual Studio that allowed you to add
multiple projects to a solution.</p>
<p>However, I'd left the VSIX manifest version at 11.0, meaning it
would only install on Visual Studio 2012. I've updated this so
that it <em>should</em> install on Visual Studio 2012 or higher -
certainly it's now installed on my fresh install of Visual
Studio 2013.</p>
<p>I've made no other code changes so I haven't updated the source
archive (I'll save that for the WPF overhaul!), but just updated
the VSIX file, which you can download below.</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-18 - First published</li>
<li>2014-10-14 - Updated to include Visual Studio Gallery and
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/cyotek-add-projects-extension-updated-for-visual-studio-2013-rtm .
</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.comAssembly Reference Scanner Sampleurn:uuid:b858a352-9a9b-49bf-be65-74e96525f9282012-10-06T19:07:32Z2012-10-06T19:07:32Z<p>Due to an issue with our setup programs, certain files weren't
getting updated if the version numbers hadn't changed. This led
to crashes if you upgraded from one version to another. I wrote
this tool as part of my diagnostic into why the programs were
crashing complaining of wrong version numbers. It uses a
reflection load only context to scan all assemblies in a folder,
and checks each assemblies references against each other to
ensure the versions match.</p>
<blockquote>
<p>Note: This tool doesn't support assemblies in the GAC, only
references to assemblies deployed in a folder. It is also a
tool wrote in a hurry to solve a particular problem and may
not fit your needs without modifications.</p>
</blockquote>
<p>This is an example of expected behaviour:</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ars-correct.png" class="gallery" title="A scan of Sitemap Creator showing all references correctly" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ars-correct.png" alt="A scan of Sitemap Creator showing all references correctly" decoding="async" loading="lazy" /></a><figcaption>A scan of Sitemap Creator showing all references correctly</figcaption></figure>
<p>And here's an example caused by upgrading one version of Sitemap
Creator with another with a faulty setup causing some files not
to be updated:</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/ars-invalid.png" class="gallery" title="When you run cyosmc.exe with these version mismatches, things are going to go wrong!" ><img src="https://images.cyotek.com/image/thumbnail/devblog/ars-invalid.png" alt="When you run cyosmc.exe with these version mismatches, things are going to go wrong!" decoding="async" loading="lazy" /></a><figcaption>When you run cyosmc.exe with these version mismatches, things are going to go wrong!</figcaption></figure>
<p>Full C# source for the tool is available from the download link
above.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2012-10-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/assembly-reference-scanner-sample .
</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.comImporting a SourceSafe database into Subversionurn:uuid:3772b868-449c-4b8d-a266-8762f54e02072011-07-03T12:59:37Z2011-07-03T12:59:37Z<p>In my <a href="/post/migrating-from-visual-sourcesafe-to-subversion">previous article</a> on migrating a VSS database into
SVN, I hadn't tried importing a VSS database into an existing
repository, which is something I found the original code
couldn't handle.</p>
<p>I rewrote the original tool to have a GUI front end in addition
to a console version, updated to allow importing into existing
repositories, and fixed a crash which would occur when trying to
import a file which couldn't be retrieved from SourceSafe. The
updated tool worked enough for my purposes, but hasn't been
extensively tested. However, as my three VSS databases are now
in SVN it's unlikely I'll be making further updates to the code.</p>
<p>The source code for the tool has therefore been uploaded as
<a href="http://vsstosvn.codeplex.com/" rel="external nofollow noopener">Open Source on CodePlex.com</a> in the hope that someone else
finds it useful. Some limited documentation is also available on
the CodePlex site, but if anyone has questions, please leave a
comment or <a href="https://cyotek.com/contact">contact us</a>.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2011-07-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/importing-a-sourcesafe-database-into-subversion .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comMigrating from Visual SourceSafe to Subversionurn:uuid:4f852a78-0e1c-4293-9469-7a62e8a321c92011-04-19T19:24:06Z2011-04-19T19:24:06Z<p>For years now, we've used Microsoft Visual SourceSafe (VSS) for
our source code control, but given that Microsoft dropped
support for it some time ago in favour of Team Foundation
Server, we've decided to switch VSS with an open source system.
This article describes our experiences with a test migration.</p>
<p>As I tend to prefer the client/server model I decided to trial
Subversion (SVN) over <a href="http://en.wikipedia.org/wiki/Comparison_of_revision_control_software" rel="external nofollow noopener">other systems</a> such as Git or Mercury.
I also want to be able to import the VSS databases containing
our current code, and the one with the legacy VB6 components we
used to offer.</p>
<h2 id="installing-svn">Installing SVN</h2>
<p>As SVN is a client/server system, you will need to install both
the SVN server and a client to connect to it. Or multiple
clients depending on what integration options you need. For this
migration test, I'll install everything locally, but for real
use you'll want to install the server software and repositories
on a server. You'll still need to install the client locally
however.</p>
<p>As SVN is open source, there are multiple clients and servers
out there - some free, some not so free.</p>
<h3 id="choosing-a-svn-server">Choosing a SVN Server</h3>
<p>I don't have a lot of experience with SVN - I've used
TortoiseSVN and AnkhSVN for client side. For the server, I
choose to go with <a href="http://www.visualsvn.com/server/" rel="external nofollow noopener">VisualSVN Server</a> as it promised to be
easy enough to install - and it is free (or at least the
standard version is). And surprisingly enough, it was - a few
simple clicks and the server was installed.</p>
<p>When you install you'll be asked if you want to use HTTP or
HTTPS - HTTPS will be more secure, but you'll need to verify (or
replace) the default certificate or the migration tool will
fail.</p>
<p>As our VSS user accounts mirror Windows users, I choose to
enable the Windows authentication option. As this is the free
version of VisualSVN, I have to use Basic (meaning you'll be
prompted to enter credentials), if you want Integrated (where
your Windows credentials are used automatically) you need to
purchase the Enterprise license.</p>
<h3 id="creating-a-repository">Creating a repository</h3>
<p>Next, I fired up VisualSVN Server Management console and created
a repository ready for the migration. To do this, right click
the Repositories node and choose <strong>Create New Repository</strong> from
the context menu. Enter the name of your repository, and check
the option if you want the default SVN structure created.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svn1.png" class="gallery" title="Creating a new SVN repository" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svn1.png" alt="Creating a new SVN repository" decoding="async" loading="lazy" /></a><figcaption>Creating a new SVN repository</figcaption></figure>
<p>In the next section, you'll need the URL of the repository that
you just created. If you didn't copy this from the Create
dialog, right click the node for your repository, and choose
<strong>Copy URL to Clipboard</strong> from the context menu.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svn2.png" class="gallery" title="Copying the URL of an existing SVN repository" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svn2.png" alt="Copying the URL of an existing SVN repository" decoding="async" loading="lazy" /></a><figcaption>Copying the URL of an existing SVN repository</figcaption></figure><h3 id="verifying-the-certificate">Verifying the certificate</h3>
<p>If you configure VisualSVN Server to use HTTPS, it seems to
automatically create a self-certified certificate. As it is not
issued by a trusted authority however, you'll run into problems
trying to actually use SVN. For example, running an SVN command
will issue the following error</p>
<blockquote>
<p>Error validating certificate for '&lt;servername&gt;': The
certificate is not issued by a trusted authority</p>
</blockquote>
<p>And running the migration tool will offer this:</p>
<blockquote>
<p>OPTIONS of '&lt;repository&gt;': Server certificate verification
failed: issuer is not trusted (https://&lt;servername&gt;)</p>
</blockquote>
<p>To work around this, we need to get SVN to permanently accept
the certificate, which can be done from the command line.</p>
<ul>
<li>Open a new command session, and browse to the bin subfolder of
your VisualSVN Server installation</li>
<li>Enter the command <code>svn ls &lt;repository&gt;</code> where
<code>&lt;repository&gt;</code> is the URL of your repository.</li>
<li>When the message is displayed informing you about the
certificate, press <strong>p</strong> followed by enter to accept it.</li>
</ul>
<p>This will allow you to use the command line tools, and will also
prevent the migration tool discussed below from crashing when
you try and use it.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svn3.png" class="gallery" title="Enabling a certificate for use with SVN" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svn3.png" alt="Enabling a certificate for use with SVN" decoding="async" loading="lazy" /></a><figcaption>Enabling a certificate for use with SVN</figcaption></figure><h3 id="gaining-access-to-the-repository-folder">Gaining access to the Repository folder</h3>
<p>When VisualSVN was installed and it created a folder for storing
repositories, it doesn't actually give access to that folder for
the current user. This is something else that will cause the
migration tool to fail. Using Windows 7, when trying to access
the folder, you are both told you don't have permission and have
a &quot;one click&quot; option to give you permission. For other operating
systems, you will probably have to edit the folder permissions
manually to grant yourself access.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svn6.png" class="gallery" title="Trying to access the Repository folder gives a permission error" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svn6.png" alt="Trying to access the Repository folder gives a permission error" decoding="async" loading="lazy" /></a><figcaption>Trying to access the Repository folder gives a permission error</figcaption></figure>
<p>Before performing the migration, ensure that you have access to
the repositories folder.</p>
<h3 id="choosing-a-svn-client">Choosing a SVN Client</h3>
<p>As mentioned above, the only SVN client I've used in the past
was <a href="http://tortoisesvn.tigris.org/" rel="external nofollow noopener">TortoiseSVN</a>. I've no doubt there's other clients out
there (while VisualSVN does have a <a href="http://www.visualsvn.com/visualsvn/" rel="external nofollow noopener">client</a>, you do need to
pay for it), but I decided to stick with this. As with the
VisualSVN Server, installation is a breeze. Irritatingly, you
are not able to change the installation directory.</p>
<h3 id="choosing-a-visual-studio-scc-provider">Choosing a Visual Studio SCC Provider</h3>
<p>TortoiseSVN is all well and good for managing from Windows
Explorer, but that isn't quite good enough. I need support in
the Visual Studio 2010 IDE, and for this I've gone with
<a href="http://ankhsvn.open.collab.net/" rel="external nofollow noopener">AnkhSVN</a>. As with the other two products, installation of
AnkhSVN was quick and painless.</p>
<h2 id="preparing-sourcesafe-for-migration">Preparing SourceSafe for migration</h2>
<ul>
<li><strong>Ensure all files are checked in</strong></li>
<li>If you are using SourceSafe 6, install the final service pack
for Visual Studio 6</li>
<li>If you are using SourceSafe 2005 (version 8), you may wish to
install <a href="http://www.microsoft.com/downloads/en/details.aspx?familyid=8A1A68D8-DB11-417C-91AD-02AAB484776B&amp;displaylang=en" rel="external nofollow noopener">this patch</a> from Microsoft.</li>
<li>Run the analyze.exe tool to ensure your VSS database contains
no errors</li>
</ul>
<h2 id="migrating-a-sourcesafe-database">Migrating a SourceSafe Database</h2>
<p>As neither SVN nor VSS have appropriate import/export
functionality, we need to turn to the community for help.
Fortunately, there's a tool you can download from
<a href="http://www.poweradmin.com/SOURCECODE/VSSMIGRATE.ASPX" rel="external nofollow noopener">PowerAdmin</a>. In addition to the original tool there's also
user supplied contributions - I went with <em><a href="http://www.poweradmin.com/SOURCECODE/vssmigrate.brian.zip" rel="external nofollow noopener">Update 5</a></em>, which
is the latest at the time of writing and is supplied as C#
source developed using Visual Studio 2008. While other versions
using C++ are available, the remainder of this article discusses
the C# version.</p>
<h2 id="setting-up-the-migration">Setting up the migration</h2>
<p>Before you run the migration, you need to configure a number of
parameters. Open up the tool downloaded from the link above in
Visual Studio - if you're using Visual Studio 2010 you'll be
asked to upgrade the project.</p>
<p>The first thing you'll need to do if you are running a 64bit OS
is to configure the project to be compiled as a 32bit
application otherwise the first thing you're going to get is a
nasty <a href="/post/error-80040154-when-trying-to-use-sourcesafe-via-interop-on-64bit-windows">COM error</a>.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svn4.png" class="gallery" title="The migration tool must be run as a 32bit process in order for SourceSafe interop to work" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svn4.png" alt="The migration tool must be run as a 32bit process in order for SourceSafe interop to work" decoding="async" loading="lazy" /></a><figcaption>The migration tool must be run as a 32bit process in order for SourceSafe interop to work</figcaption></figure>
<p>Next, open up <code>app.config</code> - there's lots of values in here to
change.</p>
<ul>
<li><code>VSSDIR</code> - name of the folder where your VSS database is
located (the folder which contains <code>srcsafe.ini</code>)</li>
<li><code>VSSPROJ</code> - Name of the project to import. According to the
tools documentation, you shouldn't specify the SourceSafe
root, but instead the initial children. (Meaning if you have
multiple projects at the root level you'll have to run this
tool multiple times.)</li>
<li><code>VSSUSER</code> - VSS login user name</li>
<li><code>VSSPASSWORD</code> - VSS login password</li>
<li><code>SVNUSER</code> - SVN login name</li>
<li><code>SVNPASSWORD</code> - SVN login password</li>
<li><code>SVNURL</code> - URL of your repository</li>
<li><code>SVNPROJ</code> - name of the SVN project to create</li>
<li><code>SVNREVPROPSPATH</code> - the local folder where your repository
will be stored</li>
<li><code>WORKDIR</code> - temporary directory where local files will be
extracted. Make sure this folder actually exists before
running the tool! In addition, the tool will create a folder
named _migrate - make sure this folder doesn't exist or the
tool will crash when trying to create folders if they exist.</li>
</ul>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svn5.png" class="gallery" title="Configuring the migration settings" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svn5.png" alt="Configuring the migration settings" decoding="async" loading="lazy" /></a><figcaption>Configuring the migration settings</figcaption></figure>
<p>With this information specified you're good to go - just run the
solution. And go make a cup of tea, you'll be waiting a while,
depending on the size of the database. I've tested with two
databases, one 40MB and one 4GB - the former took an hour, the
later 26 hours for a partial import.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svn7.png" class="gallery" title="Notification after a successful migration" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svn7.png" alt="Notification after a successful migration" decoding="async" loading="lazy" /></a><figcaption>Notification after a successful migration</figcaption></figure>
<p>Sadly, whilst the PowerAdmin migration tool supports comments,
it doesn't seem to support labels, so if your database uses
these they won't be present.</p>
<h2 id="verifying-the-migration">Verifying the migration</h2>
<p>On returning to the VisualSVN Server console, it wasn't showing
the new projects - pressing F5 to refresh the list solved that,
and I could see all the projects that had been imported. So far
so good. Now time to check a file.</p>
<p>And this is where VisualSVN fell flat - it seems that you can
only view the latest version of a file. So with that said, I
opened Windows Explorer, right clicked the file list and chose
<strong>TortoiseSVN</strong> | <strong>Repro-browser</strong> from the context menu. I
then search for a file which I knew had been checked in numerous
times, and was able to view it's full history... even the dates
were correct. Comparing a few random revisions of the file
looked fine too.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svn8.png" class="gallery" title="Viewing the SVN log for a file" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svn8.png" alt="Viewing the SVN log for a file" decoding="async" loading="lazy" /></a><figcaption>Viewing the SVN log for a file</figcaption></figure>
<p>I could see the initial comments from when the file was first
added, but as I suspected no label comments were present.</p>
<p>The next thing to do would be to compare the full repository
against the SourceSafe working copy.</p>
<p>To do this, I returned to the root project in the Repository
Browser, right clicked it and choose Checkout. I entered a
folder name (make sure this is something which doesn't already
exist) and then checked out the entire trunk. After that was all
checked out I compared the two folders using
<a href="http://winmerge.org/" rel="external nofollow noopener">WinMerge</a>.</p>
<p>The comparison looked good - source code was identical, except
for the Visual Studio project files - the migration tool
automatically removes the VSS bindings from them which is a nice
touch.</p>
<p>Once you open your solution, you need to tell Visual Studio to
use AnkhSVN. To do this, open the Visual Studio Options dialog
and select the <strong>Source Control</strong> | <strong>Plug-in Selection</strong>
section. Simply select AnkhSVN from the dropdown list and you
can then use SVN from within the Visual Studio IDE, in addition
to Windows Explorer via TortoiseSVN.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/svn9.png" class="gallery" title="Configuring Visual Studio to use AnkhSVN" ><img src="https://images.cyotek.com/image/thumbnail/devblog/svn9.png" alt="Configuring Visual Studio to use AnkhSVN" decoding="async" loading="lazy" /></a><figcaption>Configuring Visual Studio to use AnkhSVN</figcaption></figure><h2 id="things-that-can-go-wrong">Things that can go wrong</h2>
<p>Although the migration of our .NET code was successful, when
testing with the database containing the old VB6 components we
used to offer, the migration would crash for two different
reasons:</p>
<p>The first was an occasional (and somewhat random) Access Denied
when trying to commit files. Retrying the commit always worked,
but it was at this point that I found the <code>AUTORETRY</code> setting
isn't actually used. I manually updated the migration tool to
retry commits in this case.</p>
<p>The second one would be the following error when trying to get a
file from VSS.</p>
<blockquote>
<p>SourceSafe was unable to finish writing a file. Check your
available disk space, and ask the administrator to analyse
your SourceSafe database.</p>
</blockquote>
<p><a href="http://www.microsoft.com/downloads/en/details.aspx?familyid=8A1A68D8-DB11-417C-91AD-02AAB484776B&amp;displaylang=en" rel="external nofollow noopener">This patch</a> apparently has a fix for this but I haven't
tried it yet.</p>
<p>Once I have resolved these errors I'll post an updated version
of the migration tool. With workarounds in place, the migration
still completed successfully so I'm cautiously optimistic for
updating the tool and doing a true migration and switch.</p>
<h2 id="conclusion">Conclusion</h2>
<p>It's still a little early for me to say whether we'll stick with
SVN or try something else. While I never did like SourceSafe's
habit of cluttering directories with .scc files, I am even less
enamoured of the .svn directories and the additional disk space
they take. But I'm sure the fact that it's far less likely that
a repository will be corrupt!</p>
<p>Another minor annoyance; we currently have Visual Studio set to
check in each time the solution is closed. This functionality
doesn't exist in AnkhSVN and so a manual commit will be
required. While there are arguments both for and against this
type of functionality, I happen to prefer having the code
constantly updated the central store, regardless of whether or
not it actually builds.</p>
<p>The main problem is currently the loss of labels - tracking down
the source code for a particular product build currently looks
like a fairly large problem. It may be that the migration tool
can be modified to support this, which I'll be looking into over
the coming days.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2011-04-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/migrating-from-visual-sourcesafe-to-subversion .
</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.comError 80040154 when trying to use SourceSafe via interop on 64bit Windowsurn:uuid:ca0594a5-1883-4408-8727-a88053519b9d2010-01-22T21:53:48Z2010-01-19T19:52:31Z<p>We recently moved to Windows 7, and I decided to go with the
64bit version for my machine. One of the utilities we use is a
small tool for adding folders to Visual SourceSafe (why we
haven't moved to another SCC provider yet is another question!)
via the SourceSafeTypeLib interop dll. However, I was most
annoyed when it wouldn't work on my machine, the following
exception message would be displayed:</p>
<blockquote>
<p>Retrieving the COM class factory for component with CLSID
{783CD4E4-9D54-11CF-B8EE-00608CC9A71F} failed due to the
following error: 80040154.</p>
</blockquote>
<p>By default, .NET applications run using the CLR that matches
your operating system, ie x64 on Windows 64bit, and x86 on
Windows 32bit. I found that if I change the <em>platform target</em>
from <em>Any CPU</em> to <em>x86</em> (you can find this on the <em>Build</em> tab of
your project's properties) to force it to use the 32bit CLR,
then the interop would succeed and the utility would work again.</p>
<p>Hopefully this will be of use for the next person with this
problem. Meanwhile I'm still thinking about a new SCC provider
:)</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-01-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/error-80040154-when-trying-to-use-sourcesafe-via-interop-on-64bit-windows .
</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