How to auto-close the Caliburn Dialog window? - wpf

I have a ViewModel defined like
public class PlayerViewModel : Screen, IDiscoverableViewModel
I am showing a dialog pop up as
var result = await _dialogManager.ShowDialogAsync(item, new List<DialogResult>() { DialogResult.Cancel });
Here item is the another ViewModel which shows UI from the related View. This pop up is showing some information and needs to be auto closed after few seconds in case user doesn't select Cancel button.
Following is the Timer tick event that is getting fired after 10 seconds.
void timer_Tick(object sender, EventArgs e)
{
this.DialogHost().TryClose(DialogResult.Cancel);
}
But it's not working and throwing exception as this.DialoHost() is getting null always. I tried this solution but it is closing the whole ViewModel instead I want to close only the dialog window.

Could you confirm if your 'pop-up viewmodel' is deriving from Screen ? If so, TryClose should work. Could you please verify it ? Sample code for closing.
public class CreatePersonViewModel:Screen
{
System.Timers.Timer _timer = new Timer(5000);
public CreatePersonViewModel()
{
_timer.Elapsed += (sender, args) =>
{
_timer.Enabled = false;
TryClose(true);
};
_timer.Start();
}
}

Related

Is there a way to keep additional windows active when showing a modal window?

I'm afraid the answer is probably no...but some background. To draw a custom border on a window where the sizing logic works beyond the visible border (as it does on windows 10) I added layered windows around the edges to capture the messages and then forward them to the central window. This worked great until the form was shown modaly, at which point all the edge windows were automatically disabled. Obviously this is by design...but I'm not sure if there is some way around it. I tried making the edge windows owned by the central window, but that didn't work.
Or maybe there is a better approach entirely.
Here's a sample of the issue:
public partial class Form1 : Form
{
public Form1()
{
}
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
Form f2 = new Form();
f2.Text = "Non Modal";
f2.Show();
Form f3 = new Form();
f3.Text = "Modal";
f3.ShowDialog(this);
}
}
I think you can fake the modal window, so that it is not modal but disable the caller. I used this in a own project. I did it this way:
//Setup small Interface
public interface IDialog
{
//Our own Event which tell the caller if the Dialog is active/inactive
public event DialogChangedEventArgs DialogChanged;
}
//Setup EventArgs for our own Event
public class DialogChangedEventArgs : EventArgs
{
public bool DialogActive{get;}
public DialogChangedEventArgs(bool dialogActive)
{
DialogActive = dialogActive;
}
}
//Setup the Form which act as Dialog in any other form
public class Form2 : Form, IDialog
{
public event EventHandler<DialogChangedEventArgs> DialogChanged;
//If this Form is shown we fire the Event and tell subscriber we are active
private void Form2_Shown(object sender, EventArgs e)
{
DialogChanged?.Invoke(this, true);
}
//If the user close the Form we telling subscriber we go inactive
private void Form2_Closing(object sender, CancelEventArgs e)
{
DialogChanged?.Invoke(this, false);
}
}
public class Form1 : Form
{
//Setup our Form2 and show it (not modal here!!!)
private void Initialize()
{
Form2 newForm = new Form2();
newForm.DialogChanged += DialogChanged;
newForm.Show();
}
private void Form2_DialogChanged(object sender, DialogChangedEventArgs e)
{
//Now check if Form2 is active or inactive and enable/disable Form1
//So just Form1 will be disabled.
Enable = !e.DialogActive;
}
}
It's really simple. Just use an event to tell your first Form: Hey iam second Form and active. Then you can disable the first Form with while second is active. You have the full control which forms are active or not. Hope this helps.

Custom MessageBox using Window

In regard to the code below.
If I use the built in MessageBox, then the previous MessageBox has to be closed before the next one is displayed.
How can I achieve this with a Window so that I can create a custom message box? I tried using the ShowDialog method, but whilst this does create Modal windows, it still shows them all at the same time cascaded.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 3; ++i)
{
Dispatcher.BeginInvoke(new Action(() => ShowDialog2()));
}
}
void ShowDialog2()
{
//MessageBox.Show("A message");
Window w = new Window() { Width = 200, Height = 200, Content = "SomeText" };
w.ShowDialog();
}
}
Open first instance of window with ShowDialog and consequent instances of window with Show method.
Show open a non-modal window whereas ShowDialog open modal window.

