I have a short questions and even after a while of searching through the web I have not found the answer.
I have two windows in a WPF application. One window should just be hidden when the user closes it. When the main window is closed the complete application shall close.
I used
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
Hide();
}
inside the class of the second window and hoped it would just catch its Close() event but unfortunately it catches all Close() events.
How can I separate between the windows to handle the events independently?
Regards
Larimow
Use the sender parameter, that's what it's there for:
window1.Closing += Window_Closing;
window2.Closing += Window_Closing;
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
var w = (Window)sender;
// Simple ref test to illustrate, but you can use anything else you want instead
if (w == window1) {
e.Cancel = true;
w.Hide();
}
}
Related
I'm trying to have a click event that will open another form. I don't want the user to be able to close this window because I get the following exception when the click event is executed again.
System.ObjectDisposedException: 'Cannot access a disposed object.
Object name: 'Form2'.'
I'm not sure if I'm implementing this correctly or there's a better way of doing this.
Form1
public Form2 f = new Form2();
private void Btnsearch_Click(object sender, EventArgs e)
{
f.Show();
}
Form2
private bool allowClose = false;
private void Btnclose_Click(object sender, EventArgs e)
{
allowClose = true;
this.Hide();
}
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
if (!allowClose)
e.Cancel = true;
}
Subscribe to Form.OnClosing and set the Cancel property on the event args that are passed to the handler. This will tell the runtime to cancel the close event.
Since the event is getting canceled, you'll have to hide the form yourself (using Hide(), of course).
private void Form1_Closing(Object sender, CancelEventArgs e)
{
this.Hide();
e.Cancel = true;
}
The instance of form2 should be created within the event
private void Btnsearch_Click(object sender, EventArgs e)
{
Form2 f = new Form2();
f.Show();
}
There are a couple of ways to approach this.
It's generally more efficient, in the FormClosing event, to hide the form and cancel the event, but this can require extra logic.
Unless you have some expensive code that needs to run when the form is created, this probably doesn't matter, and it'll be easier to simply allow the form to close normally.
Either way, all you particularly need to do, is throw some safeguards into the btnSearch handler, so that it can appropriately respond to the state of form f;
public Form2 f;
public void BtnSearch_Click(object sender, EventArgs e)
{
if (f == null || f.IsDisposed || f.Disposing) f = new Form2(...);
f.Show();
}
Is there any way to run the application on the background even if the application/form is closed. All i can do now is just minimize it.
private void Form2_Resize(object sender, EventArgs e)
{
if (WindowState == FormWindowState.Normal)
{
Hide();
WindowState = FormWindowState.Minimized;
}
else if (FormWindowState.Normal == this.WindowState)
{
Show();
}
}
If your application (your process) is closed, the notify icon will disappear. There is no way to avoid that.
So what you probably want is to keep your application running even if the user closes your Form. One way to achieve this is to actually not close the form but just hide it.
Therefor you need to subscribe to the Closing event of the Form:
public class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Closing += Form1_Closing;
}
}
and implement the event handler like this:
void Form1_Closing(object sender, FormClosingEventArgs e)
{
Hide();
e.Cancel = true;
}
By setting e.Cancel = true you tell the form not to close. So you are just hiding it.
You'll need to add some code to your notify icon to reopen (Show()) the form again.
This question already has answers here:
How can I prevent a user from closing my C# application?
(4 answers)
Closed 7 years ago.
I want to close a application when a button is pressed but I wanted to disable the close button (X button upper right).
I disabled the close button with this code:
protected override void OnFormClosing(FormClosingEventArgs e)
{
e.Cancel = true;
}
But now when I try to close the program with this code it wont work.
private void button1_Click(object sender, EventArgs e)
{
Application.Exit();
}
Is there a way to close this program when the button is clicked?
In the event handler, check the CloseReason property of the FormClosingEventArgs:
This allows you to behave differently depending on how the close was initiated, so in the case of Application Exit (or Windows Shutdown) you can allow the form to close.
protected override void OnFormClosing(FormClosingEventArgs e)
{
if (e.CloseReason != CloseReason.ApplicationExitCall
&& e.CloseReason != CloseReason.WindowsShutDown)
{
e.Cancel = true;
}
}
You're cancelling ALWAYS the closign of the form, so that's why it doesn't works.
Try this:
bool blockClosing = true;
protected override void OnFormClosing(FormClosingEventArgs e)
{
e.Cancel = blockClosing;
}
private void button1_Click(object sender, EventArgs e)
{
blockClosing = false;
Application.Exit();
}
In this way, when you press your button it will allow tha pp to be closed.
FormClosingEventArgs has a Reason member that tells you what exactly is trying to close your form. Simply allow ApplicationExitCall through without canceling it.
I have a small issue with my WPF application.
I have a splash image (as a XAML Window) and the main app (as another XAML window that gets caleld from Splash)
Even when I use this .Close() on Splash window and work on Main app, the Splash window is still visible in taskbar.
If I were to use this .Close on main app I would have two blank windows in taskbar application that I have to press X to close completely.
I've tried with Application.Current.Shutdown() as well but results were same.
Splash:
public Splash()
{
InitializeComponent();
}
private void Window_ContentRendered(object sender, EventArgs e)
{
CloseProcedure();
}
public async void CloseProcedure()
{
//Missing data loading which will be async.
UC_Main UCMain = new UC_Main();
MainWindow window = new MainWindow();
window.AppContent.Children.Add(UCMain);
this.Close();
await Task.Delay(500); //for fade effect.
window.Show();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
Closing -= Window_Closing;
e.Cancel = true;
var anim = new DoubleAnimation(0, (Duration)TimeSpan.FromSeconds(0.5));
this.BeginAnimation(UIElement.OpacityProperty, anim);
anim.Completed += (s, _) => this.Close();
}
Main App
private void BTN_Close_MouseUp(object sender, MouseButtonEventArgs e)
{
this.Close();
}
private void titleBar_MouseDown(object sender, MouseButtonEventArgs e)
{
titleBar.Background = Brushes.White;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
Closing -= Window_Closing;
e.Cancel = true;
var anim = new DoubleAnimation(0, (Duration)TimeSpan.FromSeconds(0.2));
this.BeginAnimation(UIElement.OpacityProperty, anim);
anim.Completed += (s, _) => Application.Current.Shutdown();
}
}
The default shutdown mode of a WPF app is "OnLastWindowClose" which means as soon as the last window is closed, the applicaton shutdown procedure will be called, so you should not need to explicitly write System.Windows.Application.Shutdown().
You probably have something referencing the splash screen that isn't being closed off. When you open your main window, you should close the splash screen properly at that time and ensure that any reference to it is null.
If you post all your code, it will be easier to know the correct fix.
To prevent a window showing in the taskbar in the first place, set the ShowInTaskbar property of your Window XAML declaration:
<Window ShowInTaskbar="False" ...
I've come up with a solution for this.
Instead of trying to shut down the process and animate the fade inside the closing event handler, I wrote an asynchronous method that will fade out the screen, await for the same amount of time and then kill the process. Works like a charm.
private void BTN_Close_MouseUp(object sender, MouseButtonEventArgs e)
{
this.FadeOut();
}
async private void FadeOut()
{
var anim = new DoubleAnimation(0, (Duration)TimeSpan.FromSeconds(0.2));
this.BeginAnimation(UIElement.OpacityProperty, anim);
await Task.Delay(200);
this.Close();
System.Diagnostics.Process.GetCurrentProcess().Kill();
}
I have 2 forms, one is MainForm and second is DebugForm. The MainForm has a button that sets up and shows the DebugForm like this, And passes a reference to an already opened SerialPort:
private DebugForm DebugForm; //Field
private void menuToolsDebugger_Click(object sender, EventArgs e)
{
if (DebugForm != null)
{
DebugForm.BringToFront();
return;
}
DebugForm = new DebugForm(Connection);
DebugForm.Closed += delegate
{
WindowState = FormWindowState.Normal;
DebugForm = null;
};
DebugForm.Show();
}
In the DebugForm, I append a method to handle the DataReceived event of the serialport connection (in DebugForm's constructor):
public DebugForm(SerialPort connection)
{
InitializeComponent();
Connection = connection;
Connection.DataReceived += Connection_DataReceived;
}
Then in the Connection_DataReceived method, I update a TextBox in the DebugForm, that is using Invoke to do the update:
private void Connection_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
_buffer = Connection.ReadExisting();
Invoke(new EventHandler(AddReceivedPacketToTextBox));
}
But I have a problem. As soon as I close the DebugForm, it throws an ObjectDisposedException on the Invoke(new EventHandler(AddReceivedPacketToTextBox)); Line.
How can I fix this? Any tips/helps are welcome!
UPDATE
I found out if I remove the event in a button event click , and close the form in that button click, everything is fine and my debugform gets closed without any exception...how odd!
private void button1_Click(object sender, EventArgs e)
{
Connection.DataReceived -= Connection_DebugDataReceived;
this.Close();
}
Closing a form disposes of the Form object but cannot forcibly remove references that other classes have to it. When you register your form for events, you are basically giving a reference to your form object to the source of the events (the SerialPort instance in this case).
This means that, even though your form is closed, the event source (your SerialPort object) is still sending events to the form instance and the code to handle these events is still being run. The problem then is that when this code tries to update the disposed form (set its title, update its controls, call Invoke, &c.) you will get this exception.
So what you need to do is ensure that the event gets deregistered when your form closes. This is as simple as detecting that the form is closing and unregister the Connection_DataReceived event handler. Handily you can detect the form is closing by overriding the OnFormClosing method and unregistering the event in there:
protected override OnFormClosing(FormClosingEventArgs args)
{
Connection.DataReceived -= Connection_DataReceived;
}
I would also recommend moving the event registration to an override of the OnLoad method as otherwise it may receive events before the form has been fully constructed which could cause confusing exceptions.
You haven't shown the code for the AddReceivedPacketToTextBox method.
You could try checking for a disposed form in that method:
private void AddReceivedPacketToTextBox(object sender, EventArgs e)
{
if (this.IsDisposed) return;
...
}
Detaching the DataReceived event handler when closing the form is probably a good idea, but isn't sufficient: there is still a race condition which means your AddReceivedPacketToTextBox can be called after the form is closed/disposed. The sequence would be something like:
Worker thread: DataReceived event fired, Connection_DataReceived starts executing
UI thread: Form closed and disposed, DataReceived event detached.
Worker thread: calls Invoke
UI thread: AddReceivedPacketToTextBox executed while form is disposed.
I found out if I remove the event in a button event click , and close the form in that button click, everything is fine and my debugform gets closed without any exception...how odd!
That's not odd. Multithreading bugs ("Heisenbugs") are timing-related and small changes like that can affect the timing. But it's not a robust solution.
The problem could be solved by adding a timer:
bool formClosing = false;
private void Connection_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (formClosing) return;
_buffer = Connection.ReadExisting();
Invoke(new EventHandler(AddReceivedPacketToTextBox));
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
if (formClosing) return;
e.Cancel = true;
Timer tmr = new Timer();
tmr.Tick += Tmr_Tick;
tmr.Start();
formClosing = true;
}
void Tmr_Tick(object sender, EventArgs e)
{
((Timer)sender).Stop();
this.Close();
}
Thanks to JohnWein from MSDN