在从另一个线程抛出异常后创建一个模态错误框。 [英] Creating a modal error box after Exception thrown from another Thread.

查看:80
本文介绍了在从另一个线程抛出异常后创建一个模态错误框。的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

大家好,



我用C#编写了一个使用WPF(利用MVVM设计模式)和LINQ to CRM从数据库中检索数据的应用程序。



我遇到线程问题似乎......



此应用程序使用BackgroundWorker运行数据库查询。一旦它检索到这些信息,它就会在内存中操作它并创建一个Structs列表。在处理此数据的同时,我定期更新UI并显示加载动画。



到目前为止一切正常。



但是,当部署此应用程序时,此操作可能会失败,在这种情况下,我想捕获此错误并让我的程序正常失败。我通过将数据处理封装在'try-catch'块中来处理这种可能性。如果发生错误,例如数据库超时,我清除所有临时变量,包括我的UI控件绑定的数据源,隐藏我的加载动画并返回到初始屏幕。然后程序处于首次初始化时应该处于的状态。



要提醒用户注意此事件,我会显示一个MessageBox,告诉他们发生了什么。



这就是问题..



这个MessageBox不是模态的,它仍然允许用户与主窗口进行交互。这可能会导致问题,因为用户可能会在选择确定之前尝试重新启动此操作,以确认错误,清除临时变量并重置加载动画。



这看起来似乎微不足道,但由于这是最终用户应用程序,因此需要进行优化。



我认为问题是什么......



MessageBox正在STA线程之外的一个线程上创建(我理解为执行的主线程,如果我错了就纠正我),因此,你可以最小化它并仍然与其他窗口交互,因为它不是阻止主线程的执行。



