WPF showing dialog before main window - wpf

How one can show dialog window (e.g. login / options etc.) before the main window?
Here is what I tried (it apparently has once worked, but not anymore):
XAML:
<Application ...
Startup="Application_Startup">
Application:
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
Window1 myMainWindow = new Window1();
DialogWindow myDialogWindow = new DialogWindow();
myDialogWindow.ShowDialog();
}
}
Outcome: myDialogWindow is shown first. When it is closed, the Window1 is shown as expected. But as I close Window1 the application does not close at all.

Here's the full solution that worked for me:
In App.xaml, I remove the StartupUri stuff, and add a Startup handler:
<Application x:Class="MyNamespace.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="ApplicationStart">
</Application>
In App.xaml.cs, I define the handler as follows:
public partial class App
{
private void ApplicationStart(object sender, StartupEventArgs e)
{
//Disable shutdown when the dialog closes
Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
var dialog = new DialogWindow();
if (dialog.ShowDialog() == true)
{
var mainWindow = new MainWindow(dialog.Data);
//Re-enable normal shutdown mode.
Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
Current.MainWindow = mainWindow;
mainWindow.Show();
}
else
{
MessageBox.Show("Unable to load data.", "Error", MessageBoxButton.OK);
Current.Shutdown(-1);
}
}
}

Okay apologizes, here is the solution:
My original question worked almost, only one thing to add, remove the StartupUri from the Application XAML and after that add the Show to main window.
That is:
<Application x:Class="DialogBeforeMainWindow.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="Application_Startup">
Above, StartupUri removed.
Add myMainWindow.Show() too:
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
Window1 myMainWindow = new Window1();
DialogWindow myDialogWindow = new DialogWindow();
myDialogWindow.ShowDialog();
myMainWindow.Show();
}
}

WPF sets App.Current.MainWindow to the first window opened. If you have control over the secondary window constructor, just set App.Current.MainWindow = Null there. Once your main window is constructed, it will be assigned to the App.Current.MainWindow property as expected without any intervention.
public partial class TraceWindow : Window
{
public TraceWindow()
{
InitializeComponent();
if (App.Current.MainWindow == this)
{
App.Current.MainWindow = null;
}
}
}
If you don't have access, you can still set MainWindow within the main window's constructor.

If you put Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown; into the constructor of the dialog, and add
protected override void OnClosed(EventArgs e) {
base.OnClosed(e);
Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
}
into the dialog class, you don't need to worry about making any changes to the default behaviour of the application. This works great if you want to just snap a login screen into an already-existing app without tweaking the startup procedures.

So you want to show one window, then another, but close down the app when that window is closed? You may need to set the ShutdownMode to OnMainWindowClose and set the MainWindow to Window1, along the lines ok:
Window1 myMainWindow = new Window1();
Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
Application.Current.MainWindow = myMainWindow;
DialogWindow myDialogWindow = new DialogWindow();
myDialogWindow.ShowDialog();

here, do it like this. this will actaully change your main window and will work properly w/o having to change settings of your application object.
make sure to remove the event handler for application startup and to set your StartupUri in your app.xaml file.
public partial class App : Application
{
bool init = false;
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
if (!init)
{
this.MainWindow.Closing += new System.ComponentModel.CancelEventHandler(MainWindow_Closing);
init = true;
}
}
void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
Window toClose = this.MainWindow;
this.MainWindow = new Window2();
this.MainWindow.Show();
}
}

I have the same issue when i need to disloag a login screen before my main window
In you main window cunstructor add these lines
Application.Current.MainWindow = this;
Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
Resolve the main window or just call var mainWindow = new MainWindow()
Call the loginScreen.Show() or loginScreen.ShowDialog()

Related

Change Visibility of Main Window from a different window

