Sunday, April 01, 2007

Too much "async" with this WPF

What's this post about? This post begins with DIY. Try to do the following:

  1. Add simple ListView to your application
  2. Inside the ListView add two TextBlocks
  3. For each TextBlock subscribe to OnMouseDown (or any other mouse event)
  4. OnMouseEvent create new Window and open it with Show() method.

Did it? Fine. Have you paid your attention, that each new window opens this way goes backward? Don't it looks a bit strange?Let's give ListView other try.

  1. Add simple ListView to your application
  2. Inside the ListView add two TextBlocks
  3. Subscribe to SelectionChanged event of the listview
  4. OnSelectionChanged create new Window and open it with Show() method.

The same result? Maybe all collections have this bug behavior? Let's give a try to LiveBox... Do the same as in previous try, but instead of ListView use ListBox. The behavior disappears. Now the new window opens normally over the main window. What's the difference?

Return to second try (with SelectionChanged event), but put SelectionMode property of ListView to "simple" or "multiple". Works fine, right. So the problem with SelectorItem selection. Let's dive a bit deeper.

What's actually happens? If you have rather slow computer, you can clearly see the process. First occurs "mouse event", then opens window at the front of openner, next the item becomes selected and steals focus. Why ListViewItem doing it? According my short investigation, the root of the problem is Automation boundary. If you have time and wish to do it, trace all events (by using global events handling) and see it yourself. If you have either time nor wish, the answer is following.

WPF process almost any operation asynchronously, so with new window opening, the item becomes selected  and grab focus. How to solve this. Simple. Do your work very asynchronous way. Following the code, that opens window with normal thread priority, so it waits for openner to finish all tasks to process new job.

 

delegate void OpenWindowDelegate(bool bringFocus);

        void OpenWindow(bool bringFocus)

        {

            if (!bringFocus)

            {

                Window1 w = new Window1();

 

                w.Show();

            }

            else

            {

                this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,

                    new OpenWindowDelegate(OpenWindow), false);

            }

 

 

        }

We finished, now your windows will be opened above the opener and you're be able to do anything with both of those windows (ShowDialog() method or setting owner property for opened window will works fines, even without it, 'cos it locks the focus on newly opened window.

Happy Passover to all of you. Oh, I almost forgot my original question: Does this behavior is bug or not? I'm really interested with your opinion.

Source code for this article

No comments: