代码之家  ›  专栏  ›  技术社区  ›  biasedbit

c wpf如何强制执行单个Windows实例

  •  11
  • biasedbit  · 技术社区  · 15 年前

    我想知道在WPF中为每个应用程序提供一个给定窗口的单个实例的最佳方法(阅读起来最优雅)。

    我是.net和wpf的新手,我想到的看起来很差劲。

    private static readonly Object MUTEX = new Object();
    private static AboutWindow INSTANCE;
    
    public static AboutWindow GetOrCreate() {
        lock (MUTEX) {
            if (INSTANCE == null) {
                INSTANCE = new AboutWindow();
            }
            INSTANCE.Show();
            return INSTANCE;
        }
    }
    
    private AboutWindow() {
        InitializeComponent();
    }
    
    private void AboutWindow_Closed(object sender, EventArgs e) {
        // the Closed events are handy for me to update values across
        // different windows.
        lock (MUTEX) {
            INSTANCE = null;
        }
    }
    

    事情是…这看起来真是一派胡言。一定有某种方法可以更优雅地实现相同的目标,对吧?

    附言:我经常用 Closed 事件以更改其他打开的窗口中的值。例如,我有 筑篱 使用“帐户”按钮。当我按下那个按钮时, 会计责任 弹出。当我关闭 圆环 ,我想要一些 筑篱 改变(标签)因此不断地创建窗口。
    此外,由于窗框上的X按钮,关闭是你必须处理的事情…

    5 回复  |  直到 8 年前
        1
  •  6
  •   Wonko the Sane    15 年前

    如果您确实需要强制执行窗口的单个实例,那么使用工厂创建方法的静态实例(某种风格的实例)肯定是一个可行的选项,就像使用数据库时的单个DataContext实例一样。

    您也可以编写自己的WindowManager类,尽管这看起来像是多余的,而且本质上是相同的(除了工厂方法将在单个类中)。

    然而,重新阅读你的帖子,我想知道这是否是一个失踪的树木森林案件。您提到的settingsWindow又称accountWindow,让我觉得您应该只使用showDialog()。这将以模态方式打开一个窗口,这意味着不能与调用窗口(或应用程序中的任何其他窗口)进行交互。只需在该对话框中设置一个属性,按下OK按钮时将DialogResult设置为true,然后在父窗口中读取该属性。

    基本上,您只需要像这样使用ShowDialog。我遗漏了很多实现细节,比如绑定和硬编码到控件。这些细节并没有看到ShowDialog的工作原理那么重要。

    为了简单起见,假设您有一个名为MyAppOptions的类,它很好地反映了应用程序的选项。为了简单起见,我将省略大部分实现细节,但它可能实现inotifyPropertiesChanged、具有方法、字段和属性等。

    public class MyAppOptions
    {
        public MyAppOptions()
        {
        }
    
        public Boolean MyBooleanOption
        {
            get;
            set;
        }
    
        public String MyStringOption
        {
            get;
            set;
        }
    }
    

    然后,让我们简单地说明这一点,并假设您想在按下某个窗口上的按钮时显示一个选项对话框。此外,我将假设有一些变量是用您的选项设置的,这些变量是在启动时加载的。

    void btnOptions_Click(object sender, RoutedEventArgs e)
    {
        MyAppOptions options = new MyAppOptions();
        options.MyBooleanOption = mSomeBoolean;
        options.MyStringOption = mSomeString;
    
        OptionsDialog optionsDialog = new optionsDialog(options);
        if (optionsDialog.ShowDialog() == true)
        {
            // Assume this function saves the options to storage
            // and updates the application (binding) appropriately
            SetAndSaveOptions(optionsDialog.AppOptions);
        }
    }
    

    现在假设OptionsDialog是您在项目中创建的一个窗口,它上面有一个与MyBooleanOption相关的复选框和一个用于MyStringOption的文本框。它还有一个确定按钮和一个取消按钮。后面的代码可能会使用绑定,但现在我们将硬编码这些值。

    public class OptionsDialog : Window
    {
        public OptionsDialog(MyAppOptions options)
        {
            chkBooleanOption.IsChecked = options.SomeBooleanOption;
            txtStringOption.Text = options.SomeStringOption;
            btnOK.Click += new RoutedEventHandler(btnOK_Click);
            btnCancel.Click += new RoutedEventHandler(btnCancel_Click);
        }
    
        public MyAppOptions AppOptions
        {
            get;
            set;
        }
    
        void btnOK_Click(object sender, RoutedEventArgs e)
        {
            this.AppOptions.SomeBooleanOption = (Boolean) chkBooleanOption.IsChecked;
            this.AppOptions.SomeStringOption = txtStringOption.Text;
    
            // this is the key step - it will close the dialog and return 
            // true to ShowDialog
            this.DialogResult = true;
        }
    
        void btnClose_Click(object sender, RoutedEventArgs e)
        {
            // this will close the dialog and return false to ShowDialog
            // Note that pressing the X button will also return false to ShowDialog
            this.DialogResult = false;
        }
    }
    

    就实现细节而言,这是一个非常基本的示例。联机搜索ShowDialog以了解更多详细信息。要记住的重要关键是:

    • ShowDialog以模态方式打开一个窗口, 这意味着它是你的 可交互的应用程序 用。
    • 将DialogResult设置为true 将关闭对话框,可以 已从调用的父级检查。
    • 将DialogResult设置为False将 同时关闭对话框,在这种情况下 跳过更新 调用窗口。
    • 按下X按钮 在窗口上自动设置 DialogResult变为False
    • 您可以在对话框窗口中具有公共属性,这些属性可以在执行ShowDialog之前设置,并且可以在对话框消失后从中获取值。当对话框仍在作用域中时,它将可用。
        2
  •  15
  •   Muad'Dib    15 年前

    也许有更好的方法可以做到这一点,但这里有一个相对简单的方法…… 在window类上放置一个静态bool来标记它是否打开。然后,在load()事件中将其设置为true,在close事件中将其设置为false。然后,在打开窗口的代码中,检查标志。

    这是一些伪代码给你一个主意…

    public class AboutWindow
    {
    
        public static bool IsOpen {get;private set;}
    
        onLoadEvent(....) 
        {
            IsOpen = true;
        }
    
        onUnloadEvent(...) 
        {
            IsOpen = false;
        }
    
    }
    
    
    public void OpenAbout()
    {
        if ( AboutWindow.IsOpen ) return;
        AboutWindow win = new AboutWindow();
        win.Show();
    
    }
    
        3
  •  1
  •   AllenM    15 年前

    下面扩展上述解决方案,以便在窗口已打开时重新显示窗口。在这种情况下,它是一个帮助窗口。

        ///<summary>
        /// Show help from the resources for a particular control by contextGUID
        ///</summary>
        ///<param name="contextGUID"></param>
        private void ShowApplicationHelp(string contextGUID = "1")
        {
    
            if (HelpWin != null)
            {
                if (HelpWin.IsOpen)
                {
                    HelpWin.BringToFront();
                    return;
                }
            }
    
            HelpWin = new MigratorHelpWindow();
            HelpWin.Owner = Application.Current.MainWindow;
            HelpWin.ResizeMode = ResizeMode.CanResizeWithGrip;
            HelpWin.Icon = new Image()
                               {
                                   Source =
                                       new BitmapImage(
                                       new Uri(
                                           "pack://application:,,,/ResourceLibrary;component/Resources/Images/Menu/Help.png",
                                           UriKind.RelativeOrAbsolute))
                               };
            HelpWin.Show();
            HelpWin.BringToFront();
        }
    

    此代码都在与窗口关联的ViewModel(MVVM)中。它由一个钩住窗口按钮的ICommand调用(很自然,它会显示一个问号!!) 涉及以下属性(在本例中,它是一个telerik radwindow,但它可以是任何窗口对象,您也可以只存储窗口句柄,但使用此属性可以更平滑地操作对象,例如helpwin.bringtofront(),如上例所示…

        ...
        ...
        private Telerik.Windows.Controls.RadWindow **HelpWin**
        {
            get;
            set;
        }
        ...
        ...
    

    在窗口本身(WPF窗口)中

        ///<summary>
        /// Flag to indicate the window is open - use to prevent opening this particular help window multiple times...
        ///</summary>
        public static bool IsOpen { get; private set; }
        ...
        ...
        ...
    
      private void HelpWindowLoaded(object sender, RoutedEventArgs e)
        {
            IsOpen = true;
        }
    
        private void HelpWindowUnloaded(object sender, RoutedEventArgs e)
        {
            IsOpen = false;
        }
    

    在视图中XAML … …

      DataContext="{Binding Path=OnlineHelpViewModelStatic,Source={StaticResource Locator}}" 
      RestoreMinimizedLocation="True" 
      **Loaded="HelpWindowLoaded" Unloaded="HelpWindowUnloaded"** >
    
        4
  •  0
  •   mikesl    12 年前

    用单件怎么样?

    public class MyWindow : Window {
    
        private static MyWindow instance;
    
        public static MyWindow Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new MyWindow();
                }
                return instance;
            }
        }
    }
    

    然后就用

        MyWindow.Instance.Show() and MyWindow.Instance.Hide()
    
        5
  •  0
  •   aybe    8 年前

    这里有一种替代方法,不需要在每个窗口中设置和更新静态属性:

    public static bool IsWindowInstantiated<T>() where T : Window
    {
        var windows = Application.Current.Windows.Cast<Window>();
        var any = windows.Any(s => s is T);
        return any;
    }
    

    用途:

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        if (IsWindowInstantiated<SettingsWindow>())
            return;
    
        var window = new SettingsWindow();
    
        window.Show();
    }