Sorry for the stupid question but I can't get it to work.
I got a MainWindow that opens another window.
public static Window2 LoadWindow = new Window2();
public MainWindow()
{
InitializeComponent();
LoadWindow.Show();
Later in the code, I Start a function that creates a Background worker in the new window
if (MainWindow.Start == true)
{
MainWindow.LoadWindow.LoadImage(null, null);
MainWindow.Start = false;
}
public void LoadImage(object sender, RoutedEventArgs e)
{
worker = new BackgroundWorker();
...
And then I tried this to Change the Visibility of the MainWindow.
private void worker_Completed(object sender, RunWorkerCompletedEventArgs e)
{
Application.Current.Dispatcher.Invoke(new Action(() => {
Application.Current.MainWindow.Visibility = Visibility.Visible;
}));
}
I thought Application.Current.MainWindow would point to my MainWindow but the debugger said that Window2 is the Current.MainWindow.
Actually, I am completely confused about the MainWindow.
Typically I initialize a class with a name and use it with that name. (e.g. Window2=class, LoadWindow=it's name)
But what is the name of the MainWindow or how can I interact with it from another window. It's so confusing when the MainWindow != the MainWindow >.<.
You could either inject LoadWindow with a reference to the MainWindow when you create it in the constructor, or you could get a reference to the MainWindow using the Application.Current.Windows collection:
var mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
if (mainWindow != null)
mainWindow.Visibility = Visibility.Visible;

How to open a child Window like a splash screen before MainWindow in WPF?

I have two xaml. One is MainWindow and other is NewWindow.
I want show NewWindow 5 seconds, when program is run.
And after 5 seconds, I want show MainWindow.
How to change xaml in WPF?
Here is MainWindow.
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
</Grid>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
Here is NewWindow.
<Window x:Class="WpfApplication2.NewWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NewWindow" Height="300" Width="300">
<Grid>
</Grid>
public partial class NewWindow : Window
{
public NewWindow()
{
InitializeComponent();
}
}
1) First, we need to stop MainWindow from opening as soon as we run the Application. To do this, first remove the StartupUri="MainWindow.xaml" setting from the App.xaml file and replace it by setting the Startup property instead:
<Application x:Class="AppName.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="App_Startup">
2) Then, add the handler for the Application.Startup event and launch your child (or splash screen) Window:
private SplashScreen splashScreen;
...
public void App_Startup(object sender, StartupEventArgs e)
{
// Open your child Window here
splashScreen = new SplashScreen();
splashScreen.Show();
}
3) At this point, there are several different ways to go, dependent on whether you need to wait for the SplashScreen Window to do anything or not. In this particular question, the requirement is to simply open the MainWindow after 5 seconds, so we'll need a DispatcherTimer:
public void App_Startup(object sender, StartupEventArgs e)
{
// Open your child Window here
splashScreen = new SplashScreen();
splashScreen.Show();
// Initialise timer
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 5, 0);
timer.Tick += Timer_Tick;
}
...
private void Timer_Tick(object sender, EventArgs e)
{
splashScreen.Close();
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
}
That's it.
There are plenty ways to do this. As some people suggested i suggest too to DO NOT DO THIS if you're trying to create a splash screen, there are better ways to do that. But.. here what you asked for:
using System.ComponentModel; //Remember to add this
public partial class MainWindow : Window
{
private BackgroundWorker waitingWorker = new BackgroundWorker();
private NewWindow myNewWindow = new NewWindow();
public MainWindow()
{
InitializeComponent();
waitingWorker.DoWork += waitingWorker_DoWork;
waitingWorker.RunWorkerCompleted += waitingWorker_RunWorkerCompleted;
waitingWorker.RunWorkerAsync();
}
private void waitingWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
myNewWindow.Show();
this.Close();
}
private void waitingWorker_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(5000);
}
}
it's a simple background worker that waits for 5 seconds, then opens the NewWindow and close MainWindow. Yes, you can do it without background worker too, but Thread.Sleep(5000); will totally freeze your GUI and make your little app unresponsive, so you need another thread to wait while the main thread can keep your GUI alive. I suggest you to study at least how a background worker works.
HERE the official MSDN documentation, but google is your friend and you can find tons of tutorial and explanation about it
Here is another way to do it:
Set Startup to "App_Startup" as shown in one of the other posts.
<Application x:Class="AppName.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="App_Startup">
And in App_OnStartup:
private async void App_Startup(object sender, StartupEventArgs e)
{
var splash = new SplashWindow();
splash.Show();
await Task.Delay(5000);
var mainWindow = new MainWindow();
mainWindow.Show();
splash.Close();
}
The mainWindow should also be loaded before closing the splashScreen This way your splashscreen shows as long as it is loading.You can add additional time in the splashScreen.Close() Function.
private SplashScreen splashScreen;
private void App_Startup(object sender, StartupEventArgs e)
{
splashScreen = new SplashScreen("SplashScreen1.png"); // Or new WPF window
splashScreen.Show(false);
MainWindow mainWindow = new MainWindow();
splashScreen.Close(new TimeSpan(0, 0, 3));
mainWindow.Show();
}

