【问题标题】:Cannot set visibility or call Show, ShowDialog or EnsureHandle after a window has been closed窗口关闭后无法设置可见性或调用 Show、ShowDialog 或 EnsureHandle
【发布时间】:2014-04-29 11:19:22
【问题描述】:

这是我的 WPF 应用程序的 App 构造函数:

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


}

这是我的登录构造函数:

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

它启动 App 构造函数并通过 Login 构造函数就好了,但是一旦在完成 Login 构造函数后再次到达 App 构造函数,它就会崩溃并出现 InvalidOperationException,并附加信息:“无法设置可见性或调用 Show、ShowDialog , 或窗口关闭后的 WindowInteropHelper.EnsureHandle。

构造函数的目标如下:当应用程序第一次启动时,我想检查是否有这个应用程序的现有设置。如果它们存在,我想使用这些设置连接到第 3 方 (Dynamics CRM 2011),打开主应用程序窗口,然后关闭登录屏幕。如果他们不在那里,我希望用户设置设置。

但是,我还希望能够从主屏幕上的按钮启动此窗口,在这种情况下,它应该忽略默认设置并再次启动登录窗口,从而允许我再次设置设置。

我已经设法使用 2 个构造函数让它工作,但是当我这样做时 Resharper 会抱怨,因为我基本上忽略了第二个构造函数中的参数(我从主屏幕上的按钮启动的那个。我正在尝试有 1 个统一的构造函数,这样 Resharper 就不会抱怨。这可能吗?

编辑:我不想保留我的登录窗口,因为我在更改设置时创建了一个新窗口,在我的 MainWindow 中使用以下代码:

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

编辑:一些澄清: 我不想显示多个窗口。我想要的是:

  1. 在启动时,启动 Login.xaml;
  2. Lo​​gin.xaml 启动时,检查设置是否已经设置;
  3. 如果没有设置,显示 Login.Xaml 进行设置;
  4. 如果设置了设置,则使用保存的设置启动 MainWindow。

此外,我在 MainWindow 上有一个按钮,它必须强制启动 Login.xaml 但不检查是否有设置。这些目前是单独的构造函数,我想为它们制作 1 个构造函数。

【问题讨论】:

  • 老实说,如果你让它工作,只需禁用 R# 警告并继续。
  • 如果我找不到统一构造函数的方法,我会这样做。但我宁愿完全摆脱警告,而不是仅仅压制它。
  • 我也可以在我的第二个构造函数中检查 IgnoreSettings 是否为真。也可以解决 reshaper 警告。
  • 您的App.xaml 文件中是否存在StartupUri="Login.xaml",它应该从那里删除,因为您在App.xaml.cs 文件的构造函数中通过Run() 方法调用Login
  • @VS1 当我使用 Run 方法时,我的 App.xaml 中没有该行。编辑:我也尝试使用 Startup="App_Startup()" 并将 Run() 放在那里,但这也不起作用。

标签: c# wpf constructor app-startup


【解决方案1】:

您的更新使您更清楚您想要实现的目标。我建议重构Login 窗口,使其职责更加单一,并将验证逻辑推入App 类,以便它负责管理初始化流程。配方如下:

更改App.Xaml.cs 使其看起来像这样;重要的是没有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>

其中MyNamespaceApp 类的命名空间。

现在你要从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.
    }
}

总结一下:将您的验证逻辑从Login 窗口移除到App,仅在需要时显示Login,仅在设置实际有效时显示MainWindow

【讨论】:

  • 我目前正在实施这个。这是否也意味着我可以有 1 个构造函数 Login(bool IgnoreSettings) 而不是 1 和 1 没有 bool 参数?
  • 实际上,把那条评论划掉。通过此更改,我可以实现它,使其不需要具有 ignoreSettings bool 参数,因为当我显示它时,它总是必须忽略设置。
  • 是的,没错:Login 屏幕现在只在需要时才显示:其他东西决定是否应该显示。这使得逻辑更简单。
  • 是的。我确实必须添加一个额外的检查来验证我在第一次登录时没有调用 MainWindow() 两次,因为它在 OnStartup() 和 Loginform.BtnLogin_click() 中都被调用了。这意味着当我调用 CrmConnector.ConnectToCRM() 时,我可能需要稍作改动。
【解决方案2】:

您需要进行一些调整,然后您可以多次显示多个窗口或单个窗口。

  1. 从 App.xaml 中删除 StartupUri
  2. 将 App.xaml 的构建操作设置为 Page

这将禁用 App.g.ics 的自动生成,您可以创建自己的应用程序入口点:

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

【讨论】:

  • 我需要第二个 ShowWindow1() 做什么?其中一个还不够吗?
  • 我不想显示多个窗口。我想要的是:1)在启动时,启动Login.xaml; 2) Login.xaml 启动时,检查设置是否已经设置; 3a) 如果没有设置,显示 Login.Xaml 进行设置; 3b) 如果设置了设置,则使用适当的设置启动 MainWindow。此外,我在 MainWindow 上有一个按钮,它必须强制启动 Login.xaml 但不检查是否有设置。这些目前是单独的构造函数,我想为它们制作 1 个构造函数。
  • 另外,我尝试过调整这种方法,但仍然失败。我不断收到错误消息。
  • 你仍然会因为那个适应而出错。您是否在再次显示之前创建了一个新的登录窗口实例?我看到您不想隐藏窗口,而是重新启动。然后你必须创建一个新实例。
  • 当我需要在 ButtonSettings_Click 之后显示它时,我会创建一个新的登录窗口实例。当再次保存设置时,这个新窗口又会创建一个新的 MainWindow。但它不会在那个阶段崩溃,当我第一次启动窗口时它会崩溃。这可能是因为我的 Telerik 控件吗?
【解决方案3】:

如果你想保留它,试试 Visibility.Hidden 而不是 Close

更新: this.Visibility=Visibility.Hidden;

【讨论】:

  • 我真的不想保留它,因为如果我更改设置,我需要重新创建主窗口。
  • @Mikael 你可以添加示例源代码吗?这样会更清楚地理解您的答案。
【解决方案4】:

我也遇到过类似的挑战, 确保在加载窗口后调用任何可能关闭您正在打开的窗口的函数。

为您打开的窗口设置在 xaml 文件中

Loaded="Window_Loaded"

然后在cs文件中,

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

这解决了我的问题,希望对您有所帮助

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多