I have a progress bar that has its value databinded to a property called CurrentTotalVideoSize. I am updating CurrentTotalVideoSize in a loop that is iterating through a directly and combining the size of each file. Every time the iteration runs, the files are larger and thus changes the value of the progress bar. I am not sure that this is the best approach for a progress bar but it is the only one that I have thought of.
The problem I am having is that the file sizes in the directory only periodically update in the explorer window. If I repeatedly press the refresh button in the explorer window. My progress bar acts accordingly; however, without refreshing the folder, the progress bar only updates every once in a while. Is there a way to check the true size of the file without checking what windows explorer says it is at any given time?
Below is the method for iterating through the directory and combining file sizes:
public async void RenderStatus()
{
string vidPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\FCS\VidBin";
DirectoryInfo vidDir = new DirectoryInfo(vidPath);
while (IsBusy)
{
long tempCompleteVideoSize = 0;
foreach(var file in vidDir.GetFiles())
{
tempCompleteVideoSize += file.Length;
}
await Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
CurrentCompleteVideoSize = tempCompleteVideoSize;
NotifyPropertyChanged(nameof(CurrentCompleteVideoSize));
ExportPercentProgress = Math.Round((Convert.ToDouble(CurrentCompleteVideoSize) / Convert.ToDouble(TotalCompleteVideoSize) * 100), 2);
NotifyPropertyChanged(nameof(ExportPercentProgress));
}), DispatcherPriority.Render);
}
}
Any help would be very much appreciated.
The point is, that Windows has multiple layers to manage the filesystem (on top of the actual hardware layer). This enables asynchronous file handling, for example.
FileInfoaccesses the filesystem through a cache layer. That's why you must callFileSystemInfo.Refreshin order to get the info for the latest filesystem version.But I advise you to improve your code and don't continue to use your current implementation.
Your code has serious performance issues. Making the problem worse, all of the following performance issue are executed on the main thread and will therefore lead to a freezing UI.
You are iterating a directory multiple times (two times), this doubling the minimal required iteration time:
DirectoryInfo.GetFileswill enumerate the complete directory to create aFileInfolist - first iteration.foreach(var file in vidDir.GetFiles())will iterate the result of Getfiles (the fileInfo list) - second iteration.Instead process the
FileInfothe moment theDirectoryInfocollects it. This results in a single iteration in whichDirectoryInfolooks up aFileInfoand immediately returns it to you for your calculations. To achieve this, you must callDirectoryInfo.EnumerateFiles:Next performance issue is the way you poll the directory's size:
This is a good thing if you want to waste CPU resource as this will keep the main thread busy with doing redundant work.
The preferred way is to use an event based mechanism or other kinds of signaling to notify the progress observer about changes. You must always evaluate such solutions before falling back to resource expensive polling.
For example, you could use the
FileSystemWatcherand listen to theFileSystemWatcher.Changedevent. This event is also raised when the size of the directory has changed (when configured accordingly).But because the directory size is expected to change continuously, using the
FileSystemWatchercan lead to flooding the main thread in a similar waywhile (true) {}does. For that reason, I recommend polling the directory's size but move the blocking operation to a background thread and use a timer that allows to control the polling period (for example, poll every 2 seconds) to relieve the stress on the main thread.Using the
System.Threading.Timerwill execute the callback on a background thread and allows to configure the interval. This way, you keep the main thread available for more important tasks like rendering the UI or processing input events.Another performance issue is the use of
INotifyPropertyChangedin a control. A type that extendsDependencyObjectshould always prefer to implement properties as dependency properties. This means, all properties that serve either as binding target or binding source (are involved in data binding in general) like theCurrentCompleteVideoSizeproperty for example, must be dependency properties.INotifyPropertyChangedmust not be implemented by a control (orDependencyObjectin general).Dependency properties overview (WPF .NET)
How to implement a dependency property (WPF .NET)
The last minor performance issue is the use of string concatenation:
In general, use string interpolation instead:
Because of the special case of building a filesystem path, you should use the
System.Io.Path.Concathelper method, that concatenates path segments safely (e.g. eliminates the worries about path separators):The final and improved solution that uses polling with the help of the
Timercould look as follows:MainWindow.xaml.cs
MainWindow.xaml