【问题标题】:How close window that opened in a thread in wpf?如何关闭在 wpf 中的线程中打开的窗口?
【发布时间】:2017-04-08 01:19:31
【问题描述】:

按照规范,我构建了一个监视某些资源的应用程序。现在我有一个主窗口,当事件发生时会发出警报,说明规范必须是一个颜色鲜艳的大窗口。 我对问题的解决方法如下

 private void StatusChanges(Alarm m, EventArgs e)
        {

            //Compares two 32-bit signed integers for equality and, if they are equal, replaces one of the values.
                if (Interlocked.CompareExchange(ref running, 1, 0) == 0)
                {
                    newWindowThread = new Thread(new ThreadStart(() =>
                                                                     {

                        try
                        {
                            // Create and show the Window
                            Alert tempWindow = new Alert();

                            tempWindow.Show();
                            //if (cancelRun)
                            //    tempWindow.Close();

                            // Start the Dispatcher Processing
                            System.Windows.Threading.Dispatcher.Run();



                        }
                        catch (Exception)
                        {

                            throw;
                        }
                        finally
                        {
                            running = 0;

                        }

                    }));

                    // Set the apartment state
                    newWindowThread.SetApartmentState(ApartmentState.STA);

                    // Make the thread a background thread
                    newWindowThread.IsBackground = true;

                    newWindowThread.Start();

                }


            }
            else
            {


                if (Interlocked.CompareExchange(ref running, 0, 1) == 1)
                {
                    try
                    {
                        System.Windows.Threading.Dispatcher.FromThread(newWindowThread).InvokeShutdown();
                        Alert tempWindow = new Alert();
                        tempWindow.Close();
                    }
                    catch (Exception)
                    {

                        //throw;
                        MessageBox.Show("Todo esta ok");
                    }

                }

                //running = 0;
            }
        }

此方法由 Timer 每隔一段时间执行一次

我的目标是当警报结束时,移除警报窗口的过程,我使用了Thread.Abort();没用,有时程序抛出异常并显示没有Visual Studio in aplition发生在外面,尝试使用threa.Join ();但它没有用,直到

System.Windows.Threading.Dispatcher.FromThread use (newWindowThread) InvokeShutdown ().; 

这至少停止了窗口的显示,但是当我创建时

Alert tempWindow = new Alert(); 

在初始化构造函数中

SoundPlayer player = new Sound ("../../Sounds/BOMB_SIREN-BOMB_SIREN-247265934.wav");
  player.PlayLooping();

当我关闭窗口时

player.Stop();

现在如果没有,我会做下一个

 Alert tempWindow = new Alert();
                        tempWindow.Close();

声音继续播放

我知道这不是最好的解决方案,但它确实有效。 我想给我您的意见,并就如何改进代码给我一些建议。

【问题讨论】:

  • 不要创建多个 UI 线程。你这样做是把自己置身于一个受伤的世界。通过这样做来避免整个情况。只有一个 UI 线程负责操作整个 UI。
  • @Servy 但是我如何在没有主窗口的情况下显示另一个窗口失去焦点
  • 如果这是你的问题那么问那个问题,而不是所有这些。也就是说,如果您只想了解如何将焦点设置到给定窗口,您应该能够通过一些简单的研究找到该问题的答案。
  • @Servy 真的我的问题是我有一个运行我的主代码的窗口并且想要显示另一个窗口而没有主窗口的代码停止运行,即使我想在第二个窗口中执行操作不停止在主窗口中运行代码
  • 您不应该在 UI 线程中运行 any 非 UI 代码。如果您有非 UI CPU 绑定的工作要做,那么您应该创建一个新线程并在那里完成工作,而不是在第二个 UI 线程中长时间运行 CPU 绑定工作时创建一个新的 UI 线程来显示 UI。使用 UI 线程更新 UI,使用非 UI 线程进行非 UI 工作。

标签: c# wpf multithreading


【解决方案1】:

快速查看您的代码,您正在做的是关闭临时代码。窗口错误。当您想要关闭现有窗口时,您正在创建一个全新的窗口。 这应该更新,以便保存现有的临时窗口,以便以后可以关闭相同的实例。

我还更新了临时窗口的关闭,以便它在关闭后关闭线程。

我更新代码如下:

Alert _tempWindow;