Closing child window minimizes parent

The following code demonstrates an issue I'm having where closing a child window minimizes the parent window, which I dont want to happen.
class SomeDialog : Window
{
protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
{
base.OnMouseDoubleClick(e);
new CustomMessageBox().ShowDialog();
}
}
class CustomMessageBox : Window
{
public CustomMessageBox()
{
Owner = Application.Current.MainWindow;
}
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
{
base.OnMouseDoubleClick(e);
new SomeDialog() { Owner = this }.Show();
}
}
Window1 is the main application window.
SomeDialog is a window that pops up on some event within Window1(double clicking window1 in the example) that needs to be modeless.
CustomMessageBox is a window that pops up on some event within "SomeDialog" (double clicking SomeDialog in the example) that needs to be modal.
If you run the application, and then double click Window1's content to bring up SomeDialog, and then you then double click SomeDialog's content to bring up the CustomMessagebox.
Now you close CustomMessagebox. Fine.
Now if you close SomeDialog, Window1 minimizes? Why is it minimizing and how can I stop it?
Edit : It appears the workaround is rather simple, using the technique suggesrted by Viv.
class SomeDialog : Window
{
protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
{
base.OnMouseDoubleClick(e);
new CustomMessageBox().ShowDialog();
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
base.OnClosing(e);
Owner = null;
}
}
Why is it minimizing and how can I stop it?
Not sure about the "Why" maybe you can report it as a bug and see what they reply with as with a non-modal dialog you do not expect this to happen.
As for a workaround, Try something like this:
public partial class MainWindow : Window {
...
protected override void OnMouseDoubleClick(MouseButtonEventArgs e) {
base.OnMouseDoubleClick(e);
var x = new SomeDialog { Owner = this };
x.Closing += (sender, args) => {
var window = sender as Window;
if (window != null)
window.Owner = null;
};
x.Show();
}
}
^^ This should prevent the MainWindow(parent) from minimizing when SomeDialog is closed.
My workaround for this interesting problem is to activate the MainWindow once and after that activate the SomeDialog window again.
class SomeDialog : Window
{
protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
{
base.OnMouseDoubleClick(e);
new CustomMessageBox().ShowDialog();
Owner.Activate();
Activate();
}
}
A very late answer... I just sat having this same issue and Viv's workaround solved it for me aswell. As for the "Why" part of the answer, i believe it happens when your child window spawns a child window of its own in its lifetime.
For my experience it occured whenever pressing my Save button, which is in a flow which requires a child window to be opened. But pressing the Cancel (or escape) or the windows default quit button did not invoke the issue.
First, as your code stands, I can confirm this strange behaviour. There are two things that I noticed here. The first is that the SomeDialog.Owner is not set, or that it ends up with a null value with this code:
new SomeDialog() { Owner = this }.Show();
Adding this code fixes that problem:
public SomeDialog()
{
Owner = Application.Current.MainWindow;
}
Unfortunately, this doesn't stop the MainWindow from being minimised when the child Window is closed. Then I found that I could stop it from being minimised, but only when calling new SomeDialog().ShowDialog(); instead of new SomeDialog().Show(); However, that makes this Window into a dialog, which is not what you're after I believe.
We had similar problem, but cause was quite simple. Method Close() of Window was called twice. After we had removed second call, all got back to normal.

Custom Item Template Wizard button click doesn't fire?

