How to open window directly after main application window - c#

My app's main UI is in MainWindow.xaml.
Firstly - what is causing this to be the window the application opens on startup. It does not appear to be defined as a "starup object" and there does not appear to be any code that specifically launches this window.
I can make my login window appear when the app starts by in the loaded event of MainWindow.xaml defining a new "login.xaml" and telling it to show as a dialog. However, if I do this then MainWindow does not appear until Login has been closed.
What I want to achieve is when my app starts, the MainWindow appears and then on top of that the Login window is displayed modally.
How can this be done?

The startup of the MainWindow is defined in App.xaml by default when creating a project in VS:
<Application ...
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
Creating the dialogue in the Loaded event should work, just don't do it in the constructor where it is not yet loaded.
Loaded="Window_Loaded"
private void Window_Loaded(object sender, RoutedEventArgs e)
{
new LoginDialogue().ShowDialog();
}

One way could be to add a Loaded event handler to your main window and in that display the login window:
this.Loaded += LoadedEventHander;
void LoadedEventHander(object sender, RoutedEventArgs e)
{
// Show Login.xaml here.
}

In app.xaml the following line defines the startup window, you can change it if you want
StartupUri="MainWindowView.xaml"
If you are following MVVM you can bind a command to the windows loaded event using System.Windows.Interactivity (otherwise simply create an event handler as the others have suggested)
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding MyICommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>

Related

Why TriggerEvent "Loaded" binded command doesn't work for App.xaml

I'm trying to load a Splash Screen when my app loads.
The LoadSplashScreen is a Delegate Command that checks if there is already another program running, if not it shows the splash screen.
But to check the property, I need to trigger a Command when the App.xaml is loaded.
It shows me that "Triggers can't be attached to elements of type Application"
And other errors also which I think are related to the first one where it says that Triggers binding can only be derived types of "DependencyObject".
Here's the xaml code:
<Application x:Class="FST.CWI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModel="clr-namespace:FST.CWI.Sources.ViewModel"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
StartupUri="MainWindow.xaml"
xmlns:interactivity="http://schemas.microsoft.com/expression/2010/interactivity">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadSplashScreen}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Application.Resources>
<viewModel:BaseViewModel x:Key="BaseVM" />
<viewModel:AppViewModel x:Key="AppVM" />
<viewModel:GeneralViewModel x:Key="GeneralVM" />
<viewModel:SteeringViewModel x:Key="SteeringVM" />
<viewModel:AdvancedViewModel x:Key="AdvancedVM" />
<viewModel:SittingViewModel x:Key="SittingVM" />
</Application.Resources>
</Application>
Wpf has a spash screen feature, maybe you can use it:
https://learn.microsoft.com/en-us/dotnet/framework/wpf/app-development/how-to-add-a-splash-screen-to-a-wpf-application
It can display an image until your MainWIndow appears.
Application is not a dependency object so don't try and use anything which relies on it being one. Because that just isn't going to work.
Remove the startup url from app.xaml that opens mainwindow.
Instead point it to an override for onstartup in you app.xaml.cs
<Application …
Startup="OnStartup">
And of course your code in app.xaml.cs
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// Your logic here
}
Put whatever your logic in there and new up, show whichever window or whatever you like.
Which will look something very roughly like:
SplashScreen ss = null;
if ( your criteria goes here)
{
ss = new SplashScreen("whatever.bmp");
ss.Show();
}
MainWindow mw = new MainWindow();
if ( your criteria goes here )
{
ss.Close();
}
mw.Show();

Can't find resource Locator when creating window in App.xaml

I am creating my main window inside the App.xaml.cs constructor like this:
MainWindow wnd = new MainWindow();
Application.Current.MainWindow = wnd;
wnd.Show();
Starting the application gives me a XamlParseException, the resource with the name "Locator" cannot be found.
This is the suspicious line:
<DockPanel x:Name="MainPanel" DataContext="{Binding MainWindowViewModel, Source={StaticResource Locator}}" LastChildFill="True">
Using StartupUri in App.xaml works just fine.
What am I doing wrong?!
I presume you have your Locator resource in the App.xaml. The reason why it does not work when you put your code in the constructor is because App.xaml is not loaded yet. If you see the default Main method generated by visual studio, you can see that App.InitializeComponent is called after the constructor. The resources in the xaml file are initialized at this point.
You can fix the issue by putting your code in the Application.Startup event which Occurs when the Run method of the Application object is called. (If StartupUri is set, it's also initialized after Run is called.)
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var window = new MainWindow();
window.Show();
}
Of course you can subscribe to that event and write the code in an event hander. However, when we want to subscribe to events in a base class, it's better to override the OnXXX method for the corresponding event.
On and btw, you don't need this line Application.Current.MainWindow = wnd;. It will be done automatically for you by wpf.

Using several DispatcherFrames to show Modal windows not working properly, issue can be duplicated with WPF MessageBox

I have a problem showing modal windows in my application. My application functions like a web browser in that it can host multiple windows (i.e. the same application runs in two or more windows). Due to this, I want Modal windows to only be modal to the window that called it (so if I have two windows open, each window can have its own modal window). This means I can't use ShowDialog() as it blocks the entire application.
I implemented the solution proposed in this stackoverflow answer by using DispatcherFrames and Dispatcher.PushFrame(). This seemed to work, but after some testing I've found the following:
I start the application, open up a new window, open up a Modal window in the first window and then a Modal window in the second. Then I close the first Modal window, set the DispatcherFrame.Continue to false, but the PushFrame() does not return control to the code that called the method to show the modal window until the Modal window belonging to the second window has also been closed. I suspect this happens because when the first Modal window closes, it returns control to the second DispatcherFrame belonging to the other Modal window instead of the Application DispatcherFrame. However, if I close the second Modal window first, it works the way it should and returns control.
As an additional test, I tested the same situation in a fresh application with WPF MessageBoxes, and it turns out the same problem exists here as well.
MainWindow.xaml:
<Window x:Class="TestMessageBoxDispatcherFrame.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<Button Click="OpenMessageBoxClick" Content="Open message box" />
<Button Click="OpenNewWindowClick" Content="Open new window" />
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OpenMessageBoxClick(object sender, RoutedEventArgs e)
{
var messageBoxResult = MessageBox.Show("Hello World!");
Console.WriteLine("MessageBox closed");
}
private void OpenNewWindowClick(object sender, RoutedEventArgs e)
{
var window = new MainWindow { ShowInTaskbar = true };
window.Show();
}
}
If I open up a second window, and open up a message box in each of these two windows, MessageBox.Show() will only return once the second opened MessageBox has been closed (so the first one blocks until the second one has been closed).
Is this a known issue? Working as intended? Are there any workarounds?
I have been able to create something close to what I want by using async/await, but this requires all methods that wants to call a MessageBox/Modal window to be async. What is the best way to show Modal windows the way I want?

How to differ click input between inside and outside of a UserControl?

(Please take each of controls stated below as control created using MVVM pattern)
So, I have a UserControl which I place on my MainWindow. I want my UserControl, if clicked (in the MainWindow, inside the UserControl), the background changed into another color, and if I click in the MainWindow, but outside of UserControl, then the UserControl's background will change to the original color.
What I've tried :
I've tried to apply a Command inside the UserControl.InputBindings which to detect Mouse Input (MouseBinding), but the only MouseBinding raised is the MouseBinding in the Window.InputBindings (which should be raised ONLY when the click input is outside the UserControl), but apparently, wherever a click happen, the only MouseBinding raised is only the one in Window.InputBindings.
Differ the CommandParameter between MouseBinding in Window.InputBindings and UserControl.InputBindings.
Question :
How to differ the MouseBinding between clicking inside the UserControl and outside?
Thanks
The solution is simple. Just attach a PreviewMouseDown event handler to both the Window and the UserControl and handle both events in the Window:
<Window ... PreviewMouseDown="Window_PreviewMouseDown">
<UserControl Name="Control" PreviewMouseDown="UserControl_PreviewMouseDown" ... / >
</Window>
...
private void Window_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Control.Background = someNewColourBrush;
}
private void UserControl_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Control.Background = originalColourBrush;
}

Using a command on a custom control

I'm trying to use the search text box (which I made by following this tutorial: http://davidowens.wordpress.com/2009/02/18/wpf-search-text-box/).
I use MVVM & WPF. The above user control works when you write the "Search"-event in the code-behind file of the View, but I can't get it to work with a command (using the ViewModel).
(The search-event fires when you haven't typed something for about 2 seconds.)
I've tried using Caliburn, so it can "map" the view event to the viewmodel method. However when the event fires, the application crashes: "No target found for method SearchText()." on the RaiseSearchEvent method from the custom user control.
See the following test application: Test application
Could somebody tell me what I'm doing wrong? I told CaliBurn to do the following:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Search">
<cal:ActionMessage MethodName="SearchText()" />
</i:EventTrigger>
</i:Interaction.Triggers>
So I figure this is correct. It means that when the "Search" event fires, caliburn will look for the method SearchText in the ViewModel. This doesn't happen though, and it causes my app to crash and burn.
Do you know why? Or how I could solve this problem (doesn't have to be with Caliburn).
I already tried adding "Extending Command Support" (http://msdn.microsoft.com/en-us/library/dd458928.aspx), but this is a little too complex for me :/
Thanks for any help!!
You are using the Caliburn's ActionMessage but because you do not use its Bootstrapper class to start up your application, the MainView's DataContext is not set to an instance of the MainViewModel. If you check the SearchTextBox's DataContext at runtime, you'll see it's null.
Here's a series of steps that may solve your problem (using your linked example project)
Create a class called MyBootstrapper. It should look like this
public class MyBootstrapper : Bootstrapper<MainViewModel> {}
Add your new bootstrapper to the Application's Resources collection, like I show below (App.xaml)
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplicationParadise"
x:Class="WpfApplicationParadise.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<local:MyBootstrapper x:Key="bootstrapper" />
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Not sure why, but if the bootstrapper isn't nested in my build, it never is instantiated when App.InitializeComponent() is run...
Change App.xaml.cs to simply run InitializeComponent. Note that I had to tweak your build a bit to get this to work... InitializeComponent() is only defined in the App.g.cs file if you have the nested resource dictionary from step 2, or if you have an x:Name attribute on App.xaml, or perhaps other things...
using System.Windows;
namespace WpfApplicationParadise
{
public partial class App : Application
{
public App()
{
InitializeComponent();
}
}
}
Finally, you need to remove the parens as Wallstreet Programmer suggested.
Those steps should cause your App to instantiate your bootstrapper, which in turn instantates the MainViewModel as the root viewmodel of your application, and then create a MainView and hook up its DataContext to the MainViewModel. At that point, your application should work as expected.
Remove ()
<cal:ActionMessage MethodName="SearchText" />
After I run your application, I see that you need to initialize the MainViewModel and also to bind Text of SearchTextBox with TekstBoxTekst.
Codebehind
public partial class MainView : Window
{
public MainView()
{
InitializeComponent();
this.Loaded += (s, e) =>
{
this.DataContext = new MainViewModel();
};
}
}
XAML
<StackPanel>
<l:SearchTextBox
Text="{Binding TekstBoxTekst, UpdateSourceTrigger=PropertyChanged}"
Height="24" x:Name="TekstBoxTekst" Margin="145,144,145,143">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Search">
<cal:ActionMessage MethodName="SearchText">
</cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</l:SearchTextBox>
</StackPanel>

Categories

Resources