So I'm new to C#. I had been building all my tools (7 years worth of tools) in Powershell as I could never obtain a Visual Studio License but now that I have it...
I've been reading about system.componentmodel.backgroundworker however I'm having trouble implementing it.
Heres my code without Background worker and with. Can someone help me in seeing how I can implement it so my UI Doesn't go unresponsive when I kick off the Powershell jobs and I can actually get (somewhat) real updates in the text box
using System;
using System.Text;
using System.Windows.Forms;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.IO;
namespace ServerStatusChecks
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// run our script and put the result into our textbox
// NOTE: make sure to change the path to the correct location of your script
textBox1.Text = RunScript(LoadScript(#"c:\utils\Script.ps1"));
}
// helper method that takes your script path, loads up the script
// into a variable, and passes the variable to the RunScript method
// that will then execute the contents
private string LoadScript(string filename)
{
try
{
// Create an instance of StreamReader to read from our file.
// The using statement also closes the StreamReader.
using (StreamReader sr = new StreamReader(filename))
{
// use a string builder to get all our lines from the file
StringBuilder fileContents = new StringBuilder();
// string to hold the current line
string curLine;
// loop through our file and read each line into our
// stringbuilder as we go along
while ((curLine = sr.ReadLine()) != null)
{
// read each line and MAKE SURE YOU ADD BACK THE
// LINEFEED THAT IT THE ReadLine() METHOD STRIPS OFF
fileContents.Append(curLine + "\n");
}
// call RunScript and pass in our file contents
// converted to a string
return fileContents.ToString();
}
}
catch (Exception e)
{
// Let the user know what went wrong.
string errorText = "The file could not be read:";
errorText += e.Message + "\n";
return errorText;
}
}
// Takes script text as input and runs it, then converts
// the results to a string to return to the user
private string RunScript(string scriptText)
{
// create Powershell runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
// open it
runspace.Open();
// create a pipeline and feed it the script text
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);
// add an extra command to transform the script output objects into nicely formatted strings
// remove this line to get the actual objects that the script returns. For example, the script
// "Get-Process" returns a collection of System.Diagnostics.Process instances.
pipeline.Commands.Add("Out-String");
// execute the script
Collection<PSObject> results = pipeline.Invoke();
// close the runspace
pipeline.Dispose();
runspace.Close();
// convert the script result into a single string
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
// return the results of the script that has
// now been converted to text
return stringBuilder.ToString();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
}
}
Now here's the code with me trying to implement BW (It's a bit of a monster as I've tried to get it to work several ways and it's a mess now)
using System;
using System.Text;
using System.Windows.Forms;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.ComponentModel;
using System.Threading;
using System.IO;
namespace ServerStatusChecks
{
public partial class Form1 : Form
{
private BackgroundWorker bw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
}
private string LoadScript(string filename)
{
try
{
// Create an instance of StreamReader to read from our file.
// The using statement also closes the StreamReader.
using (StreamReader sr = new StreamReader(filename))
{
// use a string builder to get all our lines from the file
StringBuilder fileContents = new StringBuilder();
// string to hold the current line
string curLine;
// loop through our file and read each line into our
// stringbuilder as we go along
while ((curLine = sr.ReadLine()) != null)
{
// read each line and MAKE SURE YOU ADD BACK THE
// LINEFEED THAT IT THE ReadLine() METHOD STRIPS OFF
fileContents.Append(curLine + "\n");
}
// call RunScript and pass in our file contents
// converted to a string
return fileContents.ToString();
}
}
catch (Exception e)
{
// Let the user know what went wrong.
string errorText = "The file could not be read:";
errorText += e.Message + "\n";
return errorText;
}
}
private void button1_Click(object sender, EventArgs e)
{
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
// run our script and put the result into our textbox
// NOTE: make sure to change the path to the correct location of your script
textBox1.Text = RunScript(LoadScript(#"c:\utils\Script.ps1"));
}
private void bw_DoWork(object sender, EventArgs e)
{
}
// Takes script text as input and runs it, then converts
// the results to a string to return to the user
private string RunScript(string scriptText)
{
// create Powershell runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
// open it
runspace.Open();
// create a pipeline and feed it the script text
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);
// add an extra command to transform the script output objects into nicely formatted strings
// remove this line to get the actual objects that the script returns. For example, the script
// "Get-Process" returns a collection of System.Diagnostics.Process instances.
pipeline.Commands.Add("Out-String");
// execute the script
Collection<PSObject> results = pipeline.Invoke();
// close the runspace
pipeline.Dispose();
runspace.Close();
// convert the script result into a single string
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
// return the results of the script that has
// now been converted to text
return stringBuilder.ToString();
}
// helper method that takes your script path, loads up the script
// into a variable, and passes the variable to the RunScript method
// that will then execute the contents
private void Form1_Load(object sender, EventArgs e)
{
Thread.Sleep(1000);
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
Thread.Sleep(1000);
}
}
}
The problem is that you're not running anything in the bw_DoWork event, which is where the actual background work is run. You should change this section of your code to the following:
private void button1_Click(object sender, EventArgs e)
{
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
}
private void bw_DoWork(object sender, EventArgs e)
{
// run our script and put the result into our textbox
// NOTE: make sure to change the path to the correct location of your script
textBox1.Text = RunScript(LoadScript(#"c:\utils\Script.ps1"));
}
The reason this works is that the bw_DoWork method is an event listener, which means it "listens" for when the RunWorkerAsync() method is called, which you do when the button is clicked. bw_DoWork knows to listen for the RunWorkerAsync() function is because the line you put at the top of the program:
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
This basically tells DoWork to sit and wait for the event to fire off. This is commonly referred to as "subscribing to the event". When it does, a new thread spawns that does whatever is in bw's DoWork event while your original thread, which contains the UI (and is commonly refered to as the 'UI Thread') keeps on trucking. Events and Asynchronous threads are a tricky subject to wrap your head around, but once you do it's a fantastic piece of .NET.
EDIT:
Alright, so the above is correct as far as getting bw to run properly, but it causes this error:
Error: {"Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on."}
Above I said that the main thread is called the "UI Thread". This also means that the UI is locked to that thread. Therefore, your BackgroundWorker cannot touch the UI elements. Therefore the offending line is textBox1.Text = RunScript(LoadScript(#"c:\utils\Script.ps1"));
The easiest way to fix this is to have RunScript() pass its value to a public variable, then assign the variable after bw has done its processing. We do this by subscribing to another built-in event in BackgroundWorker called RunWorkerCompleted.
First, declare a new static string at the top of the class:
static string RunScriptResult;
Then, add a new listener to bw by modifying Form1:
public Form1()
{
//...
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new bw_RunWorkerCompleted; //new listener event
}
Next, change bw_DoWork to assign the output to the string we created earlier:
private void bw_DoWork(object sender, EventArgs e)
{
RunScriptResult = RunScript(LoadScript(#"c:\utils\Script.ps1"));
}
Next, add a new event handler method for RunWorkerCompleted:
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
textBox1.Text = RunScriptResult;
}
The above event does not fire until bw is finished, and it runs in the UI thread. These modifications should get you where you need to be.
Related
im trying to do a program that read a string from a website e send it to another. the process to read string for the first works correctly, and also the function to send a string to the other works, but have problem when this function was called from the timer..
here is part of my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
using CefSharp;
using CefSharp.WinForms;
using CefSharp.WinForms.Internals;
namespace CodePinger
{
public partial class Form1 : Form
{
public bool login = true;
private static System.Timers.Timer TimerCheck;
public Form1()
{
InitializeComponent();
TimerCheck = new System.Timers.Timer(5000);
TimerCheck.Elapsed += new ElapsedEventHandler(CheckEvent);
TimerCheck.AutoReset = true;
CheckForIllegalCrossThreadCalls = false;
webBrowser1.ScriptErrorsSuppressed = true;
chromiumWebBrowser1.Load("https://firstwebsite.com");
webBrowser1.Navigate("http://secondwebsite.com");
}
private async void CheckEvent(Object source, ElapsedEventArgs e)
{
if (login) {
string script = "document.getElementById('SecondSDISP').innerText;";
JavascriptResponse jr = chromiumWebBrowser1.EvaluateScriptAsync(script).Result;
if (jr.Success)
{
if (jr.Result.ToString().Contains("01"))
{
label2.ForeColor = Color.Red;
label2.Text = jr.Result.ToString();
sendCode(jr.Result.ToString());
}
}
else
{
label2.ForeColor = Color.Black;
label2.Text = "no data";
}
label4.Text = timer.ToString();
}
}
private void button2_Click(object sender, EventArgs e)
{
TimerCheck.Enabled = true;
TimerCheck.Start();
button2.Enabled = false;
}
public void sendCode(string code)
{
string msg = "";
if (code == "1") msg = "1 coda";
else msg = code.ToString() + " code";
var textarea = webBrowser1.Document.GetElementsByTagName("textarea")[0];
textarea.InnerHtml = msg;
textarea.Focus();
var allsvg = webBrowser1.Document.GetElementsByTagName("svg");
foreach (HtmlElement svg in allsvg)
{
if (svg.GetAttribute("className").Contains("-send"))
{
svg.InvokeMember("click");
break;
}
}
}
private void button4_Click(object sender, EventArgs e)
{
sendCode("1");
}
}
}
also after i started the timer, if i click to button4 to test the function, it works correctly. instead of when its called from timer
the error is:
System.InvalidCastException
HResult=0x80004002
Message=Specified cast is not valid.
Source=System.Windows.Forms
StackTrace:
at System.Windows.Forms.UnsafeNativeMethods.IHTMLDocument2.GetLocation()
at System.Windows.Forms.WebBrowser.get_Document()
at CodePinger.Form1.sendCode(String code) in C:\Users\Flynns82\source\repos\CodePinger\Form1.cs:line 105
at CodePinger.Form1.<CheckEvent>d__9.MoveNext() in C:\Users\Flynns82\source\repos\CodePinger\Form1.cs:line 63
the indicated line are:
105) var textarea = webBrowser1.Document.GetElementsByTagName("textarea")[0];
63) sendCode(jr.Result.ToString());
can someone explain me what is the problem?
Most likely your problem is you are trying to access the WebBrowser from a non-STA thread (aka the 'UI Thread')
When you use the button4_click handler your code is running on the STA Thread (by default), however, when a Time Event handler is called back it happens in a different thread (which is not the STA one) thus you will have problems invoking/accessing properties on ActiveX/Components (who reside in the STA thread) if you do not "invoke" back into the STA.
I recommend to take a look to the following SO Question: STA vs MTA for a technical explanation.
For solving the invoke problem look into the following SO Question: Automating the InvokeRequired code pattern
On the other hand the browser exposes a NavigateComplete event, you do not need to have a time checking when the page is loaded, just hook yourself to the event and wait for it after navigate, the DOM will be stable once this event fires.
I have a button on a C# Windows forms application that runs the code below:
private void btnAddDataToCSV_Click(object sender, EventArgs e)
{
string path = "D:\MyCsvFile.csv";
Process.Start(path);
}
Basically, the user needs to be able to make changes to a CSV file. The user is most comfortable making these changes in Microsoft Excel. I found the above code online from someone who has many years of experience working with MS Office products and C#. This guy explained that the code below works but is more error prone than using "Process.Start()" as shown above.
string path = "D:\MyCsvFile.csv";
var ExcelApp = new Excel.Application();
ExcelApp.Workbooks.OpenText(path, Comma: true);
ExcelApp.Visible = true;
This guy explains that "Process.Start()" works because Windows is set up to use Excel as the default program for opening CSV files.
Anyways, when the user clicks the button above (btnAddDataToCSV), I need my C# form to become disabled (all the buttons grayed out) until the user closes the Excel workbook that is displaying the CSV file. Potentially, I may need the C# program to read the CSV file immediately after the user closes Excel.
Ho do I do this?
I assume... I need to do something like... "disable C# form... then Wait for Excel close event... then once excel close event happens... enable C# form and read CSV file."
Below is the code I have for reading the CSV file:
string path = "D:\MyCsvFile.csv";
var reader = new StreamReader(File.OpenRead(path));
List<string> listA = new List<string>();
List<string> listB = new List<string>();
List<string> listC = new List<string>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
listA.Add(values[0]);
listB.Add(values[1]);
listC.Add(values[2]);
}
I think that you need to use an event handler for process that exits.
Something like this:
private void btnAddDataToCSV_Click(object sender, EventArgs e)
{
//disable form
string path = "D:\MyCsvFile.csv";
using (myProcess = new Process())
{
myProcess.EnableRaisingEvents = true;
myProcess.Exited += new EventHandler(Excel_Exit);
myProcess.Start(path);
}
}
public void Excel_Exit(object sender, System.EventArgs e){
//enableForm
}
Process.Exited example in documentation: https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.exited?view=netcore-3.1
I was unable to get the solution above to work even though the solution is similar to Microsoft's solution.
https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.exited?view=netcore-3.1
I found this:
Why is my process's Exited method not being called?
It says:
"I've come across examples that place new Process() in a using clause. Do not do that if you want to use the Exited feature. The using clause destroys the instance along with any event handles on Exited."
It says:
using(var process = new Process())
Should be:
var process = new Process();
This makes no since because the link above that I provided for Microsoft is an example of how to use the "Process.Exited Event" and it uses "using(var process = new Process())"
But sure enough... for some reason when I tried "var process = new Process();" it worked... This is the code that worked:
private void btnAddDataToCSV_Click(object sender, EventArgs e)
{
var myProcess = new Process();
myProcess.EnableRaisingEvents = true;
myProcess.Exited += new EventHandler(Excel_Exit);
myProcess.StartInfo.FileName = "D:\\MyCsvFile.csv";
myProcess.Start();
}
public void Excel_Exit(object sender, System.EventArgs e)
{
MessageBox.Show("Success!!");
}
Here is the code that does not work... But I don't understand why because it is similar to Microsoft's example.
private Process myProcess;
private void btnAddDataToCSV_Click(object sender, EventArgs e)
{
using (myProcess = new Process())
{
myProcess.EnableRaisingEvents = true;
myProcess.Exited += new EventHandler(Excel_Exit);
myProcess.StartInfo.FileName = "D:\\MyCsvFile.csv";
myProcess.Start();
}
}
public void Excel_Exit(object sender, System.EventArgs e)
{
MessageBox.Show("Success!!");
}
When you close excel... the event never fires... Can someone explain this? Surely Microsoft's example isn't wrong.
I have a stream of strings coming in via serial Port continuously at a fixed baud rate. And I need to separate a specific string every time it is received, split it into useful parts and print those parts in Winform interface. Any help and/or example is appreciated
I am receiving GPS data and have connected it to PC via UART to serial port. Setting up serial port with WinForms and receiving data is no problem. Placing filters to synthesize required messages and handling them is what I need help for.
////////////// This code is only for reference to the GUI ////////////
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.IO.Ports;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Clear_Click(object sender, EventArgs e)
{
richTextBox1.Clear();
}
private void Start_Click(object sender, EventArgs e)
{
serialPort1.Open();
}
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
richTextBox1.ScrollToCaret();
}
private void Read_Click(object sender, EventArgs e)
{
string data = serialPort1.ReadExisting(); //reads the whole
readbuffer
if (data.Length >= 10)
{
richTextBox1.Text = data;
}
}
private void Stop_Click(object sender, EventArgs e)
{
serialPort1.Close();
}
}
}
For Example: If you see the GPS data in the image
(link: https://www.google.com/imgres?imgurl=https%3A%2F%2Fidyl.io%2Fwp-content%2Fuploads%2F2017%2F05%2Fraw-gps-data-8.png&imgrefurl=https%3A%2F%2Fidyl.io%2Farduino%2Fhow-to%2Finterface-gps-module-arduino%2F&docid=zNdHsCmZc93QIM&tbnid=P0_E1tvN1Ur0HM%3A&vet=10ahUKEwjh9NudvqTgAhVL4eAKHYtyDHYQMwhbKA0wDQ..i&w=597&h=496&bih=938&biw=1920&q=gps%20data&ved=0ahUKEwjh9NudvqTgAhVL4eAKHYtyDHYQMwhbKA0wDQ&iact=mrc&uact=8#h=496&imgdii=P0_E1tvN1Ur0HM:&vet=10ahUKEwjh9NudvqTgAhVL4eAKHYtyDHYQMwhbKA0wDQ..i&w=597)
you will see that different messages are coming in cyclic order. I am interested in every message that starts with "$GPGGA". I want to read only this message every time it occurs, split it into useful parts, print them, and then go back to receive the next message that starts with "$GPGGA".
Note: When reception is not good, gps skips the positions in message that are reserved for actual readings so the string size gets smaller.
You could do this with a regular expression, or by splitting the received string using '$' - since each line starts with this character.
// Holds the partial/unprocessed data we've read from the serial port.
private string _serialRxString = string.Empty;
private void Start_Click(object sender, EventArgs e)
{
serialRxString = string.Empty;
serialPort1.Open();
}
private void Read_Click(object sender, EventArgs e)
{
// Add the new data to what we have.
_serialRxString += serialPort1.ReadExisting();
// Each line starts with a '$'.
string[] lines = _serialRxString.Split(
new char[] { '$' },
StringSplitOptions.None);
// Don't process the last one yet, it might not be complete.
for (int i = 0; i < lines.Length - 1; i++)
{
// Check if it's the line we're looking for.
if (lines[i].StartsWith("GPGGA"))
{
string[] values = lines[i].Split(new char[] { ',' });
// Do what you want with the values from the $GPGGA line.
}
}
// Keep the last line, since it might not be complete yet.
_serialRxString = lines.Last();
}
You also might want to subscribe to the serial port data received event. This way any data received on the serial port will automatically be processed, without having to click the read button. You can do this either through the designer or in your form constructor. Then the data received event would be the same as what's in the Read_Click function above.
To subscribe to the DataReceived event in the designer:
Or in the constructor:
public Form1()
{
InitializeComponent();
serialPort1.DataReceived += serialPort1_DataReceived;
}
The data received event then would be:
private void serialPort1_DataReceived(
object sender,
SerialDataReceivedEventArgs e)
{
// Add the new data to what we have.
_serialRxString += serialPort1.ReadExisting();
// Each line starts with a '$'.
string[] lines = _serialRxString.Split(
new char[] { '$' },
StringSplitOptions.None);
// Don't process the last one yet, it might not be complete.
for (int i = 0; i < lines.Length - 1; i++)
{
// Check if it's the line we're looking for.
if (lines[i].StartsWith("GPGGA"))
{
string[] values = lines[i].Split(new char[] { ',' });
// Do what you want with the values from the $GPGGA line.
}
}
// Keep the last line, since it might not be complete yet.
_serialRxString = lines.Last();
}
Then whenever anything is received on the serial port this function automatically gets called.
There are also some packages available for parsing NMEA messages that might be helpful. Such as this one: NmeaParser
I feel like this question comes up a lot. You've already done the hard part, that is sending/receiving data. Now you just need to parse it. Looks like from the example image, everything is comma delimited and each line ends with a Carriage Return Line Feed. See my answer to this, you should be able to build a string for each line then split it on commas and get the field you are looking for.
I want to get a console window within my form. Basically when you click button1, it runs a batch script(test.exe). I don't want a separate batch window but instead I want it to show up within my form.
I figure there are probably two ways of doing this, either 1, somehow embedding the console within my form, or 2, set StartInfo.CreateNoWindow = true; when you click button1 and get the output to funnel into a listbox to simulate a console within my form.
I am just a little stuck because I have found methods for doing both but my own testing with the various other methods people have suggested, nothing has worked. But either way, my user needs to be able to send input back to the console.
Which method would be simpler and how would I go about it?
I believe the best way to do this is to redirect output. Basically things will still execute as you want, but you will get the output wherever you want/need.
using System;
using System.Diagnostics;
using System.Text;
using System.Windows.Forms;
namespace ConsoleOutput_test
{
public partial class Form1 : Form
{
Process sortProcess;
private static StringBuilder sortOutput = null;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
sortProcess = new Process();
sortProcess.StartInfo.FileName = "C:\\Windows\\System32\\cmd.exe";
// Set UseShellExecute to false for redirection.
sortProcess.StartInfo.CreateNoWindow = true;
sortProcess.StartInfo.UseShellExecute = false;
// Redirect the standard output of the sort command.
// This stream is read asynchronously using an event handler.
sortProcess.StartInfo.RedirectStandardOutput = true;
sortProcess.StartInfo.RedirectStandardInput = true;
sortProcess.StartInfo.RedirectStandardError = true;
sortOutput = new StringBuilder("");
// Set our event handler to asynchronously read the sort output.
sortProcess.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
sortProcess.ErrorDataReceived += new DataReceivedEventHandler(SortErrorHandler);
// Redirect standard input as well. This stream
// is used synchronously.
sortProcess.StartInfo.RedirectStandardInput = true;
// Start the process.
sortProcess.Start();
// Start the asynchronous read of the sort output stream.
sortProcess.BeginOutputReadLine();
while (!sortProcess.HasExited)
{
Application.DoEvents(); // This keeps your form responsive by processing events
}
}
private void SortOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (txtConsole.InvokeRequired) { txtConsole.BeginInvoke(new DataReceivedEventHandler(SortOutputHandler), new[] { sendingProcess, outLine }); }
else
{
txtConsole.AppendText(Environment.NewLine + outLine.Data);
}
}
private void SortErrorHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (txtConsole.InvokeRequired) { txtConsole.BeginInvoke(new DataReceivedEventHandler(SortErrorHandler), new[] { sendingProcess, outLine }); }
else
{
txtConsole.AppendText(Environment.NewLine + outLine.Data);
}
}
private void button2_Click(object sender, EventArgs e)
{
sortProcess.StandardInput.WriteLine(txtOutput.Text);
txtOutput.Text = "";
}
}
}
I already have the saving part down and I know it works, but when I click load button, it will not display anything that I have saved from the text boxes that go to the saying.txt file
using System;
using System.IO;
using System.Windows.Forms;
namespace WindowsFormsApplication2
{
public partial class Grades : Form
{
private StreamWriter fil;
public Grades()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
fil = new StreamWriter("saying.txt"); //This is the txt file
}
catch (DirectoryNotFoundException exc)
{
lstBxDisplay.Text = "Nothing " +
exc.Message;
}
catch (System.IO.IOException exc)
{
lstBxDisplay.Text = exc.Message;
}
}
// saving the files to the saying.txt
private void btnSaveAs_Click(object sender, EventArgs e)
{
try
{
fil.WriteLine(txtBxLastName.Text);
txtBxLastName.Text = "";
txtBxLastName.Focus();
}
catch (System.IO.IOException exc)
{
lstBxDisplay.Text = exc.Message;
}
}
// next is the load button to load the files into the list/display box
private void btnLoad_Click(object sender, EventArgs e)
{
string inValue;
try
{
using (StreamReader infil =
new StreamReader("saying.txt"))
{
inValue = infil.ReadLine();
while (inValue != null)
{
inValue = infil.ReadLine();
if (inValue != null)
this.lstBxDisplay.Items.Add(inValue);
} // end of while
} // end of using
}
catch (System.IO.IOException exc)
{
lstBxDisplay.Text = exc.Message;
}
}
private void Grades_FormClosing(object sender, FormClosingEventArgs e)
{
try
{
fil.Close();
}
catch
{
}
}
}
}
any reason why it is not loading into the list box? I have tried both label and text box to display the message and neither of them work. I debugged the program and it is executing fine
You have multiple issues here but I will point out two main issues that will get your code working.
You are not closing the stream when you try to save. If the stream stays open, you will never be able to read the values when you try to "Load" the file. You need to call fil.Close(); at the end of your btnSaveAs_Click method.
This is how the save should read.
fil.WriteLine(txtBxLastName.Text);
txtBxLastName.Text = "";
txtBxLastName.Focus();
fil.Close();
You're skipping the first line of the file in your "Load" method. You call infil.ReadLine(); then in the loop, you call it again before you add it to your listbox. You need to move your second ReadLine(). If you are only ever writing a single line to the file, your existing code will skip that first line and try to read it again which will be null (no second line). So, it will never add anything to your listbox.
This is how the reads should be ordered.
using (StreamReader infil = new StreamReader("saying.txt"))
{
inValue = infil.ReadLine();
while (inValue != null)
{
this.lstBxDisplay.Items.Add(inValue);
inValue = infil.ReadLine();
} // end of while
} // end of using
Those changes will get you working. Now to point out, you are going about reading and writing to a file all wrong. You should not be opening a stream in your form load and waiting for button clicks to be writing/reading from that stream. Unless you have a very good reason to do what you are doing, you should be opening your stream, performing the read/write operation, and closing your stream right away. For a simple file IO, I would even suggest using a different mechanism. Look at the MSDN for the System.IO.File Class for easier methods to read lines or write lines to a file.