<?xml version="1.0" encoding="utf-8"?><rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0"><channel><title>Key Mapper Developer Blog</title><description>Post feed for the Key Mapper Developer Blog</description><copyright>Copyright (C) Stuart Dunkeld 2008. All rights reserved.</copyright><generator>ASP.Net 3.5 Syndication classes</generator><a10:link title="Key Mapper Home" href="http://justkeepswimming.net/keymapper" /><item><guid isPermaLink="false">9</guid><author>keymappersupport@gmail.com</author><title>Strongly typed Windows Forms databinding</title><description>&lt;p&gt;The default way to create Windows Forms DataBindings is like this:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;this&lt;/span&gt;.txtClientName.DataBindings.Add("Text", client, "Name");&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I wanted to be able to specify the properties I want to use with expressions to give me compile-time validation and refactoring - like this:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;this&lt;/span&gt;.txtClientName.DataBindings.Add&amp;lt;Client&amp;gt;(CurrentClient, tb =&amp;gt; tb.Text, c =&amp;gt; c.Name);
&lt;span class="kwrd"&gt;this&lt;/span&gt;.cboGroup.DataBindings.Add&amp;lt;Client, ComboBox&amp;gt;(CurrentClient, cbo =&amp;gt; cbo.SelectedItem, c =&amp;gt; c.Group);&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;/p&gt;
&lt;p&gt;These extension methods for data binding allow me to do just that:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; DataBindingsExtensionMethods
{
    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; Binding Add&amp;lt;T&amp;gt;
        (&lt;span class="kwrd"&gt;this&lt;/span&gt; ControlBindingsCollection dataBindings,
         &lt;span class="kwrd"&gt;object&lt;/span&gt; dataSource,
         Expression&amp;lt;Func&amp;lt;Control, &lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt;&amp;gt; controlExpression,
         Expression&amp;lt;Func&amp;lt;T, &lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt;&amp;gt; objectExpression)
    {
        &lt;span class="kwrd"&gt;return&lt;/span&gt; Add(dataBindings, dataSource, controlExpression, objectExpression, &lt;span class="kwrd"&gt;false&lt;/span&gt;);
    }

    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; Binding Add&amp;lt;T&amp;gt;
        (&lt;span class="kwrd"&gt;this&lt;/span&gt; ControlBindingsCollection dataBindings,
         &lt;span class="kwrd"&gt;object&lt;/span&gt; dataSource,
         Expression&amp;lt;Func&amp;lt;Control, &lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt;&amp;gt; controlExpression,
         Expression&amp;lt;Func&amp;lt;T, &lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt;&amp;gt; objectExpression,
         &lt;span class="kwrd"&gt;bool&lt;/span&gt; formattingEnabled)
    {
        &lt;span class="kwrd"&gt;string&lt;/span&gt; controlPropertyName = ProcessExpression(controlExpression.Body);
        &lt;span class="kwrd"&gt;string&lt;/span&gt; bindingTargetName = ProcessExpression(objectExpression.Body);

        &lt;span class="kwrd"&gt;return&lt;/span&gt; dataBindings.Add(controlPropertyName, dataSource, bindingTargetName, formattingEnabled);
    }

    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; Binding Add&amp;lt;T, K&amp;gt;
        (&lt;span class="kwrd"&gt;this&lt;/span&gt; ControlBindingsCollection dataBindings,
         &lt;span class="kwrd"&gt;object&lt;/span&gt; dataSource,
         Expression&amp;lt;Func&amp;lt;K, &lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt;&amp;gt; controlExpression,
         Expression&amp;lt;Func&amp;lt;T, &lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt;&amp;gt; objectExpression)
    {
        &lt;span class="kwrd"&gt;return&lt;/span&gt; Add(dataBindings, dataSource, controlExpression, objectExpression, &lt;span class="kwrd"&gt;false&lt;/span&gt;);
    }

    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; Binding Add&amp;lt;T, K&amp;gt;
        (&lt;span class="kwrd"&gt;this&lt;/span&gt; ControlBindingsCollection dataBindings,
         &lt;span class="kwrd"&gt;object&lt;/span&gt; dataSource,
         Expression&amp;lt;Func&amp;lt;K, &lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt;&amp;gt; controlExpression,
         Expression&amp;lt;Func&amp;lt;T, &lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt;&amp;gt; objectExpression,
         &lt;span class="kwrd"&gt;bool&lt;/span&gt; formattingEnabled
        )
    {
        &lt;span class="kwrd"&gt;string&lt;/span&gt; controlPropertyName = ProcessExpression(controlExpression.Body);
        &lt;span class="kwrd"&gt;string&lt;/span&gt; bindingTargetName = ProcessExpression(objectExpression.Body);

        &lt;span class="kwrd"&gt;return&lt;/span&gt; dataBindings.Add(controlPropertyName, dataSource, bindingTargetName, formattingEnabled);
    }

    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; ProcessExpression(Expression expression)
    {
        &lt;span class="kwrd"&gt;string&lt;/span&gt; propertyName;

        &lt;span class="kwrd"&gt;if&lt;/span&gt; (expression &lt;span class="kwrd"&gt;is&lt;/span&gt; MemberExpression)
        {
            propertyName = ((MemberExpression) (expression)).Member.Name;
        }
        &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;span class="kwrd"&gt;if&lt;/span&gt; (expression &lt;span class="kwrd"&gt;is&lt;/span&gt; UnaryExpression)
        {
            propertyName = ((MemberExpression) ((UnaryExpression) (expression)).Operand).Member.Name;
        }
        &lt;span class="kwrd"&gt;else&lt;/span&gt;
        {
            &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; InvalidOperationException(
                &lt;span class="str"&gt;"Unknown expression type error in DataBindingsExtensionMethods: "&lt;/span&gt; + expression.GetType());
        }
        &lt;span class="kwrd"&gt;return&lt;/span&gt; propertyName;
    }
}&lt;/pre&gt;</description><a10:link title="Key Mapper Blog" href="http://justkeepswimming.net/keymapper/default.aspx?p=9.aspx" /></item><item><guid isPermaLink="false">8</guid><author>keymappersupport@gmail.com</author><title>Koogra sample</title><description>&lt;p&gt;&lt;a href="http://sourceforge.net/projects/koogra/"&gt;Koogra&lt;/a&gt; is an open-source MIT Licensed library for reading Excel files: it's latest release&amp;nbsp; supports Excel 2007 format.&lt;br /&gt;&lt;br /&gt; I use it for an app which imports Excel spreadsheets: using the Office interop libraries is awkward and brittle, and it has to work with an existing install of Office which you can't control versioning or upgrading. So, Koogra. I don't need to write to the spreadsheets, just read them.&lt;br /&gt;&lt;br /&gt; There are at least three distinct syntaxes in Koogra:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Excel 2007 syntax&lt;/li&gt;
&lt;li&gt;Excel 2003 syntax (more accurately, 97-2003)&lt;/li&gt;
&lt;li&gt;The interface syntax which links the two together&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For example, finding a worksheet by name:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;using Net.SourceForge.Koogra;
using Excel2003 = Net.SourceForge.Koogra.Excel; 
using Excel2007 = Net.SourceForge.Koogra.Excel2007; 

