Brothers In Code

...a serious misallocation of .net resources

Keyboard Input Differences Between Winforms and WPF

I'm currently converting a winforms app into a WPF app and discovered the key events are significantly different in WPF.  Here's a few tips and things to look out for:

  • The old KeyPressEventArgs.KeyChar is just a char and included the modifier key stroke (CTRL-S = 'S'-64 = (Char)19, SHIFT-S = 's'-32 = 'S').
  • The new KeyEventArgs.Key is an enum that includes every key.  Modifier keys are their own key stroke.
  • (Char)KeyInterop.VirtualKeyFromKey(e.Key) will get you the char value from the new enum, although it is always upper case. 
  • Since modifier keys raise an event in WPF, I find I'm ignoring them alot:

    //ignore modifier keys
    if (e.Key == Key.LeftCtrl ||
            e.Key == Key.RightCtrl ||
            e.Key == Key.LeftAlt ||
            e.Key == Key.RightAlt ||
            e.Key == Key.LeftShift ||
            e.Key == Key.RightShift)
    {
      e.Handled = false;
      return;
    }

Keep a Winforms Application in the Foreground

I needed a way to force an application to stay on top.  I thought it would be as simple as "this.Activate" in the form's deactivate event.  It looked like it was trying to work - when clicking out of the app, it's start bar button would blink, but it wouldn't bring the app back on top.  I then proceeded to try just about everything under the sun:

  • Form.Activate
  • Form.BringToFront
  • Form.Focus
  • Form.Show
  • [DllImport("User32.dll")]
    public static extern Int32 SetForegroundWindow(int hWnd);

Everything seemed to do the same thing.  Finally I thought that maybe I was trying to refocus the form too soon.  I decided to create a timer and delay the Activate call:

//declare the timer
private System.Timers.Timer restoreFocusTimer = new System.Timers.Timer();

//setup the timer in the constructor
restoreFocusTimer.AutoReset = false;
restoreFocusTimer.SynchronizingObject = this;
restoreFocusTimer.Interval = 1000;
restoreFocusTimer.Elapsed += new System.Timers.ElapsedEventHandler(restoreFocusTimer_Elapsed);

//define the handler
void restoreFocusTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
  this.Activate();
}


//enable the timer in the deactivate event (I also added a preprocessor directive so i could disable it when debugging.
private void MainForm_Deactivate(object sender, EventArgs e)
{
  if (!this.formClosing)
  {
    #if !NOALWAYSONTOP
    restoreFocusTimer.Enabled = true;
    #endif
   }
}

 

Wrong Tab Order in Windows Mobile

I had a form that didn't seem to want to follow the TabIndex that I had set on a number of controls.  The cursor jumped around passed controls with a lower index and then would come back to them later.  The difference between this form and others was that this form had a panel on it that hid a couple of controls until they were displayed upon the click of a radio button.  I did notice that if I deleted the controls and recreated them in the order I wanted them to tab, they would tab correctly.  However, starting over is a lousy fix so I dug into it a bit deeper.  After debugging I found that the TabIndex on the controls within the panel were being reset.  It also turns out that even though a Panel control has no mention of a TabIndex property in the Properties pane or with intellisense, it does in fact have that property.  Setting the Panel's TabIndex in the form load event fixed the issue for me.  To make it so I wouldn't get bit by this again,  I set the panel's TabIndex to the index of the first control in the panel.

B

Event-Based Keyboard Wedge Scanner

I just came off a project programming Motorola barcode canners based on Windows Mobile.  The Motorola/Symbol API was top notch and provided access to everything I needed.  This included an event driven API for the scanner.  This meant I had a nice "Scan" event to use from within my code rather than rely on simple keyboard emulation.  This great when a single barcode had multiple fields of data.  I could parse everything behind the scenes without using a "capture" field on my forms.

The next project also needed a barcode scanner but would be a Winforms app rather than a mobile app.  To my surprise the APIs for these usb and serial scanners were not nearly as good.  Only a select few scanners supported Motorola's usb SNAPI interface, and there was no native .net support there (just a bunch of DllImports).  Others had an RS232 based interface but it wasn't much better than reading raw data from the serial port.  A few of the scanners supported the OPOS standard which means I could have used POS for .net but that seemed overkill for my needs.

Finally I realized there was a much simpler solution.  Most of these scanners supported a keyboard emulation mode.  More importantly they supported the ability to be programmed to include prefixes and suffixes in the scan.  While most would use this feature to send an 'Enter' after a successful scan, I realized I could use this to send special characters to indicate the start and end of a scan and separate it from normal keyboard input.  From there making my own API was simple.

There were two main points to this.  The first was to monitor keypresses:

