Возможное состояние гонки с WPF ItemsCollection.ItemContainerGenerator - PullRequest
3 голосов
/ 12 января 2011

В моем приложении мне необходимо получить содержимое ItemsCollection сразу после изменения ItemsSource. Или, по крайней мере, до возможности визуального отображения контента.

Я проверил что-то похожее на следующее:

void UserControl_Loaded(object sender, EventArgs eventArgs) {
    this.itemsControl.ItemsSource = GetItemsSource();

    int ctrIndex = 0;
    DependencyObject container;
    while((container = this.itemsControl.ItemContainerGenerator.
        ContainerFromIndex(ctrIndex++)) != null) {

        DoSomething(VisualTreeHelper.GetChild(container, 0));
    }
}

Проблема в том, что в точке вызова DoSomething значение VisualTreeHelper.GetChildrenCount(container) равно 0. Если вместо этого этот код вызывается в более поздний момент времени - например, в ответ на срабатывание события Button.Click, VisualTreeHelper.GetChildrenCount является ожидаемым значением, и код предположительно будет работать.

PS. Я также попытался провести цикл while внутри анонимной функции:

this.itemsControl.ItemContainerGenerator.ItemsChanged += (_sender, _ea) => {
    int ctrIndex = 0;
    DependencyObject container;
    while((container = this.itemsControl.ItemContainerGenerator.
        ContainerFromIndex(ctrIndex++)) != null) {

        DoSomething(VisualTreeHelper.GetChild(container, 0));
    }
};

К сожалению, поведение идентично.

редактировать

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

Я полагаю, что нашел самый ранний момент, когда я смог безопасно захватить сгенерированные контейнеры. Тем не менее, я все еще не обращаю внимания на сгенерированный контент внутри этих контейнеров. Обратите внимание на следующее:

this.itemsControl.ItemContainerGenerator.StatusChanged += new EventHandler(StatusChanged);

void StatusChanged(object sender, EventArgs e) {
  var cg = this.itemsControl.ItemContainerGenerator;
  if(cg.Status == GeneratorStatus.ContainersGenerated && cg.ContainerFromIndex(0) != null) {
    DoStuff();
  }
}

В момент вызова DoStuff () контейнеры, возвращаемые из ContainerFromIndex, не равны NULL. Тем не менее, VisualTreeHelper.GetChildrenCount(container) равно 0. Мне бы очень хотелось узнать, решил ли кто-нибудь это.

Ответы [ 2 ]

3 голосов
/ 30 октября 2013

Я столкнулся с той же проблемой несколько минут назад. Небольшая разница в том, что мне нужна была точная информация о местоположении и размере контейнеров с предметами. Я попытался с вашей попыткой прослушать событие StatusChanged ItemContainerGenerator и, наконец, обнаружил, что, хотя статус стал ContainersGenerated и контейнеры действительно были сгенерированы, они еще не были размечены.

Так что я сделал что-то действительно грубое. Сначала я установил флаг, скажем, _updatePending, в то время как статус ItemContainerGenerator стал ContainersGenerated, затем я обработал событие LayoutUpdated ItemsControl, которое будет запускаться довольно часто, чтобы проверить _updatePending флаг и обозначены ли контейнеры с предметами:

var firstContainer = this.ItemsContainer.ItemContainerGenerator.ContainerFromIndex(0) as FrameworkElement;
if (_updatePending 
    && firstContainer != null 
    && firstContainer.IsLoaded)
{
    // do stuff
    _updatePending = false;
}

Это жестоко, но как-то эффективно.

0 голосов
/ 12 января 2011

Проблема в том, что вы не можете перебирать коллекцию во время ее изменения.Один из способов преодолеть это - смотреть на объект ItemsSource сразу после заполнения данными, а не пытаться перебирать ItemsSource в элементе управления.Если вы используете шаблон MVVVM, вы должны иметь возможность заполнить свойство коллекции (то, которое вы связываете с ItemsSource вашего элемента управления) в своей ViewModel и проверить, когда данные возвращаются из базы данных / службы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...