英文:
How to know if ToolStripButton click was fired?
问题
在我的Windows Forms项目中,我在一个用户控件中有一个BindingNavigator控件(这是一个数据绑定场景)。按下其中一个按钮,First, Previous, Next或Last会引发异常,导致整个应用程序崩溃。为了解决这个问题,我编写了以下代码来处理异常,在用户控件中。
protected override void OnLoad(EventArgs e)
{
Application.ThreadException += Application_ThreadException;
base.OnLoad(e);
}
private void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
if (bindingNavigatorMovePreviousItem.Pressed)
{
MessageBox.Show(e.Exception.Message);
bsRestrictions.CancelEdit();
}
}
我如何知道引发的异常是由于ToolStripButton点击事件引起的?
上面的代码无法解决问题,因为在这个方法中,Pressed属性的值为false,所以消息框不会弹出。
以下是来自我的Program.cs文件的代码:
[STAThread]
static void Main()
{
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new AppForm());
}
有经验的开发者可能已经了解我正在尝试解决BindingNavigator的已知问题。这个控件简单地管理BindingSource组件。在使用强类型数据集的情况下,导航记录会引发异常,导致应用程序崩溃。
英文:
In my Windows Forms project I have a BindingNavigator control in a user control (It is a data-binding scenario). Pressing one of the buttons, First, Previous, Next or Last throws exception, crashing the application entirely. To tackle this I have written following code to handle the exception, in the user control.
protected override void OnLoad(EventArgs e)
{
Application.ThreadException += Application_ThreadException;
base.OnLoad(e);
}
private void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
if (bindingNavigatorMovePreviousItem.Pressed)
{
MessageBox.Show(e.Exception.Message);
bsRestrictions.CancelEdit();
}
}
How do I know if the exception thrown is the result of ToolStripButton click event?
The code above is helpless as Pressed property is false in the method, so the message box will not pop out.
Following is from my Program.cs file
[STAThread]
static void Main()
{
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new AppForm());
}
Experienced developers must have understood that I am trying to solve the well known issue with BindingNavigator. This control simply manages BindingSource component. In scenarios where strongly typed data sets are used, navigating through records throws exception crashing the application.
答案1
得分: 1
只调试您的应用程序。如果您知道异常抛出的位置,请在那里设置断点。当异常被抛出时,您可以返回到调用堆栈。在调用堆栈中,您应该能够看到异常来自哪里。根据您提供的代码,似乎很难提供更多帮助。
英文:
Just debug your application. If you have an idea where the exception is thrown, put a breakpoint there.
When the exception is thrown, you can go back in the call stack.
In the call stack, you should be able to see where the exception came from.
From the code that you are providing, seems hard to help more.
答案2
得分: 1
当单击BindingNavigator按钮时,异常不会从您的代码中引发,而是从框架类中引发,因此您无法设置断点。您需要避免异常,或者如果要在Application.ThreadException中处理它,需要检查堆栈跟踪。
1 - 如何在单击导航按钮或添加/删除/保存按钮时避免异常?
在窗体加载时禁用约束条件。然后在保存按钮单击时启用它们,处理异常(如果必要),然后再次禁用它们:
private async void Form1_Load(object sender, EventArgs e)
{
dataSet1.EnforceConstraints = false;
}
private void dataTable1BindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
try
{
dataSet1.EnforceConstraints = true;
}
catch (Exception ex)
{
//处理错误(如果需要)
}
dataSet1.EnforceConstraints = false;
}
还有其他避免异常的选项吗?
是的,作为避免错误的另一种选择,您可以只删除按钮的默认功能;例如,选择绑定导航器,在属性中找到AddNewItem,并选择其值为(none)
。然后双击按钮以处理其单击事件,并像这样处理它:
private void bindingNavigatorAddNewItem_Click(object sender, EventArgs e)
{
try
{
myBindingNavigator.BindingSource.AddNew();
}
catch (Exception)
{
//处理错误,例如显示消息
}
}
2 - 如何知道是否ToolStripButton参与了导致异常抛出的操作?
此异常不是从您的代码中引发的,而是从框架类中引发的。查看您在Application.ThreadException
中处理的异常的e.Exception.StackTrace
,您将看到包括类名和方法名在内的所有必要详细信息:
at System.Data.DataColumn.CheckNullable(DataRow row)
at System.Data.DataTable.RaiseRowChanging(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction, Boolean fireEvent)
at System.Data.DataTable.SetNewRecordWorker(DataRow row, Int32 proposedRecord, DataRowAction action, Boolean isInMerge, Boolean suppressEnsurePropertyChanged, Int32 position, Boolean fireEvent, Exception& deferredException)
at System.Data.DataTable.InsertRow(DataRow row, Int64 proposedID, Int32 pos, Boolean fireEvent)
at System.Data.DataView.FinishAddNew(Boolean success)
at System.Data.DataRowView.EndEdit()
at System.Windows.Forms.CurrencyManager.EndCurrentEdit()
at System.Windows.Forms.BindingSource.EndEdit()
at System.Windows.Forms.BindingSource.AddNew()
at System.Windows.Forms.BindingNavigator.OnAddNew(Object sender, EventArgs e)
at System.Windows.Forms.ToolStripItem.RaiseEvent(Object key, EventArgs e)
at System.Windows.Forms.ToolStripButton.OnClick(EventArgs e)
at System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e)
at System.Windows.Forms.ToolStripItem.HandleMouseUp(MouseEventArgs e)
at System.Windows.Forms.ToolStripItem.FireEventInteractive(EventArgs e, ToolStripItemEventType met)
at System.Windows.Forms.ToolStripItem.FireEvent(EventArgs e, ToolStripItemEventType met)
at System.Windows.Forms.ToolStrip.OnMouseUp(MouseEventArgs mea)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
at System.Windows.Forms.ToolStrip.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
您还可以自己获取堆栈帧:
var frames = new StackTrace(e.Exception, true).GetFrames();
然后可以在帧之间搜索,例如通过检查:
Where(x => x.GetMethod().DeclaringType == typeof(ToolStripButton))
英文:
When you click on BindingNavigator buttons, the exception is not thrown from your code, it's thrown in framework classes, so you cannot set a breakpoint. You need to avoid the exception, or if you want to handle it in Application.ThreadException, you need to check stack trace.
This post answers two questions:
- How to avoid exception when clicking on navigation buttons or add/remove/save buttons?
- How to know if ToolStripButton was involved in the actions which resulted in throwing an exception?
1 - How to avoid exception when clicking on navigation buttons or add/remove/save buttons?
Disable constraints in the form load. Then in the Save button click, enable them, handle the exceptions (if necessary) and disable them again:
private async void Form1_Load(object sender, EventArgs e)
{
dataSet1.EnforceConstraints = false;
}
private void dataTable1BindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
try
{
dataSet1.EnforceConstraints = true;
}
catch (Exception ex)
{
//process errors if necessary
}
dataSet1.EnforceConstraints = false;
}
Any other option to avoid the exception?
Yes, as another option to avoid the error, you can just remove the default functionalities from buttons; for example select the binding navigator, and in properties find the AddNewItem and select (none)
as its value. Then double click on the button to handle its click event and handle it like this:
private void bindingNavigatorAddNewItem_Click(object sender, EventArgs e)
{
try
{
myBindingNavigator.BindingSource.AddNew();
}
catch (Exception)
{
//process the error, for example show a message
}
}
2 - How to know if ToolStripButton was involved in the actions which resulted in throwing an exception?
This exception is not thrown from your code, but thrown in framework classes. Looking into the e.Exception.StackTrace
of the exception that you have handled in Application.ThreadException
, you see all the necessary details including class names and method names:
at System.Data.DataColumn.CheckNullable(DataRow row)
at System.Data.DataTable.RaiseRowChanging(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction, Boolean fireEvent)
at System.Data.DataTable.SetNewRecordWorker(DataRow row, Int32 proposedRecord, DataRowAction action, Boolean isInMerge, Boolean suppressEnsurePropertyChanged, Int32 position, Boolean fireEvent, Exception& deferredException)
at System.Data.DataTable.InsertRow(DataRow row, Int64 proposedID, Int32 pos, Boolean fireEvent)
at System.Data.DataView.FinishAddNew(Boolean success)
at System.Data.DataRowView.EndEdit()
at System.Windows.Forms.CurrencyManager.EndCurrentEdit()
at System.Windows.Forms.BindingSource.EndEdit()
at System.Windows.Forms.BindingSource.AddNew()
at System.Windows.Forms.BindingNavigator.OnAddNew(Object sender, EventArgs e)
at System.Windows.Forms.ToolStripItem.RaiseEvent(Object key, EventArgs e)
at System.Windows.Forms.ToolStripButton.OnClick(EventArgs e)
at System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e)
at System.Windows.Forms.ToolStripItem.HandleMouseUp(MouseEventArgs e)
at System.Windows.Forms.ToolStripItem.FireEventInteractive(EventArgs e, ToolStripItemEventType met)
at System.Windows.Forms.ToolStripItem.FireEvent(EventArgs e, ToolStripItemEventType met)
at System.Windows.Forms.ToolStrip.OnMouseUp(MouseEventArgs mea)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
at System.Windows.Forms.ToolStrip.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
You can also get the stack frames yourself:
var frames = new StackTrace(e.Exception, true).GetFrames();
And then you can search between the frames, for example by checking:
Where(x => x.GetMethod().DeclaringType == typeof(ToolStripButton))
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论