Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/314.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在调用应用程序之前在ApplicationContext中处理窗体。重新启动_C#_.net_Multithreading_Winforms_User Interface - Fatal编程技术网

C# 在调用应用程序之前在ApplicationContext中处理窗体。重新启动

C# 在调用应用程序之前在ApplicationContext中处理窗体。重新启动,c#,.net,multithreading,winforms,user-interface,C#,.net,Multithreading,Winforms,User Interface,我有一个winforms应用程序,它使用一个自定义应用程序上下文,该上下文提供了在系统托盘中运行应用程序的框架 public partial class MyApplicationContext : ApplicationContext { private IContainer components; private NotifyIcon notifyIcon = null; /// the main application form, which may never

我有一个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()
。因此我无法在
ApplicationContext.dispose()
方法中或直接在restart方法中处理
appForm
,否则我将得到一个跨线程异常

如果在重新启动之前,当用户单击调用
appForm.Show()
的托盘图标时,表单已显示,则将自动调用
appForm.Dispose(true)
。Per:

如果使用Show方法显示表单,将自动调用Dispose

否则,如果表单从未显示,GC终结器将调用
Dispose(false)


如何确保在重新启动之前释放此表单?

我建议您从构造函数中删除
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
    }
}
内部类MyApplicationContext:ApplicationContext
{
私有通知图标通知图标;
私人IContainer组件;
私有AppForm AppForm;
请求重新启动公共事件事件处理程序;
公共MyApplicationContext()
{
notifyIcon=新的notifyIcon()
{
ContextMenuStrip=new ContextMenuStrip{Text=“Open Form”},
Text=“应用程序”,
可见=真,
图标=新图标(“Icon.ico”)
};
appForm=新的appForm();
notifyIcon.DoubleClick+=(o,e)=>
{
appForm.Show();
};
重新启动请求+=(o,e)=>
{
appForm?.Close();//Close()也将处理该表单。
notifyIcon?.Dispose();
};
背景();
}
私人空间背景()
{
Task.Run(()=>//这里我们告诉Task运行后台操作并返回bool
{
//此主体将在单独的线程中运行
Thread.Sleep(5000);//这表示您的背景工作
var restart=true;//无论bg工作产生什么结果
返回重启;
}).ContinueWith((task)=>//task是上面的task实例,包含后台工作的结果
{
var shouldRestart=task.Result;//Result是您在上面的正文运行正文中返回的值
if(shouldRestart)RestartRequested?.Invoke(this,EventArgs.Empty);
},
TaskScheduler.FromCurrentSynchronizationContext());//这将在UI线程上返回,无需担心线程边界
}
}

您必须在构造函数中重新创建表单吗?是否可以使用当前调用Show()的相同方法新建表单?建议:先简要显示表单,然后隐藏表单form@JacobSeleznev我有过这样的想法,但它似乎有点骇人听闻?@axlj您是否建议只
使用(){}
show form方法中的表单?appForm类保存的数据需要在应用程序的整个生命周期中保持不变。不管怎样,您都会感谢您以后这么做:-)它被称为空传播——msdn上有一篇关于它的文章:但谷歌搜索它可能会给您带来更丰富的细节。感谢您提供的示例,很有趣,我并不熟悉。
MyApplicationContext
中的
RestartRequested
事件处理程序是否总是在
Main()
中的处理程序之前执行?我的所有后台工作都是通过网络通信事件的IOCP或windows消息的到达来启动的。根据文档,委托将按其出现的顺序调用。在这种情况下,
MyApplicationContext
类总是首先被命中,因为赋值在构造函数中。任何外部事件处理程序都将始终添加到内部事件处理程序之后。