HostForm.KeyPreview = true;
this.enabled = true;
if (this.enabled)
{
  HostForm.KeyPress += new KeyPressEventHandler(KeyPressHandler);
}
else
{
  HostForm.KeyPress -= KeyPressHandler;
}

The line of interest here should be the Form.KeyPreview=true.  This allows you to pickup keyboard strokes at the form level rather than from the control with focus.

From there it's just a matter of looking for the started and ending characters (i chose ctrl-s and ctrl-t, ascii char 19 and 20 resp):

public void KeyPressHandler(object sender, KeyPressEventArgs e)
{
  //if a scan has started...
  if (!IsScanning && e.KeyChar == ScanStartCharacter)
  {
    IsScanning = true;
    timeoutTimer.Start();
    e.Handled = true;
    OnScanStarted();
  }
  //if a scan is in progress and is not being terminated...
  else if (IsScanning && e.KeyChar != ScanEndCharacter)
  {
    timeoutTimer.Stop();
    //...then add the char to the buffer
    scanBuffer.Append(e.KeyChar);

    timeoutTimer.Start();
    e.Handled = true;
  }
  //if a scan has ended
  else if (e.KeyChar == ScanEndCharacter)
  {
    timeoutTimer.Stop();

    CompleteScan(false);

    e.Handled = true;
  }
  else  //not scanning, pass the character on as keyboard input.
  {
    e.Handled = false;
  }
}

I threw a timer in there just to make sure the scan eventually completes, even if the user forgets to program the suffix into the scanner.

That's pretty much it.  The full code is attached.

Conditionally Opening a Modal Form in a Parent Form's Activate Event

In a Winforms App, I wanted to see if a local configuration file existed, and if it didn't I wanted to force the user into the setup form after being prompted to do so.  I basically had the following code in my main form:

private void MainForm_Activated(object sender, EventArgs e)
{
  //if no config has been loaded...
  if (SaveData.Current.ScannerConfigId == 0)
  {
    DialogResult result = MessageBox.Show(
      "No local configuration was found.  Press OK to select a configuration.  Press Cancel to exit.",
      "Setup",
      MessageBoxButtons.OKCancel);
    if (result == DialogResult.OK)
    {
      //...then force the user into the setup form
      Form form = new SetupForm();
      form.Owner = this;
      form.ShowDialog();
    }
    else
      Application.Exit();

  }
}

Unfortunately, I found that upon clicking 'OK' to close the MessageBox, the activate event was fired again before completing the first one that opened the MessageBox.  To fix it I just added a simple semaphore:

private void MainForm_Activated(object sender, EventArgs e)
{
  if (!checkingConfig)
  {
    //if no config has been loaded...
    if (SaveData.Current.ScannerConfigId == 0)
    {
      checkingConfig = true;
      DialogResult result = MessageBox.Show(
        "No local configuration was found.  Press OK to select a configuration.  Press Cancel to exit.",
        "Setup",
        MessageBoxButtons.OKCancel);
      checkingConfig = false;
      if (result == DialogResult.OK)
      {
        //...then force the user into the setup form
        Form form = new SetupForm();
        form.Owner = this;
        form.ShowDialog();
      }
      else
        Application.Exit();

    }
  }
}

After that, the second call quickly exited allowing the first to pick up where it left off.

Databound Winforms TextBox Reverts Back to Original Value

I had a TextBox that I had bound to a property on a class that implements INotifyProperyChanged.  No matter what I did, I just couldn't get the TextBox to update the bound property.  Each time the text box just reverted back to it's original value.  A simplified version of my class looked like this:

  public class KeyboardWedgeScanner : INotifyPropertyChanged
  {
    public TimeSpan ScanTimeout
    {
      get
      {
        return TimeSpan.FromMilliseconds(timeoutTimer.Interval);
      }
      set
      {
        timeoutTimer.Interval = value.TotalMilliseconds;
        OnPropertyChanged("ScanTimeout");
      }
    }


    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(String propertyName)
    {
      if (PropertyChanged != null)
      {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
    }

    #endregion
  }

In my form I bound the textbox with this in my constructor:

InScannerTimeout.DataBindings.Add("Text", scanner, "ScanTimeout");

This is exactly how a bound TextBoxes in another project and it worked fine there.  I messed around with the DataSourceUpdateMode parameter on an overload of Add but that didn't help either.  It wasn't until I set the formattingEnabled property to true did it start working:

InScannerTimeout.DataBindings.Add("Text", scanner, "ScanTimeout", true);

The only thing I can think of is that something about the TimeSpan class requires this to return the TextBox.Text's string value back into an instance of TimeSpan.