天天看點

Windows Phone 7 如何實作高效率的橫向排版ListBox

使用ListBox進行資料綁定的時候預設都是豎向的排版方式,意思就是一個Item會占用一行的位置,豎向地并排下去。當我們使用ListBox時,使用橫向排版的時候該怎麼辦呢?也就是說要在一行的位置上放兩個或者兩個以上的Item。通常的解決方法,我們會使用toolkit控件裡面的WrapPanel排版。

例如:

<ListBox Name="StackPanelListBox"> 

    <ListBox.ItemTemplate> 

        <DataTemplate> 

                <TextBlock Text="{Binding Name}" Height="110" Width="110"></TextBlock> 

        </DataTemplate> 

    </ListBox.ItemTemplate> 

    <ListBox.ItemsPanel> 

        <ItemsPanelTemplate> 

            <toolkit:WrapPanel></toolkit:WrapPanel> 

        </ItemsPanelTemplate> 

    </ListBox.ItemsPanel> 

</ListBox> 

通過設定ListBox的ItemsPanel屬性的模闆為WrapPanel控件的排版方式就會自動地根據Item的寬度大小并排地排在一行上,當排滿了一行的時候就會繼續排列在下面的一行上,如此類推不斷地排下去,就實作了橫向的資料綁定排版。但是這種方式有一個很緻命的性能缺陷,因為會一次性地把所有的Item都初始化完成并展現在UI上,當Item的數量很多的時候就需要耗費很長的響應時間,導緻使用者體驗很差,也會影響程式的性能。

下面使用一種新的方法來解決WrapPanel橫向排版引發的性能問題。

當我們使用ListBox預設的排版方式綁定資料時,它不會一次性地将所有的Item全部初始化完畢并展示在UI上,它會根據螢幕的位置初始化部分的Item,這部分Item是在你看到的螢幕上的Item和螢幕上下一屏的Item。那就利用這種原理來設計一個橫向排版的ListBox資料綁定。

實作的方法是先将Item進行分組,一行要排列多少個Item那麼就一組有多少個Item,分好組之後再把組作為一個新的Item建構一個新的資料綁定源。假如我們需要綁定的資料源的Item有200個,那麼我們一行要排4個Item就要分50組,這時候構成的新的資料綁定源就是50行,在整體的ListBox裡面是豎向排版,在一行的資料裡面是橫向排版,這就實作了跟WrapPanel的自動排版一樣的綁定效果了,但是性能卻比WrapPanel的自動排版要好很多。

執行個體:

Item.cs 資料源的Item

using System;  

using System.Collections.Generic;  

using System.Linq;  

using System.Text;  

using System.ComponentModel;  

using System.Windows.Media;  

using System.Threading;  

namespace GridListDemo  

{  

    public class Item : INotifyPropertyChanged  

    {  

        private string _name;  

        public string Name  

        {  

            get  

            {  

                return this._name;  

            }  

            set  

                if (this._name != value)  

                {  

                    this._name = value;  

                    RaisePropertyChanged("Name");  

                }  

        }  

        public event PropertyChangedEventHandler PropertyChanged;  

        public void RaisePropertyChanged(string info)  

            PropertyChangedEventHandler propertyChanged = this.PropertyChanged;  

            if (propertyChanged != null)  

                propertyChanged(this, new PropertyChangedEventArgs(info));  

    }  

GridDataRow.cs 組的資料源集合

using System.Collections;  

using System.Reflection;  

    public class GridDataRow<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable  

        private IList<T> _items;//所有的item集合  

        private int _offset;//偏移量 即 前面的item數量  

        private int _rowItemCount;//行數  

        public GridDataRow(IList<T> itemsList, int offset, int rowItemCount)  

            this._items = itemsList;  

            this._offset = offset;  

            this._rowItemCount = rowItemCount;  

        public void Add(T item)  

            throw new NotImplementedException();  

        public void Clear()  

        public bool Contains(T item)  

        public void CopyTo(T[] array, int arrayIndex)  

        public IEnumerator<T> GetEnumerator()  

        public int IndexOf(T item)  

        public void Insert(int index, T item)  

        public bool Remove(T item)  

        public void RemoveAt(int index)  

        IEnumerator IEnumerable.GetEnumerator()  

        public int Count  

                //取行數和剩下的條數的最小的一個  

                int num = this._items.Count - this._offset;  

                return Math.Min(this._rowItemCount, num);  

        public bool IsReadOnly  

                return true;  

        public T this[int index]  

                return this._items[this._offset + index];  

