Code Focused

Windows Forms: Don't Skimp on the Caps Lock Warning

If you've ever worked in an enterprise environment and fielded calls from employees unable to get into their accounts due to a depressed Caps Lock key, you know how essential the following tweak to your code can be.

When you activate the password-entry features on a Windows Forms TextBox control, you get a Caps Lock warning bubble for free (see Figure 1). The popup message provides a gentle reminder that it might be prudent to check the keyboard state before entering that secure password. And that gentleness is a big part of the problem. This isn't the era of ho-hum black-and-white Flash Gordon serials. We have the Marvel Cinematic Universe! If people are already finding those multi-hero fight scenes mundane, you better believe that you need more power in your Caps Lock warnings.

Well, perhaps it's not that bad. But there’s also the issue that if Caps Lock is already activated when the password field first appears, the message bubble doesn't show up. For these and various other reasons, I sometimes add a dedicated warning message right on the form. (See Figure 2.)

[Click on image for larger view.] Figure 1. The Standard Caps Lock Warning
[Click on image for larger view.] Figure 2. A Better Caps Lock Warning

It's a fairly straightforward coding process. First, add your password TextBox control to a form, and next to it put a Panel control, laden with warning signs and with its Visible property set to False. Then add the event handlers that manage the visibility of the warning message, as shown in Listing 1.

Listing 1: Managing Warning Message Visibility
private void PasswordField_Enter(object sender, EventArgs e)
{
  // ----- Monitor caps lock on arrival.
  PanelWarning.Visible = Control.IsKeyLocked(Keys.CapsLock);
}

private void PasswordField_Leave(object sender, EventArgs e)
{
  // ----- Monitor caps lock on departure.
  PanelWarning.Visible = false;
}

private void PasswordField_KeyDown(object sender, KeyEventArgs e)
{
  // ----- Monitor caps lock on activity.
  PanelWarning.Visible = Control.IsKeyLocked(Keys.CapsLock);
}

The problem is that the .NET-supplied message bubble still shows up, obscuring that lovely handcrafted warning panel. Under the hood, Windows Forms is deciding to show that bubble by monitoring control-level Windows messages, akin to .NET events. For tool-tip bubbles, a message known as EM_SHOWBALLOONTIP triggers the standard Caps Lock presentation. If you disable the monitoring of that message in the password textbox, the interruption goes away.

To build a tip-less text entry control, add a new class to your project named QuietTextBox and adjust it to use the standard TextBox class as its base class:

// ----- Assumes: using System.Windows.Forms;
public class QuietTextBox : TextBox
{
}

Internal messages within Windows are identified by 32-bit pre-defined integers, and the balloon-tip message has been assigned the hexadecimal value of 0x1503. Add that definition to the class:

private const int EM_SHOWBALLOONTIP = 0x1503;

Every form and control class in Windows Forms applications has a protected method named WndProc that handles all of the event-driven activity exposed by that form or control. To disable the balloon-tip message, override that method, watch for the specific message and add code that causes it to have no impact:

protected override void WndProc(ref Message m)
{
  // ----- Prevent the system-level Caps Lock message from showing up.
  if (m.Msg == EM_SHOWBALLOONTIP)
  {
    m.Result = (IntPtr)0;
    return;
  }
  base.WndProc(ref m);
}

That's all you need to hide the popup message. Build the project to make it available within the Windows Forms designer. There are a few ways to integrate this new control into a form, but I've found it easiest to add a regular password TextBox control to a form and then modify its definition within the form's Designer.cs file. Open that file and locate the two lines that define the password TextBox control:

// ----- Within the InitializeComponent method:
this.PasswordField = new System.Windows.Forms.TextBox();

// ----- And later, near the bottom of the file:
private System.Windows.Forms.TextBox PasswordField;

Modify these two lines, replacing the reference to the built-in TextBox control with the new QuietTextBox class:

// ----- Within the InitializeComponent method:
this.PasswordField = new QuietTextBox();

// ----- And later, near the bottom of the file:
private QuietTextBox PasswordField;

Implementing a similar Caps Lock warning in a Windows Presentation Foundation (WPF) application is even easier, because the PasswordBox control in that system doesn't present a bubble message automatically when Caps Lock is activated. As in a Windows Forms application, you add your password entry field and the warning-related elements to the XAML window surface:

<!-- NOTE: Most attributes omitted for brevity. -->
<PasswordBox x:Name="PasswordField" TabIndex="1"
  GotFocus="PasswordField_GotFocus"
  LostFocus="PasswordField_LostFocus"
  KeyDown="PasswordField_KeyDown" ... />
<Border x:Name="PanelWarning" Visibility="Hidden" ... >
  <Canvas ...>
    <Label Content="Warning!" ... />
    <Label Content="Caps Lock Is On" ... />
  </Canvas>
</Border>

Then add the event handlers, which will have slightly different event names and content from those in Windows Forms, as shown in Listing 2.

Listing 2: Adding Event Handlers
private void PasswordField_GotFocus(object sender, RoutedEventArgs e)
{
  // ----- Monitor caps lock.
  PanelWarning.Visibility =
    ((Keyboard.GetKeyStates(Key.CapsLock) & KeyStates.Toggled) > 0)
    ? Visibility.Visible : Visibility.Hidden;
}

private void PasswordField_LostFocus(object sender, RoutedEventArgs e)
{
  // ----- Monitor caps lock.
  PanelWarning.Visibility = Visibility.Hidden;
}

private void PasswordField_KeyDown(object sender, KeyEventArgs e)
{
  // ----- Monitor caps lock.
  PanelWarning.Visibility =
    ((Keyboard.GetKeyStates(Key.CapsLock) & KeyStates.Toggled) > 0)
    ? Visibility.Visible : Visibility.Hidden;
}

Caps Lock warnings might not seem like a big deal. But if you’ve ever worked in an enterprise environment and fielded calls from employees unable to get into their accounts due to a depressed (physically, not emotionally) Caps Lock key, then you know how essential such small updates to your code can be.

About the Author

Tim Patrick has spent more than thirty years as a software architect and developer. His two most recent books on .NET development -- Start-to-Finish Visual C# 2015, and Start-to-Finish Visual Basic 2015 -- are available from http://owanipress.com. He blogs regularly at http://wellreadman.com.

comments powered by Disqus

Featured

  • Top 3 Blazor Extensions for Visual Studio Code

    Some developers prefer to create applications with Microsoft's open-source Blazor tooling from within the open-source, cross-platform Visual Studio Code editor. Here are the top tools in the VS Code Marketplace for those folk, as measured by the number of installations.

  • How to Invert a Machine Learning Matrix Using C#

    VSM Senior Technical Editor Dr. James McCaffrey, of Microsoft Research, explains why inverting a matrix -- one of the more common tasks in data science and machine learning -- is difficult and presents code that you can use as-is, or as a starting point for custom matrix inversion scenarios.

  • Microsoft Engineer: 'It's Time to Move OData to .NET 5'

    Microsoft engineer Sam Xu says "it’s time to move OData to .NET 5" and in a new blog post he shows how to do just that.

  • Microsoft Goes Virtual with Developer Education in Face of COVID-19

    Like many organizations that host developer educational events, Microsoft has gone virtual amid shelter-in-place directives and a surge in remote work stemming from the COVID-19 pandemic.

  • Microsoft Enhances Low-Code Power Apps

    Microsoft's nod to the low-code movement, Power Apps, has been enhanced with a bevy of new features, including mixed reality, canvas/model support in a new mobile app, UX improvements and more.

.NET Insight

Sign up for our newsletter.

Terms and Privacy Policy consent

I agree to this site's Privacy Policy.

Upcoming Events