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

  • Creating Reactive Applications in .NET

    In modern applications, data is being retrieved in asynchronous, real-time streams, as traditional pull requests where the clients asks for data from the server are becoming a thing of the past.

  • AI for GitHub Collaboration? Maybe Not So Much

    No doubt GitHub Copilot has been a boon for developers, but AI might not be the best tool for collaboration, according to developers weighing in on a recent social media post from the GitHub team.

  • Visual Studio 2022 Getting VS Code 'Command Palette' Equivalent

    As any Visual Studio Code user knows, the editor's command palette is a powerful tool for getting things done quickly, without having to navigate through menus and dialogs. Now, we learn how an equivalent is coming for Microsoft's flagship Visual Studio IDE, invoked by the same familiar Ctrl+Shift+P keyboard shortcut.

  • .NET 9 Preview 3: 'I've Been Waiting 9 Years for This API!'

    Microsoft's third preview of .NET 9 sees a lot of minor tweaks and fixes with no earth-shaking new functionality, but little things can be important to individual developers.

  • Data Anomaly Detection Using a Neural Autoencoder with C#

    Dr. James McCaffrey of Microsoft Research tackles the process of examining a set of source data to find data items that are different in some way from the majority of the source items.

Subscribe on YouTube