File in Use while trying to Clear Text File - c#

In my program I am loading a text files information into a rich text box. When the user clicks the clear button I want the files contents to be empty and display it again on the rich text box. However when I try to clear an error pops up that the file is already in use.
I am not sure of of what is going on with this, I have a suspicion it has to do with closing the stream reader or creating a new one. Either way I am not quite sure.
Does anyone have any thoughts of what is going on with this?
Code:
namespace FileLocationAutomation
{
public partial class ViewLog : Form
{
public ViewLog()
{
InitializeComponent();
}
#region Variables
string str = "";
#endregion
#region Ok Button
private void btnOK_Click(object sender, EventArgs e)
{
this.Close();
}
#endregion
#region Form Load
private void ViewLog_Load(object sender, EventArgs e)
{
//catch any exception
try
{
//load the log thats kept on the users machine into the rich text object
StreamReader read = new StreamReader(GlobalVars.strLogPath);
str = read.ReadToEnd();
rtxtView.Text = str;
read.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
#endregion
#region Clear Log
//clear the log file and display it on the rich text box
private void btnClear_Click(object sender, EventArgs e)
{
try
{
StreamReader read = new StreamReader(GlobalVars.strLogPath);
File.WriteAllText(GlobalVars.strLogPath, String.Empty);
str = read.ReadToEnd();
rtxtView.Text = str;
read.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
#endregion
}
}

The problem is that you try to write to the file to clear it while still holding it open. The simplest change is to move the call to File.WriteAllText(GlobalVars.strLogPath, String.Empty); to after where you close the file, as follows:
#region Clear Log
//clear the log file and display it on the rich text box
private void btnClear_Click(object sender, EventArgs e)
{
try
{
StreamReader read = new StreamReader(GlobalVars.strLogPath);
str = read.ReadToEnd();
rtxtView.Text = str;
read.Close();
File.WriteAllText(GlobalVars.strLogPath, String.Empty);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
A better change would probably to use the static read method, since you're reading the whole file in one go there's no need to use the StreamReader.
private void btnClear_Click(object sender, EventArgs e)
{
try
{
rtxtView.Text = File.ReadAllText(GlobalVars.strLogPath);
File.WriteAllText(GlobalVars.strLogPath, String.Empty);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

The problem is strictly here:
try
{
StreamReader read = new StreamReader(GlobalVars.strLogPath);
File.WriteAllText(GlobalVars.strLogPath, String.Empty);
str = read.ReadToEnd();
rtxtView.Text = str;
read.Close();
}
Make it look like this:
try
{
File.WriteAllText(GlobalVars.strLogPath, String.Empty);
StreamReader read = new StreamReader(GlobalVars.strLogPath);
str = read.ReadToEnd();
rtxtView.Text = str;
read.Close();
}
Or since reading a blank file back is useless, do it like this:
try
{
File.WriteAllText(GlobalVars.strLogPath, String.Empty);
rtxtView.Text = String.Empty;
}

enclose the StreamReader declaration in using block {}to make sure that it gets disposed.
Try This:
using(StreamReader read = new StreamReader(GlobalVars.strLogPath))
{
str = read.ReadToEnd();
rtxtView.Text = str;
}
OR
rtxtView.Text = File.ReadAllText(GlobalVars.strLogPath);

You may change the order.
try
{
StreamReader read = new StreamReader(GlobalVars.strLogPath);
File.WriteAllText(GlobalVars.strLogPath, String.Empty);
str = read.ReadToEnd();
// close the stream first.
read.Close();
rtxtView.Text = str;
}

Related

BackgroundWorker's progress bar is not working

The BackgroundWorker's progressbar is not updated while doing some tasks. What I would like to reach is progressbar moving while iterating through each file in DirectoryInfo. Suppose we have 20 files of ".sql" while first file completed it should be 5%, 10% and etc.
Here is my code.
private void CSV_Click(object sender, RoutedEventArgs e)
{
try
{
btnExtract.IsEnabled = false;
workerextract.RunWorkerAsync();
}
catch (Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
private void workerextract_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
try
{
this.Dispatcher.Invoke(() =>
{
DirectoryInfo di = new DirectoryInfo(txtQueryfolder.Text);
files = di.GetFiles("*.sql").Count();
currentfile = 0;
foreach (FileInfo fi in di.GetFiles("*.sql"))
{
// Open the text file using a stream reader.
using (StreamReader sr = new StreamReader(fi.FullName))
{
// Read the stream to a string, and write the string to the console.
string line = sr.ReadToEnd();
//System.Windows.MessageBox.Show(line);
ExtractToCSV(line, System.IO.Path.GetFileNameWithoutExtension(fi.Name));
currentfile++;
}
int percentage = (currentfile + 1) * 100 / files;
workerextract.ReportProgress(percentage);
}
});
}
catch(Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
private void workerextract_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
progressBarExtract.Value = e.ProgressPercentage;
}
private void workerextract_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btnExtract.IsEnabled = true;
System.Windows.MessageBox.Show("CSV Data extraction finished!");
}
I found that
private void workerextract_ProgressChanged(object sender,
System.ComponentModel.ProgressChangedEventArgs e)
is called once at the end when 100%.
Also,
private void workerextract_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
never called as I do not see Message Box at the end.
So, I think I am doing something wrong here, could you please direct me on right way?
The problem was in wrapping whole DoWork inside Dispatcher.Invoke.
I need to wrap only those code where it is interacting with UI.
So I changed the code appropriately and it works.
private void workerextract_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
try
{
this.Dispatcher.Invoke(() =>
{
di = new DirectoryInfo(txtQueryfolder.Text);
});
files = di.GetFiles("*.sql").Count();
currentfile = 0;
foreach (FileInfo fi in di.GetFiles("*.sql"))
{
// Open the text file using a stream reader.
using (StreamReader sr = new StreamReader(fi.FullName))
{
// Read the stream to a string, and write the string to the console.
string line = sr.ReadToEnd();
this.Dispatcher.Invoke(() =>
{
//System.Windows.MessageBox.Show(line);
ExtractToCSV(line, System.IO.Path.GetFileNameWithoutExtension(fi.Name));
});
currentfile++;
}
int percentage = (currentfile + 1) * 100 / files;
workerextract.ReportProgress(percentage);
}
}
catch(Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
Thanks to all for showing the direction.
Using this.Dispatcher.Invoke in the BackgroundWorker's DoWork event you are executing the whole operation in the UI thread; which is what BackgroundWorker born to avoid to do.
Also, you get an error unwrapping your code from the dispatcher because you are accessing an UI object, which is txtQueryfolder.
Just use:
private void workerextract_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
string queryFolder = e.Argument.ToString();
try
{
DirectoryInfo di = new DirectoryInfo(queryFolder);
files = di.GetFiles("*.sql").Count();
currentfile = 0;
foreach (FileInfo fi in di.GetFiles("*.sql"))
{
// Open the text file using a stream reader.
using (StreamReader sr = new StreamReader(fi.FullName))
{
// Read the stream to a string, and write the string to the console.
string line = sr.ReadToEnd();
//System.Windows.MessageBox.Show(line);
// ExtractToCSV shouldn't access to a UI object.
ExtractToCSV(line, System.IO.Path.GetFileNameWithoutExtension(fi.Name));
currentfile++;
}
int percentage = (currentfile + 1) * 100 / files;
workerextract.ReportProgress(percentage);
}
}
catch (Exception ex)
{
// Don't use MessageBox in a thread different from the UI one. Just set the result (e.Result) and get that in the RunWorkerCompleted event.
// System.Windows.MessageBox.Show(ex.Message);
}
}
When you call the RunWorkerAsync method just add the parameter like below:
workerextrac.RunWorkerAsync(txtQueryfolder.Text);

How to send CheckedListBox selection into textfile?

I'm trying to code on my own for the first time, and decided to make a music player on Winforms.
I have a CheckedListBox that is already populated with the names of songs in a folder.
When I click a button, it's supposed to send the names of my selected songs into a .txt file for further manipulation, before closing the form.
For simplification, I'm just going with 1 selected song first.
private void selectbtn_Click(object sender, EventArgs e)
{
selectedSongs = checkedListBox1.CheckedItems.ToString();
songRecord.writeRecord(selectedSongs); //i initialised my streamreader/streamwriter class and called it songRecord
this.Close();
}
in my streamreader/writer class, this is what I have
class DataRecord
{
public void writeRecord(string line)
{
StreamWriter sw = null;
try
{
sw = new StreamWriter(#"C:\Users\Me\Desktop\JAM_MACHINE\record.txt", true);
sw.WriteLine(line);
}
catch (FileNotFoundException)
{
Console.WriteLine("Error: File not found.");
}
catch (IOException)
{
Console.WriteLine("Error: IO");
}
catch(Exception)
{
throw;
}
finally
{
if (sw != null)
sw.Close();
}
}
public void readRecord()
{
StreamReader sr = null;
string myInputline;
try
{
sr = new StreamReader(#"C:\Users\Me\Desktop\JAM_MACHINE\record.txt");
while ((myInputline = sr.ReadLine()) != null) ; //readline reads whole line
Console.WriteLine(myInputline);
}
catch (FileNotFoundException)
{
Console.WriteLine("Error: File not found");
}
catch(IOException)
{
Console.WriteLine("Error: IO");
}
catch (Exception)
{
throw;
}
finally
{
if (sr != null)
sr.Close();
}
}
}
When I run it, the .txt file doesn't show my selection. It only shows:
System.Windows.Forms.CheckedListBox+CheckedItemCollection
What went wrong?
Iterate through the CheckedItems Collection and collect each item inside a string array. I assume you fill the checkedListBox with strings
private void selectbtn_Click(object sender, EventArgs e)
{
string[] checkedtitles = new string[checkedListBox1.CheckedItems.Count];
for (int ii = 0; ii < checkedListBox1.CheckedItems.Count; ii++)
{
checkedtitles[ii] = checkedListBox1.CheckedItems[ii].ToString();
}
string selectedSongs = String.Join(Environment.NewLine, checkedtitles);
songRecord.writeRecord(selectedSongs);
this.Close();
}

IO exception is generated when i try to copy from 1 file to other file

I create an empty file and try to copy the contents from an existing to the newly created file. When i do that i am getting an IOexception
private void button1_Click(object sender, EventArgs e)
{
String test = textBox1.Text.ToString();
if (string.IsNullOrEmpty(textBox1.Text))
{
MessageBox.Show("Enter the filename");
}
else
{
StreamWriter File = new StreamWriter(test);
MessageBox.Show(test + " Has been created");
}
}
private void button2_Click(object sender, EventArgs e)
{
String test = textBox1.Text.ToString();
try
{
File.Copy(#"D:\\Study this.txt", test);
}
catch (IOException)
{
MessageBox.Show("IO error occured");
}
}
You will need to close the Stream so in the else statement add File.Close(); that will release the newly created file. That section of the code will now look like this:
StreamWriter File = new StreamWriter(test);
File.Close();
MessageBox.Show(test + " Has been created");
replace line StreamWriter File = new StreamWriter(test); with below
using (File.Create(test)) ;
or with
using (StreamWriter writer = new StreamWriter(test)){}
reason for above change is you need to propery close the opend stream object before the copy.
using block will handle that for you.

how do I recall a string from a different function?

I am having trouble calling the string "rlist" from:
public void main()
{
string rlist;
if (radioButton1.Checked)
textBox1.Enabled = false;
textBox1.ReadOnly = true;
rlist = "text";
}
to
public void button1_Click(object sender, EventArgs e)
{
OpenFileDialog openFile = new OpenFileDialog();
openFile.Filter = "WTF Files (*.wtf)|*.wtf";
openFile.Title = "Please Pick your realmlist file:";
if (openFile.ShowDialog() == DialogResult.Cancel)
return;
try
{
textBox5.Text = openFile.FileName;
string file = openFile.FileName;
TextWriter rlist_writer = new StreamWriter (openFile.FileName);
rlist_writer.WriteLine(rlist);
rlist_writer.Close();
}
catch (Exception)
{
MessageBox.Show("Error opening file", "File Error",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}
I get the error on this line:
rlist_writer.WriteLine(rlist);
is it possible to call a string from one function and send it to the other with the same value it had in the function it was originally pulled from?
By the sounds of your question,
Your string is local to your main function.
So judging by your method names and knowledge of winforms(presumed again)
you need to make your string class level
string rlist;
public void main()
{
rlist = "yay"
public void button1_Click(object sender, EventArgs e)
{
someText = rlist;
As it currently stands you are not able to, as temporary (local) variables will be cleaned through garbage collection when you leave the method
Edit
You may wish to review this also
try
{
textBox5.Text = openFile.FileName;
using(TextWriter rlist_writer = new StreamWriter (openFile.FileName))
{
rlist_writer.WriteLine(rlist);
}
}
You can define that variable in your class scope, then if you call that variable in your button_click event, it will maintain the same value as in your main method.

working with StreamReader and text files

I want to put a test loop on the button click event. When I click this button, it reads the contents of the text file, but I want it to pop up an error message showing “unable to read file”, if it’s not a text file....
This is my code
private void button3_Click(object sender, EventArgs e)
{
StreamReader sr = new StreamReader(textBox1.Text);
richTextBox1.Text = sr.ReadToEnd();
sr.Close();
}
How can I go about it?
A few if-statements and the namespace System.IO will do it
string filename = textBox1.Text;
if (Path.GetExtension(filename).ToLower()) == ".txt") {
if (File.Exists(filename)) {
// Process the file here
} else {
MessageBox.Show("The file does not exist");
}
} else {
MessageBox.Show("Not a text file");
}
Not the best code, but it should work. Ideally you would separate the logic into two methods, a function to check the file exists and is a text file (returning a bool), another to read the contents if the check function returned true and populate the textbox with the contents.
EDIT: This is better:
private void button3_Click(object sender, EventArgs e)
{
string filePath = textBox1.Text;
bool FileValid = ValidateFile(filePath);
if (!IsFileValid)
{
MessageBox.Show(string.Format("File {0} does not exist or is not a text file", filePath));
}
else
{
textbox2.Text = GetFileContents(filePath);
}
}
private bool IsFileValid(string filePath)
{
bool IsValid = true;
if (!File.Exists(filePath))
{
IsValid = false;
}
else if (Path.GetExtension(filePath).ToLower() != ".txt")
{
IsValid = false;
}
return IsValid;
}
private string GetFileContents(string filePath)
{
string fileContent = string.Empty;
using (StreamReader reader = new StreamReader(filePath))
{
fileContent = reader.ReadToEnd();
}
return fileContent;
}

Categories

Resources