Brothers In Code

...a serious misallocation of .net resources

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.

Easy UI Thread Management with System.Timers.Timer

There's a great article on msdn that covers the 3 different types of timers.  I've basically boiled it down to this:

  • Use the Forms.Timer for simple updates to the UI.  For example i like to bold certain errors when they happen and then return them to a normal font.  This helps the user identify when they caused the same error twice.
  • Use the Threading.Timer to execute one-off processes off of the UI thread
  • Use the Timers.Timer to execute recurring processes.  This timer's AutoReset and Enabled properties make it much cleaner to use than Threading.Timer's Change method.

I tended to lean to Threading.Timer in most cases.  When I needed to do something on the UI thread i just used Invoke.  What I missed from the article is Timers.Timer's SynchronizingObject property.  Simply set this to your form or something else on your UI thread, and no "InvokeRequired" code is needed to access the UI.  Because of this, this timer has no become my timer of choice.  The only drawback to it compared to the Threading version, is that the Threading version's callback takes an object parameter allowing you to pass some state into the callback.  That's only a minor issue considering you can either create a field to temporarily store this additional data or you can create different "Elapsed" events.

Some other basic threading tips:

  • Don't forget to hold a reference somewhere to your timer.  If there are no references, the timer might be garbage collected before it gets a chance to fire
  • Timers.Timer AutoReset is true by default.  I almost always set this to false and then reset Enabled=true in the Elapsed event.  This avoids any reenterance issues.

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.

 

 


 

 

 

Missing 'out' parameter on a web service proxy class

After adding a "web service reference" in visual studio and instantiating the generated proxy class, I noticed I was a parameter short.  The web service method had three parameters - a custom class, an 'out' int and an 'out' string.  But the proxy class only had the first and the last of those three parameters.  I took a quick look at the WSDL and that was correct.  I had also added a reference to a similar service the week before so I knew that you could have multiple 'out' parameters.  After a little googling I found this article.  Because my web method returned 'void' the proxy class generator was using the first 'out' param as the return value instead.  I just returned a Boolean instead and that made my first out parameter show up as a parameter in the proxy class.