我有一个winforms应用程序,它使用自定义应用程序上下文,提供在系统托盘中运行应用程序的框架。
public partial class MyApplicationContext : ApplicationContext
{
private IContainer components;
private NotifyIcon notifyIcon = null;
/// the main application form, which may never be shown
private AppForm appForm = null;
public MyApplicationContext()
{
components = new System.ComponentModel.Container();
notifyIcon = new NotifyIcon(components)
{
ContextMenuStrip = new ContextMenuStrip(),
Icon = Properties.Resources.icon2a,
Text = "Application",
Visible = true
};
appForm = new AppForm();
}
}
应用程序监视配置文件的更改,并根据需要重新启动。
private void requestRestart()
{
/// dispose the application context
this.Dispose();
Application.Restart();
}
我必须在重新启动之前处置应用程序上下文,因为应用程序的新实例需要资源。但是,可以从另一个帖子调用requestRestart()
。所以我无法在appForm
方法中或直接在重启方法中处置ApplicationContext.Dispose()
,或者我会得到一个跨线程的例外。
如果表单在重新启动之前显示,当用户点击调用appForm.Show()
的托盘图标时,将自动调用appForm.Dispose(true)
。每MSDN:
如果使用Show方法显示表单,将自动调用Dispose。
否则,如果表格从未显示过,GC终结器会调用Dispose(false)
。
如何确保在重新启动之前处理此表单?
答案 0 :(得分:2)
我建议您从构造函数中删除appForm.Show()
,并将其放入从托盘图标调用的方法中。
在托盘图标打开表单方法中,将appForm.Show()
包装在使用块中:
using (appForm = new AppForm()) {
appForm.Show();
}
这样,表格会在关闭时自行处理。
此外,请将以下内容放在requestRestart()
方法中:
appForm?.Close();
这将确保在上下文强制重新启动时关闭表单。
如果您没有使用最新版本的.NET,以下内容将实现相同的目标:
if (appForm != null)
appForm.Close();
编辑
这是一个完整的例子。这个答案对于你如何处理你的后台任务更加自以为是,但我认为你会发现它的效果非常好。
解释内容为注释。
在您的Program.cs中:
private static void Main()
{
int restartCount = 0;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var applicationCtx = new MyApplicationContext();
applicationCtx.RestartRequested += (o, e) =>
{
restartCount++; //this is just here so my program would stop restarting
if (restartCount > 5) Application.Exit();
Application.Restart();
};
Application.Run(applicationCtx);
}
然后在MyApplicationContext.cs中:
internal class MyApplicationContext : ApplicationContext
{
private NotifyIcon notifyIcon;
private IContainer components;
private AppForm appForm;
public event EventHandler RestartRequested;
public MyApplicationContext()
{
notifyIcon = new NotifyIcon()
{
ContextMenuStrip = new ContextMenuStrip { Text = "Open Form" },
Text = "Application",
Visible = true,
Icon = new Icon("icon.ico")
};
appForm = new AppForm();
notifyIcon.DoubleClick += (o, e) =>
{
appForm.Show();
};
RestartRequested += (o, e) =>
{
appForm?.Close(); //Close() will dispose the form as well.
notifyIcon?.Dispose();
};
BackgroundWork();
}
private void BackgroundWork()
{
Task.Run<bool>(() => //Here we are telling Task to run a background operation and return a bool
{
//this body will run in a separate thread
Thread.Sleep(5000); //this represents your background work
var restart = true; //whatever result the bg work yields
return restart;
}).ContinueWith((task) => //task is an instance of Task from above containing the result fromm the background work
{
var shouldRestart = task.Result; // Result is the value you returned in the body Run body above
if (shouldRestart) RestartRequested?.Invoke(this, EventArgs.Empty);
},
TaskScheduler.FromCurrentSynchronizationContext()); //This will return on the UI thread now, no need to worry about thread boundaries
}
}