DataGrid not creating Rows when its ItemSource is updated & Sometimes Duplicate entries

277 Views Asked by At

We are making a WPF app that uses a FileSystemWatcher to monitor changes to a directory of the user's choice and outputs the changes onto a DataGrid. In my MainWindow() constructor, I bind my DataGrid to a List I call _eventList via ItemSource.

When an OnChanged or OnRenamed FileSystemWatcher event occurs my app successfully writes in four sets of strings (change type, file affected, path, last date modified) into an array I call, _event. This constitutes one event. Then, after the creation of this _event array I make a pale attempt to handle my "The calling thread cannot access this object because a different thread owns it", cross-threading exception by calling a separate method called, SetThreadSafe().

This is where I believe things break down for me. The unwanted behaviour happening in my SetThreadSafe method is my DataGrid is showing it contains in it's Items property, entries corresponding to each event. Each index in DataGrid.Items are bound to synonymous with my _eventList (as it should), and inside each index of this List/Items property are the correct values in each index of the array within.

So, one, my DataGrid shows it contains the correct data in its Items property in my debugger, but the rows won't populate in my DataGrid.

Two, sometimes, not all, the SetThreadSafe method triggers it's IF AND it ELSE conditions both, consequently adding a duplicate entry into my DataGrid.Items/_eventList. I'm not getting the cross-thread exception anymore, but I feel like there is something I'm clearly missing here.

[UPDATE: Thanks to ASh, my question was resolved. I re-edited my code to reflect the correct adjustments. Everything is working as it should now.]

C#:

using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Windows;
using WinForms = System.Windows.Forms;

namespace EyeInTheSky
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml + backend code for FileSystemWatcher project
    /// </summary>
    public partial class MainWindow : Window
    {
        #region Fields
        private FileSystemWatcher _watcher;
        private string[] _event;
        private ObservableCollection<string[]> _eventList;
        private bool _on;
        #endregion

        #region class Constructor
        public MainWindow()
        {
            InitializeComponent();
            _eventList = new ObservableCollection<string[]>();
            DataGrid_DisplayOutput.ItemsSource = _eventList;
        }
        #endregion

        #region FileSystemWatcher Methods
        private void OnChanged(object source, FileSystemEventArgs e)
        {
            _event = new string[4];
            _event[0] = e.ChangeType.ToString();
            _event[1] = e.Name;
            _event[2] = e.FullPath;
            _event[3] = DateTime.Now.ToString();

            SetThreadSafe();
        }

        private void OnRenamed(object source, RenamedEventArgs e)
        {
            _event = new string[4];
            _event[0] = e.ChangeType.ToString();
            _event[1] = e.Name;
            _event[2] = e.OldFullPath;
            _event[3] = DateTime.Now.ToString();

            SetThreadSafe();
        }

        private delegate void SetCallback();
        private void SetThreadSafe()
        {
            if (!CheckAccess())
            {
                SetCallback d = new SetCallback(SetThreadSafe);
                Dispatcher.Invoke(d);
            }
            else
            {
                _eventList.Add(_event);
            }
        }
        #endregion

WPF XAML:

<DataGrid x:Name="DataGrid_DisplayOutput" Grid.Column="1" HorizontalAlignment="Left" Margin="22,11,0,0" Grid.RowSpan="4" VerticalAlignment="Top" Width="356" Height="392" IsTextSearchEnabled="True" RowBackground="#FFFFFAE8" AlternatingRowBackground="White" HeadersVisibility="Column" AlternationCount="1" SelectionMode="Single" IsReadOnly="True">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding [0]}" Header="Change" Width="89" IsReadOnly="True"/>
        <DataGridTextColumn Binding="{Binding [1]}" Header="Change" Width="89" IsReadOnly="True"/>
        <DataGridTextColumn Binding="{Binding [2]}" Header="Change" Width="89" IsReadOnly="True"/>
        <DataGridTextColumn Binding="{Binding [3]}" Header="Change" Width="89" IsReadOnly="True"/>
    </DataGrid.Columns>
</DataGrid>
1

There are 1 best solutions below

0
On BEST ANSWER

There are multiple places in code which need to be fixed

Change List<string[]> to ObservableCollection<string[]> to fix "rows won't populate in my DataGrid." problem. Unlike List, ObservableCollection can notify UI when items are added/deleted

_event is created only once _event = new string[4]; in constructor. It means what you only have 1 array and add it multiple times and erase values when perform change/rename. I suppose you need to create new array in OnChanged/OnRenamed

none of DataGridTextColumn has "Binding" attribute configured. since each row in DataGrid displays an array, Bindings should use indices:

<DataGridTextColumn Binding="{Binding [0]}" Header="Change" Width="89" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding [1]}" Header="File" Width="89" IsReadOnly="True"/>