Updating textbox while another thread is running WPF - c#

Here is the architecture of my app. I have a Perl script that is called using the method below on click of a button.
ProcessStartInfo psStartInfo = new ProcessStartInfo("perl.exe");
psStartInfo.Arguments = paramStr;
psStartInfo.UseShellExecute = false;
psStartInfo.RedirectStandardOutput = true;
psStartInfo.RedirectStandardError = true;
psStartInfo.CreateNoWindow = false;
ps.StartInfo = psStartInfo;
ps.Start();
string os = ps.StandardOutput.ReadToEnd();
The above code executes successfully. There is a text file which is generated using the Perl script I fired in the above mentioned code. I have to read that file and show everything in that in my WPF textbox. That Perl file is updated after every 5 to 10 secs. I use the below mentioned code to read the file and show it in my textbox.
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 0, 30);
dispatcherTimer.Start();
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
txtExecLog.Text = "";
if (File.Exists("C:\\FlashAuto\\Execution_Logs\\log.txt"))
{
File.Copy("C:\\FlashAuto\\Execution_Logs\\log.txt", "C:\\FlashAuto\\Temp\\log.txt", true);
TextReader readLogs = new StreamReader("C:\\FlashAuto\\Temp\\log.txt");
string line = readLogs.ReadLine();
while (line != null)
{
txtExecLog.Text += "\n" + line;
line = readLogs.ReadLine();
txtExecLog.ScrollToEnd();
}
CountLines = txtExecLog.LineCount - 1;
readLogs.Close();
// Forcing the CommandManager to raise the RequerySuggested event
txtExecLog.ScrollToEnd();
CommandManager.InvalidateRequerySuggested();
readLogs.Dispose();
}
else
{
txtExecLog.Text += "log file not found at: "+DateTime.Now.ToString();
}
}
Here is the problem:
Reading and writing to the textbox is successfully done but there is a huge chunk of memory that is eaten up by my app. If I disable the logging, memory usage is optimal.

Related

C# pathping process freeze

I'm creating a network diagnostic application and trying to add a pathping command to it where it takes an adress from a textfield as path to ping when I press a button, but the application freezes when I press the button and nothing shows in the output window.
private void btn_PingPath_Click(object sender, EventArgs e)
{
ProcessStartInfo PathPingStartInfo = new ProcessStartInfo();
PathPingStartInfo.FileName = "CMD.EXE";
PathPingStartInfo.UseShellExecute = false;
PathPingStartInfo.CreateNoWindow = true;
PathPingStartInfo.RedirectStandardOutput = true;
PathPingStartInfo.RedirectStandardInput = true;
PathPingStartInfo.RedirectStandardError = true;
PathPingStartInfo.StandardOutputEncoding = Encoding.GetEncoding(850);
Process PathPing = new Process();
PathPing.StartInfo = PathPingStartInfo;
PathPing.Start();
PathPing.StandardInput.WriteLine("PATHPING " + txt_PingPath.Text);
while (PathPing.StandardOutput.Peek() > -1)
{
txt_Output.Text = PathPing.StandardOutput.ReadLine();
}
while (PathPing.StandardError.Peek() > -1)
{
txt_Output.Text = PathPing.StandardError.ReadLine();
}
//txt_Output.Text = PathPing.StandardOutput.ReadToEnd();
PathPing.WaitForExit();
}
EDIT
I found the while loop from another question but it did not help. I still get no output in the output text window and the application still freezes.
The PATHPING command can end up running for several minutes before exiting, so your last line, PathPing.WaitForExit(); will also not return for several minutes (or until pathping exits). You can't wait like this on the UI thread, because the UI also needs to use this thread to re-draw and listen for windows messages.
You can free up the UI thread so that your application doesnt freeze by either creating a new thread, or using async/await features in .Net 4.5+, or using the event pattern. The following example uses the event pattern.
private void btn_PingPath_Click(object sender, EventArgs e)
{
ProcessStartInfo PathPingStartInfo = new ProcessStartInfo();
PathPingStartInfo.FileName = "CMD.EXE";
PathPingStartInfo.UseShellExecute = false;
PathPingStartInfo.CreateNoWindow = true;
PathPingStartInfo.RedirectStandardOutput = true;
PathPingStartInfo.RedirectStandardInput = true;
PathPingStartInfo.RedirectStandardError = true;
PathPingStartInfo.StandardOutputEncoding = Encoding.GetEncoding(850);
Process PathPing = new Process();
PathPing.StartInfo = PathPingStartInfo;
PathPing.Start();
PathPing.StandardInput.WriteLine("PATHPING " + txt_PingPath.Text);
PathPing.StandardInput.Flush();
PathPing.OutputDataReceived += (o, args) => txt_Output.Text += args.Data;
PathPing.ErrorDataReceived += (o, args) => txt_Output.Text += args.Data;
PathPing.BeginErrorReadLine();
PathPing.BeginOutputReadLine();
}

