如何创建快速加载包装ListBox?

时间:2012-07-10 11:30:55

标签: c# windows-phone-7 xaml listbox virtualization

我做了一个很好的小三项宽的瓷砖列表,作为开关。它看起来像这样:

tile switcher

好望啊?好吧,我在垂直滚动列表中有大约130个这样的图块,加载需要很长时间。根据性能分析工具,每个元素需要大约18ms来渲染 - 这给了我大约2.3秒的渲染时间。在设备上,通常是两倍的时间。这不会是一场危机,但在这些元素被绘制之前,用户界面完全是黑色的,没有反应。

经过一些在线研究后,我意识到这是因为工具包中的WrapPanel控件没有虚拟化其项目 - 从而使GPU一次渲染所有对象(占用大量内存)进行中)。

现在,有没有办法让这个更快?

XAML:

<ListBox x:Name="ChannelsListBox" Grid.Row="2" Margin="0,40,0,0">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <toolkit:WrapPanel />
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.Template>
        <ControlTemplate>
            <ItemsPresenter />
        </ControlTemplate>
    </ListBox.Template>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid x:Name="ChannelTile" Margin="6,6,6,6" Tap="ChannelTile_Tap">
                <!-- context menu code removed -->
                <Rectangle Width="136" Height="136" Fill="{StaticResource LightGrayColor}" />    
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

ListBox的ItemsSource在代码隐藏中设置 - 如果你想知道的话。

2 个答案:

答案 0 :(得分:3)

好吧,如果您从另一个线程异步填充列表框,则可以避免无响应的UI。

EDITED2:

public partial class MainPage : UserControl
{
    public MainPage()
    {
        InitializeComponent();

        /* In the xaml code:
           <ListBox x:Name="ChannelsListBox" ItemsSource="{Binding ListOfTestClasses}" ...
        */

        var vm = new MainPageViewModel();
        DataContext = vm;

        vm.StartLoadingDataAsync(10000);
    }
}

public class MainPageViewModel
{
    public ObservableCollection<TestClass> ListOfTestClasses { get; set; }
    private BackgroundWorker workerThread;
    public MainPageViewModel()
    {
        ListOfTestClasses = new ObservableCollection<TestClass>();
        workerThread = new BackgroundWorker();
        workerThread.DoWork += new DoWorkEventHandler((object sender, DoWorkEventArgs e) =>
        {
            for (int i = 0; i < (int)e.Argument; i++)
            {
                Deployment.Current.Dispatcher.BeginInvoke(() =>
                {
                    ListOfTestClasses.Add(new TestClass { Text = "Element " + (i + 1) });
                });

                Thread.Sleep(150);
            }
        });
    }

    public void StartLoadingDataAsync(int numberOfElements)
    {
        workerThread.RunWorkerAsync(numberOfElements);   
    }
}

public class TestClass
{
    public string Text { get; set; }
}

答案 1 :(得分:1)

一些可能有用的想法:

  1. 查找或实施虚拟化WrapPanel。这是最合适的解决方案,但我认为我还没有看到一个可靠的实现方案。但是为了这个目的,也许你不需要完美,可以逃避别人已经写过的东西。
  2. 使用包含水平StackPanel子级的父虚拟化垂直StackPanel。为此,您需要将单个数据序列重新整形为一个较短的3项条目序列。但是,这可能不会太难,应该为您提供理想解决方案的大部分好处。
  3. 考虑像我为DeferredLoadListBox所做的那样实现“懒惰”容器。基本思想是延迟渲染容器直到它们出现在屏幕上。我在这里有更多信息和示例代码:http://blogs.msdn.com/b/delay/archive/2010/09/08/never-do-today-what-you-can-put-off-till-tomorrow-deferredloadlistbox-and-stackpanel-help-windows-phone-7-lists-scroll-smoothly-and-consistently.aspx