【问题标题】:How do you allow only one instance of an inherited window to be open?如何只允许打开继承窗口的一个实例?
【发布时间】:2014-01-16 22:15:44
【问题描述】:

我使用 WPF 已经有一段时间了,我在强制从 MainWindow 中仅打开一个窗口实例时遇到了麻烦。我一直在尝试找出我选择的解决方案是否是最好的解决方案,但这个主题的方式似乎并不多。让我怀疑我是不是完全走错了路。

我试图解决的原始问题是只允许打开一个窗口实例,这是我使用单例模型完成的。它最终工作得很好,但我有两个我想打开的窗口,我想要这个功能。我认为创建一个“BaseWindow”来继承会很好,所以我只需要实现一次。这就是我卡住的地方。

我创建了一个非常简单的项目,我试图在其中实现这个想法,但是我在 AboutWindow 的公共构造函数上遇到了保护级别错误,因为 WindowBase 构造函数是私有的。将其更改为 public 允许程序编译,但它似乎“跳过”了具有“InitializeComponent”的 AboutWindow 构造函数。

不过,作为旁注,只允许一个实例的部分确实有效。如果我可以使用 IniliatizeCompotent 让构造函数工作,我将被设置。澄清一下,任何从 WindowBase 派生的窗口都可以同时打开。所以在这种情况下,一次一个 OptionWindow 和一个 AboutWindow。

MainWindow.xaml:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="181" Width="259">
    <DockPanel>
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="_File"></MenuItem>
            <MenuItem Header="_Tools">
                <MenuItem Header="_Options" ></MenuItem>
            </MenuItem>
            <MenuItem Header="_Help">
                <MenuItem Header="_About" Click="AboutMenuItem_Click"></MenuItem>
            </MenuItem>
        </Menu>
        <Grid>
            <TextBlock Text="Content would go here"></TextBlock>
        </Grid>
    </DockPanel>
</Window>

MainWindow.xaml.cs:

using System.Windows;

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

        private void AboutMenuItem_Click(object sender, RoutedEventArgs e) {
            AboutWindow.Instance.Show();
        }
    }
}

关于Window.xaml:

<src:WindowBase x:Class="WpfApplication1.AboutWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:src="clr-namespace:WpfApplication1"
        Title="AboutWindow" Height="300" Width="300">
    <Grid>
        <Label Content="Label" HorizontalAlignment="Left" Margin="84,74,0,0" VerticalAlignment="Top"/>
    </Grid>
</src:WindowBase>

关于Window.xaml.cs:

namespace WpfApplication1 {
    /// <summary>
    /// Interaction logic for AboutWindow.xaml
    /// </summary>
    public partial class AboutWindow : WindowBase {
        public AboutWindow() {
            InitializeComponent();
        }
    }
}

WindowBase.cs:

using System;
using System.Windows;

namespace WpfApplication1 {
    public partial class WindowBase : Window {
        private static WindowBase instance;

        static WindowBase() {

        }

        private WindowBase() {
            Closed += AbstractWindow_Closed;
        }

        public static WindowBase Instance {
            get {
                if (instance == null) {
                    instance = new WindowBase();
                } return instance;
            }
        }

        private void AbstractWindow_Closed(object sender, EventArgs e) {
            instance = null;
        }
    }
}

究竟是什么缺失的拼图,还是我走错了路?

【问题讨论】:

  • 你的问题是 WindowBase 中的 Instance 属性创建的是 WindowBase 对象,而不是 AboutWindow 对象。
  • 首先,使用以下信息更新您的问题:假设您有两个窗口类型 A 和 B,它们都派生自 WindowBase。现在,您是希望只打开一个 A 还是只打开一个 B,还是可以同时打开一个窗口 A 和一个窗口 B?
  • 更新了我的问题。我打算选择后者,一个窗口 A 和一个窗口 B 同时打开。在这种情况下,一个 OptionWindow 和一个 AboutWindow。

标签: c# wpf inheritance singleton


【解决方案1】:

MainWindow 上使用单例模式很好,但正如@elgonzo 所说,您从WindowBase 类继承的AboutWindow 类中的Instance 属性返回WindowBase 的实例。

此外,任何派生类在初始化时都会自动调用基类构造函数,但您的AboutWindow 构造函数是public 并且public 构造函数不能从基类调用private 构造函数。

我不知道您为什么要尝试这样做,而不是简单地在 AboutWindow 类中重复单例模式。如果情况是您只想允许打开一个窗口,而不管是哪个窗口,那么您将不得不使用不同的方法......对于该要求有一个非常简单的解决方案,您可以找到它在 StackOverflow 上的 How can I make sure only one WPF Window is open at a time? 帖子中有详细说明。

【讨论】:

  • 我确实理解构造函数的概念,但我不知道我能做些什么。至于重复单例模式,我绝对可以,但我正在努力成为一个知识渊博的程序员,并且一直在寻找更好的方法。不,我不想只打开一个窗口,只打开一个特定类型的窗口。我并不特别关心是否有人要打开实际应用程序的 6 个实例,但只打开一个“AboutWindow”或一个“OptionWindow”。 AboutWindow 和 OptionWindow 可以同时打开。
  • @Rakshasas,重复单例模式是要走的路。无论您尝试其他什么方法,它都不会像AboutWindow.InstanceOtherWindow.Instance 那样可读。您可以尝试实现类似 WindowBase.GetInstance() 的东西,但只需将其丑陋的外观与简短的 AboutWindow.Instance 进行比较。 (如果你想为许多类型设置单例模式,像 Factory.GetSingletonInstance() 这样的方法可能是要走的路……)
  • 好的。那我就交给专家了。我想很高兴有一个问题,基本上发现你在正确的轨道上。谢谢你们。
【解决方案2】:

我不知道您是否需要在窗口打开时访问 MainWindow,但您可以使用 ShowDialog() 而不是 Show()。 然后你无法访问底层窗口,也无法通过单击任何内容打开另一个窗口。

【讨论】:

  • 抱歉,这是一个糟糕的想法,显然不是作者所要求的。如果你编辑你的答案并改进它,我很乐意删除这个反对票。但是,我不确定如何从现有资源开始改进它。
  • 我可以使用 ShowDialog() 但我绝对讨厌它。我发现它通常会限制应用程序的可用性。有时这样做是有目的的,但我不想在这种情况下剥夺使用主窗口的能力。
猜你喜欢
  • 1970-01-01
  • 2014-05-23
  • 2020-08-11
  • 2020-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多