How to show progress indicator in System Tray for few seconds without using await [Windows Phone 8]

Every time when a user opens my app, the app needs to check that does it need to upload/download data to/from windows azure or not. However, if the user doesn't have internet connection, the app will show a last update time in System Tray. The app gets the last update time from SQLite which doesn't need to use await method to do it. So, how can I last the text in System tray for few seconds before it will be faded out.
This is my code
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
if (SessionManagement.IsLoggedIn())
{
var userLastestPoopDataInSQLite = new SQLiteFunctions().GetUserPoopData(SessionManagement.GetEmail());
if (userLastestPoopDataInSQLite.Count != 0)
{
userLastestpoopRecordInSqlite = userLastestPoopDataInSQLite.Last();
if (!NetworkInterface.GetIsNetworkAvailable())
{
SystemTray.ProgressIndicator = new ProgressIndicator();
SystemTray.ProgressIndicator.Text = GetLastUpdatedTimeInText(userLastestpoopRecordInSqlite.Date_Time);
SystemTray.ProgressIndicator.IsVisible = true;
}
else
{
isUpdateNeeded = DateTime.Compare(userLastestpoopRecordInSqlite.Date_Time, userLastestPoopRecordInAzure.Date_Time);
Debug.WriteLine("Lastest time in Sqlite" + userLastestpoopRecordInSqlite.Date_Time);
Debug.WriteLine("Lastest time in azure" + userLastestPoopRecordInAzure.Date_Time);
Debug.WriteLine(isUpdateNeeded);
if (isUpdateNeeded == 0)
{
SystemTray.ProgressIndicator = new ProgressIndicator();
SystemTray.ProgressIndicator.Text = "Data is up-to-date";
SystemTray.ProgressIndicator.IsVisible = true;
}
else
{
userLastestPoopDataInAzure = await new AzureFunctions().GetUserPoopDataInAzure(SessionManagement.GetEmail());
userLastestPoopRecordInAzure = userLastestPoopDataInAzure.Last();
StartSyncUserLastestData();
}
}
}
}
}
Thank you
There is no built in support for showing the progress bar a limited time, so you will probably need to use a timer for this:
SystemTray.ProgressIndicator = new ProgressIndicator();
SystemTray.ProgressIndicator.IsVisible = true;
SystemTray.ProgressIndicator.Text = "Data is up-to-date";
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(2000);
timer.Tick += (sender, args) =>
{
SystemTray.ProgressIndicator.IsVisible = false;
timer.Stop();
};
timer.Start();

Linking third party CSV reader to a progress bar