Open new window after splash window in wpf

I have three window which suppose to come in this order:
Splash window
Selection window
Working window, this is main window
splash window should appear for 3 seconds and I have done it like this way and it's working fine:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
loadingThread = new Thread(load);
loadingThread.Start();
}
private void load()
{
Thread.Sleep(3000);
//close the window
this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate() { Close(); });
}
After that selection window should appear and after that working window should come.
Mainwindow code is here:
public partial class MainWindow : Window
{
private MainWindowViewModel mainWindowViewModel;
public MainWindow()
{
InitializeComponent();
this.mainWindowViewModel = new MainWindowViewModel(this);
base.DataContext = this.mainWindowViewModel;
SplashWindow wnd = new SplashWindow("text");
wnd.ShowDialog();
}
}
Tell me how can I open my window in that sequence
and one more thing on selection window, I have one button after pressing that my working window should come.
Edit
Without using async & await
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// Create and show SplashWindow
SplashWindow splashWindow = new SplashWindow();
splashWindow.Show();
var thread = new System.Threading.Thread(p =>
{
System.Threading.Thread.Sleep(3000);
Dispatcher.Invoke(() =>
{
// Create and show the SelectionWindow
SelectionWindow selectionWindow = new SelectionWindow();
selectionWindow.Show();
// Hide the SplashWindow we previosly created
splashWindow.Close();
});
});
thread.Start();
}
}
Gist with sample application: click here
I'm assuming that this is your project's structure:
App.xaml
MainWindow
SelectionWindow
SplashWindow
first, on your App.xaml, remove StartupUri attribute so it will look like:
<Application x:Class="WpfApplication2.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
</Application.Resources>
</Application>
Then, on your App.xaml.cs, override OnStartup:
async protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// Create and show SplashWindow
SplashWindow splashWindow = new SplashWindow();
splashWindow.Show();
// Let's wait 3 seconds
await Task.Delay(3000);
// Create and show the SelectionWindow
SelectionWindow selectionWindow = new SelectionWindow();
selectionWindow.Show();
// Hide the SplashWindow we previosly created
splashWindow.Close();
}
Then, in your SelectionWindow, add a button with a click event:
private void Button_Click(object sender, RoutedEventArgs e)
{
// Create and show MainWindow
var mainWindow = new MainWindow();
mainWindow.Show();
// Closes SelectionWindow
this.Close();
}

WPF - which way is better?

I have an WPF application, when at first start displays window to select language. So, in App.xaml:
<Application x:Class="MyApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="WindowLanguage.xaml">
in WindowLanguage:
public partial class WindowLanguage : Window
{
bool mainWindowOpened = false;
public WindowLanguage()
{
if (!Settings.Instance.firstStart)
{
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
Close();
}
It works, but unnecessary Window is init.
I think about following way:
App.xaml.cs
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if (!Settings.Instance.firstStart)
StartupUri = new Uri("/MyApp;component/MainWindow.xaml", UriKind.Relative);
}
This second way with changing StartupUri is better or not? Which way is the best for my situation (open WindowLanguage during first start of app)?
Setting the startupUri is always better than recreating window all over again.
Also there are other options for opening window based on some conditions like having age old console Main method for opening window. Few more options can be found here.
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if (!Settings.Instance.firstStart)
{
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
}
else
{
WindowLanguage windowLanguage = new WindowLanguage();
windowLanguage.Show();
}
}

WPF global open/close dialog event?

