如何使代码等待直到bool发出信号才能继续

时间:2014-01-28 19:14:15

标签: c# wpf multithreading winforms dll

我创建了一个WPF应用程序,然后通过删除app.xaml并将构建设置为类库将其转换为DLL。我正在使用C#Windows窗体来测试DLL。 WPF DLL已预先加载,因此可以稍后调用它以立即显示和显示,而无需等待加载。我想要完成的是调用Show(ShowWPFApp)并让代码等待,直到通过调用WPFAppResponse来翻转布尔值(此操作通过初始加载传入)。我现在拥有它的方式导致UI冻结。如何在没有用户界面冻结的情况下让它等待的任何想法?

Windows窗体调用WPF DLL

namespace WindowsFormsDLLTest
{
    public partial class Form1 : Form
    {
        WPFDLL.LoadWPFApp wpfApp = null;
        public Form1()
        {
            InitializeComponent();
        }

        private void btnLoadWPFApp_Click(object sender, EventArgs e)
        {
            wpfApp = new WPFDLL.LoadWPFApp();
            try
            {
                wpfApp.Load();
            }
            catch (Exception ex)
            {

            }
        }

        private void btnShowWPFApp_Click(object sender, EventArgs e)
        {
            try
            {
                string result = null;
                result = wpfApp.ShowWPFApp("John Doe");
            }
            catch (Exception ex)
            {

            }
        }
    }
}

WPF DLL应用程序

namespace WPFDLL
{
    public class LoadWPFApp
    {
        private Application application = null;
        private MainWindow mainWindow = null;
        private bool waitOnShowWindow {get; set;}
        private string returnResults = null;

        public void Load()
        {
            StartLoadingWPFApp();
        }

        [STAThread]
        private void StartLoadingWPFApp()
        {
            application = new Application();

            SplashScreenWindow splashWindow = new SplashScreenWindow();
            splashWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
            splashWindow.Show();

            try
            {
                mainWindow = new MainWindow(WPFAppResponse);
                mainWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                splashWindow.Close();

                application.Run();
            }
            catch (Exception ex)
            {
                splashWindow.Close();
                MessageBox.Show("Error starting application:" + Environment.NewLine + ex.ToString(), "WPF App Error", MessageBoxButton.OK, MessageBoxImage.Error);
                mainWindow = null;
            }
        }

        public string ShowWPFApp(string person)
        {
            returnResults = null;
            mainWindow.LoadPerson(person);
            mainWindow.Show();

            while(waitOnShowWindow)
            {
                //Code waits until bool is set to false
            }

            return returnResults;
        }

        public void WPFAppResponse(string person)
        {
            returnResults = person;
            waitOnShowWindow = false;
            mainWindow.Hide();
        }
    }
}

3 个答案:

答案 0 :(得分:1)

解决方法可能正在使用

await Task.Delay(1000);
在你的while循环中

。这可能会延迟while循环的每次运行,并且UI不会冻结。我不确定这是否适用于您的情况。试试让我知道。希望这会有所帮助。

答案 1 :(得分:1)

您需要将执行返回给UI线程的事件循环,以便UI不会冻结。在表单应用程序中,您可以按如下方式执行此操作:

while(waitOnShowWindow)
{
    //Code waits until bool is set to false
    System.Windows.Forms.Application.DoEvents();
}

编辑:

正如Eric指出的那样,使用DoEvents()时可能存在问题,所以不要随意使用它。请参阅How to use DoEvents() without being "evil"?

在像这样的测试应用中,它允许代码工作。但是,更好的解决方案是重新构建应用程序,以便在不需要调用时使用多线程(如果需要)。

答案 2 :(得分:1)

从Windows表单启动WPF应用程序非常麻烦。你偶然发现了一个相当复杂的线程问题。一般建议是将WPF应用程序创建为WPF控件库。但是,我发现这可能无法解决您的慢速加载问题,这就是您制作轻量级WinForms包装应用程序的原因。

问题在于你的循环:

while(waitOnShowWindow)
{
    //Code waits until bool is set to false
}

该循环正在UI线程上运行,阻止它处理Windows消息。 (如果这个概念对你来说是新的,那么请查阅它,因为它对于Windows UI的东西很重要。)为了响应UI,它必须运行Windows消息循环。我看到两个解决方案:

  1. 创建自己的消息循环。
  2. 立即返回,然后稍后获得结果。
  3. 解决方案1: 要创建自己的消息循环,您需要使用Dispatcher.Run()或Dispatcher.PushFrame()之类的东西。试试这个,看它是否有效:

    public string ShowWPFApp(string person)
    {
        returnResults = null;
        mainWindow.LoadPerson(person);
        mainWindow.Show();
        Dispatcher.CurrentDispatcher.Run();
        // do whatever to get the results
        return returnResults;
    }
    

    如果这不起作用,您可能需要使用PushFrame()。在Dispatcher.Run()不起作用的情况下,这里有一些关于该主题的更深入的文章。 http://www.codeproject.com/Articles/152137/DispatcherFrame-Look-in-Depth http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/

    解决方案2:

    public void ShowWPFApp(string person)
    {
        returnResults = null;
        mainWindow.LoadPerson(person);
        mainWindow.Show();
    }
    

    现在,调用不会阻止UI线程,并且会出现WPF窗口。 Windows窗体应用程序正在运行消息循环,因此WPF现在可以运行。但是你怎么得到结果!?由于它现在以异步方式运行,因此您必须找到一些其他方法来获取返回值。我认为MainWindow类上的事件是最简单的方法。

    另外:[STAThread]属性不做任何事情。 [STAThread]仅对应用程序的入口点有意义。幸运的是,您的Windows窗体应用程序已经将[STAThread]放在Main()方法上,因此您的线程是一个STA线程。