Make Wpf more Smooth - c#

I load a lage data fra a text File and display it in a Datatgrid,
the problem is that the windows is slow and not smootth,
how can i implemented the code below better?
the button Code:
private async void MILoadLogFile_Click(object sender, RoutedEventArgs e) {
// Configure open file dialog box
OpenFileDialog oFD = new OpenFileDialog();
// Did they click on the OK button?
if (oFD.ShowDialog() == true) {
await myLogSession.LoadfromFileAsync(oFD.FileName);
}
}
the locad method:(sorry for long Code)
public async Task LoadfromFileAsync(String fileName) {
compassLogCollection.Clear();
StreamReader streamReader = new StreamReader(fileName);
if (fileName.Contains("Compass")) {
String temp = "";
String line;
DateTime dateTime = new DateTime();
LoggingLvl loggingLvl = new LoggingLvl();
LoggingLvl.ELoggingLvl eLoggingLvl = new LoggingLvl.ELoggingLvl();
char[] delimiters = new[] {' '};
string threadId = "";
string loggingMessage;
string dateAndTimestamp = "";
int ff = 0;
try {
using (streamReader) {
while ((line = await streamReader.ReadLineAsync()) != null) {
//while ((line = streamReader.ReadLine()) != null) {
string[] parts = line.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
foreach (string t in parts) {
switch (ff) {
case 0:
dateAndTimestamp = t;
break;
case 1:
dateAndTimestamp += " " + t.Replace(",", ".");
dateTime = DateTime.Parse(dateAndTimestamp);
dateAndTimestamp = "";
break;
case 2:
eLoggingLvl = loggingLvl.ParseLoggingLvl(t);
break;
case 3:
threadId = t;
break;
default:
temp += t;
break;
}
ff++;
}
loggingMessage = temp;
temp = "";
ff = 0;
loggingLvl = new LoggingLvl(eLoggingLvl);
CompassLogData cLD = new CompassLogData(dateTime, loggingLvl, threadId, loggingMessage);
compassLogCollection.Add(cLD);
}
Console.Out.WriteLine("DOOOOOOOOOOOOONE");
}
} catch (Exception e) {
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
}
}
}

I don't see anywhere in your code where UI is updated, so I'm assuming what's causing the slowness is reading of the data from the disk. You can try setting the priority of the thread reading the data from the disk to something lower than Normal so that the UI thread has a better chance at CPU cycles. See the thread priority property.
Also, if the length of the lines in your file are not large and given that the code reading the file is already running in a background thread, I would just use ReadLine instead of using ReadLineAsync and passing the work to yet another thread.

Related

Read text file line by line using timer

