Close SaveFileDialog/OpenFileDialog programmatically without using pinvoke - c#

Due to some requirements, I have to close SaveFileDialog programmatically without using PINVOKE.
Is there any way to close SaveFileDialog other than using the PINVOKE way?
I had tried to close the owner form of the SaveFileDialog, but the SaveFileDialog still there.
What I had tried:
Close the form which execute the ShowDialog() of SaveFileDialog.
SaveFileDialog.Dispose()

Closing the owner window passed to the ShowDialog(owner); method should work. For example:
private static Form CreateDummyForm(Form owner) {
Form dummy = new Form();
IntPtr hwnd = dummy.Handle; // force handle creation
if (owner != null) {
dummy.Owner = owner;
dummy.Location = owner.Location;
owner.LocationChanged += delegate {
dummy.Location = owner.Location;
};
}
return dummy;
}
[STAThread]
static void Main() {
Form form = new Form();
form.Size = new Size(400,400);
Button btn = new Button { Text = "btn" };
btn.Click += delegate {
SaveFileDialog fsd = new SaveFileDialog();
int timeoutMillis = 5000;
Form dummy = CreateDummyForm(form); // Close disposes the dummy form
Task.Delay(TimeSpan.FromMilliseconds(timeoutMillis)).ContinueWith((t) => { dummy.Close(); dummy.Dispose(); }, TaskScheduler.FromCurrentSynchronizationContext());
fsd.ShowDialog(dummy);
fsd.Dispose();
};
form.Controls.Add(btn);
Application.Run(form);
}

If you use visual studio designer to add a SaveFileDialog, your Form will have a field with this dialog during the life time of your form.
It is way more efficient and way more easier create the SaveFileDialog only when needed. If you do this in a using statement, you won't have to take care of Disposing it, and certainly won't need PInvoke
private void MenuItem_FileSaveAs_Clicked(object sender, ...)
{
using (var dlg = new SaveFileDialog())
{
dlg.FileName = this.FileName;
dlg.InitialDirectory = ...
dlg.DefaultExt = ...
...
// Show the SaveFileDialog, and if Ok save the file
var dlgResult = dlg.ShowDialog(this);
if (dlgResult == DialogResult.OK)
{
// operator selected a file and pressed OK
this.FileName = dlg.FileName;
this.SaveFile(this.FileName);
}
}
}

Related

How to save files in specific locations without showing SaveDialog's Prompt