                throw new NotImplementedException();  

RowCollection.cs 行的綁定資料源的集合

using System.Collections.Specialized;  

using System.Windows;  

    public class RowCollection<T> : IList<GridDataRow<T>>, IList where T : new()  

        private IList<T> _itemsCollection;  

        private int _rowItemCount;//一行的數量  

        public RowCollection(IList<T> itemsCollection, int rowItemCount)  

            this._itemsCollection = itemsCollection;  

        public void Add(GridDataRow<T> item)  

        public int Add(object value)  

        public bool Contains(object value)  

        public bool Contains(GridDataRow<T> item)  

        public void CopyTo(Array array, int index)  

        public void CopyTo(GridDataRow<T>[] array, int arrayIndex)  

        public IEnumerator<GridDataRow<T>> GetEnumerator()  

        public int IndexOf(object value)  

            return -1;  

        public int IndexOf(GridDataRow<T> item)  

        public void Insert(int index, GridDataRow<T> item)  

        public void Insert(int index, object value)  

        public void Remove(object value)  

        public bool Remove(GridDataRow<T> item)  

                //總數處于一行的數量等于清單的行數  

                return Convert.ToInt32(Math.Ceiling((double)(((double)this._itemsCollection.Count) / ((double)this._rowItemCount))));  

        public bool IsFixedSize  

                return false;  

        public bool IsSynchronized  

        public GridDataRow<T> this[int index]  

                return new GridDataRow<T>(this._itemsCollection, index * this._rowItemCount, this._rowItemCount);  

        public object SyncRoot  

                return this;  

        object IList.this[int index]  

                return this[index];  

MyGridRow.cs 自定義的組控件

using System.Windows.Controls;  

using System.Windows.Data;  

    /// <summary> 

    /// 橫向排版,繼承Canvas控件  

    /// </summary> 

    public class MyGridRow : Canvas  

        //定義ItemsSource屬性  

        public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IList<Item>), typeof(MyGridRow), new PropertyMetadata(new PropertyChangedCallback(MyGridRow.OnItemsSourceChanged)));  

        /// <summary> 

        /// 初始化GridRow控件  

        /// </summary> 

        private void ApplyRaw()  

            if ((this.ItemsSource == null) || (this.ItemsSource.Count != base.Children.Count))  

                base.Children.Clear();  

                if (this.ItemsSource != null)  

                    for (int i = 0; i < this.ItemsSource.Count<Item>(); i++)  

                    {  

                        Item item = this.ItemsSource[i];  

                        TextBlock tb = new TextBlock  

                        {  

                            DataContext = item,  

                            Width = 80.0,  

                            Height = 80.0  

                        };  

                        Binding binding = new Binding("Name")  

                            FallbackValue = null 

                        BindingOperations.SetBinding(tb, TextBlock.TextProperty, binding);  

                        //添加目标到Canvas控件裡面  

                        base.Children.Add(tb);  

                        Canvas.SetLeft(tb, (double)(i * 0x72));  

                    }  

            else  

                for (int j = 0; j < this.ItemsSource.Count<Item>(); j++)  

                    Item item2 = this.ItemsSource[j];  

                    TextBlock tb2 = (TextBlock)base.Children[j];  

                    tb2.Text = item2.Name;  

        /// ItemsSource改變事件  

        /// <param name="d"></param> 

        /// <param name="e"></param> 

        private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)  

            (d as MyGridRow).ApplyRaw();  

        //ItemsSource屬性  

        public IList<Item> ItemsSource  

                return (IList<Item>)base.GetValue(ItemsSourceProperty);  

                base.SetValue(ItemsSourceProperty, value);  

在頁面中實作

 <phone:PhoneApplicationPage.Resources> 

        <DataTemplate x:Key="GridViewTemplate"> 

            <myControl:MyGridRow ItemsSource="{Binding}" Height="114" Width="480" /> 

    </phone:PhoneApplicationPage.Resources> 

.......  

<ListBox Name="GridItemsListBox" HorizontalAlignment="Left" 

                 ItemTemplate="{StaticResource GridViewTemplate}" /> 

List<Item> source = new List<Item>();  

            for (int i = 0; i < 200; i++)  

                source.Add(new Item { Name = "name" + i });  

            this.GridItemsListBox.ItemsSource = new RowCollection<Item>(source, 4); 

運作的效果

<a href="http://blog.51cto.com/attachment/201212/155106775.png" target="_blank"></a>

本文轉自linzheng 51CTO部落格,原文連結:http://blog.51cto.com/linzheng/1078356

繼續閱讀