Имейте ObservableCollection обновлять пользовательский интерфейс, когда элементы добавляются в - PullRequest
0 голосов
/ 13 января 2011

Я пишу приложение Silverlight и пытаюсь улучшить время загрузки.

Когда моя страница загружается, я сначала инициализирую свою коллекцию ObservableCollection:

        this.MyItems = new ObservableCollection<Item>();

Мой пользовательский интерфейс - это ListBox, который я связываю с ObservableCollection через код. В MainPage_Loaded:

        MyList.ItemsSource = App.ViewModel.MyItems;

Теперь я связываю пользовательский интерфейс с моей моделью. Я ожидаю, что это будет эффективно, так как коллекция пуста, а остальная часть пользовательского интерфейса может продолжать загружаться (не уверен, что мое предположение верно).

        DataContext = App.ViewModel;

Теперь я хочу добавить элементы в мою коллекцию:

        for (int i = 0; i < number_of_items; i++)
        {
            this.MyItems.Add(myItems[i]); // myItems is a List<Item> already populated
            Thread.Sleep(20);
        }

Моя цель состояла в том, чтобы позволить потоку спать, чтобы у него было время визуализировать интерфейс для каждого элемента списка. Кроме того, я ожидал, что мой пользовательский интерфейс будет отображать по одному элементу за раз.

В результате элементы ListBox появляются сразу. Если я установлю Sleep на 1 секунду, ListBox заполняется через 1 секунду, умноженное на количество элементов.

Какой хороший способ оптимизировать эту операцию? Если это бесполезно, я также могу просто связать свой ListBox с полностью заполненной коллекцией ObservableCollection. Спасибо!

Ответы [ 2 ]

4 голосов
/ 13 января 2011

Попробуйте переместить цикл в фоновый поток.Вот один из способов сделать это.

Phạm Tiểu Giao - Темы в WP7

Обратите внимание, что вам необходимо отправить обновление пользовательского интерфейса.Что-то вроде

Dispatcher.BeginInvoke( () => { this.MyItems.Add(myItems[i]); } );

Sleep будет работать, если вы хотите использовать фиксированный период времени.Просто убедитесь, что период времени всегда длиннее, чем время, необходимое для обновления дисплея, иначе вы будете потенциально перегружать поток пользовательского интерфейса обновлениями быстрее, чем он может обработать.

2 голосов
/ 28 марта 2011

Причина, по которой мы связываемся с ObservableCollection, заключается в том, что происходит встроенное уведомление об обновлениях свойств через реализацию интерфейса INotifyPropertyChanged.Это приводит к тому, что событие запускается при каждом обновлении базовой коллекции, что, в свою очередь, вызывает перерисовку связанных элементов UI (в данном случае ListBox).Шаблон данных применяется к каждому перерисовыванию для каждого элемента в коллекции и автоматически выполняется посредством привязки данных.Элементы добавляются в вашу коллекцию быстрее, чем может быть проведено рисование (в отдельном потоке), поэтому, по-видимому, ваша загрузка задерживается до тех пор, пока не будут добавлены все элементы.Вы пропускаете циклы перерисовки визуально на экране, поскольку они рисуются и становятся недействительными на экране при добавлении новых элементов.

Это означает, что ваш вызов Thread.Sleep только задерживает полную перерисовку элемента для каждого элементаэто добавляется (* количество добавляемых элементов объясняет, почему ваш пользовательский интерфейс перерисовывается полностью для каждого элемента, но только после того, как все сделали соответствующие вызовы Thread.Sleep, которые блокируют поток пользовательского интерфейса на время n * sleepValue).Вот почему нам нужно использовать объект Dispatcher, как указано выше, поскольку эти вызовы выполняются в другом потоке.Это позволяет нам перерисовывать поток пользовательского интерфейса, который, по сути, синхронизирует вызовы блокировки.

Я бы совершенно не использовал здесь Диспетчер, поскольку он является избыточным и препятствует выполнению собственной синхронизации, поскольку диспетчер может ссылаться на него.элемент, который еще не был создан и добавлен к визуальным или логическим деревьям (как ясно из вашего комментария по установке значения до 1000 мс против 20 мс).Вы по-прежнему будете перерисовывать все элементы в коллекции, поскольку каждый из них добавляется, делая ваш спящий вызов недействительным или нефункциональным из-за отсутствия лучшего термина.

В качестве альтернативного решения я предлагаю вам добавить раскадровкуанимация свойства Opacity корневого элемента шаблона данных для создания визуального эффекта от элементов, выполняющих что-то «по одному при добавлении».Таким образом, когда каждый элемент добавляется в базовую коллекцию, он будет отрисовываться с помощью анимации исчезновения непрозрачности, создавая иллюзию, что каждый элемент добавляется по одному (и анимируется в вид) с отдельными анимациями (с различными смещениями в пределахопределенная анимация).Я верю, что это даст вам эффект, который вы ищете.Но поскольку вызов draw поступает из ListBox, поскольку он поддерживает свою коллекцию элементов, вся коллекция будет аннулирована при каждом вызове .Add для вашего объекта ViewModel ObservableCollection.На самом деле нет способа переопределить это поведение, так как это происходит на одном уровне выше по иерархии.Я бы посоветовал против предоставленного подхода.

...