I found a well-written CSV parser/reader from this link written by Sebastien Lorion.
What I like of this CSV parser is that I could easily bind it to a DataGrid like:
using (CachedCsvReader csv = new
CachedCsvReader(new StreamReader(txtChosenFile.Text), true))
{
dataGridView1.DataSource = csv;
}
Which is what I need in my project because I want my users to preview it before committing it to the Database.
However, since it takes a while to load a file I need to provide at least a feedback to my user using a Progress Bar. Unfortunately, it is only one liner to get the CachedCsvReader class which make it difficult for me to link or update a progress bar as the reading of the csv file progresses.
If it is just a simple CsvReader class it would be easy to update my progress Bar like:
using (StreamReader sr = new StreamReader(openFileDialog1.FileName))
{
using (CsvReader csv = new
CsvReader(sr, true))
{
double progress = (double) sr.BaseStream.Position / (double) sr.BaseStream.Length;
progressBar1.Value = (int)progress*100;
}
}
However, since I am using a CachedCsvReader and it is only one liner (or two) to get upload the csv reader without having an information on the stream position and length then I could not update my progress bar.
So, what would be the best way to connect my progress bar to my CachedCsvReader?
Assuming you are initiating the read from a method called Open, following should work. It uses a timer control to poll the read position every 1 sec.:
private StreamReader sr;
public void Open()
{
Timer timer = new Timer();
timer.Interval = 1000;
timer.Tick += new EventHandler(timer_Tick);
timer.Enabled = true;
timer.Start();
using (this.sr = new StreamReader(openFileDialog1.FileName))
{
using (CachedCsvReader csv = new CachedCsvReader(sr, true))
{
dataGridView1.DataSource = csv;
}
}
timer.Stop();
timer.Enabled = false;
timer.Tick -= new EventHandler(timer_Tick);
}
void timer_Tick(object sender, EventArgs e)
{
if (null != this.sr)
{
double progress = (double)sr.BaseStream.Position / (double)sr.BaseStream.Length;
progressBar1.Value = (int)progress * 100;
}
}

Dispatcher timer not running

I'm trying to execute some Python scripts from my WPF app. The scripts are generating the log files and the code in the Tick event is reading them and displaying that as it is in a textbox.
My issue here is that, that LaunchProcess fires successfully, but the UI freezes. I have an indefinite progress bar, which too does not start animating. I'm a beginner with WPF and there is something very small I have to do to get this code working. I'm not getting any error/warnings. The scripts run fine and in the end I get to know the result too. But during the run, the UI of my app freezes.
private void LaunchProcess(string paramStr)
{
Process myProcess = new Process();
StartProgressBar();
try
{
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 0, 0);
dispatcherTimer.Start();
myProcess.StartInfo.UseShellExecute = false;
// You can start any process
myProcess.StartInfo.FileName = "C:\\Python32\\python.exe";
myProcess.StartInfo.Arguments = "\""+paramStr+"\"";
myProcess.StartInfo.CreateNoWindow = true;
myProcess.StartInfo.RedirectStandardOutput = true;
myProcess.StartInfo.RedirectStandardError = true;
myProcess.Start();
myProcess.WaitForExit();
// This code assumes the process you are starting will terminate itself.
// Given that is is started without a window so you cannot terminate it
// on the desktop, it must terminate itself or you can do it programmatically
// from this application using the Kill method.
dispatcherTimer.Stop();
}
catch
{
MessageBox.Show("Process Launch Failed!!", "Failure", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
//txtOutPut.Text = "";
txtOutPut.Text += "\n" + DateTime.Now.ToString();
if (File.Exists(scriptPath+"\\log.txt"))
{
//File.Copy("C:\\FlashAuto\\Execution_Logs\\log.txt", "C:\\FlashAuto\\Temp\\log.txt", true);
TextReader readLogs = new StreamReader(scriptPath + "\\log.txt");
string line = readLogs.ReadLine();
while (line != null)
{
txtOutPut.Text += "\n" + line;
line = readLogs.ReadLine();
txtOutPut.ScrollToEnd();
}
//CountLines = txtExecLog.LineCount - 1;
readLogs.Close();
// Forcing the CommandManager to raise the RequerySuggested event
txtOutPut.ScrollToEnd();
CommandManager.InvalidateRequerySuggested();
readLogs.Dispose();
}
else
{
txtOutPut.Text += "log file not found at: " + DateTime.Now.ToString();
}
}
In case you call LaunchProcess from the UI thread it will obviously be blocked at myProcess.WaitForExit().
You might simply remove the myProcess.WaitForExit() and dispatcherTimer.Stop() calls from the launch method and check if the process is still running in the timer Tick handler.
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
if (myProcess.WaitForExit(0)) // check with timeout zero
{
dispatcherTimer.Stop();
}
... // all your code
}
Calling LaunchProcess method asynchronously would resolve your UI Freeze Issue
public void LaunchProcessAsynchrousCall(string paramStr)
{
ThreadStart displayContentHandler = delegate()
{
LaunchProcess(paramStr)
};
Thread thread = new Thread(displayContentHandler);
thread.IsBackground = true;
thread.Start();
}