private void StatusChanges(Alarm m, EventArgs e)
{
    //Compares two 32-bit signed integers for equality and, if they are equal, replaces one of the values.
        if (Interlocked.CompareExchange(ref running, 1, 0) == 0)
        {
            newWindowThread = new Thread(new ThreadStart(() =>
            {
                try
                {   
                    // Create and show the Window
                    _tempWindow = new Alert();
                    _tempWindow.Close += OnTempClosed;
                    _tempWindow.Show();

                    System.Windows.Threading.Dispatcher.Run();
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    running = 0;
                }

            }));

            // Set the apartment state
            newWindowThread.SetApartmentState(ApartmentState.STA);
            // Make the thread a background thread
            newWindowThread.IsBackground = true;
            newWindowThread.Start();
        }
    }
    else
    {
        if (Interlocked.CompareExchange(ref running, 0, 1) == 1)
        {
            try
            {
                _tempWindow.Dispatcher.BeginInvoke((Action)_tempWindow.Close);
            }
            catch (Exception)
            {
                //throw;
                MessageBox.Show("Todo esta ok");
            }
        }
        //running = 0;
    }
}

private void OnTempClosed(object sender, EventArgs e)
{
    System.Windows.Threading.Dispatcher.FromThread(newWindowThread).InvokeShutdown();
}

【讨论】:

  • 第二个 UI 线程根本不应该使用。这不是程序的正确设计。
  • @Servy,那么你怎么能运行,比如说,一个动画自定义启动画面?
  • @MikeSpike 与您做任何其他事情的方式相同。您在一个 UI 线程中完成 UI 工作,而您在非 UI 线程中完成非 UI 工作。
  • @Servy 有一种情况,在后台工作之后,还必须完成一些 UI 初始化。也许,有一个更优雅的解决方案,但是有 2 个 UI 线程帮助我在显示动画启动屏幕时进行主窗口初始化(它作为一个单独的窗口实现)
【解决方案2】:

这个想法是只在一个线程中使用 UI。因此,与其创建一个全新的调度程序,不如简单地将新的窗口逻辑调度到现有的 UI 线程(毕竟,定时器不是已经完全由 UI 线程结束了吗?)。

为什么要在新线程中启动新窗口?你希望通过它实现什么?

您的关闭代码没有意义:

Dispatcher.FromThread(newWindowThread).InvokeShutdown();
Alert tempWindow = new Alert();
tempWindow.Close();

让我们来看看这个。首先,关闭第二个 UI 线程的调度程序。这意味着所有将流向窗口的消息 - 不要。

然后,您在原始 UI 线程上创建一个 new 窗口,然后关闭它。那应该完成什么?您要关闭之前打开的窗口,而不是新窗口!

实际上,您需要保留对您打开的原始窗口的引用 - 对其线程的引用并不重要。现在,如果您真的想继续使用双 UI 线程方法,您可以这样做:

alertWindow.Dispatcher.Invoke(alertWindow.Close);

这将告诉(已经打开的)警报窗口上的消息循环尽快关闭窗口。

除此之外,您的代码有太多问题了。一方面,您使用的操作级别太低(Interlocked.CompareExchange 无法保持警报/没有警报逻辑?)。二,你的 cmets 完全没用 - 你只是在说下一行的作用,字面意思 - 这没有帮助。而不是

// Compares two 32-bit signed integers for equality and, 
// if they are equal, replaces one of the values.

您想使用描述您想要达到的目标的内容,而不是如何您正在做的事情:

// If we're the first ones to attempt to change the running flag,
// we'll create a new alert window.

请注意,该评论也很好地表明您正在做一些非常奇怪的事情。

三,您不必要地创建线程(更重要的是,UI 线程 - 除了非常特殊的情况外,实际上应该只有一个)。没有任何理由在新线程中创建新窗口。你为什么要这么做?

第四,您的异常处理也不是很有帮助。一方面,如果您所做的只是重新抛出异常,则可以省略 catch 子句 - 您可以单独使用 try ... finally。更重要的是,异常将传播到新线程,而不是旧线程 - 这很可能会导致您的应用程序完全崩溃。

【讨论】:

  • 如果您要向问题作者提出澄清问题,您应该将它们作为对问题的评论发布,而不是作为对问题的回答。
  • @Luaan 关于评论你是对的,我把visual studio的帮助评论放在了这个方法上,因为它引起了怀疑,你是对的尝试......最后,但我只放了引起我怀疑的代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多