简体   繁体   中英

How do i make the code wait until a bool signals it to continue

I created a WPF application and then converted it into a DLL by removing the app.xaml and setting the build to Class Library. I'm using a C# Windows Forms to test the DLL. The WPF DLL is preloaded so it can later on be called to show and display instantly without having to wait for a load. What I am trying to accomplish is to call the Show(ShowWPFApp) and have the code wait until a boolean is flipped by calling WPFAppResponse (this action is passed in via the initial load). The way I have it right now causes the UI to freeze up. Any idea on how i can get it to wait without the UI freezing up?

Windows Form calling 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 Application

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();
        }
    }
}

A work around could be using

await Task.Delay(1000);

inside your while loop. This might delay every run of your while loop and the UI will not freeze up. Am not sure if this would work for your case. Try and let me know. Hope this helps.

You will need to give execution back to the UI thread's event loop so that the UI doesn't freeze up. In a forms app, you can do this as follows:

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

Edit:

As Pointed out by Eric, there are potential problems with using DoEvents(), so don't go wild with it. See How to use DoEvents() without being "evil"? .

In a test app like this, it allows the code to work. However, a better solution would be to re-structure the application so that the call is unnecessary, using multi-threading if needed.

Launching a WPF app from Windows forms is messy. You have stumbled onto a rather complex threading problem. The general recommendation is to instead create that WPF application as a WPF control library. However, I see that this may not resolve the slow loading issue that you have, which is why you made the lightweight WinForms wrapper app.

The problem is your loop:

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

That loop is running on the UI thread, blocking it from processing windows messages. (If that concept is new to you, go look it up as it is important for Windows UI stuff.) For the UI to respond, it must be running a Windows message loop. I see two solutions:

  1. Create your own message loop.
  2. Return immediately, then get the result later.

Solution 1: To create your own message loop, you need to use something like Dispatcher.Run() or Dispatcher.PushFrame(). Try this and see if it works:

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

If that doesn't work, you might need to use PushFrame() instead. Here are some more in depth articles on that topic in case Dispatcher.Run() doesn't work. 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/

Solution 2:

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

Now, the call will not block the UI thread and the WPF window will appear. The windows forms application is running a message loop, so the WPF can now run. But how do you get the result!? Since it is now running asynchronously, you will have to find some other way to get the return value back. I think an event on the MainWindow class would be the easiest way.

Also: That [STAThread] attribute isn't doing anything. [STAThread] only has meaning on the entry point of the app. Fortunately, your Windows forms app already puts [STAThread] on the Main() method, so your thread is an STA thread.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM