0

I have a listbox, code below, that I'm populating with 900 items. When I scroll down the list quickly I get an exception of 'An ItemsControl is inconsistent with its items source'. Any ideas as to what is causing the issue?

                <ListBox x:Name="FileList" ItemsSource="{Binding Files}" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="True" 
                     MaxHeight="520" >
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>
                            <CheckBox x:Name="FileChecked" IsChecked="{Binding Checked}" Grid.Column="0" BorderThickness="1"/>
                            <Label Content="{Binding Name}" Grid.Column="1" />
                            <Label Content="{Binding Source}" Grid.Column="2" Style="{StaticResource FileProperties}" />
                            <Label Content="{Binding Destination}" Grid.Column="3" Style="{StaticResource FileProperties}"/>
                            <Label Content="{Binding ArchiveLocation}" Grid.Column="4" Style="{StaticResource FileProperties}"/>
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>

enter image description here

Here is a video of the issue. https://www.screencast.com/t/YUlp24zoXiG

Thomas
  • 37
  • 7
  • 1
    Is the list of files changing from somewhere else? See https://stackoverflow.com/a/22528015/1383366 – redcurry Feb 11 '19 at 15:51
  • Nope, see the video below of the issue. https://www.screencast.com/t/YUlp24zoXiG – Thomas Feb 17 '19 at 16:22
  • Can I see the definition of `Files` and how those are populated? – redcurry Feb 18 '19 at 16:40
  • public List Files { get { return _Files; } set { _Files = value; PropertyChanged(this, new PropertyChangedEventArgs("Files")); } } – Thomas Feb 18 '19 at 20:48
  • List _Files { get; set; } – Thomas Feb 18 '19 at 20:48
  • Nothing out of the ordinary there (other than `_Files` being a property rather than a field, but I don't think that's an issue). Make sure neither `Files` nor `_Files` is being changed anywhere else. If you can share your full code somewhere (e.g., GitHub), I can take a deeper look. – redcurry Feb 18 '19 at 21:54
  • Here is the github link. Thanks Redcurry. https://github.com/btbowden/wcla/tree/master/WCLA – Thomas Feb 19 '19 at 22:45

1 Answers1

0

I believe I've found the problem. I've pasted the problematic code below, so that other visitors can benefit from this answer.

// Your code from GitHub unchanged
private async void Preview_Click(object sender, System.Windows.RoutedEventArgs e)
{
    vm.Files = new List<InstrumentFile>();

    try
    {
        for (int i = 0; i < InstrumentList.Items.Count; i++)
        {
            ListBoxItem lbi = (InstrumentList.ItemContainerGenerator.ContainerFromIndex(i)) as ListBoxItem;
            ContentPresenter cp = GetFrameworkElementByName<ContentPresenter>(lbi);
            DataTemplate dt = InstrumentList.ItemTemplate;
            CheckBox cb = (dt.FindName("InstrumentChecked", cp)) as CheckBox;

            if (cb.IsChecked == true)
            {
                List<InstrumentFile> instrumentFiles = new List<InstrumentFile>();
                Instrument instrument = ((Instrument)(InstrumentList.Items[i]));

                string[] files = Directory.GetFiles(instrument.FileSource);
                foreach (string file in files)
                {
                    FileInfo fi = new FileInfo(file);
                    instrumentFiles.Add(new InstrumentFile()
                    {
                        Name = fi.Name,
                        Source = instrument.FileSource,
                        Destination = instrument.Destination,
                        ArchiveLocation = instrument.ArchiveLocation
                    });
                }
                if (string.IsNullOrEmpty(instrument.FileExt) == false)
                {
                    IEnumerable<InstrumentFile> filteredFiles = instrumentFiles.Where(f => f.Name.ToUpper().EndsWith(instrument.FileExt.ToUpper()));
                    if (filteredFiles.Count() > 0)
                        vm.Files.AddRange(filteredFiles);
                }
                else
                {
                    if (instrumentFiles.Count > 0)
                        vm.Files.AddRange(instrumentFiles);
                }

            }
        }
    }
    catch (Exception ex)
    {
        await metroWindow.ShowMessageAsync("Exception Encountered", ex.Message, MessageDialogStyle.Affirmative, Helpers.DialogSettings());
    }
    FileCount.Content = vm.Files.Count.ToString() + " files";
}

Here, you're initializing the Files property in the view model. This causes the data-binding to be updated to an empty list. There's no problem so far. However, you then add things to Files, but these changes are not propagated to the data-binding system because the list in the view model is not an ObservableCollection.

You can fix this problem in a couple of ways. One type of fix is to set the Files property in the view model after you've created and filled the list. The new code would look like this (abbreviated):

private async void Preview_Click(object sender, System.Windows.RoutedEventArgs e)
{
    // Create new variable to store the list
    var files = new List<InstrumentFile>();

    // You do a bunch of things, but you now add items to files, not to vm.Files
    files.AddRange(filteredFiles);

    // Finally, change Files
    vm.Files = files
}

That last line will raise the PropertyChanged event in the view model to update the data-binding, but will do so with the full list of items.

The second kind of fix is to change the type of Files in your view model to ObservableCollection<InstrumentFile>. Any time you make a change to Files, the correct event will be raised to update the data-binding.

redcurry
  • 2,381
  • 2
  • 24
  • 38