2

This is my App constructor for my WPF application:

public partial class App
{
    public App()
    {
        Run(new Login(false));
    }


}

And this is my Login constructor:

public Login(bool ignoreSettings)
    {
        InitializeComponent();
        if (ignoreSettings)
        {
            TxtUsername.Text = SettingsSaver.LoadUsername();
            TxtCrmUrl.Text = SettingsSaver.LoadCrmUrl();
            return;
        }
        if (string.IsNullOrWhiteSpace(SettingsSaver.LoadUsername()) || string.IsNullOrWhiteSpace(SettingsSaver.LoadCrmUrl())) return;
        try
        {
            CrmConnector.ConnectToCrm();
            MainWindow mainWindow = new MainWindow();
            mainWindow.Show();
        }
        catch (SecurityAccessDeniedException)
        {
            MessageBox.Show(@"Uw inloggegevens zijn niet correct. Gelieve deze te controleren en opnieuw te proberen.");
        }
        finally
        {
            Close();
        }
    }

It starts the App constructor and goes through the Login constructor just fine, but once it reaches the App Constructor again after finishing the Login constructor, it crashes with an InvalidOperationException, with additional information: "Cannot set visibility or call Show, ShowDialog, or WindowInteropHelper.EnsureHandle after the window has been closed.

The goal of the constructor is as follows: When the application is first started, I want to check if there are existing settings for this application. If they exist, I want to use those settings to connect to a 3rd party (Dynamics CRM 2011), open the main application window, and then close the Login screen. if they are not there, I want the user to set the settings.

HOWEVER, I also want to be able to start this window from a button on my main screen, in which case it should ignore the default settings and launch the login window again, allowing me to set the settings again.

