如何创建应用程序全局进度窗口而不阻止正在运行的线程?

时间:2013-10-25 12:00:10

标签: c# wpf

我们有一个项目有几年的开发投入,现在需要一个进度窗口,因为一些过程很长。我们处理3D CAD,高级工程,模拟第三方DLL,Web服务,WCF服务。

现在我正在尝试添加一个通用流程窗口,您可以在一个地方开始并在其他任何地方结束。这说起来并不复杂。在MyClass.Open(string Message)打开一个窗口的简单静态类和一个MyClass.Close(),以便在重载代码完成后隐藏它。除了繁重的代码阻塞线程外,这种方法非常有效。

该项目是一系列项目的混合,但这部分是99%的WPF,我对WPF线程相当新,因为它们的行为不像winforms。在winforms线程中我创建开放的其他表单不受主应用程序繁重的工作阻止。在WPF中,表单被冻结的复制粘贴代码。

我尝试过Thread and Background Worker但没有成功。然后为了确保它不是我的窗口问题或我的静态类我尝试在一个繁重的工作函数中使用Form.Show();Form.Close();手动打开窗口,表单仍显示并关闭适当但仍然冷冻。然后我删除了Form.Close();并在工作完成后立即开始移动。现在我发现非常奇怪的是,当满负荷工作时主线程冻结其他线程。我在winform中为该进度窗口提供了一个实际的精确副本,除了它是一个带有标签的表单我有一个带有标签的WPF窗口。

我尝试以正确的方式进行操作,即为重度函数创建一个线程,但编译器现在给出了2,404个错误。我会至少有一个星期的工作只是尝试一些工作,我宁愿不改变整个项目,所以至少需要一年半的时间,我们大多有2-3周完成这个,所以我寻找或任何可能有助于完成它的解决方案。我可以很脏。只要它有效。

非常感谢你。

编辑: @Baldrick要求提供有关线程的更多细节。

我对线程非常简单。

public static class CWaitingMessage
{
    private static frmWaiting window = new frmWaiting();
    private static Thread t = null;
    private static string Message = "";

    public static void Open(string sMessage)
    {
        Message = sMessage;

        t = new Thread(new ThreadStart(RunForm));
        t.SetApartmentState(ApartmentState.STA);
        t.IsBackground = true;
        t.Start();
    }     

    private static void RunForm()
    {
        try
        {
            window = new frmWaiting();
            window.UpdateText(Message);
            window.Show();
            System.Windows.Threading.Dispatcher.Run();
        }
        catch
        {
            window.Close();
        }
    }

    public static void Close()
    {
        if (t != null)
        {
            t.Abort("Completed");
        }
    }      
}

如果我使用System.Windows.Threading.Dispatcher.Run();代替while (ShowForm) { },表单不会被冻结,但我的帖子会消失,我的意思是它永远不会到达f.Close(); ...永远。

编辑#2: 管理使用dispatcher.Run中止线程,但现在我有一个更大的问题。该计划永不停止。线程开始,然后我用一个特殊的catch线程异常调用abort,在中止我调用窗口中的方法来节省它打开的时间并关闭表单。我检查并创建了文件,因此中止确实有效,但如果Dispatche.Run在一个被编码的线程中调用,则它似乎独立存在。现在我创造了一个永不停止运行的怪物。我完全关闭我的应用程序,视觉工作室仍然看到它正在运行。

我非常接近忘记WPF中的一个解决方案,然后选择一个winform表单来显示该消息,因为我知道它在winforms中完美运行而没有那种冻结烦恼。

编辑#3:更新了我当前使用的课程。我检查了我的旧exampled和后台工作者我只是正常表单加载然后运行后台工作程序循环。表单与主代码在同一个线程中,因为后台工作程序是MTA并因此阻止UI线程,这是完全正常的。对于UI我需要线程是STA。

我无法调用dispatcher.Invoke来测试它。我找不到它的装配。我的编译器和intellisense默认不喜欢它:)

2 个答案:

答案 0 :(得分:3)

找到它。经过3天的工作。

对于想知道如何在WPF中打开和关闭线程而不冻结主线程的人,当新表单有动画并且没有进程工作时。

代码:

public static class CWaitingMessage
{
    private static event Action CloseWindow = delegate { };

    public static void Open(string sMessage)
    {
        Thread t = new Thread(delegate()
               {
                   frmWaiting window = new frmWaiting(sMessage);
                   CloseWindow += () => window.Dispatcher.BeginInvoke(new ThreadStart(() => window.Close()));
                   window.Closed += (sender2, e2) => Window.Dispatcher.InvokeShutdown();
                   window.Show();
                   System.Windows.Threading.Dispatcher.Run();
               });

        t.SetApartmentState(ApartmentState.STA);
        t.Start();
    }   

    public static void Close()
    {
        CloseWindow();
    }      
}

请注意,不支持错误处理和多窗口打开。这很简单,骨架工作和准备使用。我知道至少有两个人会对这段代码非常感兴趣。

如果您在该窗口上也需要进程,则后台工作人员可以在窗口中完美运行。这对于鼠标悬停在异步填充的网格上是非常有用的,当你将鼠标悬停在你身上时,你仍然可以使用另一个DataGrid打开辅助窗口而且没有问题。

答案 1 :(得分:2)

现在Thread类的使用并不多,因为现在有很多简单的方法来创建异步方法。例如,请查看MSDN上的Task Class页面,了解如何在WPF中轻松创建异步方法的示例。

如果您使用的是.NET 4.5,则还可以使用新的asyncawait关键字来实现更简单的异步方法。有关此新功能的详细说明,请参阅MSDN上的await (C# Reference)页面。

话虽如此,使用BackgroundWorker实际上可能是实现ProgressBar更新功能的最佳选择,因为它可以轻松访问UI线程上的UI。有关完整示例,请查看MSDN上的BackgroundWorker Class页面。

如果您需要特定的帮助,请编辑您的问题以添加相关代码。