I've having a problem with closing a dialog, but only the second time it is shown, and only when closing it in a certain way.
The dialog has a single button on it that is declared as a cancel button, i.e. it has IsCancel="True". There's no event handler for the button click. I can show this dialog, and close it each time by either clicking the button or pressing the Escape key, and there's no problem with that, it works every time.
I also have the concept of a user session. If the user's session expires, any open dialogs should be automatically closed. This works fine when the session expires while the dialog is shown for the first time, but if the dialog has already been shown once, and either been closed by explictly clicking the button, or allowing the session to expire, then when the next session expires while the dialog is shown I get the exception "DialogResult can be set only after Window is created and shown as dialog".
This is very curious, as I can look at the dialog's DialogResult before the second session expires, and it is null. When I set a break point at the point where the session expires and I'm about to set the DialogResult, the DialogResult is already set to false. I've have no idea how this becomes set to false.
I'm using Unity and wondered whether it was giving me the same instance of the dialog each time. But if that was the case, wouldn't DialogResult be false as soon as I resolved the reference to the dialog? It isn't, it's null as I'd expect.
I've looked at this, this, this, and this but none of them help.
Here's where I get a reference and show the dialog
public void ShowLoginBarcode(string username) {
BarcodeLoginForm frm = _Unity.Resolve<BarcodeLoginForm>();
frm.Owner = Application.Current.MainWindow;
BarcodeLoginVM vm = frm.ViewModel;
vm.Username = username;
frm.ShowDialog();
}
Here's the xaml.cs for the dialog
public partial class BarcodeLoginForm : Window, ICloseableDialog {
public void CloseDialog(bool dialogResult) {
DialogResult = dialogResult;
}
private void Window_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) {
// register the view with the view model, so the view model can call the view's CloseDialog method
if (e.OldValue != null)
((BarcodeLoginVM)e.OldValue).SetView(null);
if (e.NewValue != null)
((BarcodeLoginVM)e.NewValue).SetView(this);
}
}
Here's part of the view model.
public void SetView(ICloseableDialog view) {
_Dialog = view;
}
private void Session_OnTimeoutChange(object sender, EventArgs e) {
if (_Session.HasExpired)
CloseDialog(false);
}
private void CloseDialog(bool dialogResult) {
_Dialog?.CloseDialog(dialogResult);
}
Here are the simplest set of steps that show the problem:
- show the dialog.
- click the close button to close the dialog. (Alternatively, let the session expire so that it closes the dialog.)
- show the dialog again. (If I look at the DialogResult while the dialog is visible, it's null. I added a KeyPreview event handler so I could put a breakpoint on it.)
- let the session expire. At the point where DialogResult = dialogResult is about to execute in the dialog's CloseDialog method, the DialogResult is already set to false.
So, any ideas?
Edit: I can code around the problem by changing the xaml.cs CloseDialog method to check the DialogResult is null before assigning a value, but I don't understand why that should be necessary. How can the DialogResult be set before this code is executed?
public void CloseDialog(bool dialogResult) {
if (!DialogResult.HasValue)
DialogResult = dialogResult;
}