Relatively new to C#/.NET/GUI programming, but here goes. Right now I'm writing a business application at work using WinForms. I have about 5 textboxes and 1 combobox that I want to verify if is empty and if so then tell the user and set focus on that control. How should I go about doing this?
I could either have an if statement that checks each control:
if (usernameField IsNullOrEmpty) then:
setFocus(UsernameField);
return;
if (addressField IsNullOrEmpty) then:
setFocus(addressField);
return;
continue with rest of application as normal...
Or I could do this with exceptions:
try {
if (usernameField IsNullOrEmpty) then:
throw new Exception(usernameField);
if (addressField IsNullOrEmpty) then:
throw new Exception(addressField);
} catch (Exception e) {
setFocus(ControlField) // encapsulate in exception some way?
}
Or to prevent code duplication, just write a function:
try {
checkField(usernameField);
checkField(addressField);
} catch (Exception e) {
setFocus(ControlField) // encapsulate in exception some way?
}
void checkField(control ctrl) {
if (ctrl IsEmptyOrNull)
throw new Exception(ctrl);
}
Being relatively new to GUI programming, does a text field being empty deserve an exception or would this be considered normal program flow?
Throwing Exceptions for program flow is not recommended.
Write a helper method.
private bool IsTextboxValid(Textbox ctrl)
{
if(string.IsNullOrEmpty(ctrl.Text))
{
ctrl.Focus();
return false;
}
return true;
}
And to use it:
if(!IsTextboxValid(addressBox)) return;
if(!IsTextboxValid(nameBox)) return;
I would not use an exception, exceptions should be thrown in exceptional situations, user not filling in a field doesn't count. As for actually detecting the empty control and setting focus, there are tons of ways, like simple if checks as you have, to more complicated solutions with binding and validation and all that.
Related
I have a method that returns an ItemCollection, I want to stop the function early if there is no information provided. I would normally do this with a return; however as my method expects an ItemCollection it fails. I have this as a work around but it seems frankly hacky. Is there something I am missing. I tried just return; and I would prefer not throw an exception.
private ItemCollection loadLeft_Click(object sender, RoutedEventArgs e)
{
var leftUser = UsrLeft.Text;
if (leftUser == "")
{
MessageBox.Show("No User Entered");
GroupListLeft.Items.Add("");
var fail = GroupListLeft.Items;
return fail;
}
//Succesful test do stuff
var leftItems = GroupListLeft.Items;
return leftItems;
}
You have few options:
throw a new Exception (maybe even a custom one like NoUserEnteredException("someText")).
return null
return an empty collection or dummy object (see Null-object pattern)
The last one is better choice, in this case you don't need write a null-check or a try-catch section in client code.
You will need to return something that equates to an ItemCollection, depending on how you may want the calling procedures to handle such a return you could use a null return:
if (leftUser == "")
{
MessageBox.Show("No User Entered");
return null;
}
Or return a new ItemCollection e.g.
if (leftUser == "")
{
MessageBox.Show("No User Entered");
return new ItemCollection();
}
You will need to return null, like
private ItemCollection loadLeft_Click(object sender, RoutedEventArgs e)
{
var leftUser = UsrLeft.Text;
if (leftUser == "")
{
MessageBox.Show("No User Entered");
return null;
}
//Succesful test do stuff
var leftItems = GroupListLeft.Items;
return leftItems;
}
You can use "return null" which I do from time to time, but its not a good coding convention. Another option is to throw an exception and then catch it. Both is bad coding conventions and makes the code that uses this button add some rather obscure logic and dependencies.
From studying SOLID principles I would argue a better solution is to make a new type of object to return. f.ex. one that holds a list, but also a status message and then make it react a bit like when you have an HTTP request that depends on a success and otherwise cannot expect to have any content. This way, the object makes it clearer what to expect.
A fourth option, since this sounds UI related, is to possible have the click callback somehow and either update the collection, or update with an error message directly. But this might also be a bad coding convention that has impractical dependencies in the code.
Your code seems to return an object when you click on something.
(I am no expert in WPF, i don't know if this is possible).
I would encapsulate the function that returns the ItemCollection in a separate function and check whether the User is valid BEFORE calling this function.
This way you ensure that the ItemCollection is always valid (because you don't even try to retrieve it with an invalid user). Something like this:
private void loadLeft_Click(object sender, RoutedEventArgs e) {
var leftUser = UsrLeft.Text;
if(leftUser != "") {
ItemCollection coll = getItemCollectionForUser(leftUser);
}else {
//Error Handling
}
}
private ItemCollection getItemCollectionForUser(string user) {
//return ItemCollection here
}
Note how I wrote a separate function that returns the ItemCollection and the Click function returns nothing.
I'm trying to find a way to create a nice windows form that will pop up when I encounter an unhandled exception. I currently have:
// Add the event handler for handling UI thread exceptions
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException );
// Add the event handler for handling non-UI thread exceptions
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler( CurrentDomain_UnhandledException );
static void CurrentDomain_UnhandledException( object sender, UnhandledExceptionEventArgs e )
{
MessageBox.Show( e.ToString( ), "Unhandled Non-UI Thread Exception" );
}
static void Application_ThreadException( object sender, ThreadExceptionEventArgs e )
{
MessageBox.Show( e.ToString( ), "Unhandled UI Thread Exception" );
}
But what I'm looking for is to, in the threadexception methods, pop up a windows form with information about the error, and a continue/quit/restart whatever. This sounds very similar to something that, through gooogling, looks like its built in for certain cases, but is it possible to create some sort of modifiable / custom one that I can call?
Sorry, I unintentionally pasted the wrong part of code. I am currently using a message box, but want a somewhat more beefed out one with some functional buttons.
Thanks a bunch.
How about popping a MessageBox ? Take a look here http://www.dotnetperls.com/messagebox-show
All of your events should be wrapped in a code looking like this:
DialogResult result = DialogResult.Retry;
while (result == DialogResult.Retry) {
try {
DoProcess();
break;
}
catch (Exception ex) {
result = MessageBox.Show(ex.ToString(),
"Error Information",
MessageBoxButtons.AbortRetryIgnore,
MessageBoxIcon.Exclamation);
if (result == DialogResult.Abort) throw;
}
}
Where DoProcess() would be the risky code.
Sure, just create any old form, and display it using ShowDialog at that point. You can put whatever you want on it. You could then put various buttons on it for "continue", "restart", "quit", etc. You can then inspect a property of the form after ShowDialog returns to determine what to do, based on the button clicked.
Create a custom window that pop ups when you get an error. Then you can decide whether to close the application, continue or quit the a certain process.
The best approach i could think of is create a custom exception which you would throw when specific problems occur in your application. You should then allow this exception to bubble up and be handled in one of the following.
Application.ThreadException
AppDomain.CurrentDomain.UnhandledException
Check this link for more info.
msdn.microsoft.com/en-us/library/system.windows.forms.application.threadexception.aspx
Create a custom message box. This should be inherited from From
class MyDialog : Form
{
//your properties like buttons and all goes here
private Button okButton;
private Button cancelButton;
okButton = new Button();
okButton.DialogResult = DialogResult.OK;
okButton.Location = new Point(20, 260);
okButton.Size = new Size(80, 25);
okButton.Text = "OK";
okButton.Click += new EventHandler(okButton_ClickCompany);
Controls.Add(okButton);
//same implementation for all other controls you define
}
you implementation goes like
MyDialog myDialog = new MyDialog(); // you can constructor for using it everywhere
if (myDialog.ShowDialog() == DialogResult.OK)
{
//you code goes here
}
this is very good approach for doing it.
you can extend it according to you needs and use it in many ways by defining multiple constructor
There's a nice sample in MSDN
http://msdn.microsoft.com/en-us/library/system.threading.threadexceptioneventargs.aspx
I'm in the middle of adding new functionality to my winforms controls, and part of that requires that a variable that was once always used to now be optional (if it's null, get the data from a second source). I made some changes and ran my form, only to find out nothing was happening, even functionality that previously worked. Confused I stepped through the code and found out that my Winforms user control was throwing a NullReferenceException when it encountered my variable, but in the UI no errors were being thrown.
My setup is I have a UserControl with a combo box. When the user changes that combo box it loads a secondary UserControl in a panel the first control has. The second control is what is throwing the exception.
Here are the code paths:
private void cmbActionType_SelectedIndexChanged(object sender, EventArgs e)
{
if (_loading)
return;
// ActionType was changed, update the action.ActionType value
if (cmbActionType.SelectedItem != null)
{
if (cmbActionType.SelectedItem.ToString() == SETVALUE_OPTION)
_action.ActionType = ActionTypes.SetValue;
else if (cmbActionType.SelectedItem.ToString() == CHECKVALUE_OPTION)
_action.ActionType = ActionTypes.CheckValue;
else
_action.ActionType = ActionTypes.CustomAction;
}
RefreshActionPanel();
_editor.DataModified();
}
private void RefreshActionPanel()
{
// Control defaults
AnchorStyles styles = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;
UserControl subControl = null;
// Clear the currently active control
pnlActionDetails.Controls.Clear();
// Determine what type of control to load in the panel
if (cmbActionType.SelectedItem != null && cmbCaseType.SelectedItem != null)
{
// SetValue or CheckValue actions
if (cmbActionType.SelectedItem.ToString() == CHECKVALUE_OPTION || cmbActionType.SelectedItem.ToString() == SETVALUE_OPTION)
{
if (_caseTypeMap.ContainsKey(cmbCaseType.SelectedItem.ToString()))
subControl = new SetCheckActionControl(_action, _editor, _caseTypeMap[cmbCaseType.SelectedItem.ToString()]);
}
// CustomAction action type
else
{
// Check if the requested case is a type or defined in a script
if (_caseTypeMap.ContainsKey(cmbCaseType.SelectedItem.ToString()))
{
subControl = new CustomActionControl(_action, _editor, _caseTypeMap[cmbCaseType.SelectedItem.ToString()]);
}
else if (_editor.ScriptDefinitions.Any(x => x.CaseName == cmbCaseType.SelectedItem.ToString()))
{
var definitions = _editor.ScriptDefinitions.Where(x => x.CaseName == cmbCaseType.SelectedItem.ToString()).ToList();
subControl = new CustomActionControl(_action, _editor, definitions);
}
}
}
if (subControl != null)
{
subControl.Anchor = styles;
subControl.Height = pnlActionDetails.Height;
subControl.Width = pnlActionDetails.Width;
pnlActionDetails.Controls.Add(subControl);
}
}
public CustomActionControl(TestAction action, fmEditor editor, IList<TcScriptDefinition> scriptDefinitions) : base(action, editor)
{
_loading = true;
InitializeComponent();
_scriptDefinitions = scriptDefinitions;
PopulateActionList();
SetupDataGrid();
_loading = false;
}
private void SetupDataGrid()
{
// Clear the current contents of the datagrid
grdParameters.Rows.Clear();
if (cmbAction.SelectedItem == null)
return;
// Retrieve the action code from the drop down
string actionCode = cmbAction.SelectedValue.ToString();
// Check if any paramters are available for this action
if (!_availableActionParameters.ContainsKey(actionCode))
return;
// Add a new row for each parameter available for this action
foreach (string param in _availableActionParameters[actionCode])
{
string display = param;
// Determine if the parameter has a display string
if (_formInstance.CodeDisplayMap.ContainsCode(param))
display = _formInstance.CodeDisplayMap.GetDisplayStringFromCode(param);
// Create the array for the row, with an empty string as the current value
string[] row = { display, string.Empty };
// Check if the current action uses this action code.
// If so, retrieve the value for this parameter and use it in the row
// Note: Case-INsensitive comparison must be performed here
if (_action.Attributes["action"].Equals(actionCode, StringComparison.CurrentCultureIgnoreCase))
if (_action.Attributes.ContainsKey(param))
row[1] = _action.Attributes[param];
grdParameters.Rows.Add(row);
}
}
The NullReferenceException is coming from the SetupDataGrid() method where _formInstance is being called. However, usually when an application encounters an unhandled exception the JIT system throws an error message saying such (and as you can see, there's no try/catch statements used unless I am blind).
Why does my winforms application show no signs of an exception occurring. I'd rather an unhandled exception message occur rather than nothing happening, as that makes it harder for users to know something critical went wrong (as opposed to it not responding to their commands)
Edit: To clarify since there seems to be some confusion, I do NOT care about breaking on this exception in visual studio when debugging. The fact of the matter is that the application should not be hiding unhandled exceptions, and my application should crash (or rather show the JIT message that an unhandled exception occurred), even when not in debug mode outside of visual studio.
This is not a debug time question but a production run time question. If this code throws an OutOfMemoryException for instance, I need it to not be silently ignored. Right now it is being ignored.
Go to Debug->Exceptions... (Ctrl-D, E if you are using default shortcuts) from your menu bar in Visual Studio and check the box for Thrown under Common Language Runtime Exceptions. This will cause your program to break on exceptions even if they are in a try/catch block. You can then do a step forward and see where the code is jumping to for the next instruction. That should bring you to the catch block that is hiding your exception.
It may be jumping in to .NET code for the catch, you can go to Debug->Options and Settings.. and turn on Enable .NET framework Source Stepping to see what it is jumping in to.
Here is a example of catching the unhandeled execptions in code
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.Run(new Form1());
MessageBox.Show("0");
}
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
MessageBox.Show("1");
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
throw new ArgumentNullException();
}
}
Click the button and 1 appears, the program should still run afterwards.
However as I said in my last comment, check that user-unhandeled is checked (maybe uncheck and recheck) in Debug->Exceptions first it may solve your initial issue..
They can be caught if you have a try/catch in your Main, or if you have a ThreadException handler. Also check out SetUnhandledExceptionMode.
Note that if they're caught in Main, your program will just exit silently. ThreadException can "catch" and ignore them.
EDIT: Since the debugger doesn't break when you uncheck the box for Thrown under Common Language Runtime Exceptions, it's being caught by the code for Windows Forms. This is not ideal, but a lot of the BCL does this. You don't need to worry about it catching OutOfMemoryException or anything like that; the BCL should only be catching exceptions that it expects (vexing exceptions).
To be clear, the exception is not "unhandled". It is handled by the BCL code.
I have the following code:
public Mainform()
{
...
// scheduler
scheduler.DoWork += new System.ComponentModel.DoWorkEventHandler(scheduler_DoWork);
scheduler.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(scheduler_RunWorkerCompleted);
scheduler.WorkerReportsProgress = false;
scheduler.WorkerSupportsCancellation = true;
...
...
scheduler_DoWork(this, null);
scheduler.RunWorkerAsync(1000);
...
}
void scheduler_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
scheduler_Enabled = false;
CustomExceptionHandler eh = new CustomExceptionHandler();
eh.HandleUnhandledException(e.Error, "scheduler");
}
if(scheduler_Enabled)
{
scheduler.RunWorkerAsync(1000);
}
}
void scheduler_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
try
{
try
{
...do some stuff
}
catch(MyException ex)
{
ThreadSafeShowError();
}
finally
{}
...do more stuff
}
finally
{
if (e != null && e.Argument != null)
{
Thread.Sleep((int)e.Argument);
}
}
}
The backgroundworker thread died unexpectedly without any exception being thrown. I did not encounter this problem during development and it seems to be hard to reproduce. I suspected that maybe a cross thread exception was occurring when I am doing work in the scheduler_DoWork. I have tried to explicitly update the UI without checking if InvokeRequired and the thread continues to run without problems in a release build. How can this be? (Cross thread exception should occur) How can I determine what causes the thread to die? Any suggestions on how to figure out what is going wrong or how to debug this issue will be appreciated?
The RunWorkerCompleted event might not be fired on the UI Thread. If it is not, then the thread will end and your scheduler object will be garbage collected, which will make it seem like it just quit with no error. See this post for more details. Here and here are SO posts about this.
Your sample doesn't show enough code to determine what's going on but:
Maybe an exception is being thrown from ThreadSafeShowError? Why are you trying to show an error from the worker thread anyway - the conventional thing to do is to show e.Error if not null in the RunWorkerCompleted event handler.
To debug the issue try putting the following around all the code in your DoWork handler:
try
{
// do work
// log a trace statement here
}
catch(Exception ex)
{
// log exception, e.g. with System.Diagnostics.Debug.Write
throw;
}
finally
{
// log a trace statement here
}
You can do several things to increase the possibility of catching the exception:
Enable Managed Debugging Assistants for all exceptions in VS. To do that, go to Debug menu -> Exceptions..., and put a check mark next to "Managed Debugging Assistants" to enable all exceptions to be caught using debugger. Also, depending on the code you are executing, expand the CLR Exceptions node and select nodes of interest ("System" node, for example, will catch all exceptions from the System namespace in the debugger).
Obviously, put a try/catch block around your code, with some logging. You can also do something like this, if you are in real trouble:
try
{
// do stuff
}
catch (Exception ex)
{
#if DEBUG
// break only in DEBUG builds
System.Diagnostics.Debugger.Break();
#endif
// log the exception and throw
throw;
}
Put a try/catch with logging around your Application.Run(...) code in the Main() method. Exceptions do propagate up there sometimes, and this can catch them (even if not coming from this specific part of your code).
Add an Application.ThreadException event handler in your Main() method, before calling Application.Run, like this:
Application.ThreadException +=
new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
In Mainform, you never call scheduler.RunWorkerAsync, so your BackgroundWorker does not start at all.
Our core application is built in MFC C++, but we are trying to write new code in .NET, and have created a User Control in .NET, which will be used on an existing MFC Dialog.
However, when a unexpected/unhandled exception is thrown from the User Control, it causes the MFC app to crash (illegal op style), with no ability to recover.
I've added
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(currentDomain_UnhandledException);
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
To the Constructor of the .NET user control, but it doesn't seem to catch anything.
Is there any way to add an event in MFC to handle these?
Quick google didn't return anything useful.
Thanks.
Edit: Still haven't been able to resolve this the way I'd like, looks like the best way to do it, is try and catch around all the .NET code, so no exceptions bubble up.
I asked this same question a while ago: Final managed exception handler in a mixed native/managed executable?
What I have found is that the managed unhandled exception events ONLY fire when running in a managed thread. The managed WndProc is where the magic happens.
You have a few options: you could place a low-level override in CWinApp and catch Exception^. This could have unintended side-effects. Or, you could go with Structured Exception Handling (SEH) which will give you a hack at /all/ unhandled exceptions. This is not an easy road.
I think you want to have the unhandled exception handler in the MFC side of things:
AppDomain::CurrentDomain->UnhandledException += gcnew UnhandledExceptionEventHandler(&CurrentDomain_UnhandledException);
[same for Application.ThreadException]
Have a look at this similar question on the MSDN forums: Unhandled Exception in Mixed Mode application
Of course, the better idea would be either to handle the exception in the C# code, or else to find out what's causing it, and fix it so that the exception is never thrown. What's preventing that?
In every event handler of the user control, place a try/catch block:
private void btnOk_Click(object sender, EventArgs e) {
try {
// real code here
}
catch (Exception ex) {
LogException(ex);
// do whatever you must in order to shut down the control, maybe just
}
}
Here's what I use:
public static class UI
{
public static void PerformUIAction(Control form, Action action)
{
PerformUIAction(form, action, (string) null);
}
public static void PerformUIAction(
Control form, Action action, string message)
{
PerformUIAction(form, action, () => message);
}
public static void PerformUIAction(
Control form, Action action, Func<string> messageHandler)
{
var saveCursor = form.Cursor;
form.Cursor = Cursors.WaitCursor;
try
{
action();
}
catch (Exception ex)
{
MessageBox.Show(
messageHandler() ?? ex.Message, "Exception!",
MessageBoxButtons.OK, MessageBoxIcon.Error,
MessageBoxDefaultButton.Button1,
MessageBoxOptions.DefaultDesktopOnly);
Debug.WriteLine(ex.ToString(), "Exception");
throw;
}
finally
{
form.Cursor = saveCursor;
}
}
}
I call it like this:
private void _copyButton_Click(object sender, EventArgs e)
{
UI.PerformUIAction(
this, () =>
{
// Your code here
});
}
I added the unhandled exception handler to the constructor of the .NET user control
I don't think that will work; I believe the exception handler has to be setup before a call to Application.Run. Do you have any Application.Run calls in your app?
How about something like this on the MFC side of things before you initialize any .NET controls:
Application.SetUnhandledExceptionMode(System.Windows.Forms.UnhandledExceptionMode.CatchException);
Application.ThreadException += ...;
See if the ThreadException handler is called.