Home > OS >  WPF: automatically update ListView
WPF: automatically update ListView

Time:07-25

I have a ListView that shows me .txt files in a specific folder. I want the ListView to update automatically as soon as a file is added (or deleted) to that folder. I found the FileSystemWatcher class on the Internet, but I can't get the program to run properly. Anyone have any tips? I'm fairly new to C# programming.

My XAML Code:

        <ScrollViewer Grid.Row="1" Grid.ColumnSpan="2" DockPanel.Dock="Top">
        <ListView x:Name="TxtListView"  SelectionMode="Multiple">
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn>
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox Tag="{Binding .}" IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListViewItem}}, Path=IsSelected}" />
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn/>
                        <GridViewColumn/>
                        <GridViewColumn/>
                        <GridViewColumn/>
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
    </ScrollViewer>

Code-Behind:

    public partial class MainWindow : Window
{
    public string path = @"C:\txt";
    public MainWindow()
    {
        InitializeComponent();

        //Read txt Files
        DirectoryInfo d = new DirectoryInfo(path);
        FileInfo[] txtFiles = d.GetFiles();
        TxtListView.ItemsSource = txtFiles;

        MonitorDirectory(path);
    }

    public void MonitorDirectory(string path)

    {

        FileSystemWatcher fileSystemWatcher = new FileSystemWatcher();

        fileSystemWatcher.Path = path;

        fileSystemWatcher.Created  = FileSystemWatcher_Created;

        fileSystemWatcher.EnableRaisingEvents = true;

    }

    private void FileSystemWatcher_Created(object sender, FileSystemEventArgs e)
    {
        DirectoryInfo d = new DirectoryInfo(path);
        FileInfo[] txtFiles = d.GetFiles();
        TxtListView.ItemsSource = txtFiles;
    }
    //Refresh-Button
    private void Refresh(object sender, RoutedEventArgs e)
    {
        DirectoryInfo d = new DirectoryInfo(path);
        FileInfo[] txtFiles = d.GetFiles();
        TxtListView.ItemsSource = txtFiles;
    }
}

CodePudding user response:

Here is a simple example that uses the MVVM architectural pattern.


There is a view model class that holds the FileSystemWatcher and an ObservableCollection of FileInfo objects that is used as source of a data binding in the view.

You have to take care for the fact that the events of the FileSystemWatcher are fired in a background thread, e.g. by using the EnableCollectionSynchronization method.

public class ViewModel
{
    private readonly object filesLock = new object();

    public ObservableCollection<FileInfo> Files { get; }
        = new ObservableCollection<FileInfo>();

    public ViewModel(string path)
    {
        BindingOperations.EnableCollectionSynchronization(Files, filesLock);

        lock (filesLock)
        {
            foreach (var fileInfo in new DirectoryInfo(path).EnumerateFiles())
            {
                Files.Add(fileInfo);
            }
        }

        var fsw = new FileSystemWatcher(path);
        fsw.Created  = FileCreated;
        fsw.Deleted  = FileDeleted;
        fsw.EnableRaisingEvents = true;
    }

    private void FileCreated(object sender, FileSystemEventArgs e)
    {
        lock (filesLock)
        {
            Files.Add(new FileInfo(e.FullPath));
        }
    }

    private void FileDeleted(object sender, FileSystemEventArgs e)
    {
        lock (filesLock)
        {
            var removed = Files.FirstOrDefault(fi => fi.Name == e.Name);

            if (removed != null)
            {
                Files.Remove(removed);
            }
        }
    }
}

An instance of the view model is assigned to the DataContext property of the MainWindow

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        DataContext = new ViewModel(...); // insert the directory path here
    }
}

to enable data binding in the view.

The XAML below also uses a CollectionViewSource to enable sorting.

<Window x:Class="FileSystemWatcherTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <CollectionViewSource x:Key="cvs" Source="{Binding Files}">
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="Name"/>
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>
    </Window.Resources>
    <Grid>
        <ListView ItemsSource="{Binding Source={StaticResource cvs}}">
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn>
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Name}"/>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>
  • Related