Monday, January 2, 2012

Lazy list box in Windows phone 7.5 (Mango)

Most phone users are concerned about network usage. Network traffic comes at a premium, and a user's perception of the quality of your app depends a lot on its responsiveness. When it comes to fetching data from a network service, it should be done in the most efficient manner possible. Making the user wait while your app downloads giant reams of data doesn't cut it. It should, instead, be done in bite-sized chunks.
To make this easy for you, I have created a ScrollViewerMonitor which uses an Attached Property to monitor a ListBox and fetch data as the user needs it. It's as simple as adding an Attached Property to a control which contains a ScrollViewer, such as a ListBox, as shown in the following example:
<ListBox ItemsSource="{Binding Items}"
         u:ScrollViewerMonitor.AtEndCommand="{Binding FetchMoreDataCommand}" />
Notice the AtEndCommand. That's an Attached Property that allows you to specify a command to be executed when the user scrolls to the end of the list.

Source help to write this blog:
Lazy Loading

Use the following code to achieve and modify according to your Model class:

In c#:

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using DanielVaughan.WindowsPhone7Unleashed;
using System.Collections.Generic;
using SnapDeal;
using System.Linq;
using System.Threading;
using System.Collections.ObjectModel;
using System.ComponentModel;
using SnapDeal.ViewModels;
using Microsoft.Phone.Controls;

namespaceLazy
{
    public class OptimizedListBox : ListBox, INotifyPropertyChanged
    {
        /// <summary>
        /// Max items to display in only scroll
        /// </summary>
        public int MaxCount { get; set; }

        public delegate void FetchDataEventHandler(object sender, ResponseArgs args);
        public event FetchDataEventHandler FetchData;

        public static readonly DependencyProperty ListProperty =
           DependencyProperty.Register(
               "ListItems",
               typeof(IDataModel),
               typeof(OptimizedListBox),
               null);


        public OptimizedListBox()
        {
            fetchMoreDataCommand = new DelegateCommand(
                obj =>
                {
                    if (busy)
                    {
                       return;
                    }
                    Busy = true;
                    if (FetchData != null)
                        FetchData(this, null);
                    else
                        Busy = false;
                });
            ScrollViewerMonitor.SetAtEndCommand(this, FetchMoreDataCommand);          
        }

        /// <summary>
        /// Actual items of list
        /// </summary>
        public IDataModel ListItems
        {
            get { return GetValue(ListProperty) as IDataModel; }
            set { SetValue(ListProperty, value); }
        }

        public void FetchCompleted(bool isUpdateNeeded)
        {
            Busy = false;

            if (!isUpdateNeeded)
                return;

            if (this.ListItems != null)
            {
                int start = this.Items.Count;
                int end = start + Constant.kMaxLoadItemCount;
                if (this.ListItems.GetType() == typeof(Zone))
                {
                    Zone loadedItems = this.ListItems as Zone;
                    if (end > loadedItems.Count() - 1)
                        end = loadedItems.Count();
                    for (int i = start; i < end; i++)
                    {
                        this.Items.Add(loadedItems[i]);
                    }
                }
            }
        }

        readonly DelegateCommand fetchMoreDataCommand;
        public ICommand FetchMoreDataCommand
        {
            get { return fetchMoreDataCommand; }
        }
            
        bool busy;
        public bool Busy
        {
            get { return busy; }
            set
            {
                if (busy != value)
                {
                    busy = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("Busy"));
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, e);
            }
        }
    }
}


In XAML:

 <local:OptimizedListBox x:Name="TodaysDealList" ListItems="{Binding TodaysDealList}" SelectionChanged="OnSelectDealItem" ItemContainerStyle="{StaticResource ListboxStretchStyle}" Margin="5, 0, 0,0"/>

To display the progress indicator just use the following line:

 <TextBlock Text="Loading..." Grid.Row="1" Style="{StaticResource LoadingStyle}" Foreground="White"
                               Visibility="{Binding Busy, ElementName=TodaysDealList,
                      Converter={StaticResource BooleanToVisibilityConverter}}"/>
The Max count indicates the how many items u want to load on each scroll, list items indicates  your actual items of list.(Hope you know about converters)
May be it will helpful for you.

-Mahantesh

1 comment: