Browsing the blog archives for April, 2013.

It’s not been abstracted away yet, has it?

work safe

There is a bug in my code that I finally squashed last week. I had been visiting it and kicking it down the road for a couple of weeks as the feature is still being written. When I move to a specific view in our application, I needed a TextBox to gain focus. When navigating away from this view, some button or other will grab focus so returning will not show the TextBox as having focus. The view knows when it is being activated, so why can’t I just tell it to focus on the TextBox then?

A Timer? Really?

Elsewhere in the inherited code base, I saw them do essentially that. When the view is activated, tell the TextBox to focus. Only they used a timer with a very short duration. This is obviously a code smell, so I did it very differently. Here’s some sanitized WPF code to describe what I saw and what I did.

Their Code

A DispatchTimer runs on the UI thread. So I made an assumption the only reason this was being done was to make sure UI code was happening on the UI thread.

public void FocusTextBox(object sender, EventArgs e){
  var time = new DispatcherTimer{ Interval = TimeSpan.FromSeconds(0.01) };
  timer.Tick += delegate {
    timer.Stop();
    if(FocusedTextBox != null){
      FocusedTextBox.Focus();
      FocusedTextBox.SelectionStart = FocusedTextBox.Text.Length;
    }
  };
  timer.Start;
}

This works. It makes my skin crawl because it uses a timer, seemingly only for thread hopping.

My Initial Version

I just told the Dispatcher to run some code for me. The simplest way to get code running on the main thread.

public void FocusTextBox(){
  Action makeFocused = delegate {
    if(FocusedTextBox != null){
      FocusedTextBox.Focus();
      FocusedTextBox.SelectionStart = FocusedTextBox.Text.Length;
    }
  };
  Dispatcher.Invoke( DispatcherPriority.ApplicationIdle, makeFocused );
}

But, as the documents say,

Events and Sequence Diagrams

GUI applications run via an Event Loop. We all but ignore this by hiding it behind APIs like Application.Run that handle the loop for us. Our base classes wire in to the event loop allowing buttons to register their touches. All this abstraction gives us an illusion of concurrency. But it isn’t really concurrent.

If you debug a .NET application, you will notice that a line of code that triggers an event will immediately stop the execution of the current method and start executing the event handler.

I was using the Dispatcher to ensure I was working on the UI Thread, but what if I started on the UI Thread?

I’m not 100% positive, but here’s the theory for what’s happening:



* A touch down event changes tab


* This causes the tab selection index to change triggering an event handler


* The event handler sets the focus on the text box


* and returns control to the tab control


* The tab control finishes


* and returns control to the event loop


* which processes a touch up event changing focus

joy.

My Final Version

The fix was simple. I need to add an event to the queue without interrupting the current pass through the even queue. Fortunately, the Dispatcher allows us to enqueue a bunch of operations and even manipulate them in flight so to speak via a DispatcherOperation. Let’s try calling BeginInvoke on the dispatcher instead!

public void FocusTextBox(){
  Action makeFocused = delegate {
    if(FocusedTextBox != null){
      FocusedTextBox.Focus();
      FocusedTextBox.SelectionStart = FocusedTextBox.Text.Length;
    }
  };
  Dispatcher.BeginInvoke( DispatcherPriority.ApplicationIdle, makeFocused );
}

And it works. The touches are fully processed before I try to set focus. And I don’t use a timer for flow control.

Takeaway

The main takeaway is the event loop still exists, even if our abstractions try to hide it. Event based programming is amazing. But we must remember that it only creates an illusion of concurrent programming. All of those actions are still happening sequentially. It does pay to really understand the arcane secrets programming frameworks use to enable their abstractions.

No Comments