C#WPF如何强制执行单个windows实例
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#WPF如何强制执行单个windows实例相关的知识,希望对你有一定的参考价值。
我想知道在WPF中为每个应用程序提供给定Window的单个实例的最佳方法(阅读最优雅)是什么。
我是.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;
}
}
事情是......这看起来像完全废话。必须有一些方法以更优雅的方式实现同一目标,对吗?
PS:我经常使用Closed
事件来改变其他打开窗口中的值。例如,我有SettingsWindow和“帐户”按钮。当我按下该按钮时,会弹出AccountWindow。当我关闭AcountWindow时,我想要在SettingsWindow中更改(标签)。因此不断创建窗口。
此外,由于窗框上的X按钮,Close总是需要处理的...
如果你真的需要强制执行窗口的单个实例,那么使用工厂创建方法的静态实例(你所拥有的某些东西)当然是一个可行的选择,就像使用数据库时的单个DataContext实例一样。
您也可以编写自己的WindowManager类,虽然这看起来有点过分,但基本上是相同的(除了Factory方法将在单个类中)。
然而,重新阅读你的帖子,我想知道这是否是一个错过森林树木的案例。你提到你的SettingsWindow,它反过来调用AccountWindow,让我觉得你应该只使用ShowDialog()。这将以模态方式打开一个窗口,这意味着不能与调用窗口(或应用程序中的任何其他窗口)进行交互。您只需在该对话框中设置属性,在按下确定按钮时将DialogResult设置为true,并在父窗口中读取该属性。
基本上,你只需使用这样的ShowDialog。关于控件的绑定与硬编码,我省略了很多实现细节。这些细节并不像看到ShowDialog如何工作那么重要。
为简单起见,假设您有一个名为MyAppOptions的类,它反映了应用程序的选项。为简单起见,我将省略大部分实现细节,但它可能会实现INotifyPropertyChanged,具有方法,字段和属性等。
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相关的CheckBox和一个用于MyStringOption的TextBox。它还有一个Ok按钮和Cancel按钮。代码隐藏可能会使用Binding,但是现在我们将对这些值进行硬编码。
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之前设置的公共属性,并且可以在对话框消失后从中获取值。当对话框仍在范围内时,它将可用。
有可能更好的方法来做到这一点,但这是一个相对简单的方法....在你的窗口类上放置一个静态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();
}
以下解决方案扩展了如果窗口已经打开则重新显示窗口。在这种情况下,它是一个帮助窗口。
///<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();
}
此代码全部位于与窗口关联的视图模型(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"** >
如何使用Singleton?
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()
这是一种替代方法,不需要在每个窗口中设置和更新静态属性:
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();
}
以上是关于C#WPF如何强制执行单个windows实例的主要内容,如果未能解决你的问题,请参考以下文章
单个实例[Windows必须在处理单个实例时重新打开...]
如何强制 C# .net 应用程序在 Windows 中仅运行一个实例? [复制]