I am following this exactly:
http://msdn.microsoft.com/en-us/library/ms185301.aspx
but can't get it to work. The form appears when I try and add my new item, but when I input text and click the button, nothing happens.
For posterity's sake here is my code:
The non-empty methods in the Wizard class which extends IWizard
public void RunStarted(object automationObject,
Dictionary<string, string> replacementsDictionary,
WizardRunKind runKind, object[] customParams)
{
try
{
// Display a form to the user. The form collects
// input for the custom message.
inputForm = new UserInputForm();
inputForm.ShowDialog();
customMessage = inputForm.get_CustomMessage();
// Add custom parameters.
replacementsDictionary.Add("$custommessage$",
customMessage);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
// This method is only called for item templates,
// not for project templates.
public bool ShouldAddProjectItem(string filePath)
{
return true;
}
The user input form code:
public partial class UserInputForm : Form
{
private string customMessage;
public UserInputForm()
{
InitializeComponent();
}
public string get_CustomMessage()
{
return customMessage;
}
private void button1_Click(object sender, EventArgs e)
{
customMessage = textBox1.Text;
this.Dispose();
}
}
And the button is indeed named button 1:
this.button1.Location = new System.Drawing.Point(200, 180);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(100, 40);
this.button1.TabIndex = 0;
this.button1.Text = "Click Me";
this.button1.UseVisualStyleBackColor = true;
So I don't have much experience with Windows Forms (do web apps), but I am following the directions on MSDN and it's pretty clear cut. Any suggestions? Can anyone else get this to work?
Okay I figured it out. I had to add the event handler in the form's constructor manually:
public UserInputForm()
{
InitializeComponent();
button1.Click += button1_Click;
}
Why this isn't in the documentation on MSDN boggles my mind.
If you use the WinForms designer mode to drag your button from the Toolbox, and then double-clicked the button in the designer view, it would have added the event handler and stubbed that Click method for you. Just FYI.

Button Click Event Getting Lost

I have a Menu and Submenu structure in Silverlight, and I want the submenu to disappear when the parent menu item loses focus - standard Menu behavior. I've noticed that the submenu's click events are lost when a submenu item is clicked, because the parent menu item loses focus and the submenu disappears.
It's easier to explain with code:
ParentMenuBtn.Click += delegate
{
SubMenu.Visibility = (SubMenu.Visibility == Visibility.Visible) ? SubMenu.Collapsed : SubMenu.Visible;
};
ParentMenuBtn.LostFocus += delegate
{
SubMenu.Visibility = Visibility.Collapsed;
};
SubMenuBtn.Click += delegate
{
throw new Exception("This will never be thrown.");
};
In my example, when SubMenuBtn is clicked, the first event that triggers is ParentMenuBtn.LostFocus(), which hides the container of SubMenuBtn. Once the container's visibility collapses, the Click event is never triggered.
I'd rather avoid having to hide the sub-menu each time, but I'm a little surprised that the Click event is never triggered as a result...
I can't put any checks inside the LostFocus() event to see if my SubMenuBtn has focus, because it does not gain focus until after the LostFocus() event is called. In other words, SubMenuBtn.IsFocused = false when LostFocus() is triggered.
Anyone have any thoughts about this?
I've found out the solution - albeit, it's not as simple, or elegant as I would have liked. The solution is to use a secondary thread that pauses only for a moment before executing.
ie.
public partial class Navigation : UserControl
{
public Navigation()
{
ParentMenuBtn.Click += delegate
{
SubMenu.Visibility = (SubMenu.Visibility == Visibility.Visible) ? Visibility.Collapsed : Visibility.Visible;
};
ParentMenuBtn.LostFocus += delegate(object sender, RoutedEventArgs e)
{
HideSubMenu(SubMenu);
};
SubMenuBtn.Click += delegate
{
//Sub Menu Button actions...
};
private void HideSubMenu(UIElement subMenu)
{
//Get the Main Page
App app = (App)Application.Current;
MainPage mainPage = (MainPage)app.RootVisual;
Thread thread = new Thread(Navigation.HideSubMenu);
thread.Start(new ThreadState(mainPage, subMenu));
}
private static void HideSubMenu(object threadStateObj)
{
ThreadState threadState = (ThreadState)threadStateObj;
//Execute after 5 milliseconds...
System.Threading.Thread.Sleep(5);
threadState.MainPage.Dispatcher.BeginInvoke(delegate() {
threadState.TargetElement.Visibility = Visibility.Collapsed;
});
}
I just use a simple object called ThreadState to handle all the state objects I want to preserve:
public class ThreadState
{
public MainPage MainPage = null;
public UIElement TargetElement = null;
public ThreadState(MainPage mainPage, UIElement targetElement)
{
this.MainPage = mainPage;
this.TargetElement = targetElement;
}
}

Resources