I have a FileInfo property which belongs to my file class:
File
public class File : ObservableObject
{
private string _fileInfo;
public string FileInfo
{
get { return _fileInfo; }
set
{
_fileInfo = value;
OnPropertyChanged();
}
}
}
Observable Object
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
My RichTextBox is Binded to this property:
<Run Text="{Binding CurrentFile.FileInfo, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
I have a method in the File Class which reads lines from the file, one at a time
while ((currentLine = file.ReadLine()) != null)
{
_fileInfo = currentLine;
}
My problem is that the textbox is not updating upon each line I read, it only updates after the whole file is read. How can I fix this?
CodePudding user response:
Like quaabaam said in the comments you need to call FileInfo not _fileInfo in order for the OnPropertyChanged to get called. Or you could use _fileInfo and call OnPropertyChanged("FileInfo"); with it every time you change it.
So code could be something like the below.
private string _fileInfo;
public string FileInfo
{
get { return _fileInfo; }
set
{
_fileInfo = value;
OnPropertyChanged("FileInfo");
//Or
OnPropertyChanged();
}
}
And also the below.
while ((currentLine = file.ReadLine()) != null)
{
FileInfo = currentLine;
}
// Or
while ((currentLine = file.ReadLine()) != null)
{
_fileInfo = currentLine;
OnPropertyChanged("FileInfo");
}
CodePudding user response:
Your loop is too tight. It does not leave the UI enough time to update. You must slow the loop down to free resources for the GUI.
The problem is, that you don't want to slow down the loop more than necessary, as you want to have your data processing as fast as possible. The other extreme is to interrupt the loop not long enough (update frequency is too high). The result would be a UI that will update your changes, but is still too busy to handle other input like mouse or other user interaction.
In other words, you don't want to flood the UI, but at the same time avoid to slow down the processing more than necessary.
Recommended: first solution is to use the Dispatcher
asynchronously in the background. This way, the Dispatcher
can decide the optimal update rate.
This solution scales perfectly independent from the machine that the code executes on. This means, the delay is dynamic:
public async Task DisplayFileAsync()
{
using var fileReader = new StreamReader("file.txt");
while (!fileReader.EndOfStream)
{
var line = await fileReader.ReadLineAsync();
Dispatcher.InvokeAsync(() => AppendLine(line), DispatcherPriority.Background);
}
}
private void AppendLine(string line)
=> this.FileInfo = $"{line}{Environment.NewLine}";
Second solution is to force a static delay on the UI with the help of Task.Delay
and taking the risk of not using the optimal interruption length.
This solution becomes even more problematic if the code is intended to run on different machines, where we must expect a wide range of available resources and therefore require a different optimal delay:
public async Task DisplayFileAsync()
{
using var fileReader = new StreamReader("file.txt");
while (!fileReader.EndOfStream)
{
var line = await fileReader.ReadLineAsync();
AppendLine(line);
await Task.Delay(TimeSpan.FromMilliseconds(50));
}
}
private void AppendLine(string line)
=> this.FileInfo = $"{line}{Environment.NewLine}";