We are trying to redirect focus from an ancestor control to a child using GettingFocus but we are seeing ArgumentExceptions getting thrown on some cases.
The cases I've noticed so far:
NewFocusedElement has ActualHeight or ActualWidth of 0
NewFocusedElement can't get focus
Is there anyway to guard args.NewFocusedElement = element; ?
We are catching exceptions as a temporary solution.
Here is the code:
private void OnGettingFocus(UIElement sender, GettingFocusEventArgs args)
{
if ((args.Direction == FocusNavigationDirection.Up || args.Direction == FocusNavigationDirection.Down)
&& (sender == this) && (DefaultFocusButton != null))
// Add extra guard here so that the try / catch is no longer needed
{
try
{
args.NewFocusedElement = DefaultFocusButton;
}
catch (ArgumentException e)
{
// This focus redirect causes an exception when the element can't receive focus
}
}
}
Related
My Xamarin Forms application freezes whenever any exception occurs. I have created an exception handler as well to handle the exceptions but whenever I face an exception from the API end it automatically crashes the application. This only happens in Android and not in iOS.
Here's the code for the exception handler -
public async Task<bool> HandleExceptionAsync(Exception exception)
{
if (exception.GetType().Name == nameof(UnauthorizedException) ||
exception.Message.ToLowerInvariant().Contains("refresh token has expired") ||
exception.Message.ToLowerInvariant().Contains("invalid refresh token"))
{
//await loginFacade.LogoutAsync(System.Threading.CancellationToken.None);
}
else if (exception.GetType().Name == nameof(NoInternetConnectionException))
{
HandleNoInternetException();
}
else if (CheckIfLoggable(exception))
{
Log.Error(exception, string.Format(Common.Constants.LoggingFormats.ExceptionWithUserDetails, _userContext.Serialize(), exception));
}
else
{
#if !DEBUG
try
{
//await emailManager.SendFeedbackEmail(exception);
}
catch (Exception ex)
{
}
#endif
}
return true;
}
private bool CheckIfLoggable(Exception exception)
{
return exception.Message.ToLowerInvariant().Contains("not found".ToLowerInvariant()) ||
exception.Message.ToLowerInvariant().Contains("BadInternetConnectionException".ToLowerInvariant()) ||
exception.GetType() == typeof(NullReferenceException) ||
exception.GetType() == typeof(OperationCanceledException);
}
private void HandleNoInternetException()
{
if (Xamarin.Essentials.Connectivity.NetworkAccess != Xamarin.Essentials.NetworkAccess.Internet)
{
}
Xamarin.Essentials.Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
}
private void Connectivity_ConnectivityChanged(object sender, Xamarin.Essentials.ConnectivityChangedEventArgs e)
{
if (Xamarin.Essentials.Connectivity.NetworkAccess != Xamarin.Essentials.NetworkAccess.Internet)
{
}
else
{
}
}
I have tried logging it and then removed the buggy codes which caused the exception.
It is not possible to recover from exceptions that happen during UI drawing or events, using a global exception handler.
Use try-catch in every UI class, both in constructor, and in all event handlers (e.g. a button handler).
IMHO it’s a major PITA of Xamarin. But that is the way it is.
Even then, low level exceptions inside Xamarin code or graphics code can
crash app.
Isolate the problem manually. Print statements, breakpoints, commenting out different code. All are sometimes necessary debugging.
It appears that exceptions that occur in a property's Set method do not bubble up to the Application's ThreadException event.
We use that event along with the AppDomain.CurrentDomain.UnhandledException event to catch any unexpected mishaps that occur in the application. The exception details are written to a log so our support and development team can better evaluate the issue. Sadly it looks like this Catch All falls short in this particular case.
There are several similar questions on StackOverflow, But no answer addresses the issue of the global exception handling not catching the exception. I already know we can fix it so no exception occurs. We could add a TryCatch block to every setter. We could add the BindingComplete event to each databinding and get the exception that way. But all of that defeats the purpose of having global exception handling which works perfectly in any other case.
How do I 'globally' catch exceptions thrown in object instances
Data Binding and throwing exception in setter
Neither Application.ThreadException nor AppDomain.CurrentDomain.UnhandledException are respected
To reproduce the issue, simply create a form with a text box, bind the text box to a property and throw an exception in the property's set method. Add the ThreadException and UnhandledException events to the program.cs. Run the program and type in the text box to trigger the exception. The debugger will break on the exception, press Continue (F5) the let the exception bubble up as it would outside of the debugger. Any normal exception would end up in those events, but this one does not.
Form1.cs
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
textBox1.DataBindings.Add("Text", this, "TestValue", true, DataSourceUpdateMode.OnPropertyChanged);
}
private string _TestValue = "";
public string TestValue
{
get{return _TestValue;}
set
{
_TestValue = value;
throw new Exception("Something bad happened in here");
}
}
Program.cs
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.ThreadException += ThreadExceptionHandler;
AppDomain.CurrentDomain.UnhandledException += new System.UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
Application.Run(new Form1());
}
private static void ThreadExceptionHandler(object sender, System.Threading.ThreadExceptionEventArgs args)
{
try
{
//RR.Common.ErrorLogRt.WriteError(args.Exception.StackTrace.ToString(), args.Exception.Message.ToString(), true);
MessageBox.Show(args.Exception.Message);
}
catch
{
MessageBox.Show("Error writing to exception log. This program will now terminate abnormally.");
Application.Exit();
}
}
static void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e)
{
try
{
if (e != null)
{
Exception ex = e.ExceptionObject as Exception;
//RR.Common.ErrorLogRt.WriteError(ex.StackTrace.ToString(), ex.Message.ToString(), true);
MessageBox.Show(ex.Message);
}
else
{
MessageBox.Show("Unhandled Error: " + e.ToString());
}
}
catch
{
MessageBox.Show("Error writing to exception log. This program will now terminate abnormally.");
Application.Exit();
}
}
static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
try
{
if (e != null && e.Exception != null && e.Exception.InnerException != null)
{
//The unobserved exception is always the same, The actual exception that cause it will be the inner exception.
Exception ex = e.Exception.InnerException;
MessageBox.Show(e.Exception.Message);
//RR.Common.ErrorLogRt.WriteError(ex.StackTrace.ToString(), ex.Message.ToString(), true);
}
else
{
MessageBox.Show("Unhandled Error: " + e.ToString());
}
}
catch
{
MessageBox.Show("Error writing to exception log. This program will now terminate abnormally.");
Application.Exit();
}
}
}
Remarks from Binding.FormattingEnabled Property
Setting this property to true also enables error-handling behavior and
causes the BindingComplete event to be raised. The handler of this
event can take the appropriate action, based on the success, error, or
exceptions in the binding process, by examining the
BindingCompleteState property of the BindingCompleteEventArgs
parameter.
The code involved
internal bool PushData(bool force)
{
Exception ex = null;
if (!force && this.ControlUpdateMode == ControlUpdateMode.Never)
{
return false;
}
if (this.inPushOrPull && this.formattingEnabled)
{
return false;
}
this.inPushOrPull = true;
try
{
if (this.IsBinding)
{
object value = this.bindToObject.GetValue();
object propValue = this.FormatObject(value);
this.SetPropValue(propValue);
this.modified = false;
}
else
{
this.SetPropValue(null);
}
}
catch (Exception ex2)
{
ex = ex2;
if (!this.FormattingEnabled)
{
throw;
}
}
finally
{
this.inPushOrPull = false;
}
if (this.FormattingEnabled)
{
BindingCompleteEventArgs bindingCompleteEventArgs = this.CreateBindingCompleteEventArgs(BindingCompleteContext.ControlUpdate, ex);
this.OnBindingComplete(bindingCompleteEventArgs);
return bindingCompleteEventArgs.Cancel;
}
return false;
}
As you can see, passing 4th parameter as true: DataBindings.Add("Text", this, "TestValue", true is responsible for catching the exception inside PushData and passing it to BindingComplete event. There is no other way (except AppDomain.CurrentDomain.FirstChanceException) to find the exception anywhere else than in BindingComplete if formatting is enabled.
I know a solution exists for WPF, but I could not make it work for winforms.
It seems like the exception is somehow trapped by the framework and I cant find the right trace to listen to.
What you could do though is handle first-chance exceptions (beware that this will probably make you catch way more than what you want).
This will show a message box with "Something bad happened in here" in your example:
AppDomain.CurrentDomain.FirstChanceException += OnFirstChanceException;
//...
private static void OnFirstChanceException(object sender, FirstChanceExceptionEventArgs firstChanceExceptionEventArgs)
{
if(firstChanceExceptionEventArgs.Exception is TargetInvocationException)
{
if(firstChanceExceptionEventArgs.Exception.InnerException != null)
MessageBox.Show(firstChanceExceptionEventArgs.Exception.InnerException.Message);
else
MessageBox.Show(firstChanceExceptionEventArgs.Exception.Message);
}
}
If you are curious, this is the WPF solution I was talking about:
https://web.archive.org/web/20140809204919/https://www.tech.pro/tutorial/940/wpf-snippet-detecting-binding-errors
It looks like it has been reported as a defect to Microsoft, who have closed it as 'Won't Fix':
ReflectPropertyDescriptor.SetValue does not preserve stack trace
in the Microsoft Reference Source, the code for the SetValue method (lines 1085 to 1173) contains a block with a structure like this:
try // <--- This ...
{
try
{
// Code to invoke SetMethod.
}
catch(Exception)
{
// Code to rewind.
// Code to throw inner or rethrow.
}
}
finally // <--- ... and this consume the exception before you can handle it.
{
// Code to raise change notification.
}
The outer try ... finally block is consuming the (second chance) exception that is preventing you from handling it in your code. The dbugger can still catch the (first chance) exception, but it wont get out of the SetValue method.
I am using Awesomium 1.7.0.5 in order to load a page, fill some textboxes and click a button. I am trying to fill a textbox using an example from this thread: http://answers.awesomium.com/questions/857/webcontrol-type-in-webbrowser.html
Here is my code (I am using WPF control):
private void WbAwsOnDocumentReady(object sender, UrlEventArgs urlEventArgs)
{
if (wbAws == null || !wbAws.IsLive)
return;
//Thread.Sleep(555);
dynamic document = (JSObject)wbAws.ExecuteJavascriptWithResult("document");
if (document == null)
return;
using (document)
{
dynamic textbox = document.getElementById("email");
if (textbox == null)
return;
using (textbox)
{
textbox.value = "gaaffa";
}
}
}
It works but only with Thread.Sleep for 0.1-0.5 sec. Otherwise document is empty (not null) and/or textbox is null.
What should I do? Why it is not ready in DocumentReadyEvent?
Here is how I solved it:
WbAws.LoadingFrameCompleted += OnLoadingFrameCompleted;
WbAws.Source = new Uri("http://google.com");
private void OnLoadingFrameCompleted(...)
{
if (webView == null || !webView.IsLive ||
webView.ParentView != null || !e.IsMainFrame)
return;
LoadingFrameCompleted -= OnLoadingFrameCompleted;
// do something
}
LoadingFrameCompleted instead of DocumentReady and because it fires not only when I need it but also on app startup I subscribe to it just before navigating and unsubscribe after it. Also checking that it IsMainFrame.
edit: but with this solution it sometimes throws exception that document is not ready. So I am also waiting for it using Thread.Sleep.
Very old question. but for someone like me, who is facing this problem-
use LoadingFrameCompleted event with WebControl.IsNavigating == false and e.IsMainFrame == true condition.
if these conditions are true in this event then page is finished loading and you are ready to get HTML content.
Hey so I have all my code working and it's fine. But I'd like to clean it up a little.
At the moment I just have a message box show if there is an error in the input, so it would show "Please check your input", however I'd like it to show something like "Please check the following: firstname, secondname etc."
if ((FirstnameText.Text.Trim().Length == 0) || (SurnameText.Text.Trim().Length == 0)
|| (DateOfBirthText.Text.Trim().Length == 0) || (CourseText.Text.Trim().Length == 0)
|| (MatricNoText.Text.Trim().Length == 0) || (YearMarkText.Text.Trim().Length == 0)
|| (int.Parse(MatricNoText.Text) < 10000 || int.Parse(MatricNoText.Text) > 99999)
|| (int.Parse(YearMarkText.Text) < 0 || int.Parse(YearMarkText.Text) > 100))
{
errorMessage();
return;
}
public void errorMessage()
{
MessageBox.Show("Please check your input");
}
I know it's messy, but hey it works
Currently it just outputs that message, is there a simple way to output the specific textbox which has the error?
thanks
The built-in ErrorProvider component will work wonders for your situation. Drag it from the toolbox onto the designer for your form. It will appear at the bottom, where NotificationIcons and ContextMenuStrips appear. The great thing about the ErrorProvider is it give a visual feedback icon with a mouse over tooltip next to the control.
You can then use the "Validating" event of the control to check what you need:
private void FirstnameText_Validating (object sender, CancelEventArgs e)
{
string error = null;
if (FirstnameText.Text.Length == 0)
{
error = "You must enter a First Name";
e.Cancel = true; // This is important to keep focus in the box until the error is resolved.
}
ErrorProvider.SetError((Control)sender, error); // FirstnameText instead of sender to avoid unboxing the object if you care that much
}
You can also stick it in the Save button instead of raising it on the "Validating" event. To clean your code up even more, make a class that validates the input to keep non-UI stuff out of the UI.
Splitting up your code would be a start:
if ((FirstnameText.Text.Trim().Length == 0){
errorMessage("firstname is empty");
}
if (SurnameText.Text.Trim().Length == 0){
errorMessage("surname is empty");
}
Get the idea?
I often use Fluent Validation. The WithMessage method lets you specify an error message. The validator then returns you an enumerable of all error messages. There might also be a better fitting method for your specific problem.
If possible you re-write your code like below
Control errorControl =null;
foreach (Control ctrl in this.Controls)
{
if (ctrl is TextBox)
{
if (ctrl.Name == "MatricNoText")
{
if ((int.Parse(MatricNoText.Text) < 10000 || int.Parse(MatricNoText.Text) > 99999))
{
errorControl = ctrl;
}
}
else if (ctrl.Name == "MatricNoText")
{
if (int.Parse(YearMarkText.Text) < 0 || int.Parse(YearMarkText.Text) > 100)
{
errorControl = ctrl;
}
}
else
{
if (ctrl.Text.Length == 0)
{
errorControl = ctrl;
}
}
}
}
MessageBox.Show("Please check your input." + errorControl.Focus());
I am maintaining a program with customer informations. It consists of many forms that each show some relevant info from the database. This error is in a single form after doing the following
Open the customer search form
View random customer A info in the customerinfo form
open the crm form and it automatically shows customer A. Then add a file to him via draganddrop.
Close the last two forms and select random customer B and do the same.
Close the last two forms and select customer A and add a new file. Error!!!
Here is the code that fails:
private void FireFileCountChanged() {
if (FileCountChanged != null)
BeginInvoke(new DeferEvent(FireFileCountChangedDeferred), 2); // FAILS
"An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll
Additional information: Invoke or BeginInvoke cannot be called on a control until the window handle has been created."
I tried adding the following:
private void FireFileCountChanged() {
if (FileCountChanged != null && this.Handle != null) // CHANGED AND FAILS.
BeginInvoke(new DeferEvent(FireFileCountChangedDeferred), 2);
}
But the this.handle gives:
'this.Handle' threw an exception of type 'System.ObjectDisposedException' and
"Cannot access a disposed object.\r\nObject name: 'AttachmentsControl'."
Then I added a timeout of 10 seconds as the first line in the method, but the handle is still not created. Has the handle somehow been disposed when one of the window were closed? And what can be done about this? Any help is appreciated. I'm kind of stuck.
private void FireFileCountChangedDeferred(int repostCount) {
if (FileCountChanged != null) {
if (repostCount > 0) {
//black magic is somehow involved in getting this event to fire *after* the filewatcher reports the change.
System.Threading.Thread.Sleep(10);
BeginInvoke(new DeferEvent(FireFileCountChangedDeferred), repostCount - 1);
} else
FileCountChanged(this, null);
}
}
private void CopyFiles(string[] files, bool reload) {
if (CreatePath()) {
foreach (string src in files) {
try {
string dest = MakeSafeFilename(src);
File.Copy(src, dest);
FireFileCountChanged();
} catch (Exception ex) {
//Util.Print("Copy ex: {0}", ex.Message);
ErrMsg("Error while copying:{1}{0}", ex.Message, environment.NewLine);
}
}
}
}
private void Lstv_DragDrop(object sender, DragEventArgs ea) {
if (m_CanAdd) {
string[] files = GetDraggedFiles(ea);
if (files != null)
CopyFiles(files, true);
else if (OutlookDataObject.HoldsOutlookData(ea) && CreatePath()) {
try {
OutlookDataObject.CopyDroppedFiles(ea, m_Path, OutlookFilenameCallback);
} catch (Exception ex) {
//Util.Print("Copy ex: {0}", ex.Message);
ErrMsg("Error copying from Outlook:{1}{0}", ex.Message, Environment.NewLine);
}
}
}
}
Solution
private void FireFileCountChanged() {
while (!this.IsHandleCreated) // added
System.Threading.Thread.Sleep(100); //added
if (FileCountChanged != null)
BeginInvoke(new DeferEvent(FireFileCountChangedDeferred), 2);
You need to check the IsHandleCreated property, not compare the Handle to null. Reading the Handle property is considered a UI operation itself.
private void FireFileCountChanged() {
if (FileCountChanged != null && this.IsHandleCreated)
BeginInvoke(new DeferEvent(FireFileCountChangedDeferred), 2);
}
However, based on the complex steps that you need to take to reproduce the bug, I suspect that there are some form instance re-use issues or other more complex issues at play here, and it isn't just a matter of making this call to BeginInvoke work.