views:

901

answers:

3

I have to buil a rather complex WPF UI (a hughe Grid) in code (no xaml invloved). Is there a way to stop the main UI thread from blocking while beeing in the process of building the Grid? Are there some portions of building the UI that can be outsourced to a workerthread? What parts of UI creation actually have to be on the UI thread?

  • Calling the constructor of the
    Controls?
  • Setting properties of the controls?
  • Setting up Databinding?
  • Building the logical tree (adding children)?

What other options do I have to increase performance while building the UI? Is suspending the dispatcher or calling FrameworkElement.BeginInit a good idea?

+4  A: 

If you can break the construction of your UI up into steps, you can do each of these steps as a separate message. This will allow other messages to be processed in between. Something like:

private delegate void BuildHandler();

private void BuildGridPart1()
{
    //build first part of grid

    Dispatcher.BeginInvoke(new BuildHandler(BuildGridPart2), DispatcherPriority.Background);
}

private void BuildGridPart2()
{
    //build second part of grid

    Dispatcher.BeginInvoke(new BuildHandler(BuildGridPart3), DispatcherPriority.Background);
}

//etc

You can refactor this to be cleaner (eg. one method with a state machine that tells you where in the grid you are up to), and automatically queue the next message at a lower priority so that input is always processed first. Bingo, you will have a responsive UI whilst building UI on the UI thread.

HTH, Kent

Kent Boogaart
You wrote:"You can refactor this to be cleaner (eg. one method with a state machine that tells you where in the grid you are up to), and automatically queue the next message at a lower priority so that input is always processed first." can you elaborate a little more. I I am not sure I understand.
bitbonk
A: 

My feeling is that you might not have many options other than running on the UI thread.

If it were a case that the long time involved was due to gathering and arranging the data then a background thread would be the best option, and then call any UI dependency properties using the Dispatcher object. I'm assuming the time is not to gather any data it is purely down to programmatic creation of the UIElements.

As I understand it UI elements derive from DispatcherObject that gets the Dispatcher from the current working thread so that rules out creation of controls on another thread.

Then all Dependency Properties call Dispatcher.VerifyAccess when written to, that will raise an exception if accessed on the wrong thread. So that rules out updating databinding and properties on a different thread.

My first reaction would be like @Kent wrote.
To elaborate slightly, if you have a foreach loop for each row with another foreach for each column then you could, even though you are in the correct UI Thread, call Dispatcher.BeginInvoke to "yield" momentarily from your long running method. i.e to break it up and then pass control back to the UI thread to later carry on adding to the grid. The layout effort might be significantly greater and the overall time would be longer but a bit more responsive.

private void Window_Loaded(object sender, RoutedEventArgs e)
{
  CreateRow(1);
}

private void CreateRow(int i)
{
  // 
  // Construct row i of the grid
  //
  Dispatcher.BeginInvoke(DispatcherPriority.Background, new EventHandler(delegate { CreateRow(i + 1); }));
}
Rhys
A: 

Is there a reason why you HAVE to build it in code? If it's because the grid is dynamic (I.E. columns aren't known until run time), there are ways around it.

What is the precise problem that's requiring you to create the UI in code? Once we know that we can generalize it to templates and binding.

Anyway, to address your immediate question you can shuttle work to a worker thread and have it update the UI using the Dispatcher as the others have mentioned. I'd also like to point out that it's best to break down your work into fine grained units...so for example if you are creating UI for each individual column on each item in a list you should break it down at the least on the item level, preferably on the column level.

If you're using a GOOD grid, it probably uses virtualization and has an API for you to hook into the virtualizing events. (Essentially the grid only builds the UI as it needs it or request UI elements as it needs them). This allows you to avoid building the ENTIRE grid upon load.

Mike Brown
I am using the normal Grid class. Virtualization would be pretty tuff in my scenario because it displays a nested table structure that constantly keeps rearranging. I though about using a treeview but that doesn't work well because I need row headers and column headers. I sort of behaves like a treelistview ony that rows and columns are interchanged. The column headers are a tree structure the row header is a list.
bitbonk