I already managed to get it to work using 2 constructors, but Resharper complains when i does that because I basically ignore the parameter in the second constructor (the one which I launch from the button on the main screen. I'm trying to have 1 unified constructor so Resharper does not complain. Is that possible?

Edit: I don't want to keep my login window because I create a new window when I change the settings, using the following code in my MainWindow:

private void BtnSettings_Click(object sender, RoutedEventArgs e)
    {
        Login login = new Login(true);
        login.Show();
        Close();
    }

edit: some clarification: I don't want to show multiple windows. What I want is:

  1. on startup, launch Login.xaml;
  2. when Login.xaml is launched, check if the settings have already been set;
  3. if no settings, show Login.Xaml for setting;
  4. if Settings set, start MainWindow with saved settings.

In addition, I have a button on MainWindow which has to force-start Login.xaml but not check if there are settings. These are currently separate constructors and I would like to make 1 constructor of them.

Nzall
  • 3,439
  • 5
  • 29
  • 59
  • 1
    To be honest, if you got it to work just disable the R# warning and move on. – satnhak Apr 29 '14 at 11:22
  • If I can't find a way to have a unified constructor, I'm going to do that. But I'd prefer to get rid of the warning entirely, instead of just suppressing it. – Nzall Apr 29 '14 at 11:26
  • I could also just check if IgnoreSettings is true in my second constructor. would solve the resharper warning as well. – Nzall Apr 29 '14 at 11:34
  • Is there `StartupUri="Login.xaml"` in your `App.xaml` file present, it should be removed from there, as you are calling `Login` thru `Run()` method in `App.xaml.cs` file's constructor! – S2S2 Apr 29 '14 at 11:35
  • @VS1 I don't have that line in my App.xaml when i'm using the Run method. Edit: And I also tried using Startup="App_Startup()" and putting Run() there, but that also does not work. – Nzall Apr 29 '14 at 11:36
  • Ok @NateKerkhofs, you should set `System.Windows.Application.Current.MainWindow = mainWindow;` in your Login constructor before calling `Show()` method, please try and see if it works.. – S2S2 Apr 29 '14 at 11:39
  • @VS1 I tried that and it does not work. same exception. – Nzall Apr 29 '14 at 11:41
  • @NateKerkhofs What code is inside `Run()` method? – S2S2 Apr 29 '14 at 11:48
  • The parameter for Run() is the same as above. the code for Run() is proprietary C# Microsoft code. I don't have a custom Run() method declared anywhere in my code. – Nzall Apr 29 '14 at 11:50

4 Answers4

2

Your update makes it a bit clearer what it is you want to achieve. I suggest restructuring the Login window to make it more single responsibility and pushing the validation logic up into the App class so that it is responsible for managing initialization flow. A recipe is as follows:

Alter App.Xaml.cs so that it looks something like this; importantly there is no StartupUri:

<Application 
    x:Class="MyNamespace.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Application.Resources />
</Application>

Where MyNamespace is the namespace of your App class.

Now you are going to manually start the application from App.OnStartup

public partial class App
{
    protected override void OnStartup(System.Windows.StartupEventArgs e)
    {
        base.OnStartup(e);

        if (!AreSettingsSet())
        {
            this.MainWindow = new Login();
            this.MainWindow.ShowDialog(); // Waits until closed.

            // Recheck the settings now that the login screen has been closed.
            if (!AreSettingsSet())
            {
                // Tell the user there is a problem and quit.
                this.Shutdown();
            }
        }

        this.MainWindow = new MainWindow();
        this.MainWindow.Show();
    }

    private bool AreSettingsSet()
    {
        // Whatever you need to do to decide if the settings are set.
    }
}

To summarise: remove your validation logic from the Login window to App, only show Login if needed and only show MainWindow if the settings are actually valid.

satnhak
  • 9,407
  • 5
  • 63
  • 81
  • I'm currently implementing this. Does this also mean that I can have 1 constructor Login(bool IgnoreSettings) instead of 1 with and 1 without the bool parameter? – Nzall Apr 29 '14 at 14:05
  • actually, scratch that comment. With this change, I can implement it so that it doesn't need to have an ignoreSettings bool parameter, because when I show it, it always has to ignore the settings. – Nzall Apr 29 '14 at 14:08
  • Yes that's right: the `Login` screen now only gets displayed when it needs to: something else decides if it should be shown. That makes the logic more simple. – satnhak Apr 29 '14 at 14:41
  • True. I did have to add an extra check to verify that I don't call MainWindow() twice when I'm logging in for the first time, because it was called both in OnStartup() and in Loginform.BtnLogin_click(). This means I'll likely have to slightly alter when I call CrmConnector.ConnectToCRM(). – Nzall Apr 29 '14 at 14:46
0

Try Visibility.Hidden instead of Close if you want to keep it

Update: this.Visibility=Visibility.Hidden;

  • I don't really want to keep it, because I need to recreate the main window if I change the settings. – Nzall Apr 29 '14 at 11:32
  • @Mikael can you add example source code? That would be clearer to understand your answer. – Reporter Apr 29 '14 at 11:56
0

You will need to do some tweaking and then you can show several windows or single window multiple times.

  1. Remove StartupUri from App.xaml.
  2. Set Build action to Page for App.xaml.

This will disable autogenerating of App.g.ics and you can create own application entry point:

public partial class App : Application
{
    [STAThread]
    public static void Main()
    {
        App app = new App();
        app.InitializeComponent();
        app.ShowWindow1();
        app.ShowWindow1(); // show second time same window (new instance)
    }

    public void ShowWindow1()
    {
        // show window1 in separate method, so that instance will be deleted after method ends
        Window1 window1 = new Window1();
        // optional (as it seems)
        // MainWindow = window1
        widow1.Show();
    }
}
Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • What do I need the second ShowWindow1() for? Isn't one of those enough? – Nzall Apr 29 '14 at 12:37
  • I don't want to show multiple windows. what I want is: 1) on startup, launch Login.xaml; 2) when Login.xaml is launched, check if the settings have already been set; 3a) if no settings, show Login.Xaml for setting; 3b) if Settings set, start MainWindow with proper settings. In addition, I have a button on MainWindow which has to force-start Login.xaml but not check if there are settings. These are currently separate constructors and I would like to make 1 constructor of them. – Nzall Apr 29 '14 at 12:45
  • Also, I've tried to adapt this method and it still fails. I keep getting the error. – Nzall Apr 29 '14 at 12:52
  • You still getting error because of that *adapting*. Do you create a new instance of login window before displaying it again? I saw you don't want to hide window, but restart. Then you have to create a new instance. – Sinatr Apr 29 '14 at 13:07
  • I create a new instance of the login window when I need to show it after the ButtonSettings_Click. This new window in turn also creates a new MainWindow when the settings are saved again. But it does not crash on that stage, it crashes when I first launch the window. Could this be because of my Telerik controls? – Nzall Apr 29 '14 at 13:10
  • I've no idea, but my answer will ensure what the **first** instance of window1 is deleted (disposed) when second instance is used to show the same window1. – Sinatr Apr 29 '14 at 13:19
  • but window1 (Login) only needs to be shown on 2 occasions: Either it's called with the parameter true or there are no settings to retrieve. on any other case, it needs to start, see that there are settings to retrieve, start MainWindow with those settings and then close itself. – Nzall Apr 29 '14 at 13:23
0

I faced a similar challenge, make sure that any functions that may close the window you are opening are called after the window has loaded.

for the window you are opening set in the xaml file

Loaded="Window_Loaded"

then in the cs file,

private void Window_Loaded(object sender, RoutedEventArgs e)
    {
         //call your function here
    }

This resolved my issue, hope that helps