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>