I'm been wondering is there a way to save a file in specific's folder in C#?
The only method that I know is 'SaveFileDialog' but the problems is I want to save files
in folder without showing saveFilesDialog's Box.
saveFilesDialog's Box : is a box that prompts you to Click 'YES' or 'CANCEL'.
Code samples
-In form1
public Form1()
{
InitializeComponent();
}
private string Path =#"D:\Files"; //locaction i wanna stores all the files in
private int i = 0;
private button1_Click(object sender,EventArgs e)
{
i++;
SaveDialogFile save = new SaveDialogFile();
if(Save.
if (save.ShowDialog() != DialogResult.OK)return; //Prompt's Dialog will show
save.Filter = "File Text(*.txt)|*.txt";
save.InitialDirectory = Path;
save.FileName = "txt"+i.ToString();
//Goal : i want 'save.FileName' store in 'Path' without Click 'OK' or Show Prompt Dialog's box
}
Expect Result
[1]: https://i.stack.imgur.com/9JqWO.png
Anyone can help me? I kinda stuck rn :)
This is my full code it's hard to read but you'll get the point
public partial class convertMp3ToWav : Form
{
public convertMp3ToWav()
{
InitializeComponent();
}
BackgroundWorker bw;
string withoutEx;
List<string> song_lists = new List<string>();
private void button1_Click(object sender, EventArgs e)
{
bw = new BackgroundWorker();
bw.DoWork += (obj, ae) => newThread();
bw.RunWorkerAsync();
}
void newThread()
{
Thread th = new Thread
((ThreadStart)(() =>
{
file();
}));
th.SetApartmentState(ApartmentState.STA);
th.Start();
th.Join();
}
void file()
{
string path = #"D:\musics\wav";
Directory.CreateDirectory(path);
FolderBrowserDialog f = new FolderBrowserDialog();
f.ShowDialog();
string[] lists = Directory.GetFiles(f.SelectedPath, "*.*", SearchOption.AllDirectories);
foreach (string list in lists)
{
if (Path.GetExtension(list) == ".mp3")
{
string fn = Path.GetFullPath(list);
withoutEx = Path.GetFileNameWithoutExtension(fn);
song_lists.Add(fn);
Console.WriteLine(withoutEx);
SaveFileDialog save = new SaveFileDialog();
save.Filter = "Wav FIle (*.wav)|*.wav;";
//save.FileName = song_lists[0];
save.FileName = withoutEx;
save.InitialDirectory = path;
if (save.ShowDialog() == DialogResult.OK)
{
using (Mp3FileReader mp3 = new Mp3FileReader(fn))
{
using (WaveStream pcm = WaveFormatConversionStream.CreatePcmStream(mp3))
{
WaveFileWriter.CreateWaveFile(save.FileName, pcm);
}
}
}
}
}
}
}
this code's work pretty well!! but i need to click 'OK' everytime!!
so is there anyway to save file without click 'OK' everytime!!
Conceptually the only thing SaveFileDialog is doing, if you merely click OK when it shows, is changing the file extension*. Use Path.ChangeExtension instead
var pathToSaveWavTo = Path.ChangeExtension(pathToMp3, "wav");
The wav will be saved alongside the mp3. If you want to save it elsewhere, use Path.Combine to build a new path e.g.
pathToSaveWavTo = Path.Combine(#"c:\temp", Path.GetFileName(pathToSaveWavTo));
*I say this because giving it a filter of *.WAV and a filename xxx without the mp3 extension will cause it to give you a filename of xxx.wav when you only click OK, thus xxx.mp3 -> xxx.wav

CommonOpenFileDialog allows usage of parent window

Hello my application uses some OpenFileDialogs for file picking. Furthermore, I need a folder picker for which I used the CommonOpenFileDialog with the option IsFolderPicker = true.
Now when I open an OpenFileDialog in the app the parent window is locked and can't be used anymore, exactly the behavior I want.
But when I use the CommonOpenFileDialog I can still access the parent window and open even more CommonOpenFileDialogs.
The OpenFileDialog is intialized like this:
//init dialog
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.InitialDirectory = Path.GetFullPath(SecondQPcrFilePath);
openFileDialog.CheckFileExists = true;
openFileDialog.CheckPathExists = true;
openFileDialog.Multiselect = false;
openFileDialog.ReadOnlyChecked = false;
openFileDialog.ShowReadOnly = false;
//show dialog
bool? dialogResult = openFileDialog.ShowDialog();
The CommonOpenFileDialog like this:
//is this mvvm conform?
Button senderButton = (Button)sender;
string clickedButton = senderButton.Name;
//init dialog
CommonOpenFileDialog openPathDialog = new CommonOpenFileDialog();
openPathDialog.IsFolderPicker = true;
openPathDialog.EnsurePathExists= true;
openPathDialog.Multiselect = false;
openPathDialog.EnsureFileExists = false;
openPathDialog.AllowNonFileSystemItems = true;
if(clickedButton == "limsOpenButton")
{
openPathDialog.InitialDirectory = Path.GetFullPath(LimsPath);
}
else if(clickedButton == "qpcrOpenButton")
{
openPathDialog.InitialDirectory = Path.GetFullPath(QpcrPath);
}
//show dialog
CommonFileDialogResult dialogResult = openPathDialog.ShowDialog();
Is there a way to prevent that behavior? The common dialog does not have a property like Owner or similiar.
The common file dialog exposes an overload for the ShowDialog method.
public CommonFileDialogResult ShowDialog(Window window);
Pass the parent window to this method and the dialog will be modal.
var window = // Get the parent window here.
var dialogResult = openPathDialog.ShowDialog(window);
It looks like you use code-behind, so this would be the window. Alternatively you could use MainWindow or the Windows collection in Application.Current to find the window.

C# winform: new form can't be TopMost before any messagebox shows

Recently I'm writing some code to display unhandled exceptions of the winforms app.
I want those exception-display-windows to be TopMost.
So I add an event handler to Application.ThreadException. The handler creates a new thread, opens a new form with TopMost attribute set to true.
Then I realize, new windows can't be TopMost even if their TopMost attribute is true. What's more, if any MessageBox was shown, subsequent new windows regain the ability to be TopMost!
There already is a post discussing this problem: TopMost form in a thread? But that answers still can't make my windows TopMost. Besides, I want to know why TopMost is valid after a MessageBox is shown.
Here is my minimal issue demo:
using System;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException += (o, e) => handleException();
Application.Run(new Form1());
}
static void handleException()
{
// before messagebox
doDisplay(); //Count:1
doDisplay(); //Count:2
doDisplay(); //Count:3
// Delay a while for the display threads to run
Thread.Sleep(300);
// show messagebox
if (MessageBox.Show("It doesn't matter you choose YES or NO",
"Message", MessageBoxButtons.YesNo) == DialogResult.No)
; // empty statement, just show msgbox
// after messagebox
doDisplay(); //Count:4
doDisplay(); //Count:5
doDisplay(); //Count:6
}
static int count = 0;
static void doDisplay()
{
Thread t = new Thread(new ThreadStart(() =>
{
Form f = new Form();
f.TopMost = true;
f.Text = "Count: " + ++count;
f.ShowDialog();
}));
t.IsBackground = true;
t.Start();
}
}
public class Form1 : Form
{
public Form1()
{
Button b = new Button();
b.Text = "throw!";
b.Click += (o, e) => { throw new Exception(); };
this.Controls.Add(b);
}
}
}
Output: window with Count: 1/2/3 aren't topmost, window with Count: 4/5/6 are topmost.
I'll just answer the question instead of trying to talk you out of this mistake. You need to create a new form and paste this code:
protected override CreateParams CreateParams {
get {
var cp = base.CreateParams;
cp.ExStyle |= 8; // Turn on WS_EX_TOPMOST
return cp;
}
}
Now it will be top-most even if displayed from another thread.

