Dienstag, 8. November 2011

Wrong week number in calendar control

Are your European customers complaining about wrong week numbers in calendar controls?

Numbering weeks is different from country to country and therefore information is included in Windows' "locale" settings, which are selected through Control Panel's Region and Language Settings. Control vendors (like DevExpress) use the locale information provided by Windows when displaying week numbers.

Unfortunately, Microsoft doesn't always get it perfectly right. E.g. for locale "de-AT" (Austria) Windows defines the wrong rule "CalendarWeekRule.FirstDay" instead of CalendarWeekRule.FirstFourDayWeek.

To work around this issue you can override your app's locale settings to use the correct rule. I use this code in my main/UI thread to setup the right rule in Austria:


      if (Thread.CurrentThread.CurrentCulture.Name == "de-AT")
      {
        var cultureInfo = new System.Globalization.CultureInfo("de-AT");
        cultureInfo.DateTimeFormat.CalendarWeekRule = System.Globalization.CalendarWeekRule.FirstFourDayWeek;
        Thread.CurrentThread.CurrentUICulture = cultureInfo;
        Thread.CurrentThread.CurrentCulture = cultureInfo;
      }

MouseWheel scrolling

Have you ever tried to scroll a control with the mouse wheel and something else started moving instead? 

The reason for that is that Windows always sends the WM_MOUSEWHEEL message to the control that has the keyboard focus and not as one might assume to the control underneath the mouse cursor.

Windows doesn't provide a means to change this behavior, but there are tools and workarounds. If you have full control over a computer, you can install small programs like "KatMouse" or "WizMouse" to bring universal scroll support to all Windows apps. 

If you develop software and don't have control over the deployment machines, you might want to add universal scroll directly into your app. I did this by implementing IMessageFilter in my main window, intercepting the WM_MOUSEWHEEL message and forward it to the control under the mouse cursor:

using System;
using System.Windows.Forms;

public class MainForm : System.Windows.Forms.IMessageFilter
{
    // .....

    public bool PreFilterMessage(ref Message msg)
    {
      if (msg.Msg == (int)Win32.Msgs.WM_MOUSEWHEEL || msg.Msg == (int)Win32.Msgs.WM_MOUSEHWHEEL)
      {
        // redirect WM_MOUSEWHEEL to the window under the mouse cursor
        int x = (int)(((long)msg.LParam) & 0xFFFF);
        int y = (int)(((long)msg.LParam) >> 16);

        IntPtr handle = Win32.WindowFromPoint(new Point(x, y));
        if (handle != msg.HWnd)
        {
          Win32.SendMessage(handle, (int)msg.Msg, msg.WParam, msg.LParam);
          return true;  // msg won't be processed
        }
      }
      return false;  // msg will be processed normally
    }
}

public static class Win32
{
  public enum Msgs
  {
    WM_MOUSEWHEEL = 0x020A,
    WM_MOUSEHWHEEL = 0x020E,
  }

  [System.Runtime.InteropServices.DllImport("User32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
  public static extern int PostMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

  [System.Runtime.InteropServices.DllImport("User32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
  public static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

  [System.Runtime.InteropServices.DllImport("User32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
  public static extern IntPtr WindowFromPoint(System.Drawing.Point screenPoint);
}

RichTextBox loses format

A System.Windows.Forms.RichTextBox control loses the text format when its parent control chain is changed or the visibility of a parent control changes.

This happens in particular when

  • setting the RichTextBox.Rtf property before the control's handle is created
  • the RichTextBox is inside a DevExpress DockingPanel and the panel is re-docked 

A workaround for this is to create a class derived from RichTextBox and override OnFontChanged with an empty method body.