Bhawk1990
Bhawk1990

Reputation: 136

Rendering ListBox takes too long on Windows Phone

I am working on a Windows Phone 7 Application using Local SQLite Database and I'm having an issue with the rendering time of pages that use DataBinding.

Currently it takes 60-70ms to retrieve the data from the database. Then it takes about 3100ms to render the data retrieved using a ListBox with DataBinding.

Here you can see the DataTemplate of the ListBox:

<DataTemplate x:Key="ListBoxItemTemplate">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="68" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <TextBlock x:Name="TimeColumn"
                        Text="{Binding TimeSpan}" Grid.Column="0" Grid.Row="0"
                        Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" />
        <TextBlock Text="{Binding Stop.StopName}" Grid.Column="1" Grid.Row="0" 
                                Margin="15,0,0,0" TextWrapping="NoWrap" Foreground="Black"
                                HorizontalAlignment="Left" VerticalAlignment="Center" />
    </Grid>
 </DataTemplate>

Comment: I have tried it using Canvas instead of Grid too, same result.

Then, the database loads data into a CSList (using ViciCoolStorage) and that gets Binded to the ListBox:

StationList.ItemsSource = App.RouteViewModel.RouteStops;

Comment: I have tried to add the elements of the CSList to an ObservableCollection and bind that to the interface but didn't seem to change anything.

Question: Am I doing something wrong that results in a huge load time - even if just loading 10 elements -, or this is normal? Do you have any recommendations to get a better performance with DataBinding?

Thank you for your answers in advance!

Corresponding Code Parts:

RouteViewModel.cs

private Route rRoute;
public Route Route
{
    get
    {
        if (rRoute != null)
        {
            return rRoute;
        }
        else
        {
            return new Route();
        }
    }
}

public void LoadRoute(string index)
{
    try
    {
        if (rRoute.RouteId != index)
        {
            RouteLoaded = false;
            StationsLoaded = false;
            TimetableLoaded = false;
        }
    }
    catch (Exception) { }

    this.index = index;

    if (!RouteLoaded)
    {
        NotifyPropertyChanging("Route");
        rRoute = Route.ReadSafe(index);
        RouteLoaded = true;
        NotifyPropertyChanged("Route");
    }
}

private CSList<RouteTime> rtLine;
public CSList<RouteTime> RouteStops
{
    get
    {
        if (rtLine != null)
        {
            return rtLine;
        }
        else
        {
            return new CSList<RouteTime>();
        }
    }
}

public void LoadRouteStops()
{
    LoadRoute(index);

    if (!this.StationsLoaded)
    {
        NotifyPropertyChanging("RouteStops");
        rtLine = rRoute.RouteTimes.FilteredBy("DirectionId = @DirectionId", "@DirectionId", this.direction).OrderedBy("TimeSpan");
        NotifyPropertyChanged("RouteStops");

        StationsLoaded = true;
    }
}

RouteView.xaml.cs

private string index;
private bool visszaut = false;

public RouteView()
{
    InitializeComponent();
    Loaded += new System.Windows.RoutedEventHandler(RouteView_Loaded);
}

void RouteView_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
    DataContext = App.RouteViewModel;
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    NavigationContext.QueryString.TryGetValue("index", out index);

    App.RouteViewModel.LoadRoute(index);
    App.RouteViewModel.Direction = Convert.ToInt32(visszaut);
    App.RouteViewModel.LoadRouteStops();
    StationList.ItemsSource = App.RouteViewModel.RouteStops;
}

RouteTime.cs - Class Implementation

[MapTo("RouteTimes")]
public class RouteTime : CSObject<RouteTime, int>
{
    public int RouteTimeId
    {
        get
        {
            return (int)GetField("RouteTimeId");
        }
        set
        {
            SetField("RouteTimeId", value);
        }
    }

    public int RouteId
    {
        get
        {
            return (int)GetField("RouteId");
        }
        set
        {
            SetField("RouteId", value);
        }
    }

    public int StopId
    {
        get
        {
            return (int)GetField("StopId");
        }
        set
        {
            SetField("StopId", value);
        }
    }

    public int TimeSpan
    {
        get
        {
            return (int)GetField("TimeSpan");
        }
        set
        {
            SetField("TimeSpan", value);
        }
    }

    public Direction DirectionId
    {
        get
        {
            return (Direction)GetField("DirectionId");
        }
        set
        {
            SetField("DirectionId", value);
        }
    }

    [OneToOne(LocalKey = "StopId", ForeignKey = "StopId")]
    public Stop Stop
    {
        get
        {
            return (Stop)GetField("Stop");
        }
        set
        {
            SetField("Stop", value);
        }
    }

    [ManyToOne(LocalKey = "RouteId", ForeignKey = "RouteId")]
    public Route Route
    {
        get
        {
            return (Route)GetField("Route");
        }
        set
        {
            SetField("Route", value);
        }
    }
}

Upvotes: 1

Views: 578

Answers (5)

pegasuspect
pegasuspect

Reputation: 1031

The problem is mostly because of setting a lot of binding. Try to keep bindings minimal and try to do the hard work on the code behind. Such as;

"binding a list of string to xaml one by one"
versus
"by string.join method with just one binding to a text block"

I had

<TextBlock Text="{Binding Times[0]}"/>
<TextBlock Text="{Binding Times[1]}"/>
...
<TextBlock Text="{Binding Times[9]}"/>

And changed to

<TextBlock Text="{Binding TimesToLongString}"/>

This fixed the delay.
Used to load in 9 sec with 3 items.
Now it loads within 110ms with 10 items.

To check the time it takes to load the list, you can check out this post

Upvotes: 0

Bhawk1990
Bhawk1990

Reputation: 136

Okay so, in this scenario it seems the source of the slow rendering was the [OneToOne] connection between the RouteTime and Stop classes. If I bind to any of the variables that is stored in the linked class, the rendering takes a long time.

Fixed by

I have added a new partial class in the code where I need to show the results.

public partial class StopTimes
{
    public int TimeSpan
    {
        get;
        set;
    }

    public string StopName
    {
        get;
        set;
    }
}

Using an ad-hoc query in Vici CoolStorage, I've made my own query to request the data needed and viola, everything is there and rendering didn't take more then 1 second. Perhaps, during the rendering it requests the StopName field with an SQLQuery one by one?

Don't know, but thank you for your help anyway :)

Upvotes: 1

dotMorten
dotMorten

Reputation: 1966

I'm confused: How do you do local SQLite on Windows Phone 7? Did you mean WP8 where this is supported? In any case, consider using LongListSelector (built into WP8 - Toolkit in WP7), since it does a better job with virtualization when you have a lot of items to render.

Upvotes: 0

Lance McCarthy
Lance McCarthy

Reputation: 1917

Have you tried doing the filtering of your list outside of the LoadRouteStops() method? Maybe in the code behind instead of the viewModel? It seems like a lot of work to be done in between propertyChanging and propertyChanged notifications.

Upvotes: 0

user1146887
user1146887

Reputation:

DataBinding should not be the problem in this scenario - I never had any problems with it. It has something to do with your SQL DB for sure. I had lists of around 200 items, and it rendered fine under a reasonable time-slot of 100ms.

It either is your SQL implementation or you are using ViciCoolStorage wrong. Post your code.

Upvotes: 0

Related Questions