How to delete the file after 30 seconds/ or delete the file once after the job is completed

I am working on a C# project and i need the file to deleted after 30 seconds. So once the file sent to the machine i need the software to count till 30 seconds and at same time show a splash form and once 30 seconds crossed close the splash screen and then delete the file.
I have added a splash screen called "image". So now what happens is, the data is only sent to the printer after the splash screen is closed. I need to multi thread the job. I mean the data should print in one side while the splash screen should show at the same time. Is there a way i can come out!!.. Please help me out.
So in my case i am copying the file to the bin/debug folder. then sending data to the machine simultaneously show the splash screen for 30 seconds and close the splash screen and then i need to delete the file..
codes:
private void button4_Click(object sender, EventArgs e)
{
//string filePath = image_print();
// MessageBox.Show(filePath, "path");
string s = image_print() + Print_image();
if (String.IsNullOrEmpty(s) || img_path.Text == "")
{
return;
}
else
{
//here its coming to the splash screen code, But data is transferred to the machine only after the splash screen is close :-(
this.Hide();
omg = new image();
omg.ShowDialog();
this.Show();
//splash screen closed and then data is transferred.. which i don't need.. i need simultaneous job to be done at the same time..
PrintFactory.sendTextToLPT1(s);
}
}
private string image_print()
{
OpenFileDialog ofd = new OpenFileDialog();
string path = "";
string full_path = "";
string filename_noext = "";
ofd.InitialDirectory = #"C:\ZTOOLS\FONTS";
ofd.Filter = "GRF files (*.grf)|*.grf";
ofd.FilterIndex = 2;
ofd.RestoreDirectory = true;
if (ofd.ShowDialog() == DialogResult.OK)
{
filename_noext = System.IO.Path.GetFileName(ofd.FileName);
path = Path.GetFullPath(ofd.FileName);
img_path.Text = filename_noext;
//MessageBox.Show(filename_noext, "Filename"); - - -> switching.grf
// MessageBox.Show(full_path, "path");
//move file from location to debug
string replacepath = #"\\bin\Debug";
string fileName = System.IO.Path.GetFileName(path);
string newpath = System.IO.Path.Combine(replacepath, fileName);
// string newpath = string.Empty;
if (!System.IO.File.Exists(filename_noext))
System.IO.File.Copy(path, newpath);
filename_noext = img_path.Text;
MessageBox.Show(filename_noext, "path");
}
if (string.IsNullOrEmpty(img_path.Text))
return "";//
StreamReader test2 = new StreamReader(img_path.Text);
string s = test2.ReadToEnd();
return s;
}
private string Print_image()
{
//some codes
return s;
}
In image form: I have the following codes
public partial class image : Form
{
string filePath;
public image()
{
InitializeComponent();
// this.filePath = FileToDeletePath;
System.Timers.Timer timer1 = new System.Timers.Timer();
timer1.Interval = 30000;
timer1.Elapsed += timer1_Elapsed;
timer1.Start();
}
private void image_Load(object sender, EventArgs e)
{
}
void timer1_Elapsed(object sender, ElapsedEventArgs e)
{
//delete the file using "filePath"
string Filename = img_path.Text; // here i cannot pass the old string file name with extension to this form.. Any ways please help me out
if (string.IsNullOrEmpty(Filename))
return;
if (Filename.ToCharArray().Intersect(Path.GetInvalidFileNameChars()).Any())
return;
File.Delete(Path.Combine(#"\\bin\Debug", Filename));
}
}
something like this????
Task waitfordelete = Task.Run(() =>
{
image im = new image();
});
Assumptions: window image should be shown as a dialog (modal), and only while the call to PrintFactory.sendTextToLPT1 is in progress.
If that's correct, then something like this could work for you:
// Don't forget, you need to dispose modal dialogs
image omg = new image();
// Ensure the dialog has been shown before starting task. That
// way the task knows for sure the dialog's been opened and can
// be closed.
omg.Loaded += (sender, e) =>
{
// Run the print task in a separate task
Task.Run(() =>
{
PrintFactory.sendTextToLPT1(s);
// But get back onto the main GUI thread to close the dialog
Dispatcher.Invoke(() => omg.Close());
});
};
this.Hide();
omg.ShowDialog();
this.Show();
Apologies in advance for any typos/syntax errors/etc. Hopefully the above is sufficient to express the general idea.
The answer given by Narzul and Peter both are correct. You can implement any one. But, I know your next question will be how to implement that method in your code.
you can use Thread or Task class object to separate the process. So when one process is running then other process can perform their taks at that time. There are two process in your login. The first one is send the file to the printer and the second one is the show dialog for 30 seconds and then delete the file. You should create the another thread to invoke the any one of the process so other process can perform asynchronously.
1st: make the seperate process for Print file.
Task waitfordelete = Task.Run(() =>
{
PrintFactory.sendTextToLPT1(s);
});
this.Hide();
omg = new image();
omg.ShowDialog();
this.Show();
2nd: make the seperate process for show dialog and delete the file. But, I think you may get the error in this method. You cannot change the UI from other thread
Task waitfordelete = Task.Run(() =>
{
Dispatcher.Invoke(() => this.ShowSplashScreen());
});
PrintFactory.sendTextToLPT1(s);
private void ShowSplashScreen()
{
this.Hide();
omg = new image();
omg.ShowDialog();
this.Show();
}
if you don't want to use the thread or task then just simply handle the close event of Image form
this.Hide();
omg = new image();
omg.Show();
PrintFactory.sendTextToLPT1(s);
omg.FormClosed += (object sender, EventArgs e) => {
File.Delete(Path.Combine(Application.StartupPath, Path.GetFileName(img_path.Text));
this.Show();
};
and modify the code in timer_tick event in Image form and add the this.Close() after delete file statement.
void timer1_Elapsed(object sender, ElapsedEventArgs e)
{
....
//File.Delete(Path.Combine(#"\\bin\Debug", Filename)); comment this line
this.Close();
}
Another hidden question I have found here. here i cannot pass the old string file name with extension to this form.. Any ways please help me out
void timer1_Elapsed(object sender, ElapsedEventArgs e)
{
//delete the file using "filePath"
string Filename = img_path.Text; // here i cannot pass the old string file name with extension to this form.. Any ways please help me out
for that, you can create the property in Image class and assign the file name from the parent form.
Image omg = new Image()
omg.FileName = Path.Combine(Application.StartupPath, Path.GetFileName(img_path.Text));
omg.Show();
and the property in Image form will be created like this
public class Image : Form
{
public string FileName { get; set; }
public Image()
{
}
void timer1_Elapsed(object sender, ElapsedEventArgs e)
{
....
File.Delete(Path.Combine(Application.StartupPath, this.Filename));
this.Close();
}
}
NOTE: Use the Application.StartupPath istead of \\bin\debug

Exception when using FolderBrowserDialog

I'm getting the following Exception when trying to use FolderBrowserDialog:
System.Threading.ThreadStateException: Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it. This exception is only raised if a debugger is attached to the process.
I have Googled this problem extensively and the solutions that everybody suggests seem to be to put [STAThreadAttribute] above the Main method, to delete all dll's from the Debug folder, or to use the Invoke method. I have tried all of these, and I still get the same exception.
Here's the code:
public partial class Form1 : Form
{
public event EventHandler ChooseLocationHandler = null;
public string DestFolder
{
set { textBox1.Text = value; }
get { return textBox1.Text; }
}
public Form1()
{
InitializeComponent();
}
private void ChooseLocationButton_Click(object sender, EventArgs e)
{
if (ChooseLocationHandler != null)
ChooseLocationHandler(this, e);
}
}
And in my presenter is the following:
public partial class Presenter
{
Form1 myForm;
public Presenter()
{
myForm = new Form1();
myForm.ChooseLocationHandler += ChooseLocationHandler;
myForm.Show();
}
public void ChooseLocationHandler(object obj, EventArgs e)
{
Form1 sender = (Form1)obj;
FolderBrowserDialog fbd = new FolderBrowserDialog();
fbd.RootFolder = System.Environment.SpecialFolder.MyComputer;
fbd.ShowNewFolderButton = true;
if (fbd.ShowDialog() == DialogResult.Cancel)
return;
sender.DestFolder = fbd.SelectedPath;
}
}
I'm getting the Exception on fbd.ShowDialog().
A thread is either STA or MTA it can't be specified just for one method so the attribute must be present on the entry point.
From STAThreadAttribute in MSDN :
Apply this attribute to the entry point method (the Main() method in
C# and Visual Basic). It has no effect on other methods.
If this code is called from a secondary thread you have 3 choices :
IMPORTANT NOTE: Running (as you seem to do) System.Windows.Forms code inside an MTA thread is unwise, some functionalities like file open dialogs (not only folder) require a MTA thread to work.
Changing your secondary thread apartment
If you create the thread yourself (and don't use the specificity of MTA) you could just change it's apartment before starting it :
var t = new Thread(...);
t.SetApartmentState(ApartmentState.STA);
Creating a thread just for it
If you don't control the thread creation you could do it in a temporary thread :
string selectedPath;
var t = new Thread((ThreadStart)(() => {
FolderBrowserDialog fbd = new FolderBrowserDialog();
fbd.RootFolder = System.Environment.SpecialFolder.MyComputer;
fbd.ShowNewFolderButton = true;
if (fbd.ShowDialog() == DialogResult.Cancel)
return;
selectedPath = fbd.SelectedPath;
}));
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
Console.WriteLine(selectedPath);
Invoking in another(STA) thread
If your main thread also contain System.Windows.Forms code you could invoke in it's message loop to execute your code :
string selectedPath = null;
Form f = // Some other form created on an STA thread;
f.Invoke(((Action)(() => {
FolderBrowserDialog fbd = new FolderBrowserDialog();
fbd.RootFolder = System.Environment.SpecialFolder.MyComputer;
fbd.ShowNewFolderButton = true;
if (fbd.ShowDialog() == DialogResult.Cancel)
return;
selectedPath = fbd.SelectedPath;
})), null);
Console.WriteLine(selectedPath);
This fixed my issue.
[STAThread]
static void Main()
Just an extra question: why can't microsoft make things simple?
Are they trying to disgust people to do some coding?
As simple as the below :
using System.Windows.Forms;
namespace fileConverterBaset64
{
class Program
{
[STAThread]
static void Main(string[] args)
Add the command [STAThread] before your main method. That's it, it would work.
I had the same issue with ASP.NET MVC project. When I export my crystal report to some format it shows me the error. What I have done is replace
This:
SaveFileDialog browser = new SaveFileDialog();
string fileName = "";
browser.Filter = "Pdf|*.pdf|Txt|.txt";
if (browser.ShowDialog() == DialogResult.OK)
{
ExportFormatType formatType = ExportFormatType.NoFormat;
switch (browser.FilterIndex)
{
case 2:
formatType = ExportFormatType.WordForWindows;
break;
case 1:
formatType = ExportFormatType.PortableDocFormat;
break;
}
fileName = browser.FileName;
crReportDocument.ExportToDisk(formatType, fileName);
Into:
Thread thread = new Thread((ThreadStart)(() =>
{
SaveFileDialog browser = new SaveFileDialog();
string fileName = "";
browser.Filter = "Pdf|*.pdf|Txt|.txt";
if (browser.ShowDialog() == DialogResult.OK)
{
ExportFormatType formatType = ExportFormatType.NoFormat;
switch (browser.FilterIndex)
{
case 2:
formatType = ExportFormatType.WordForWindows;
break;
case 1:
formatType = ExportFormatType.PortableDocFormat;
break;
}
fileName = browser.FileName;
crReportDocument.ExportToDisk(formatType, fileName);
}
}));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
The STAThread attribute must be in front of main as far as i know.
I Had This Same Issue, I Remove 3 Un-Used Dll's And it Fixed... Thank's So Much!
Now, check all dll in Reference and delete dll not use.
That was unbelievable. I could have never imagined those dll's are causing this problem.

Categories

Resources