In my WPF application I would like to subscribe to some event/callbeck/whatever that tells me whenever a dialog window opens (and closes) in my application.
I found the window collection but this is a simple container and it doesn't seem to provide any means of subscription.
I also tried using event handlers but there seems not be an event that tells me what I need.
Any ideas?
One way to do it without a base class is adding a handler to MainWindow deactivated
If a new window is opened, the main window will lose focus = your "new window event"
private readonly List<Window> openWindows = new List<Window>();
public void ApplicationMainWindow_Deactivated(object sender, EventArgs e)
{
foreach (Window window in Application.Current.Windows)
{
if (!openWindows.Contains(window) && window != sender)
{
// Your window code here
window.Closing += PopupWindow_Closing;
openWindows.Add(window);
}
}
}
private void PopupWindow_Closing(object sender, CancelEventArgs e)
{
var window = (Window)sender;
window.Closing -= PopupWindow_Closing;
openWindows.Remove(window);
}
Without creating a Base class for all your windows where you can hook into the opened event (or manually adding the opened event to each window), I'm not sure how you'd be able to know when new windows were create.
There may be a more elegant way, but you could poll the Application.Current.Windows to see if any new windows were created while keeping track of the one's you've found.
Here is a crude example that will demonstrate how to use a DispatchTimer to poll for new windows, keep track of found windows and hook into the closed event.
Code Behind
public partial class MainWindow : Window
{
private DispatcherTimer Timer { get; set; }
public ObservableCollection<Window> Windows { get; private set; }
public MainWindow()
{
InitializeComponent();
// add current Window so we don't add a hook into it
Windows = new ObservableCollection<Window> { this };
Timer = new DispatcherTimer( DispatcherPriority.Background );
Timer.Interval = TimeSpan.FromMilliseconds( 500 );
Timer.Tick += ( _, __ ) => FindNewWindows();
Timer.Start();
this.WindowListBox.ItemsSource = Windows;
this.WindowListBox.DisplayMemberPath = "Title";
}
private void FindNewWindows()
{
foreach( Window window in Application.Current.Windows )
{
if( !Windows.Contains( window ) )
{
window.Closed += OnWatchedWindowClosed;
// inserting at 0 so you can see it in the ListBox
Windows.Insert( 0, window );
Feedback.Text = string.Format( "New Window Found: {0}\r\n{1}",
window.Title, Feedback.Text );
}
}
}
private void OnWatchedWindowClosed( object sender, EventArgs e )
{
var window = (Window)sender;
Windows.Remove( window );
Feedback.Text = string.Format( "Window Closed: {0}\r\n{1}",
window.Title, Feedback.Text );
}
private void CreateWindowButtonClick( object sender, RoutedEventArgs e )
{
string title = string.Format( "New Window {0}", DateTime.Now );
var win = new Window
{
Title = title,
Width = 250,
Height = 250,
Content = title,
};
win.Show();
e.Handled = true;
}
}
XAML
<Grid>
<ListBox Name="WindowListBox"
Width="251"
Height="130"
Margin="12,12,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top" />
<TextBox Name="Feedback"
Width="479"
Height="134"
Margin="12,148,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
VerticalScrollBarVisibility="Auto" />
<Button Name="CreateWindowButton"
Width="222"
Height="130"
Margin="269,12,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Click="CreateWindowButtonClick"
Content="Create New Window"
FontSize="20" />
</Grid>
Click away and create as many new windows as you want; then close them. You'll see the feedback as it happens. Granted, there will be a 500ms delay whenever a new window is created since the DispatchTimer's interval is set at 500ms.
You could register a class handler in App.cs as demonstrated here
https://gist.github.com/mwisnicki/3104963
...
EventManager.RegisterClassHandler(typeof(UIElement), FrameworkElement.LoadedEvent, new RoutedEventHandler(OnLoaded), true);
EventManager.RegisterClassHandler(typeof(UIElement), FrameworkElement.UnloadedEvent, new RoutedEventHandler(OnUnloaded), true);
...
private static void OnLoaded(object sender, RoutedEventArgs e)
{
if (sender is Window)
Console.WriteLine("Loaded Window: {0}", sender);
}
private static void OnUnloaded(object sender, RoutedEventArgs e)
{
if (sender is Window)
Console.WriteLine("Unloaded Window: {0}", sender);
}
The link above seems to register an empty handler on instances to make things work properly.
I have never heard of any global open/close event.
It should somehow be possible to do, but that provides that you have control over all windows opening and closing. Like if you build a "base window" (which naturally inherit "Window") that all your dialogs windows inherit from.
Then you clould have a static event on the "base window" which you fire from the base window's opening and closing/closed (or unloaded) events, sending "this" as "sender".
You can attafh to that static event in your App.xaml.cs class.
It's a hack, but it's possible.

Resources