StreamReader sr = new StreamReader("C:/CR EZ Test/Log.txt"); //use with IF
private void timer2_Tick(object sender, EventArgs e)
{
if ((line = sr.ReadLine()) != null)
{
//FileStream fs = File.Open("C:/CR EZ Test/Log.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
//StreamReader sr = new StreamReader(fs); //use with While can't use with }else{
//while ((line = sr.ReadLine()) != null)
//{
string[] dataLog = line.Split(new[] { ',' }, StringSplitOptions.None);
mpa = (dataLog[1]);
ml = (dataLog[2]);
lph = (dataLog[3]);
elapsedTime = float.Parse(dataLog[4]) / 1000;
if (testStatus > 0) time = elapsedTime.ToString("0.0");
tb2.Value = int.Parse(dataLog[6]);
if (chart1.Series[0].Points.Count > tb1.Value && tb1.Value > 0)
{
chart1.Series[0].Points.RemoveAt(0);
chart1.Series[1].Points.RemoveAt(0);
}
chart1.Series[0].Points.AddXY(dataLog[5], int.Parse(dataLog[1]));
chart1.Series[1].Points.AddXY(dataLog[5], int.Parse(dataLog[6]));
//}
}
else
{
sr.DiscardBufferedData();
sr.BaseStream.Seek(0, SeekOrigin.Begin);
sr.BaseStream.Position = 0;
//sr.Close();
//alertTB.Text = "";
timer2.Enabled = false;
}
alertTB.ForeColor = Color.Red;
alertTB.Text = "Data Log Viewing In Progress";
}
The issue is I am reading a text file full of variables back through a GUI, like replaying a video. As the code is shown, it works and I can control the timer tick to change the replay speed. The issue is the file is in use, so I can't write to or delete the text while the file is in use, without closing it first. I would like to either be able to find a workaround of the Streamreader, or use the Filestream to Streamreader code that will allow me to edit the file while it is in use. The issue there is, I can't figure out how to make it work with the timer, it just reads the entire file very quickly. Any help or ideas are greatly appreciated.
The issue here is how to have the commented out code to:
read a line of the text file,
have the timer to tick
then read the next line of the text file, and so on. Obviously handling the data as it arrives.
Opening a file while it is in use
I think what you are looking for is FileStream with FileShare.ReadWrite for the instance of your StreamReader (not the instance you have commented out),
var fs = new FileStream("C:\foo.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
var sr = new StreamReader(fs);
Setting the position of the stream
It also seems like based on your comments, you are having trouble with positioning the stream, this is how you could do that...
fs.Position = 0; // note this is the FileStream not the StreamReader!
// alternatively, you could use Seek
Difference between sequential and random access
Lastly, you might want to take a look below to see the difference between sequential and random access
A Potential Solution
Here is a class called FileMonitor that will check the file and update the list whenever the file is changed / updated.
I understand that you want a timer to poll the data in the text file, but in case the timer is very fast, I have optimized the FileMonitor to watch the file for changes and only extract when there is a change.
Please note that this only continues to read where it was left off, based on the position of the stream. So, it will not work if lines are deleted or modified prior to getting "extracted". This means it only functions based on your requirements and is not improved to handle a lot of other scenarios, but it should adequately cover your requirements.
public class FileMonitor : IDisposable
{
private readonly FileStream _file;
private readonly StreamReader _reader;
private long _position;
private List<string> _lines;
public FileMonitor(string file)
{
if (String.IsNullOrEmpty(nameof(file))) throw new ArgumentNullException(nameof(file));
_lines = new List<string>();
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = Path.GetDirectoryName(file);
watcher.Filter = Path.GetFileName(file);
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
watcher.Changed += new FileSystemEventHandler(OnChanged);
//watcher.Created += new FileSystemEventHandler(OnCreated);
//watcher.Deleted += new FileSystemEventHandler(OnDeleted);
//watcher.Renamed += new RenamedEventHandler(OnRenamed);
// begin watching.
watcher.EnableRaisingEvents = true;
// begin reading
_file = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
_reader = new StreamReader(_file);
_lines = ReadLines(_reader).ToList();
_position = _file.Position;
}
private void OnChanged(object source, FileSystemEventArgs e)
{
List<string> update = ReadLines(_reader).ToList();
// fix to remove the immidate newline
if (update.Count() > 0 && String.IsNullOrEmpty(update[0])) update.RemoveAt(0);
_lines.AddRange(update);
_position = _file.Position;
// just for debugging, you should remove this
Console.WriteLine($"File: {e.FullPath} [{e.ChangeType}]");
}
public IEnumerable<string> Lines { get { return _lines; } }
public void Reset()
{
_file.Position = 0;
_position = _file.Position;
_lines.Clear();
}
private static IEnumerable<string> ReadLines(StreamReader reader)
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
public void Dispose()
{
_reader.Dispose();
_file.Dispose();
}
}
Here is how you could use it with your timer
private IEnumerable<string> _lines; // holds all the lines "extracted"
void Main()
{
string file = #"C:\Data\foo.txt";
using (var timer = new System.Timers.Timer())
{
timer.Interval = 2000; // 2 second interval
timer.Elapsed += OnTimedEvent; // attach delegate
timer.Enabled = true; // start the timer
// open the file
using (var monitor = new FileMonitor(file))
{
_lines = monitor.Lines;
// loop forever, remove this
while (true) { }
}
}
}
public void OnTimedEvent(object sender, EventArgs e)
{
// just for debugging, you should remove this
Console.WriteLine($"current count: {_lines.Count()}");
}
If it isn't clear, the data extracted is held in a list of strings. Above, you can grab the "extracted" data from the monitor using the monitor.Line property.
A Proven Working Solution
string line;
if (!File.Exists(logFile))
{
viewLog.Text = "Play";
alertTB.ForeColor = Color.Red;
alertTB.Text = "File Does Not Exist | Log Data To Create File";
chart.Text = "Scope On";
}
if (File.Exists(logFile))
{
var lineCount = File.ReadLines(logFile).Count();//read text file line count to establish length for array
if (lineCount < 2)
{
viewLog.Text = "Play";
alertTB.ForeColor = Color.Red;
alertTB.Text = "File Exists | No Data Has Been Recorded";
chart.Text = "Scope On";
}
if (counter < lineCount && lineCount > 0)//if counter is less than lineCount keep reading lines
{
line = File.ReadAllLines(logFile).Skip(counter).Take(lineCount).First();
string[] dataLog = line.Split(new[] { ',' }, StringSplitOptions.None);
//-----------------------------------------Handling my data
counter++;
}
else
{
counter = 0;
timer2.Enabled = false;
}
}
This is the fix I arrived at, it allows editing the file or deleting the contents of the file. I get the line count before trying to load the file. I then use the counter to iterate through the lines. I can change the delay between the next line read based upon the timer tick interval, pause it, or stop it.

c# and serial port

I'm trying to do an application in c #, which sends a text file through a serial port to a machine.
all good and beautiful if the machine is put in download mode.
if the operator forgets to put the machine on download mode .... here comes the problem, I have a writetimout exception.
I would like to use this exception or anything else to resume the process and my program will try to reload the file into the machine.
below is my code
private void incarca_button_Click(object sender, EventArgs e)
{
string open_folder = cale_txt.Text.Replace(Environment.NewLine, "");
OpenFileDialog theDialog = new OpenFileDialog();
theDialog.Title = "Open Text File";
theDialog.Filter = "TXT files|*.txt";
theDialog.InitialDirectory = open_folder;
if (theDialog.ShowDialog() == DialogResult.OK)
{
var onlyFileName = System.IO.Path.GetFileName(theDialog.FileName);
piesa_lbl.Hide();
piesa_txt.Hide();
SerialPort cnc_com = new SerialPort(com);
cnc_com.BaudRate = Int32.Parse(bit);
parity = parity.ToString();
cnc_com.Parity = (Parity)Enum.Parse(typeof(Parity), parity);
cnc_com.StopBits = (StopBits)Enum.Parse(typeof(StopBits), stop_bit);
cnc_com.DataBits = Int32.Parse(data_bit);
cnc_com.Handshake = (Handshake)Enum.Parse(typeof(Handshake), flow_control);
cnc_com.Encoding = Encoding.ASCII;
cnc_com.ReadTimeout = 500;
cnc_com.WriteTimeout = 500;
cnc_com.ReadBufferSize = 1000000;
cnc_com.WriteBufferSize = 1000000;
//cnc_com.DtrEnable = true;
//cnc_com.RtsEnable = true;
try
{
cnc_com.Open();
}
catch (UnauthorizedAccessException) { }
catch (System.IO.IOException) { }
catch (ArgumentException) { }
using (StreamReader sr = new StreamReader(open_folder + #"\" + onlyFileName))
{
while (sr.Peek() >= 0)
{
try
{
cnc_com.WriteLine(sr.ReadLine());
//cnc_com.Handshake = (Handshake)Enum.Parse(typeof(Handshake), flow_control);
}
catch(TimeoutException wtout)
{
MessageBox.Show("Masina nu este pregatita\nReincarcati programul");
cnc_com.Close();
cnc_com = null;
}
}
}
piesa_txt.Show();
piesa_lbl.Show();
piesa_txt.Focus();
//cale_txt.Text = "";
cnc_com.DiscardOutBuffer();
cnc_com.DiscardInBuffer();
cnc_com.Close();
cnc_com = null;
}
}
I would like this piece of code to be executed only if the file was passed successfully to the machine
piesa_txt.Show();
piesa_lbl.Show();
piesa_txt.Focus();
//cale_txt.Text = "";
cnc_com.DiscardOutBuffer();
cnc_com.DiscardInBuffer();
cnc_com.Close();
cnc_com = null;
thank you

File.Delete the process cannot access the file because it is being used by another process

public bool DownloadMp3File (DownloadedMp3 mp3) {
WebClient client = new WebClient ();
string filePath = "";
bool wasDownload = false;
try {
string song = mp3.SongName;
filePath = #"mp3\" + song + ".mp3";
if (File.Exists (filePath)) {
File.Delete (filePath);
}
DateTime tryCountNow = DateTime.Now;
client = new WebClient ();
client.DownloadFileAsync (new Uri (mp3.Url), filePath);
client.DownloadProgressChanged += client_DownloadProgressChanged;
client.DownloadFileCompleted += client_DownloadFileCompleted;
DateTime start = DateTime.Now;
bool notDownload = false;
downloadComplete = false;
while (!downloadComplete) {
DateTime now = DateTime.Now;
TimeSpan ts = now - start;
int min = ts.Minutes;
int sec = ts.Seconds;
if (10 < sec && 0 == downloadProgress) {
notDownload = true;
client.CancelAsync ();
break;
}
if (min == 1) {
notDownload = true;
client.CancelAsync ();
break;
}
Thread.Sleep (30);
}
if (!notDownload) {
client.CancelAsync ();
client.OpenRead (mp3.Url);
int downloadedFileSize = Convert.ToInt32 (client.ResponseHeaders["Content-Length"]);
FileInfo localFile = new FileInfo (filePath);
if (localFile.Length == downloadedFileSize) {
wasDownload = true;
}
}
}
catch {
downloadProgress = 0;
downloadComplete = false;
}
finally {
client.CancelAsync ();
client.Dispose ();
downloadComplete = false;
downloadProgress = 0;
GC.Collect ();
if (!wasDownload) {
if (File.Exists (filePath)) {
FileSecurity fs = File.GetAccessControl (filePath);
File.Delete (filePath);
}
}
Application.Current.Dispatcher.BeginInvoke (
DispatcherPriority.Background,
new Action (() =>
MainWindow.label3.Content = ""
));
}
return wasDownload;
}
Please help! I sometimes get that exception:
File.Delete the process cannot access the file because it is being used by another process
I can't find out why (I disposed WebClient).
Your code suggests you're getting the "file being used" exception on a file that was newly downloaded. Many anti-virus programs automatically scan newly created and/or newly downloaded files, and may delay closing the file handle until the scan is done.
If that is your problem, then there's nothing more you can to do close the file on time. You can either switch to a different anti-virus that doesn't keep files locked during scans, or you can implement a delay+retry loop when trying to use a file that's recently been closed.

Cannot read from txt file although the file close

In this class i am watching a txt file and every new line is handle:
If the first word is Start (the second is the file name) i am opening Wiresahrk process and start capturing.
If it start with Stop i am kill the process who running (all running processes stored in list and associate to the file name)
string _file = #"D:\file.txt";
public void startWatch()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = Path.GetDirectoryName(_file);
watcher.Filter = Path.GetFileName(_file);
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Changed += watcher_Changed;
watcher.EnableRaisingEvents = true;
}
public void watcher_Changed(object sender, FileSystemEventArgs e)
{
readLine();
}
private void readLastLine()
{
string lastLine = string.Empty;
using (StreamReader sr = new StreamReader(_file))
{
string str = sr.ReadToEnd();
int x = str.LastIndexOf('\n');
lastLine = str.Substring(x + 1);
}
validateString(lastLine);
}
private void validateString(string str)
{
string[] arr = str.Split(' ');
if (arr.Length != 2 && arr[0] != "start" && arr[0] != "stop" && arr[0] != "finish")
return;
Tshark tshark = new Tshark(arr[1]);
tshark.startCapturing(); // Start wireshark process and start capturing
}
After i read the last line from my file everything works fine, after the second time i try to read an error occurs: The process cannot access the file because it is being used by another process.
Try explicitly setting, how file will be opened/shared (assuming file exists, but anyway good idea will by to wrap this region in try/catch block).
using (var stream = new StreamReader( File.Open(_file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
string str = stream.ReadToEnd();
int x = str.LastIndexOf('\n');
string lastline = str.Substring(x + 1);
}

C# ListBox Import from Text File Crash

I am using this code to import text file to my ListBox
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.Filter = "Text Files|*.txt";
openFileDialog1.Title = "Select a Text file";
openFileDialog1.FileName = "";
DialogResult result = openFileDialog1.ShowDialog();
if (result == DialogResult.OK)
{
string file = openFileDialog1.FileName;
string[] text = System.IO.File.ReadAllLines(file);
foreach (string line in text)
{
listBox2.Items.Add(line);
}
listBox2.Items.Add("");
}
It works fine for small text files, with 10 lines or so, but when I try to import bigger list, (4-5 megabytes) the program isn't responding and it's crashing.
Any help?
Use the BufferedStream class in C# to improve performance.
http://msdn.microsoft.com/en-us/library/system.io.bufferedstream.aspx
By using this:
string[] text = System.IO.File.ReadAllLines(file);
listBox1.Items.AddRange(text);
instead of this:
string[] text = System.IO.File.ReadAllLines(file);
foreach (string line in text)
{
listBox2.Items.Add(line);
}
you will speed up the execution at least 10-15 times because you are not invalidating listBox on every Item insert.
I have measured with few thousand lines.
The bottleneck could also be ReadAllLines if your text has too many lines. Even though I can't figure out why you would be inserting so many lines, will user be able to find the line he/she needs?
EDIT OK then I suggest you to use BackgroundWorker, here is the code:
First you initialize BackGroundWorker:
BackgroundWorker bgw;
public Form1()
{
InitializeComponent();
bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
}
Then you call it in your method:
private void button1_Click(object sender, EventArgs e)
{
if (!bgw.IsBusy)
{
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.Filter = "Text Files|*.txt";
openFileDialog1.Title = "Select a Text file";
openFileDialog1.FileName = "";
DialogResult result = openFileDialog1.ShowDialog();
if (result == DialogResult.OK)
{
string file = openFileDialog1.FileName;
listView1.BeginUpdate();
bgw.RunWorkerAsync(file);
}
}
else
MessageBox.Show("File reading at the moment, try later!");
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
listView1.EndUpdate();
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
string fileName = (string)e.Argument;
TextReader t = new StreamReader(fileName);
string line = string.Empty;
while ((line = t.ReadLine()) != null)
{
string nLine = line;
this.Invoke((MethodInvoker)delegate { listBox1.Items.Add(nLine); });
}
}
It will add each line when it reads it, you will have responsive UI, and lines won't affect the listBox before it finishes loading.
It maybe simply not completing its job, and you should have to wait for more. Try with this solution:
http://www.bytechaser.com/en/articles/f3a3niqyb7/display-large-lists-in-listview-control-quickly.aspx
could use a stream to store the data:
class Test
{
public static void Main()
{
string path = #"c:\temp\MyTest.txt";
//Create the file.
using (FileStream fs = File.Create(path))
{
AddText(fs, "This is some text");
AddText(fs, "This is some more text,");
AddText(fs, "\r\nand this is on a new line");
AddText(fs, "\r\n\r\nThe following is a subset of characters:\r\n");
for (int i=1;i < 120;i++)
{
AddText(fs, Convert.ToChar(i).ToString());
}
}
//Open the stream and read it back.
using (FileStream fs = File.OpenRead(path))
{
byte[] b = new byte[1024];
UTF8Encoding temp = new UTF8Encoding(true);
while (fs.Read(b,0,b.Length) > 0)
{
Console.WriteLine(temp.GetString(b));
}
}
}
private static void AddText(FileStream fs, string value)
{
byte[] info = new UTF8Encoding(true).GetBytes(value);
fs.Write(info, 0, info.Length);
}
}
then you event handler
privateasyncvoid Button_Click(object sender, RoutedEventArgs e)
{
UnicodeEncoding uniencoding = new UnicodeEncoding();
string filename = #"c:\Users\exampleuser\Documents\userinputlog.txt";
byte[] result = uniencoding.GetBytes(UserInput.Text);
using (FileStream SourceStream = File.Open(filename, FileMode.OpenOrCreate))
{
SourceStream.Seek(0, SeekOrigin.End);
await SourceStream.WriteAsync(result, 0, result.Length);
}
}
Your application becomes unresponsive because it's waiting for the ReadAllLines method to complete and blocks the UI thread. You may want to read files on a separate thread to avoid blocking the UI. I cannot guarantee that the code below will work without errors but it should give you an idea on how to tackle the problem.
First of all, you'll need a method to append an item to the ListBox:
private void AddListBoxItem(string item)
{
if(!InvokeRequired)
{
listBox2.Items.Add(item);
}
else
{
var callback = new Action<string>(AddListBoxItem);
Invoke(callback, new object[]{item});
}
}
The method above checks if it is executed on UI thread and if yes, it simply adds an item to the listBox2.Items collection; if not, it creates a delegate from itself and invokes that delegate on UI thread.
Next, you'll need to move the code that reads the file to another thread and call AddListBoxItem method. For the sake of readability, let's put that into a separate method:
private void AddFileContentsToList(string fileName)
{
using(var reader = new System.IO.StreamReader(fileName))
{
while(!reader.EndOfStream)
{
var line = reader.ReadLine();
AddListBoxItem(line);
}
}
}
And now we will call the method on a separate thread:
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.Filter = "Text Files|*.txt";
openFileDialog1.Title = "Select a Text file";
openFileDialog1.FileName = "";
DialogResult result = openFileDialog1.ShowDialog();
if (result == DialogResult.OK)
{
var thread = new Thread(AddFileContentsToList);
thread.Start();
}
Hope this helps!

Categories

Resources