我已经尝试过的事情......



  • try catch块在 background.DoWork + = delegate {...} 块内,所以不是在该委托的catch块内创建消息框,而是抛出一个自定义事件,天真地认为在该委托的范围之外它将返回到STA线程并创建一个线程阻塞模式。没有骰子。



  • 在这个事件处理程序中,我创建了一个新线程,并将它的公寓状态设置为STA。

    myThread.SetApartmentState(ApartmentState.STA); 这是一个坏主意但没有用,但这是一个很长的镜头。



    这是我的代码的基本结构:

    Hi everyone,

    I've written an application in C# that uses WPF (utilising the MVVM design pattern) and LINQ to CRM to retrieve data from a database.

    I have a problem with threading it seems...

    This application uses the BackgroundWorker to run a database query. Once it retrieves this information it manipulates it in memory and creates a list of 'Structs'. Whilst it is processing this data I update the UI at regular intervals and show a loading animation.

    So far everything works fine.

    When this application is deployed however, there is a possibility that this operation could fail and in this case I want to capture this error and let my program fail gracefully. I handle this possibility by encapsulating the data processing in a 'try-catch' block. If an error occurs such as a database time out, I clear all temporary variables, including the data sources that my UI controls are bound to, hide my loading animation and return to the initial screen. The program is then in the state it should be when first initialised.

    To alert the user to this event I show a MessageBox which tells them what has happened.

    Here lies the problem..

    This MessageBox is not modal, it still allows the user to interact with the main window. This could cause an issue, as a user could try to restart this operation before they have selected OK, to acknowledge the error, clearing the temporary variables and resetting the loading animation.

    This might seem trivial but as this is an end-user application it needs to be polished.

    What I think the issue is...

    The MessageBox is being created on a thread other then the STA thread (which I understand to be the main thread of execution, correct me if I'm wrong) therefore, you are able to minimise it and still interact with the other window as it is not blocking the execution of the main thread.

    Things I have already tried...

  • The try catch block is inside the background.DoWork += delegate {...} block, so instead of creating the message box inside the catch block of that delegate, I throw a custom event, naively thinking that outside the scope of that delegate it would return to the STA thread and create a thread blocking modal. No dice.

  • In this event handler I create a new thread and set it's apartment state to STA.
    myThread.SetApartmentState(ApartmentState.STA); This was a bad idea and didn't work but it was a very long shot.

    Here is the basic structure of my code :

    BackgroundWorker background = new BackgroundWorker();
    								background.RunWorkerCompleted += new 
    
    WorkerFailed += new workerFailedEvent(GetDD_Command_WorkerFailed);	
    			
    			background.DoWork += delegate
    			{
    			try
    			{
    			 // Do very long operation
    			}
    			catch (Exception e)
    			{	
    				// Something went wrong, call event						
    				WorkerFailed(e, new EventArgs()); 
    			}	
    		};
    		background.RunWorkerAsync();	
    	}
    
    	void myProgram_Command_WorkerFailed(Exception ex, EventArgs e)
    	{						
    		Exception exception = (Exception)ex; 
    		// Creates Message box
    		System.Windows.Forms.MessageBox.Show
    			(
    			exception.InnerException == null ? exception.Message.ToString() : exception.InnerException.ToString(),
    			"Error",
    			MessageBoxButtons.OK,
    			MessageBoxIcon.Error
    			);
    			// Also clears cache and clears temp variables and hides loading animation
    		}

  • 感谢您提供的任何帮助或建议! :)



    Larry

    Thanks for any help or advice anyone gives! :)

    Larry

    推荐答案

    要从UI线程显示消息框,只需使用 BeginInvoke



    To display your message box from the UI thread, just use BeginInvoke:

    yourMainForm.BeginInvoke((Action)(() =>
    {
        //all the code here will be executed in the main thread
        MessageBox.Show("...");
    }));





    其中 yourMainForm 是对您的主表单的引用。实际上它可以是对主线程上创建的任何表单/控件的引用。



    BeginInvoke 将创建一个异步调用(该函数不会等待调用完成并将返回而不会阻塞调用线程)。如果需要同步调用,请使用调用(但要注意死锁问题)。



    Where yourMainForm is a reference to your main form. Actually it can be a reference to any form/control created on the main thread.

    BeginInvoke will make an asynchronous call (the function will not wait that the call is finished and will return without blocking the calling thread). If you need a synchronous call, use Invoke (but be careful to deadlock issues).


    请参阅我对解决方案的评论Olivier。



    这是一个解决方案:拥有的共享实例System.Threading.EvenWaitHandle 并重置make在处理异常之前。在线程处理异常中,使用Dispatcher.BeginInvoke并立即调用 EvenWaitHandle.Wait 。该线程将由OS进入等待状态,并且在唤醒之前不会花费任何CPU时间。调用的代码将在UI线程中运行。在MessageBox完成其模态状态(顺便说一句,它是真正的模态,请参阅我的注释)后,在同一个实例上调用 EvenWaitHandle.Set 。它会唤醒等待的线程。需要将一些数据从 MessageBox 传递给线程吗?添加一些共享数据,使用 lock 保护对它的访问。



    小心这一切。



    -SA
    Please see my comments to the solution by Olivier.

    Here is one resolution: have a shared instance of System.Threading.EvenWaitHandle and make is reset before handling exception. In the thread handling exception, use Dispatcher.BeginInvoke and immediately call EvenWaitHandle.Wait. This thread will go to the wait state by OS and will not spend any CPU time until awaken. The invoked code will run in the UI thread. After MessageBox has finished its modal state (it is truly modal, by the way, see my comment) call EvenWaitHandle.Set on the same instance. It will wake up the waiting thread. Need to pass some data from the MessageBox to the thread? Add some shared data, protect the access to it with lock.

    Be careful with all that.

    —SA


    DispatcherSynchronizationContext类 [ ^ ]为Windows Presentation Foundation(WPF)提供同步上下文。



    你会在这里找到一个例子:任务并行库:n中的1个 [ ^ ]



    问候

    Espen Harlinn
    The DispatcherSynchronizationContext Class[^] provides a synchronization context for Windows Presentation Foundation (WPF).

    You'll find an example here: Task Parallel Library: 1 of n[^]

    Regards
    Espen Harlinn


    这篇关于在从另一个线程抛出异常后创建一个模态错误框。的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