WPF -为什么在不同的工作线程中创建和显示窗口时会出现InvalidOperationException?[副本]

h6my8fg2  于 2023-10-22  发布在  其他
关注(0)|答案(1)|浏览(135)

此问题已在此处有答案

The calling thread cannot access this object because a different thread owns it(15个回答)
上个月关门了。
在我开始之前,我需要先声明这些代码和机制是一个巨大软件产品中的遗留代码,该软件产品处于从WinForms到WPF的完整迁移的最后阶段。代码和实践是过时的,但是,由于资源和进度的限制,有必要使用这些过时的实践,并使它们以某种方式工作。
在我们的WPF应用程序中,我有一个“action”类对象,它有一个 Execute() 方法,每次都在一个单独的线程上调用。这个特殊的“action”对象只是根据对象的 IsModal 属性的值显示一个模态或非模态窗口。这个应用程序代码是我从一个遗留的WinForms应用程序中迁移过来的。
我的问题是,第一次创建窗口时,当创建窗口后调用Show()或ShowDialog()方法时,会抛出经典的“调用线程无法访问此对象,因为另一个线程拥有它”异常。我甚至在窗口关闭后添加了一个GC.Collect()调用,
下面是Execute()方法的代码:

/// <summary>
        /// Executes the sequence.  Shows the given string in to the user.
        /// </summary>
        /// <returns></returns>
        public override bool Execute()
        {
            bool bActionResult = true;

            string sMessageString;
            if (!GetStringFromParm("CmiActionMessageBox", m_StringToShow, out sMessageString))
            {
                return false;
            }

            if (this.ConvertEscapeSequences)
            {
                sMessageString = this.GetStringConvertEscapeSequences(sMessageString);
            }

            #region Show Message Box
            // Create the form
            UserPromptWindow frm = new UserPromptWindow(sMessageString);
            frm.SetOKButtonText(m_sOkButtonName);
            frm.SetCancelButtonText(m_sCancelButtonName);
            frm.WindowStartupLocation = WindowStartupLocation.CenterOwner;

            #region Adjust Dialog based upon action properties
            bool? dr;

            if (m_bDialogIsModal)
            {
                dr = frm.ShowDialog();
            }
            else
            {
                frm.Show();

                // Wait until it goes away
                while (frm.ModelessResult == null)
                {
                    Thread.Sleep(50);

                    System.Windows.Forms.Application.DoEvents();

                    if (SequenceMgr.Mgr.BgSequenceDone)
                    {
                        ModelessDialogResult = false;

                        // Close the window, if it is still open
                        if (frm.IsLoaded)
                        {
                            frm.Close();
                        }

                        break;
                    }
                }

                dr = ModelessDialogResult;
            }

            // Make sure the window goes completely away
            GC.Collect();
            #endregion

            #region Save the result to variable (if needful)
            if (m_bDialogSavesResult)
            {
                if (dr == true)
                {
                    SequenceMgr.Mgr.SetVariableValue(m_sDialogResultVar, "True");
                }
                else
                {
                    SequenceMgr.Mgr.SetVariableValue(m_sDialogResultVar, "False");
                }
            }
            #endregion

            #region Set the action result to false if configured to stop the sequence upon hitting the cancel button
            if (m_bDialogCanStopSequence)
            {
                if (dr == false)
                {
                    bActionResult = false;
                }
            }
            #endregion

            return bActionResult;
        }

我试图生成另一个线程来创建和显示窗口,但同样的事情发生了。

xvw2m8pv

xvw2m8pv1#

首先,感谢那些提供反馈的人。
经过多次尝试,例如。创建并在任务中显示用户提示窗口,我发现使其工作的方法是在应用程序调度器上下文中运行代码。同样,使用线程的整个解决方案是过时的,将来某个时候将更新为使用Tasks,但目前有效的解决方案使用 Application.Current.Dispatcher. Dispatcher()System.Windows.Application.Current. Dispatcher. Dispatcher(DispatcherPriority.Background,new Action(delegate { }))的组合来确保窗口在UI线程中创建和显示; 允许窗口的UI在等待用户关闭窗口时运行,或者确定用户已经取消(停止)当前正在执行的操作序列。
下面是生成的代码:

/// <summary>
    /// Executes the sequence.  Shows the given string in to the user.
    /// </summary>
    /// <returns></returns>
    public override bool Execute()
    {
        bool bActionResult = true;

        string sMessageString;
        if (!GetStringFromParm("CmiActionMessageBox", m_StringToShow, out sMessageString))
        {
            return false;
        }

        if (this.ConvertEscapeSequences)
        {
            sMessageString = this.GetStringConvertEscapeSequences(sMessageString);
        }

        bool? dr = ShowMessageBox(sMessageString);

        if (m_bDialogSavesResult)
        {
            if (dr == true)
            {
                SequenceMgr.Mgr.SetVariableValue(m_sDialogResultVar, "True");
            }
            else
            {
                SequenceMgr.Mgr.SetVariableValue(m_sDialogResultVar, "False");
            }
        }

        if (m_bDialogCanStopSequence)
        {
            if (dr == false)
            {
                bActionResult = false;
            }
        }

        return bActionResult;
    }

    /// <summary>
    /// Shows the message box
    /// </summary>
    /// <param name="sMessageString"></param>
    /// <returns></returns>
    private bool? ShowMessageBox(string sMessageString)
    {
        bool? dr = null;

        System.Windows.Application.Current.Dispatcher.Invoke(new Action(() =>
        {
            UserPromptWindow userPromptWindow = new UserPromptWindow(sMessageString);
            userPromptWindow.SetOKButtonText(m_sOkButtonName);
            userPromptWindow.SetCancelButtonText(m_sCancelButtonName);
            userPromptWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;

            if (m_bDialogIsModal)
            {
                dr = userPromptWindow.ShowDialog();
            }
            else
            {
                try
                {
                    userPromptWindow.Show();

                    // Wait until it goes away
                    while (userPromptWindow.ModelessResult == null)
                    {
                        // Let the UI events run
                        GlobalMethods.WpfApplicationsDoEvents();

                        // Did the user press the stop button?
                        if (SequenceMgr.Mgr.BgSequenceDone)
                        {
                            userPromptWindow.ModelessResult = false;

                            // Close the window, if it is still open
                            if (userPromptWindow.IsLoaded)
                            {
                                userPromptWindow.Close();
                            }

                            break;
                        }
                    }
                }
                catch (Exception e)
                {
                    SystemEventsMgr.AddException("Exception in MessageBox sequence action", e);
                    userPromptWindow.ModelessResult = false;
                }

                dr = userPromptWindow.ModelessResult;
            }
        }));

        return dr;
    }

相关问题