我创建了一个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();
}
}
}
答案 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: 要创建自己的消息循环,您需要使用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线程。