Stop audio recorder on its own after time delay in c# ( using NAudio)

I am making an audio recorded using NAudio in C# and i need to remove the stop button used and simply stop the recording on its own after some time delay.
The code for the record event is
private void cmbRecord_Click(object sender, EventArgs e)
{
outputFilename = "file address";
waveInStream = new WaveIn(44100,2);
writer = new WaveFileWriter(outputFilename, waveInStream.WaveFormat);
waveInStream.DataAvailable += new EventHandler<WaveInEventArgs>(waveInStream_DataAvailable);
waveInStream.StartRecording();
// Just controling the objects on the screen.
cmbRecord.Enabled = false;
cmbStop.Enabled = true;
}
void waveInStream_DataAvailable(object sender, WaveInEventArgs e)
{
writer.WriteData(e.Buffer, 0, e.BytesRecorded);
int secondsRecorded = (int)(writer.Length / writer.WaveFormat.AverageBytesPerSecond);
}
The stop button is given as
private void cmbStop_Click(object sender, EventArgs e)
{
waveInStream.StopRecording();
waveInStream.Dispose();
waveInStream = null;
writer.Close();
writer = null;
cmbRecord.Enabled = true;
cmbStop.Enabled = false;
}
I need to stop the recording automatically inside the cmbRecord_Click event.
Thanks in advance.
use a Timer, set the Interval and copy the code in cmbStop_Click event over to timer's OnTick event. Enable the timer in the mbRecord_Click event and & remember to disable the timer in cmbStop_Click event
Edit:
Create a new timer and set its value
//put this line in your form class level
System.Windows.Forms.Timer mytimer=new System.Windows.Forms.Timer(); //create a new Timer
//put these two into your form constructor just after InitializeComponent();
mytimer.Interval=1000; //set the interval to 1 second.
mytimer.Tick += new EventHandler(mytimer_Tick);
Enable the timer in the mbRecord_Click event
private void cmbRecord_Click(object sender, EventArgs e)
{
outputFilename = "file address";
waveInStream = new WaveIn(44100,2);
writer = new WaveFileWriter(outputFilename, waveInStream.WaveFormat);
waveInStream.DataAvailable += new EventHandler<WaveInEventArgs>(waveInStream_DataAvailable);
waveInStream.StartRecording();
// Just controling the objects on the screen.
cmbRecord.Enabled = false;
cmbStop.Enabled = true;
//Enable the timer to fire
mytimer.Enabled = true;
}
Stop recording after 1 second..
void mytimer_Tick(object sender, EventArgs e)
{
waveInStream.StopRecording();
waveInStream.Dispose();
waveInStream = null;
writer.Close();
writer = null;
cmbRecord.Enabled = true;
cmbStop.Enabled = false;
//disable the timer here so it won't fire again...
mytimer.Enabled = false;
}
One thing you may want to bear in mind - there will be a DataAvailable callback after the call to StopRecording (or during, depending on the callback model used), so you might want to delay closing the WaveFileWriter until you have written everything.
Have a look at the VoiceRecorder sample project which uses NAudio and stops recording after 60 seconds. I explain in this article how recording is automatically stopped.
long maxFileLength = this.recordingFormat.AverageBytesPerSecond * 60;
int toWrite = (int)Math.Min(maxFileLength - writer.Length, bytesRecorded);
if (toWrite > 0)
writer.WriteData(buffer, 0, bytesRecorded);
else
Stop();

Categories

Resources