Почему ItemContainerGenerator.ContainerFromIndex() возвращает null и как избежать этого поведения?

Я использую этот фрагмент для анализа строк, которые я выбрал в сетке данных.

for (int i = 0; i < dgDetalle.Items.Count; i++)
{
    DataGridRow row = (DataGridRow)dgDetalle.ItemContainerGenerator.ContainerFromIndex(i);
    FrameworkElement cellContent = dgDetalle.Columns[0].GetCellContent(row);
    // ... code ...
}

Цикл проходит гладко, но при обработке определенных индексов вторая строка выдает нулевое исключение. Документация MSDN гласит, что ItemContainerGenerator.ContainerFromIndex(i) вернет ноль, если "если элемент не реализован", но это не поможет мне догадаться, как я могу получить желаемое значение.

Как я могу отсканировать все строки? Есть ли другой путь?

ОБНОВИТЬ

Я использую этот фрагмент, чтобы прочитать CheckBox как объяснено здесь. Так что я не могу использовать привязку или ItemSource вообще, если я не изменю много вещей. И я не могу. Я делаю обслуживание кода.

3 ответа

Решение

DataGrid при виртуализации элементов соответствующие строки (т.е. контейнеры) создаются только тогда, когда строка находится в поле зрения.

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

Попробуй это,

DataGridRow row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(index);
        if (row == null)
        {
                          grid.UpdateLayout();
            grid.ScrollIntoView(grid.Items[index]);
            row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(index);
        }

Используйте эту подписку:

TheListBox.ItemContainerGenerator.StatusChanged += (sender, e) =>
{
  TheListBox.Dispatcher.Invoke(() =>
  {
     var TheOne = TheListBox.ItemContainerGenerator.ContainerFromIndex(0) as ListBoxItem;
       if (TheOne != null)
         // Use The One
  });
};

В дополнение к другим ответам: элементы недоступны в конструкторе класса управления (страница / окно / и т. Д.).

Если вы хотите получить к ним доступ после создания, используйте Loaded событие:

public partial class MyUserControl : UserControl
{
    public MyUserControl(int[] values)
    {
        InitializeComponent();

        this.MyItemsControl.ItemsSource = values;

        Loaded += (s, e) =>
        {
            for (int i = 0; i < this.MyItemsControl.Items.Count; ++i)
            {
                // this.MyItemsControl.ItemContainerGenerator.ContainerFromIndex(i)
            }
        };
    }
}

В моем случае grid.UpdateLayout(); не помогло мне нужно DoEvents() вместо:

    DataGridRow row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(index);

    if (row == null)
    {

        WPFTools.DoEvents();

        grid.ScrollIntoView(grid.Items[index]);
        row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(index);
    }


    /// <summary>
    /// WPF DoEvents
    /// Source: https://stackru.com/a/11899439/1574221
    /// </summary>
    public static void DoEvents()
    {
        var frame = new DispatcherFrame();

        Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
            new DispatcherOperationCallback(
                delegate (object f)
                {
                    ((DispatcherFrame)f).Continue = false;
                    return null;
                }), frame);

        Dispatcher.PushFrame(frame);
    }
Другие вопросы по тегам