views:

526

answers:

5

I am building an wpf app using MVVM. I have viewModels the employ lazy loading like below:

public class AssignmentsViewModel
{

    List<AssignmentViewModel> _Assignments;
    public List<AssignmentViewModel> Assignments
    {
        get
        {
            if (_Assignments == null)
                _Assignments = new List<AssignmentViewModel>(Data.GetAssignments());
            return _Assignments;
        }
        set { _Assignments = value; }
    }
}
public class AssignmentViewModel
{
    List<NoteViewModel> _Notes;
    public List<NoteViewModel> Notes
    {
        get
        {
            if (_Notes == null)
                _Notes = new List<NoteViewModel>(Data.GetNotes());
            return _Notes;
        }
        set { _Notes = value; }
    }
}

The View has the AssignmentViewModels in a ListView and the Notes in a ListBox with a datatemplate.

When I display one of the AssignmentViewModels that has 160 items it takes 1000 ms to load. I thought it was because of the Notes property grabbing from a database table with 1.5million rows. I checked it and it only took 60ms to populate the Notes list. So I'm guessing that it's the databinding of loading the 160 items into the listbox. But that shouldn't be the case because listBoxes virtualize their content(I Snooped it and verified that the items are in a Virtualizing stackpanel).

So I'm at a loss, I don't know how to find out what is taking the extra 940 ms.

What can I do to track this down? Performance is key, and I don't know how to improve it.

+2  A: 

Could we see your XAML?

If you have your listbox inside a stackpanel (or similar control) the listbox will not virtualize its entries.

This will virtualize:

<Grid>
  <Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition Height="*" />
  </Grid.RowDefinitions>

  <Label Grid.Row=0>Your label here</Label>
  <ListBox Grid.Row=1 Your properties here... />

</Grid>

This will not:

<StackPanel>
  <Label>Your label here</Label>
  <ListBox Your properties here... />
</StackPanel>

Inside the stackpanel the listbox will render to its maximum height and depend on the stackpanel for scrolling. (No virtualizing)

Inside the grid the listbox will render to the grid row height and use its own scrollviewer. (Virtualizing)

You could also just set the Height or MaxHeight properties on the listbox instead of putting it inside a grid, but if your using a stackpanel I suspect you want autosizing of some sort.

Note that you can't cheat by putting the stackpanel inside of the grid; your listbox has to be able to derive a max height from somewhere.

Josh
+3  A: 

Without knowing what the UI is like there is the possibility that something from the UI is slowing it like a large usage of BitmapEffects.

Also, you may want to switch your list over to an ObservableCollection, as binding to that can be significantly faster then a List.

There are also some good performance profiling tools that may assist you.

rmoore
Thank you so much for these performance profiling link. I need to get into learning how to use them now. :)
Jose
A: 

Have you tried perf-testing w/o lazy loading?

Also, how complicated are your DataTemplates/Views for your items? Try running a test w/simple TextBlock representations to make sure it's not how complicated your views are.

micahtan
A: 

I would take another look at the SQL and make sure that your collection is filling. L2S will not actually populate until you enumerate the result.

private IEnumerable<Thing> Method1()
{
    return _db.Things.Where(t => t.ID > 500);
}
private void Method2()
{
    var things = Method1(); // here, 'things' doesn't really contain anything
                            // even though you've already done the db fetch
    foreach (var thing in things) // 'things' gets populated here.
    {
        // do something
    }
}

ex: Call .ToList() immediately after the data is returned. You'll see a lot more activity in the data tier that may not have been happening until your business or UI layer.

Jarrett Meyer
yeah, i understand that, the GetNotes function iterates through the list.
Jose
A: 

I figured it out. I had a textblock that needed textwrapping because it's quite large at times. I explicitly set the width of it(so that it would wrap) inside the datatemplate and for some reason that causes the VirtualizingStackPanel inside the ListBox to realize all the items in the list. I reworked it to fit it into a Grid like so..

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="3*" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Grid Grid.Column="0">
       ....
    </Grid>
    <ListBox Grid.Column="1" ItemsSource="{Binding}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" >
       ...//DataTemplate
    </ListBox>
</Grid>

This caused the listbox to load quickly. Thank your for your answers they brought me on the right path.

Jose