WPF - which way is better? - wpf

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();
}
}

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;

Add cofirmation message when ever user clicks on close button in WPF with MVVM

In WPF with MVVM,how to add a confirmation message when a user clicks on close button. Everytime,it just closes the window without any confirmation message whereas the same thing is happening in windows form.
In APP.XAML.CS
public partial class App : Application
{
protected override void OnExit(ExitEventArgs e)
{
if (MessageBox.Show("Sure you wanna close?", "..", MessageBoxButton.YesNo) == MessageBoxResult.No)
e.ApplicationExitCode = 110;
base.OnExit(e);
}
}
When i click on Close button it doesnt ask for confirmation just closes the window.
You could use a behavior.
First of all, you have to add a reference to Interactivity in your project.
Go to References-addReference-assemblies-System.Windows.Interactivity.
Now, you create the behavior.
public class CloseWindowBehavior : Behavior<Window>
{
protected override void OnAttached()
{
this.AssociatedObject.Closing += AssociatedObject_Closing;
}
private void AssociatedObject_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (MessageBox.Show("Sure you wanna close?", "..", MessageBoxButton.YesNo) == MessageBoxResult.No)
e.Cancel = true;
}
}
When you click on the close button, the event assigned will be fired, showing the message box.
Now, you have to declare this created behavior in your xaml file.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<i:Interaction.Behaviors>
<local:CloseWindowBehavior />
</i:Interaction.Behaviors>
<Grid>
</Grid>
Since you are using the pattern MVVM, behavior is a good choice since you don't need to write a single line in the code behind.
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
base.ShutdownMode = ShutdownMode.OnMainWindowClose;
//update user settings (if first run after software upgrade)
}
// Initialise the application directory//
// Set up the main window and its view model
var mainWindow = new MainWindow();
mainWindowViewModel = new MainWindowViewModel();
mainWindow.DataContext = mainWindowViewModel;
mainWindow.Show();
}
protected override void OnExit(ExitEventArgs e)
{
e.ApplicationExitCode = 110;
Dispose();
base.OnExit(e);
}

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();
}

MahApps Metro capture close window event

I'm using the MahApps Metro window style, and I want to capture the event when the user clicks on the close button of the window.
I've set my ShutdownMode to OnExplicitShutdown so I need to call Application.Current.Shutdown(); when that button is clicked
How can I do this ?
I believe that I am also trying to do the same thing as you (bind to the close window button) using WPF and MahApps.Metro. I wasn't able to find a way yet to bind to that command explicitly, but I was able to accomplish this by setting the ShowCloseButton property to false (to hide it) and then created my own close window command button and handled the logic in my viewmodel. Took me some digging, but I found that you can easily add your own window command controls in the command bar with MahApps.Metro just add similar markup to your XAML:
<Controls:MetroWindow.WindowCommands>
<Controls:WindowCommands>
<Button Content="X" Command="{Binding CancelCommand}" />
</Controls:WindowCommands>
</Controls:MetroWindow.WindowCommands>
You need to create a DependencyProperty to handle the close window behavior:
DependencyProperty
namespace MyApp.DependencyProperties
{
public class WindowProperties
{
public static readonly DependencyProperty WindowClosingProperty =
DependencyProperty.RegisterAttached("WindowClosing", typeof(RelayCommand), typeof(WindowProperties), new UIPropertyMetadata(null, WindowClosing));
public static object GetWindowClosing(DependencyObject depObj)
{
return (RelayCommand)depObj.GetValue(WindowClosingProperty);
}
public static void SetWindowClosing(DependencyObject depObj, RelayCommand value)
{
depObj.SetValue(WindowClosingProperty, value);
}
private static void WindowClosing(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
var element = (Window)depObj;
if (element != null)
element.Closing += OnWindowClosing;
}
private static void OnWindowClosing(object sender, CancelEventArgs e)
{
RelayCommand command = (RelayCommand)GetWindowClosing((DependencyObject)sender);
command.Execute((Window)sender);
}
}
}
In your ViewModel
public RelayCommand WindowClosedCommand { get; set; }
private void WindowClose()
{
Application.Current.Shutdown();
}
In Constructor of ViewModel
this.WindowCloseCommand = new RelayCommand(WindowClose);
In your XAML
<mah:MetroWindow x:Class="MyApp.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:dp="clr-namespace:MyApp.DependencyProperties"
dp:WindowProperties.WindowClosing="{Binding WindowClosedCommand}" />
Since the solution from gotapps.net did't worked for me because I did't found how to programatically do that (My window does not have the Xaml file, it's just a base class). I found another workaround to use the same button to close the Window as follows:
internal class BaseWindow : MetroWindow
{
public BaseWindow()
{
this.Loaded += BaseWindow_Loaded;
}
void BaseWindow_Loaded(object sender, EventArgs e)
{
Button close = this.FindChild<Button>("PART_Close");
close.Click += close_Click;
}
void close_Click(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown(0);
}
}
You can use "Closing" or "Closed" event
Closing handler gets triggered when user clicks on the Close button. This also gives you control on whether the application should be closed.
Similarly, Closed handler gets triggered just before the window is closed
<Controls:MetroWindow x:Class="MyClass.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
Title="My Class"
Closing="MainWindow_OnClosing">
</Controls:MetroWindow>

WPF showing dialog before main window

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()

Resources