// The Excel 2003 syntax for opening a worksheet 
Excel2003.Workbook workbook = new Excel2003.Workbook(fileName); 
Excel2003.Worksheet worksheet = workbook.Sheets.GetByName(sheetName); 

// The Excel 2007 syntax 
Excel2007.Workbook workbook = new Excel2007.Workbook(fileName); 
Excel2007.Worksheet worksheet = workbook.GetWorksheetByName(sheetName);

// The interface syntax 
IWorkbook workbook; 
if (Path.GetExtension(fileName).ToLower() == ".xlsx") 
{ 
    workbook = new Excel2007.Workbook(fileName); 
} 
else 
{ 
    workbook = new Excel97.Workbook(fileName);&lt;br /&gt;}

IWorksheet sheet = workbook.Worksheets.GetWorksheetByName(sheetName);
&lt;/pre&gt;
&lt;p&gt;The only one I am interested in is the interface syntax, as I have to support both file types, so here is a basic code sample for reading some data from a given sheet in a given file.&lt;/p&gt;
&lt;pre class="csharpcode"&gt;namespace JustKeepSwimming.Net.Koogra.Example
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using Net.SourceForge.Koogra;
    using Excel97 = Net.SourceForge.Koogra.Excel;
    using Excel2007 = Net.SourceForge.Koogra.Excel2007;

    public class DataImporter
    {
        public List ImportData(IImportData importData, string fileName)
        {
            // The Koogra Excel library uses 0 based counting
            // unlike some others
            List results = new List();

            IWorkbook workbook;

            if (Path.GetExtension(fileName).ToLower() == ".xlsx")
            {
                workbook = new Excel2007.Workbook(fileName);
            }
            else
            {
                workbook = new Excel97.Workbook(fileName);
            }

            IWorksheets sheets = workbook.Worksheets;
            IWorksheet sheet = null;

            if (string.IsNullOrEmpty(importData.SheetName) == false)
            {
                // GetWorksheetByName returns null if name not found..
                sheet = sheets.GetWorksheetByName(importData.SheetName);
            }

            // .. use the first sheet in the workbook.
            if (sheet == null)
            {
                sheet = sheets.GetWorksheetByIndex(0);
            }

            uint lastRow = sheet.LastRow;
            if (importData.IsLastRowTotal)
            {
                lastRow--;
            }

            for (ushort i = (ushort)(importData.StartRow - 1); i &amp;lt;= lastRow; i++)
            {
                IRow row = sheet.Rows.GetRow(i);
                string name = GetCellValue(row, (uint)importData.NameColumn);
                string amountString = GetCellValue(row, (uint)importData.AmountColumn);

                // Parse as double so if Excel returns 7.0000000E-2
                // for 0.07 it's still recognized.
                double amountDouble;
                if (double.TryParse(amountString, out amountDouble))
                {
                    ImportItem item = new ImportItem(name, amountDouble);
                    results.Add(item);
                }
            }
            return results;
        }

        private string GetCellValue(IRow cells, uint column)
        {
            if (cells != null &amp;amp;&amp;amp; cells.GetCell(column) != null)
            {
                object value = cells.GetCell(column).Value;
                if (value != null)
                {
                    return value.ToString().Trim();
                }
            }
            return string.Empty;
        }

        private string GetFormattedValue(IRow cells, uint column)
        {
            if (cells != null &amp;amp;&amp;amp; cells.GetCell(column) != null)
            {
                return cells.GetCell(column).GetFormattedValue();
            }
            return string.Empty;
        }
    }
}
&lt;/pre&gt;</description><a10:link title="Key Mapper Blog" href="http://justkeepswimming.net/keymapper/default.aspx?p=8.aspx" /></item><item><guid isPermaLink="false">7</guid><author>keymappersupport@gmail.com</author><title>User Mappings and Windows 7</title><description>&lt;p&gt;Windows 7 (the RC build, 7100) no longer implements &lt;a href="../?p=1"&gt;per-user key mappings&lt;/a&gt;: as Microsoft &lt;a href="http://support.microsoft.com/kb/310595"&gt;never documented it anyway&lt;/a&gt;, removing it is their perogative. This is unfortunate, as it's back to the days of Windows 2000 where key mappings apply to all users without possibility of exception and require administrative privileges to be created or removed. I can only speculate whether they were dropped because they were unsupported, or because they just wouldn't work with Fast User Switching, or accidentally, or they were in some way incompatible with the Windows 7 code.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;It's a pain for me as KeyMapper expects Windows 7 to behave like Vista (*) and Windows Server 2008 and to implement scancode mappings in &lt;code class="regkey"&gt;HKEY_CURRENT_USER\Keyboard Layout&lt;/code&gt; and so the program sets the mappings, we log off and on again, and Windows 7 then ignores the scancode mappings, and the key remapping fails and I get a very polite support email with 'broken' or 'doesn't work' in the subject line.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;It's still possible to set mappings, though, in Key Mapper but you have to tell the program to 'show / boot mappings' from the Mappings menu and then authorise the Registry change and then reboot your computer.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;So it looks like some new behaviour for KeyMapper under Windows 7 will be required. I might maybe just try and get a recent build of Windows 7 just to see if &lt;em&gt;just maybe&lt;/em&gt; the RTM code will implement per-user key mappings before I even think about what to do (and how to deal with UAC).&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;(*) Because it's 'later' than Vista: Windows 7 was only a gleam when Key Mapper 1.0 was released.&lt;/p&gt;</description><a10:link title="Key Mapper Blog" href="http://justkeepswimming.net/keymapper/default.aspx?p=7.aspx" /></item><item><guid isPermaLink="false">5</guid><author>keymappersupport@gmail.com</author><title>UK Bank Holidays and Working Days in C#</title><description>&lt;p&gt;I needed to calculate working days for a project: easy enough to discount Saturdays and Sundays, but UK Bank Holidays a bit trickier. I looked for existing code but by the time I got to page three of a Google search I realised I was going to have to write my own (ironic, really, as this will probably end out on &lt;a href="http://www.google.com/search?q=UK+Bank+Holidays+and+Working+Days+in+C%23&amp;amp;hl=en&amp;amp;start=800&amp;amp;sa=N"&gt;page 64&lt;/a&gt;) so I did it on my own time so I can post it here in the hope of perhaps saving someone else the trouble.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Apart from Easter, they're all straightforward enough - first and last Monday in May, last Monday in August, checking what day of the week Christmas Day, Boxing Day and New Year's Day fall on and what the substitute days will be: I got the code to do Easter Sunday from &lt;a href="http://bloggingabout.net/blogs/jschreuder/archive/2005/06/24/7019.aspx"&gt;here&lt;/a&gt; and from that getting the Good Friday and Easter Monday holidays are trivial.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Before the code listing, a couple of comments:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There is no error handling - i.e. it will break for a year value less than 1 or greater than 9999&lt;/li&gt;
&lt;li&gt;If one-off bank holidays are declared, the program won't take account of them. If you absolutely must count every bank holiday including one-offs, you're better off storing the values in a database, allowing extras to be added, and querying the database for the holidays (you could use this program to populate the data)&lt;/li&gt;
&lt;li&gt;I've tested them against the new few years of published Bank Holidays, which is good enough for me right now: this project has to be delivered, and as soon as possible. They may not be perfect for all scenarios. The code may nor be correct.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;This is the bank holiday code:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; JustKeepSwimming.Net&lt;br /&gt;{&lt;br /&gt;    &lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;br /&gt;    &lt;span class="kwrd"&gt;using&lt;/span&gt; System.Collections.Generic;&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; BankHolidays&lt;br /&gt;    {&lt;br /&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; List&amp;lt;DateTime&amp;gt; GetBankHolidays(&lt;span class="kwrd"&gt;int&lt;/span&gt; year)&lt;br /&gt;        {&lt;br /&gt;                return GetBankHolidaysForYear(year);&lt;br /&gt;        }&lt;br /&gt;         &lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; List&amp;lt;DateTime&amp;gt; GetBankHolidaysForYear(&lt;span class="kwrd"&gt;int&lt;/span&gt; year)&lt;br /&gt;        {&lt;br /&gt;            List&amp;lt;DateTime&amp;gt; holidays = &lt;span class="kwrd"&gt;new&lt;/span&gt; List&amp;lt;DateTime&amp;gt;&lt;br /&gt;                {&lt;br /&gt;                    NewYearsDay(year),&lt;br /&gt;                    GoodFriday(year),&lt;br /&gt;                    EasterMonday(year),&lt;br /&gt;                    MayDayBankHoliday(year),&lt;br /&gt;                    SpringBankHoliday(year),&lt;br /&gt;                    SummerBankHoliday(year),&lt;br /&gt;                    ChristmasDay(year),&lt;br /&gt;                    BoxingDay(year)&lt;br /&gt;                };&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; holidays;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; DateTime ChristmasDay(&lt;span class="kwrd"&gt;int&lt;/span&gt; year)&lt;br /&gt;        {&lt;br /&gt;            DateTime start = &lt;span class="kwrd"&gt;new&lt;/span&gt; DateTime(year, 12, 25);&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (start.DayOfWeek == DayOfWeek.Saturday)&lt;br /&gt;            {&lt;br /&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; start.AddDays(2);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (start.DayOfWeek == DayOfWeek.Sunday)&lt;br /&gt;            {&lt;br /&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; start.AddDays(1);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; start;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; DateTime BoxingDay(&lt;span class="kwrd"&gt;int&lt;/span&gt; year)&lt;br /&gt;        {&lt;br /&gt;            DateTime start = &lt;span class="kwrd"&gt;new&lt;/span&gt; DateTime(year, 12, 26);&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (start.DayOfWeek == DayOfWeek.Saturday)&lt;br /&gt;            {&lt;br /&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; start.AddDays(2);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (start.DayOfWeek == DayOfWeek.Sunday)&lt;br /&gt;            {&lt;br /&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; start.AddDays(2); &lt;span class="rem"&gt;// The Monday will be the substitute day for Christmas Day&lt;/span&gt;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (start.DayOfWeek == DayOfWeek.Monday)&lt;br /&gt;            {&lt;br /&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; start.AddDays(1); &lt;span class="rem"&gt;// The Monday will still be the substitute day for Christmas Day&lt;/span&gt;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; start;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; DateTime NewYearsDay(&lt;span class="kwrd"&gt;int&lt;/span&gt; year)&lt;br /&gt;        {&lt;br /&gt;            DateTime start = &lt;span class="kwrd"&gt;new&lt;/span&gt; DateTime(year, 1, 1);&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (start.DayOfWeek == DayOfWeek.Saturday)&lt;br /&gt;            {&lt;br /&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; start.AddDays(2);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (start.DayOfWeek == DayOfWeek.Sunday)&lt;br /&gt;            {&lt;br /&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; start.AddDays(1);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; start;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; DateTime GoodFriday(&lt;span class="kwrd"&gt;int&lt;/span&gt; year)&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; EasterSunday(year).AddDays(-2);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; DateTime EasterMonday(&lt;span class="kwrd"&gt;int&lt;/span&gt; year)&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; EasterSunday(year).AddDays(1);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; DateTime SpringBankHoliday(&lt;span class="kwrd"&gt;int&lt;/span&gt; year)&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="rem"&gt;// Last Monday in May&lt;/span&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; LastDayOfTheWeekInMonth(year, DayOfWeek.Monday, 5);&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; DateTime SummerBankHoliday(&lt;span class="kwrd"&gt;int&lt;/span&gt; year)&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="rem"&gt;// last Monday in August&lt;/span&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; LastDayOfTheWeekInMonth(year, DayOfWeek.Monday, 8);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; DateTime LastDayOfTheWeekInMonth(&lt;span class="kwrd"&gt;int&lt;/span&gt; year, DayOfWeek day, &lt;span class="kwrd"&gt;int&lt;/span&gt; month)&lt;br /&gt;        {&lt;br /&gt;            DateTime start = FirstDayOfTheWeekInMonth(year, day, month);&lt;br /&gt;            DateTime lastDayInMonth = &lt;span class="kwrd"&gt;new&lt;/span&gt; DateTime(year, month, DateTime.DaysInMonth(year, month));&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;while&lt;/span&gt; (start.AddDays(6) &amp;lt; lastDayInMonth)&lt;br /&gt;            {&lt;br /&gt;                start = start.AddDays(7);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; start;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; DateTime FirstDayOfTheWeekInMonth(&lt;span class="kwrd"&gt;int&lt;/span&gt; year, DayOfWeek day, &lt;span class="kwrd"&gt;int&lt;/span&gt; month)&lt;br /&gt;        {&lt;br /&gt;            DateTime firstOfMonth = &lt;span class="kwrd"&gt;new&lt;/span&gt; DateTime(year, month, 1);&lt;br /&gt;            DateTime firstDay = firstOfMonth.AddDays((&lt;span class="kwrd"&gt;int&lt;/span&gt;) day - (&lt;span class="kwrd"&gt;int&lt;/span&gt;) firstOfMonth.DayOfWeek);&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (firstDay &amp;lt; firstOfMonth)&lt;br /&gt;            {&lt;br /&gt;                firstDay = firstDay.AddDays(7);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; firstDay;&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; DateTime MayDayBankHoliday(&lt;span class="kwrd"&gt;int&lt;/span&gt; year)&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="rem"&gt;// First Monday in May&lt;/span&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; FirstDayOfTheWeekInMonth(year, DayOfWeek.Monday, 5);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; DateTime EasterSunday(&lt;span class="kwrd"&gt;int&lt;/span&gt; year)&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="rem"&gt;// Tidied up a little from code at &lt;/span&gt;&lt;br /&gt;            &lt;span class="rem"&gt;// http://bloggingabout.net/blogs/jschreuder/archive/2005/06/24/7019.aspx&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;int&lt;/span&gt; g = year % 19;&lt;br /&gt;            &lt;span class="kwrd"&gt;int&lt;/span&gt; c = year / 100;&lt;br /&gt;            &lt;span class="kwrd"&gt;int&lt;/span&gt; h = (c - c / 4 - (8 * c + 13) / 25 + 19 * g + 15) % 30;&lt;br /&gt;            &lt;span class="kwrd"&gt;int&lt;/span&gt; i = h - h / 28 * (1 - h / 28 * (29 / (h + 1)) * ((21 - g) / 11));&lt;br /&gt;            &lt;span class="kwrd"&gt;int&lt;/span&gt; day = i - ((year + year / 4 + i + 2 - c + c / 4) % 7) + 28;&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;int&lt;/span&gt; month = 3;&lt;br /&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (day &amp;gt; 31)&lt;br /&gt;            {&lt;br /&gt;                month++;&lt;br /&gt;                day -= 31;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; DateTime(year, month, day);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Given that, the working day calculation seems easy enough, although the implementation will vary depending on specific requirements: Do you start from the start date, or end at the end? Is your count inclusive or exclusive or neither? (i.e. does the starting day and/or ending day or neither  count in the calculation?)&lt;/p&gt;
&lt;p&gt;&lt;br /&gt; For this example implementation, the start date does not count, and it is  inclusive of the end date.&lt;br /&gt;&lt;br /&gt;As the time of the dates is immaterial - it doesn't affect the result - we discard it, then add a minute and a day to start, and two minutes to end.&lt;br /&gt;This avoids issues with DateTimes not having a time, and calculates correctly for the parameters of this implementation.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; GetWorkingDays(DateTime start, DateTime end)&lt;br /&gt;{&lt;br /&gt;     start = start.Date.AddDays(1).AddMinutes(1);&lt;br /&gt;     end = end.Date.AddMinutes(2);&lt;br /&gt;&lt;br /&gt;     int workingDays = 0;&lt;br /&gt;     int currentYear = start.Year;&lt;br /&gt;     List&amp;lt;DateTime&amp;gt; holidays = BankHolidays.GetBankHolidays(currentYear);&lt;br /&gt;     while (start &amp;lt; end)&lt;br /&gt;        {&lt;br /&gt;            if (start.DayOfWeek != DayOfWeek.Saturday&lt;br /&gt;                 &amp;amp;&amp;amp; start.DayOfWeek != DayOfWeek.Sunday&lt;br /&gt;                 &amp;amp;&amp;amp; holidays.Contains(start.Date) == false)&lt;br /&gt;                  {&lt;br /&gt;                      workingDays++;&lt;br /&gt;                  }&lt;br /&gt;               start = start.AddDays(1);&lt;br /&gt;               if (start.Year != currentYear)&lt;br /&gt;                {&lt;br /&gt;                    currentYear = start.Year;&lt;br /&gt;                    holidays = BankHolidays.GetBankHolidays(currentYear);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            return workingDays;&lt;br /&gt;}&lt;/pre&gt;</description><a10:link title="Key Mapper Blog" href="http://justkeepswimming.net/keymapper/default.aspx?p=5.aspx" /></item><item><guid isPermaLink="false">3</guid><author>keymappersupport@gmail.com</author><title>Toggling Insert on a keyboard with no Insert key</title><description>&lt;p&gt;If you find yourself using a keyboard without an Insert key (eg recent Mac keyboards) but Overtype mode has enabled itself somehow, then it's useful to remember that the 0 key on the Numberpad is a surrogate Insert key, but only when Num Lock is turned off. If you have NumLock set On and the NumLock key disabled, though, this is a small problem: KeyMapper lets you toggle the value of Num Lock from the Toggle Lock Key menu, so you can set it off temporarily, switch back to Insert mode using Numberpad/0, then toggle Num Lock on again.&lt;/p&gt;</description><a10:link title="Key Mapper Blog" href="http://justkeepswimming.net/keymapper/default.aspx?p=3.aspx" /></item><item><guid isPermaLink="false">4</guid><author>keymappersupport@gmail.com</author><title>Removing a watermark from a Word document in C#</title><description>&lt;p&gt;If you have a Word Document based on a template with a watermark on it and you want to remove the watermark in the new document, calling the WordBasic RemoveWatermark method seems the easiest way to go: except it's a COM object so you have to use a bit of late-binding trickery..&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;string templateLocation = "c:\template-dir\template.dot";&lt;/p&gt;
&lt;p&gt;object wordFalse = false;&lt;/p&gt;
&lt;p&gt;object wordTemplateLocation = (object)templateLocation;&lt;br /&gt;object wordNewDocument = (object)Word.WdNewDocumentType.wdNewBlankDocument;&lt;/p&gt;
&lt;p&gt;wordApp = new Word.ApplicationClass();  &lt;br /&gt;&lt;br /&gt;doc = wordApp.Documents.Add(ref wordTemplateLocation, ref wordFalse, ref wordNewDocument, ref wordFalse);  &lt;br /&gt;&lt;br /&gt;doc.ActiveWindow.Activate(); // Otherwise Word complains a document is not active&lt;br /&gt; &lt;br /&gt;object oWordBasic = wordApp.WordBasic;  &lt;br /&gt;&lt;br /&gt;oWordBasic.GetType().InvokeMember("RemoveWatermark", BindingFlags.InvokeMethod, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   Type.DefaultBinder, oWordBasic, new object[]{});&lt;/p&gt;</description><a10:link title="Key Mapper Blog" href="http://justkeepswimming.net/keymapper/default.aspx?p=4.aspx" /></item><item><guid isPermaLink="false">2</guid><author>keymappersupport@gmail.com</author><title>Remapping Pause and Num Lock</title><description>&lt;p&gt;The Num Lock key (along with the Insert key) is one of the keys most likely to be disabled by any scancode mapping program: it is hardly ever used and the effects of accidental pressing it are confusing and annoying, to users and support teams. There are &lt;a href="http://support.microsoft.com/search/default.aspx?catalog=LCID%3D1033&amp;amp;query=%22num+lock%22&amp;amp;mode=r"&gt;&lt;span style="text-decoration: line-through;"&gt;133&lt;/span&gt; 637 results when searching the Microsoft support site for Num Lock&lt;/a&gt;. Keyboard design is staunchly conservative, though, and the 101/102 keyboard keys have remained essentially unchanged since introduced by IBM in 1984.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;One of the reasons for the conservatism is that keyboards use numeric codes - scancodes - to identify which physical key has been pressed. These scancodes include one bit to identify the key itself, and an extra bit to show if the key is extended. This derives from the extension of the original 83/84 PC/AT keyboard to the 101 key keyboard: in some cases where the functionality was the same, rather than assign a whole scancode to the extra keys, an extra bit was added. For example the Enter and Return keys both have a scancode of 28 (0x1C), while the Enter key (the one on the numeric keypad) has the extended bit set; similarly the Left arrow key and the number 4 on the numeric keypad (which is Left with Num Lock set off) both have a scancode of 75 (0x4B), but the arrow key has the extended bit set. This was presumably for backward compatibility with systems which didn't expect the extra extended bit.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Keyboard drivers can change or interpret these codes themselves - and often do: although keyboard power and standby keys have been assigned specific scancodes, the actual operations are controlled by the keyboard driver; this means that a) the power keys usually can't be disabled using scancode mapping and b) it isn't possible to assign an existing key to these functions. Scancode mapping programs like KeyMapper usually allow the selection of the key to be disabled by reading the keypress: Num Lock, unlike all other keys, reports itself differently on different keyboards. Most keyboards use the usual code - 69 (0x45) - to identify the key with the extended bit set off, but some set the extended bit on. This can cause confusion, as systems expect the code of 69 with extended on to represent the Euro symbol, on keyboards which have that. Using that key code in a scancode mapping also fails to correctly map Num Lock: scancode of 69 without the extended bit set must be used in mappings no matter what the keyboard reports as the scancode.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;The Num Lock key is also involved in the most complicated remapping scenario, the Pause key. At first I couldn't figure out how to remap the Pause key at all, and thought that it couldn't be done. Then I stumbled across &lt;a href="http://www.neox.net/w/2008/02/13/keyboard-remap-pause-break-key-as-del-key/"&gt;a blog post&lt;/a&gt; which indicated it was possible and provided me with enough information to work out how to implement it.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;When the Pause key is pressed, internally this generates multiple keystrokes, specifically Ctrl and Num Lock. This is explained by &lt;a href="http://blogs.msdn.com/oldnewthing/archive/2008/02/11/7596539.aspx"&gt;another blog post&lt;/a&gt;, coincidentally posted within two days of the other one.&lt;/p&gt;
&lt;blockquote cite="http://blogs.msdn.com/oldnewthing/archive/2008/02/11/7596539.aspx"&gt;in the original PC/XT keyboard layout, there was no Break key. The key sequence for Break was Ctrl+ScrollLock. (And for completeness, the key sequence for Pause was Ctrl+NumLock.) Even though the enhanced keyboard moved the Pause and Break functions to their own key, pressing the Pause key internally generated scan codes that simulated a press of Ctrl+NumLock. In other words, when you pressed Pause, the keyboard hardware actually tells the computer, "The user pressed the Ctrl key and then pressed the NumLock key."&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;That's not quite the whole story, though - so systems can distinguish this from a regular Ctrl-NumLock, a different extended value is used - instead of 224 (0xe0) the keyboard will report 225 (0xE1). This is as far as I know the only time this extra extended value is used.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;This complicated the UI for KeyMapper: should a user want to disable or remap the Pause key, then Num Lock will still fire each time it's pressed: to remap Pause to a single keystroke, Num Lock must be disabled as otherwise Num Lock will be toggled each time Pause is pressed.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;However, it also introduced an interesting new possibility: if Num Lock is remapped to another key then that key will also be 'pressed' when the remapped Pause is pressed: this means the Pause key can be remapped to a keyboard command which requires two key strokes, for example Windows-L (the shortcut to Lock Computer), or Right-Alt and 4 for the Euro symbol. These require the Pause key to be remapped to the first key in the sequence and Num Lock to be remapped to the second.&lt;/p&gt;</description><a10:link title="Key Mapper Blog" href="http://justkeepswimming.net/keymapper/default.aspx?p=2.aspx" /></item><item><guid isPermaLink="false">1</guid><author>keymappersupport@gmail.com</author><title>User Key Mappings</title><description>&lt;p&gt;One thing that distinguishes Key Mapper from other scancode mapping programs is that it lets you map or disable keys on a per-user basis: when Microsoft &lt;a href="http://www.microsoft.com/whdc/archive/w2kscan-map.mspx"&gt;originally implemented scancode mappings in Windows 2000&lt;/a&gt;, they stated in the "disadvantages" section:&lt;/p&gt;
&lt;blockquote&gt;The mappings stored in the registry work at system level and apply to all users. These mappings cannot be set to work differently depending on the current user.&lt;/blockquote&gt;
&lt;p&gt;This is because the mappings are stored in &lt;code class="regkey"&gt;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout&lt;/code&gt; which needs Administrative access to change and is only loaded at boot time.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;In Windows XP, though, per-user mappings were quietly introduced, with no fanfare or documentation: scancode mappings set in the &lt;code class="regkey"&gt;HKEY_CURRENT_USER\Keyboard Layout&lt;/code&gt; key are recognised, and apply to an individual user profile. This means that mappings can be added or removed by logging off and logging back on again - still inconvenient, but less so than a full reboot: it also means that mappings can be set up users without Administrative rights. (Mappings set in &lt;code class="regkey"&gt;HKEY_LOCAL_MACHINE&lt;/code&gt; are overridden by those in &lt;code class="regkey"&gt;HKEY_CURRENT_USER&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;It's possible that Microsoft kept this quiet because user mappings are incompatible with Fast User Switching: when you switch to an account that's already logged on, the mappings are not reloaded. It's also possible that because they kept it quiet, the Fast User Switching development team didn't realise that user mappings should be reloaded when switching users. Boot mappings persist through Fast User Switching.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;While this is a possible disadvantage to using user mappings, most people probably don't use more than one account on their computer anyway, and in computers attached to a domain (i.e. corporate PCs) which may often be used by different people Fast User Switching isn't available anyway.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;There are some other advantages to user mappings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They don't require Administrative rights to be set or removed.&lt;/li&gt;
&lt;li&gt;Different users can have different mappings - one can have Caps Lock disabled but Num Lock enabled, another can have them the other way round&lt;/li&gt;
&lt;li&gt;Keys can be mapped on shared computers without affecting all users&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;br /&gt; There is yet another place scancode mappings can be set - in the &lt;code class="regkey"&gt;HKEY_USERS\.DEFAULT\Keyboard Layout&lt;/code&gt; key. These apply at the login prompt, but are then removed when logged in.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description><a10:link title="Key Mapper Blog" href="http://justkeepswimming.net/keymapper/default.aspx?p=1.aspx" /></item></channel></rss>