I have a SaveFileDialog in my program. The issue is that when I click "Cancel" on the dialog, another SaveFileDialog opens up. But when I click cancel on the second SaveFileDialog, a third does NOT appear, so it's not a loop or anything like that. I can't see what is causing my SaveFileDialog to behave in such an odd manner. Obviously I need to fix this so that if the user clicks cancel on the first SaveFileDialog, it returns them to the form.
The code for saving in my program is as follows:
private void SaveFile()
{
if (filepath == null)
{
SaveFileAs();
}
else
{
StreamWriter sw = new StreamWriter(filepath);
try
{
sw.WriteLine(richTextBoxPrintCtrl1.Rtf);
richTextBoxPrintCtrl1.Modified = false;
sw.Close();
lastsave.Text = "Last Saved: " + DateTime.Now.ToString();
}
catch (Exception exc)
{
MessageBox.Show("Failed to save file. \n \n" + exc.Message);
}
finally
{
if (sw != null) sw.Close();
}
And SaveFileAs
private void SaveFileAs()
{
SaveFileDialog sfdSaveFile = new SaveFileDialog();//Creates a new instance of the SaveFileDialog
sfdSaveFile.Title = "Save File";//The title of the SaveFileDialog window
sfdSaveFile.FileName = "Untitled";//The default filename in the SaveFileDialog window
sfdSaveFile.Filter = "Rich Text Files (*.rtf)|*.rtf|Text Document (*.txt)|*.txt";//The supported file extensions when saving
if (sfdSaveFile.ShowDialog() == DialogResult.OK)//If the condition is correct, run the lines of code
try//try to run the code
{
filepath = sfdSaveFile.FileName;//get the filepath of the file once it is saved
SaveFile();//Calls the SaveFile object
this.Text = string.Format("{0} - Basic Word Processor", Path.GetFileName(sfdSaveFile.FileName));//Set the form name
lastsave.Text = "Last Saved: " + DateTime.Now.ToString();//Writes the text to the lastsave.Text label, followed by the current date and time
richTextBoxPrintCtrl1.Modified = false;
return;
}
catch (Exception exc)//Catches any errors
{
MessageBox.Show("An error occured whilst saving. " + exc.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else if (sfdSaveFile.ShowDialog() == DialogResult.Cancel)
{
return;
}
else if (sfdSaveFile.ShowDialog() == DialogResult.Cancel)//If the condition is true, run the line of code
{
return;
}
If anyone could help me determine why this is occurring, I'd really appreciate it..
--EDIT--
I forgot to mention that if the user does go through and save the file, the SaveFileDialog doesn't open up another SaveFileDialog. It is something to do with cancelling the SaveFileDialog which causes the issue.
sfdSaveFile.ShowDialog() opens the file dialog. If it's not DialogResult.OK the first time, it goes to the else clause and gets called again. Store the result of ShowDialog and check what it is, don't call it every time.
In order to do so, use this sort of if/else:
DialogResult dialogResult = sfdSaveFile.ShowDialog();
if (dialogResult == DialogResult.OK)
{
}
else if (dialogResult == DialogResult.Cancel)
{
}
Related
I am trying to write all of the processes to a text file, but it will only write the first process in my system. Would you guys mind seeing if there is anything wrong or that I can adjust to fix this issue?
private void button5_Click(object sender, EventArgs e)
{
try
{
var ap = Process.GetProcesses();
SaveFileDialog sfdv2 = new SaveFileDialog();
if(sfdv2.ShowDialog() == DialogResult.OK)
{
foreach(Process process in ap)
{
string path = sfdv2.FileName;
BinaryWriter bw2 = new BinaryWriter(File.Create(path));
bw2.Write("test" + " " + ap.ToString());
bw2.Dispose();
}
}
}
catch(Exception ex) { MessageBox.Show(ex.Message); }
}
Take a step back and see what you are writing.
For each process in the list, you create a file with the same name (and overwrite the previous one) and write "test {process}" in it.
The code does what you tell it, and that's why you end up with only one process in the file.
You can fix this by opening the file outside the loop and closing it afterwards, or even better, you can write it with a using statement. Also, please don't use BinaryWriter for writing to text file. There are many other methods, and the suggested one is StreamWriter. Take a look at this documentation page to see some examples.
string path = sfdv2.FileName;
// with using, the file will be also closed when it's disposed.
using (var file = new System.IO.StreamWriter(path))
{
foreach(Process process in ap)
{
file.WriteLine("test " + ap.ToString());
}
}
You may wish to consider using the Async version of the button click event handler. This is because writing to files is potentially a long running process. So you might want to start it and not wait to block your UI. Combine this with StreamWriter.WriteAsync methods and use in conjunction with await.
e.g.
private async void button5_Click(object sender, EventArgs e)
{
try
{
var ap = Process.GetProcesses();
SaveFileDialog sfdv2 = new SaveFileDialog();
if(sfdv2.ShowDialog() == DialogResult.OK)
{
string path = sfdv2.FileName;
using(StreamWriter bw2 = new StreamWriter(File.Create(path)))
{
foreach(Process process in ap)
{
await bw2.WriteAsync("test" + " " + process.ProcessName.ToString());
}
}
}
}
catch(Exception ex) { MessageBox.Show(ex.Message); }
}
Try splitting the initial problem into smaller, easier tasks:
Lines we want to write down:
var linesToWrite = Process
.GetProcesses()
.Select(process => $"test {process.ProcessName}");
UI:
using (SaveFileDialog dialog = new SaveFileDialog() {
//TODO: here we put dialog's parameters
}) {
if (dialog.ShowDialog() == DialogResult.OK) {
//TODO: here we put the main routine
}
}
Combining it all together:
private void button5_Click(object sender, EventArgs e) {
using (SaveFileDialog dialog = new SaveFileDialog() {
//TODO: here we put dialog's parameters
}) {
if (dialog.ShowDialog() == DialogResult.OK) {
File.WriteAllLines(dialog.FileName, Process
.GetProcesses()
.Select(process => $"test {process.ProcessName}"));
}
}
}
I'm trying to make a program that loads a configuration file from another application.
If the file exists, it loads it and displays a message, but if the configuration file is not valid, it displays an error message and then opens a dialog box to load the correct file. But if the user reloads the wrong file, the same dialog box should appear again but that's when my code fails.
Similarly, if the file did not exist from the beginning, it displays a dialog box to load the file, but if it is given to cancel the dialog box or an incorrect file is selected again, my code fails.
I know that the solution would be to use loops but I'm not sure how to structure it.
Pd: searchfile() is my function to open dialog box and readconfig() is my function to read config file of another application.
strfilenamepath = #"C:\Users\test\dogs.exe.config";
if (File.Exists(strfilenamepath))
{
onlyFilename = System.IO.Path.GetFileName(strfilenamepath);
textBox1.Text = onlyFilename;
try
{
string[] valores = readConfig(strfilenamepath);
MessageBox.Show(valores[0] + valores[1] + valores[2]);
}
catch (Exception ex)
{
MessageBox.Show("Error loading config file." + ex.Message);
searchFile();
onlyFilename = System.IO.Path.GetFileName(strfilenamepath);
textBox1.Text = onlyFilename;
string[] valores = readConfig(strfilenamepath);
MessageBox.Show(valores[0] + valores[1] + valores[2]);
}
}
else
{
searchFile();
onlyFilename = System.IO.Path.GetFileName(strfilenamepath);
textBox1.Text = onlyFilename;
try
{
readConfig(strfilenamepath);
string[] valores = readConfig(strfilenamepath);
MessageBox.Show(valores[0] + valores[1] + valores[2]);
}
catch (Exception ex)
{
MessageBox.Show("Error loading config file." + ex.Message);
searchFile();
onlyFilename = System.IO.Path.GetFileName(strfilenamepath);
textBox1.Text = onlyFilename;
string[] valores = readConfig(strfilenamepath);
MessageBox.Show(valores[0] + valores[1] + valores[2]);
}
}
It is easier to design it if you extract the reading logic to another method that handles exceptions and returns a Boolean to signal the success and the computed result. The TryDoSomething pattern does exactly this.
In pseudo code
public bool TryReadConfig(string path, out string[] valores)
{
valores = null;
try {
valores = read the values;
return true;
} catch {
Display message;
return false;
}
}
The main loop in pseudo code
strfilenamepath = #"C:\Users\test\dogs.exe.config";
while (true) {
if (File.Exists(strfilenamepath) && TryReadConfig(strfilenamepath, out var valores)) {
Do something with the valores;
break;
}
var ofd = new OpenFileDialog{ ... };
if (ofd.ShowDialog() == DialogResult.OK) {
strfilenamepath = ofd.Filename;
} else {
break; // The user canceled the operation.
}
}
You can do something like this:
try
{
//Code to try open the file to memory
}
catch (Exception ex)
{
while (true)
{
MessageBox.Show(#"Select an valid file");
var path = searchFile();
if (string.IsNullOrWhiteSpace(path))
continue;
try
{
//Code to try open the file to memory
}
catch (Exception ex2)
{
MessageBox.Show(#"The selected file is not valid");
continue;
}
break;
}
}
I am working on a school project but I cant figure out this last bug. It is supposed to open a saveFileDialog when the first if statement returns false. But instead of continuing on into the else statement, it goes straight to throwing the exception and never opens the saveFile Dialog. It gives me the following error: Code: The path is not of a legal form.
I dont understand what the problem is. The user should be able to select the path in the save dialog that pops up. The file doesnt exist yet and its supposed to open the save file dialog to make the file.
private void btnSave_Click(object sender, EventArgs e)
{
// Declare StreamWriter object
StreamWriter outputFile;
// Try to write file
try
{
// If current file is > 0
if (new FileInfo(currentFile).Length > 0)
{
// Create output file using current file
outputFile = File.CreateText(currentFile);
// Loop through current file and write lines to output file
for (int i = 0; i < lstBoxLog.Items.Count; i++)
{
outputFile.WriteLine(lstBoxLog.Items[i].ToString());
}
// Close text file
outputFile.Close();
}
// Else open save dialog for user to save file
else
{
// If save file dialog is equal to dialog result
if (saveFile.ShowDialog() == DialogResult.OK)
{
// Open output file object with create text
outputFile = File.CreateText(saveFile.FileName);
// Set currentFile to = savefile dialog
currentFile = saveFile.FileName;
// Loop through each line and write to file
for (int i = 0; i < lstBoxLog.Items.Count; i++)
{
outputFile.WriteLine(lstBoxLog.Items[i].ToString());
}
// Close text file
outputFile.Close();
}
// Else show error message
else
{
// Display message box dialog
MessageBox.Show("Cannot save file.", "Not Saved");
}
}
}
// Display error message.
catch (Exception ex)
{
// Display message box dialog
MessageBox.Show("Save canceled. \n\nCode: " + ex.Message, "Save Error!");
}
}
try
{
if (File.Exists(currentFile))
{
if (new FileInfo(currentFile).Length > 0)
{
...
}
}
else
{
//show save file dialog
}
}
catch
{
...
}
Per Rob's suggestion this is what I used.
try
{
// If current file is > 0
if (currentFile.Length > 0)
{
// Create output file using current file
outputFile = File.CreateText(currentFile);
I have the following code:
Open File Code
OpenFileDialog ofd = new OpenFileDialog();
ofd.Title = "Open File";
ofd.FileName = "";
ofd.Filter = "Rich Text Files (*.rtf)|*.rtf|Text Document (*.txt)|*.txt|Microsoft Word Document (*.doc)|*.doc|Hypertext Markup Language Document (*.html)|*.html"; StreamReader sr = null;
if (ofd.ShowDialog() != DialogResult.Yes) return;
{
NewFile();
}
try
{
sr = new StreamReader(ofd.FileName);
this.Text = string.Format("{0} - Basic Word Processor", Path.GetFileName(ofd.FileName));
richTextBoxPrintCtrl1.Text = ofd.FileName;
richTextBoxPrintCtrl1.Text = sr.ReadToEnd();
filepath = ofd.FileName;
richTextBoxPrintCtrl1.LoadFile(fileName, RichTextBoxStreamType.RichText);
}
catch
{
}
finally
{
if (sr != null) sr.Close();
}
New File Code
if (richTextBoxPrintCtrl1.Modified)
{
DialogResult r = MessageBox.Show(this, "Save Current Document?", "Save?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (r == DialogResult.Yes) SaveFile();
if (r == DialogResult.Cancel) return;
}
this.Text = string.Format("Untitled - Basic Word Processor");
richTextBoxPrintCtrl1.Text = "";
filepath = null;
}
}
SaveFileAs Code
SaveFileDialog sfdSaveFile = new SaveFileDialog();
sfdSaveFile.Title = "Save File";
sfdSaveFile.FileName = "Untitled";
sfdSaveFile.Filter = "Rich Text Files (*.rtf)|*.rtf|Text Document (*.txt)|*.txt|Microsoft Word Document (*.doc)|*.doc|Hypertext Markup Language Document (*.html)|*.html";
if (sfdSaveFile.ShowDialog() == DialogResult.OK)
try
{
filepath = sfdSaveFile.FileName;
SaveFile();
this.Text = string.Format("{0} - Basic Word Processor", Path.GetFileName(sfdSaveFile.FileName));
}
catch (Exception exc)
{
}
SaveFile Code
if (filepath == null)
{
SaveFileAs();
return;
}
StreamWriter sw = new StreamWriter(filepath);
//StreamWriter stwrite = null;
try
{
sw.WriteLine(richTextBoxPrintCtrl1.Text);
richTextBoxPrintCtrl1.Modified = false;
sw.Close();
}
catch (Exception e)
{
MessageBox.Show("Failed to save file. \n" + e.Message);
}
finally
{
if (sw != null) sw.Close();
}
Currently, the program skips the NewFile event (even if the text has been modified). How can I make it so that when I click "Open", it asks me if I would like to save (if the text is modified). Then if I click cancel, it returns me to the form?
Sorry. I'm really new to programming so this is all a learning curve.
Okay, I think I see what's going on here. First off I don't believe return; works the way you think it does.
if (ofd.ShowDialog() != DialogResult.Yes) return;
{
NewFile();
}
You have a return; call that happens if the show dialog is not yes. The { newFile() } code doesn't need braces around it. So those lines are really:
if (ofd.ShowDialog() != DialogResult.Yes) return;
NewFile();
Now, given your requirement, NewFile is called WAY too late in the game anyway. You want that to happen before you ask them what to open; just like most other windows programs work.
But, there's another issue. Your return statement in the NewFile method is simply returning from NewFile. It's not telling the previous method to bail out.
So the NewFile method needs a return type to indicate whether to allow the calling method to go forward or not.
And, looking at your save file you have a return method there too. What's with all of the return; calls?
Which brings us back to how do we fix this?
Answer: rewrite the whole thing. Starting with the following method:
private Boolean CanClear() {
Boolean result = false;
if (richTextBoxPrintCtrl1.Modified)
{
DialogResult r = MessageBox.Show(this, "Save Current Document?", "Save?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (r == DialogResult.Yes) {
SaveFile();
result = true;
}
} else {
result = true;
}
return result;
}
Now, in your Open and New file methods do the following (assuming these are the method headers)
protected void OpenFile(...) {
if (!CanClear()) return;
.... now execute the code to load the open dialog and the selected file.
}
protected void NewFile(...) {
if (!CanClear()) return;
this.Text = string.Format("Untitled - Basic Word Processor");
richTextBoxPrintCtrl1.Text = "";
filepath = null;
}
The problem is here:
if (ofd.ShowDialog() != DialogResult.Yes) return;
{
NewFile();
}
remove that return. But, as #Chris says, you should ask whether to save the current file or not before the user selects the new file to open.
I am experiencing a problem with saving a currently opened file without it popping up the dialog asking what name to save it under.
To clarify myself a little more, I open a .txt file and work with it, then would like to just click 'Save' and it save the file without popping up a 'Save As' dialog box.
Here is my save code:
private void SaveFile()
{
SaveFileDialog fileChooser = new SaveFileDialog();
fileChooser.Title = "Choose Save Location";
fileChooser.Filter = "Text Files (*.txt)|*.txt";
fileChooser.OverwritePrompt = false; //Removes warning
DialogResult result = fileChooser.ShowDialog();
if (result == DialogResult.Cancel)
{
return;
}
try
{
string fileName = fileChooser.FileName;
output = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write);
fileWriter = new StreamWriter(output);
foreach (Employee emp in employee)
{
fileWriter.WriteLine(emp.Firstname + "," + emp.Lastname + "," + emp.Position + "," + emp.Bmonth + "," + emp.Bday + "," + emp.BYear + "," + emp.Salary + "," + emp.Hiremonth + "," + emp.Hireday + "," + emp.Hireyear);
}
fileWriter.Close();
output.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
fileWriter.Close();
output.Close();
}
}
Everything works great as far as saving it to a .txt file and loading it back in, it's just that popup that irks me.
The fileChooser object is a SaveFileDialog object. You're causing it to display by calling:
DialogResult result = fileChooser.ShowDialog();
If you don't want to show the dialog, just omit the fileChooser code and instead use:
string fileName = strAlreadyKnownFileName;
I'd firstly save the full path of the opened file in some variable lets say:
private string filepath = "path/to/my/file";
Then you need to create a button and call it i.e. "Save" double click on the button and write this simple code to save whatever you want to the current opened file:
as simple as that...
EDIT:
private void SaveFile()
{
//do your loop and stuff in here and finally write your text to the file using this
File.WriteAllText(filepath, yourtexttobesaved);
}