C# WPF - 希望同时打开多个窗口,但每个窗口只有一个实例

4

我是WPF的新手,一直在寻找答案,这肯定不难吧?

我创建了一个主窗口,其中包含指向多个窗口的链接,并且我希望它们可以并行无模式运行,但我不想打开相同窗口的多个实例。

简单来说,我可以同时打开A、B、C三个窗口,但不能同时打开A、A、B、C、C五个窗口。

我需要为我要打开的窗口(在本例中为EditSettings)实现一个检查。

如果已经打开-激活它

如果没有打开,则打开它。

我在Main中有以下代码,但它没有起作用。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void MenuItem_Click(object sender, RoutedEventArgs e)
    {
        EditSettings winEditSettings = new EditSettings();
        string isOpen = null;
        if (isOpen == "true")
        {
            winEditSettings.Activate();
        }
        else
        { 
            winEditSettings.Show();
            isOpen = "true";
        }
    }

}

现在我知道这个逻辑的问题了——每次我按按钮打开EditSettings时,它会将isOpen设为null。如果我不给isOpen赋值,则If条件会被打破。
我可以将变量“isOpen”作为公共变量初始化到MenuItem_Click方法之外,但是这样我认为我需要为我创建的每个窗口都设置一个isOpen变量!!肯定有更好的方法吧?
我尝试的另一个选项是:
    private void MenuItem_Click(object sender, RoutedEventArgs e)
    {
        EditSettings winEditSettings = new EditSettings();
        if (winEditSettings.IsLoaded)
        {
            winEditSettings.Activate();
        }
        else { winEditSettings.Show(); }

我想不出为什么这个功能不能正常工作,我试过使用isVisible、isLoaded、isActive等方法,但无法阻止窗口多次打开。谢谢您的帮助!

4个回答

1
有些人可能会对这个想法感到不满,但每当我需要这样做时,我都会将子窗口对象作为应用程序的一部分。然后,在你的MenuItem_Click()中,测试winEditSettings是否为null
它仍然是每个窗口的成员变量(就像你临时的isOpen解决方案一样),但是如果以后需要在窗口之间桥接信息,拥有窗口对象可带来优势。在我的情况下,我们希望能够同时关闭所有子窗口,这(最简单的)意味着在一个中央位置跟踪这些对象。
或者,如果您希望设置完全解耦,可以采用类似单例的方法,并将逻辑放入子窗口类中。具体而言,您可以调用EditSettings.Activate并让该类跟踪是否需要创建窗口或仅Show()现有窗口。
如果要重写您的代码,我会将其移动到类似以下的位置:
private static EditSettings winEditSettings = null;

public static void WakeUp()
{
    if (winEditSettings == null)
    {
        winEditSettings = new EditSettings();
    }
    winEditSettings.Activate();  // This may need to be inside the block above
    winEditSettings.Show();
}

这两个都是类(static)的一部分,而不是实例。因此,您的应用程序对象在原始的MenuItem_Click()中调用了EditSettings.WakeUp(),并且从未真正看到子窗口本身。

如果您稍后改变对解耦架构的想法,可以向winEditSettings添加一个get访问器,并保持所有人都相当满意。


谢谢John,如果可能的话,您能否确定我应该将代码的哪个部分放入子窗口类中?据我所知,您所说的“子窗口类”是指将代码放在EditSettings.xaml.cs而不是MainWindow.xaml.cs中?每个窗口都与其他窗口没有关系,因此解耦一切会起作用。我只需要更多有关语法的信息:) 谢谢 - QueenSaphos
啊,抱歉。我以为我已经说得很清楚了,因为你已经写过了。MenuItem_Click() 中的所有内容都可以通过该类的几乎完全相同的方法进行管理。唯一的区别是 winEditSettings 将成为一个成员变量,而 isOpen 可以被删除。如果仍然感到奇怪,我会在我的答案中快速重写你的代码。 - John C

1
               if (_adCst == null)
               {
            _adCst = new AddCustomerPage();
            _adCst.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
            _adCst.WindowState = System.Windows.WindowState.Normal;
            _adCst.ResizeMode = System.Windows.ResizeMode.NoResize;
            _adCst.Activate();  // This may need to be inside the block above
            _adCst.Show();
               }

              else
        {
            if (!_adCst.IsLoaded == true) 
            {
                _adCst = new AddCustomerPage();
                _adCst.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
                _adCst.WindowState = System.Windows.WindowState.Normal;
                _adCst.ResizeMode = System.Windows.ResizeMode.NoResize;
                _adCst.Show();
            }

            _adCst.Activate();
        }

0

对于其他有这个问题的人,我已经找到了另一个解决方案——它可以工作,但无法将打开的窗口置于前台(激活)。然而,它确实可以防止多次打开同一窗口。

        foreach (Window n in Application.Current.Windows)
            if (n.Name == "winEditSettings")
            { winEditSettings.Activate(); }
            else
            { winEditSettings.Show(); }

有人能猜测一下为什么窗口没有被置于前台,使用Activate()方法吗? < p > < em > 编辑 对于其他有此问题的人,将winEditSettings.Activate()放在foreach循环之外可以实现我想要的一切:
        foreach (Window n in Application.Current.Windows)
            if (n.Name == "winEditSettings")
            { }
            else
            { winEditSettings.Show(); }

    winEditSettings.Activate(); 

这将阻止同一窗口的多个实例打开,并在用户尝试重新打开它时将窗口置于前台。


0

我的建议是您设置某种计数器。这将防止打开多个窗口实例。

    int windowOpen = 1;

    private void button_Click(object sender, RoutedEventArgs e)
    {        
        if (windowOpen == 1) 
        {
            WindowA winA = new WindowA();
            winA.Show();
            windowOpen++; //increments windowOpen by 1, windowOpen will now = 2
        }
        else if (windowOpen > 1)
        {
            MessageBox.Show("Window is already open"); 
        }
    }

希望这可以帮到你。


谢谢,我已经通过迭代Application.Current.Windows来阻止多个实例的打开。但是,如果窗口已经打开,我仍然无法将其置于前台! - QueenSaphos
我认为你没有创建多个实例的原因是你的else语句从未被调用。由于窗口存在,它将始终激活,但永远不会显示在最前面。我的建议是交换ActivateShow方法的顺序来测试这个理论。根据我的经验,Show方法似乎总是可以将窗口置于我所创建的任何应用程序的前面。 - Rhys Hamilton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接