3

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

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

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

我尝试了线程和后台工作者但没有成功。然后为了确保不是我的窗口问题或我的静态类,我尝试在繁重的工作功能之一中使用 aForm.Show();和 a手动打开窗口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 中止线程,但现在我有一个更大的问题。程序永远不会停止。线程开始,然后我用一个特殊的线程异常捕获来调用中止,在中止时我调用窗口中的一个方法来保存它被打开的时间并关闭表单。我检查并创建了文件,因此中止确实有效,但如果 Dispatche.Run 在一个被调用的线程中被调用,它似乎是独立存在的。现在我创造了一个永不停息的怪物。我完全关闭了我的应用程序,视觉工作室仍然认为它正在运行。

我非常接近忘记 WPF 中的解决方案并使用 winform 表单来显示该消息,因为我知道它在 winforms 中完美运行而不会冻结烦恼。

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

我不能调用 dispatcher.Invoke 来测试它。我找不到它的组件。我的编译器和智能感知默认不喜欢它:)

4

2 回答 2

3

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

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

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

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

于 2013-10-25T12:07:21.287 回答
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 打开辅助窗口,并且繁重的工作不会出现问题。

于 2013-10-29T15:14:11.750 回答