WPF后台方法打开窗口

时间:2017-06-16 12:11:46

标签: c# wpf

我目前正在开发一个通过IMAP检查来自电子邮件帐户的电子邮件的应用程序。此功能每5秒调用一次,需要一些时间才能完成。

    private void CheckForRequests()
    {
        List<string[]> mails = CollectAllMails();

        for (int i = 0; i < mails.Count; i++)
        {
            if (mails[i][0].Split('_')[0] == "request")
            {
                //INVITATION TO ME
                if (mails[i][0].Split('_')[2] == username && mails[i][0].Split('_')[3] == "request")
                {
                    DeleteMail(mails[i][0]);
                    MessageBoxResult result = MessageBox.Show("Do you accept the request from " + mails[i][0].Split('_')[1], "Invitation", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
                    if (result == MessageBoxResult.Yes)
                    {
                        DeleteMail("request_" + mails[i][0].Split('_')[1] + "_" + mails[i][0].Split('_')[2] + "_" + mails[i][0].Split('_')[3]);
                        SendMail("request_" + mails[i][0].Split('_')[1] + "_" + mails[i][0].Split('_')[2] + "_accept", "");

                        ChatWindow chat = new ChatWindow();
                        chat.ShowDialog();
                        //do open chat window
                    }
                    else if (result == MessageBoxResult.No)
                    {
                        DeleteMail("request_" + mails[i][0].Split('_')[1] + mails[i][0].Split('_')[2]);
                        SendMail("request_" + mails[i][0].Split('_')[1] + "_" + mails[i][0].Split('_')[2] + "_decline", "");
                    }
                }
                //ACCEPTION FROM ANOTHER DUDE
                else if (mails[i][0].Split('_')[2] != username && mails[i][0].Split('_')[3] == "accept")
                {
                    ChatWindow chat = new ChatWindow();
                    chat.ShowDialog();
                }
                //REJECTION FROM ANOTHER DUDE
                else if (mails[i][0].Split('_')[2] != username && mails[i][0].Split('_')[3] == "decline")
                {
                    MessageBox.Show("Your invitation was declined.", "Sorry", MessageBoxButton.OK, MessageBoxImage.Exclamation);
                }

            }
            else if (mails[i][0].Split('_')[0] == "somethingelse")
            {

            }

        }
    }

我的循环每隔5秒调用一次这个方法,在这段时间里我无法在我的应用程序中编写或执行任何操作。我很确定我必须使用ThreadTask来解决问题,但我没有发现如何解决这个问题。当我在Task中调用该方法时,我单击是它会崩溃并说它必须是STA-Thread ...在这种情况下我甚至不想访问GUI线程,我只想检查邮件,如果方法发现了什么,它应该做什么,比如从任务中断并调用方法(不是来自任务)。

这个问题最干净的解决方案是什么?

2 个答案:

答案 0 :(得分:3)

您的线程问题是由您尝试在非UI线程上执行UI操作引起的。每当您调用此类UI内容时,您都可以使用Dispatcher.Invoke解决此问题

Sheets("Dinamicos").Range(Cells(45, W), Cells(46, W1))

所以在你的情况下,你有类似的东西

Application.Current.Dispatcher.Invoke(() => 
{
    // Your stuff here
});

答案 1 :(得分:1)

你对需要使用线程是正确的

虽然@Gareth关于是否需要使用调度程序来正确访问各个元素,但我实际上并没有在代码中看到任何线程,尽管错误消息清楚地证明您已经尝试了一些。

实现线程,你有各种选择

首先,您可以通过Tasks或较旧的Thread类直接执行此操作

这样就可以这样做了

private void CheckForRequestsAsync()=> Task.Run(()=>CheckForRequests());

这将立即创建并启动一个任务,该任务将在一个单独的线程中执行CheckForRequests,释放GUI以继续其在GUI上的工作,但这是一个非常基本的实现,可能需要进一步增强才能可靠地满足您的需求

另一种选择是利用.Net中的一些较新功能并使用async关键字

如果您将CheckForRequests声明为private async void CheckForRequests (object sender, EventArgs e),那么void将自动转换为一个任务,可以由事件处理程序触发,作为异步任务说明Timer

例如

  Timer timer = new Timer(5000);
  timer.Elapsed += CheckForRequests; //where CheckForRequests has the async keyword
  timer.Start();

将此与@Gareth建议的调度程序信息结合起来,这些信息会引发跨线程访问异常并且您应该准备好了

这看起来像这样:

MessageBoxResult result = Dispatcher.Invoke(() => 
    MessageBox.Show("Do you accept the request from " + mails[i][0].Split('_')[1], "Invitation", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
);

请注意,如果您使用async而不使用await关键字,那么您将收到线程可能在任何工作线程完成之前退出的警告,但这只是一个警告,就好像您不是t调用方法中的任何工作线程,或者在退出之前不需要它们完成,那么就没有任何损害

最后 其中一条评论建议使用DispatcherTimer而不是Timer,我不建议这样做,因为每次计时器滴答时,它都会在GUI线程中运行你的代码,就像你已经看到的那样锁定它,DispatcherTimer最好用于定时器严重改变GUI并快速运行

虽然如果你重新定义了代码,那么你可以通过从慢速运行的进程中分解gui元素来使用DispatcherTimer

dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += (s,e)=>{
   if( MessageBox.Show("Do you accept the request", "Invitation", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes) == MessageBoxResult.Yes)
   {
       Task.Run(()=>CheckForRequests());
   }
}
dispatcherTimer.Interval = new TimeSpan(0,0,1);
dispatcherTimer.Start();