How to handle exception from underlying dataset of an Ultrawingrid (winforms) - winforms

I have an UltraWinGrid with a dataset behind it. In the datatable's Row_Changing event handler, I do some checking of the data and throw an exception if it is invalid. I want to display a message for this exception within my application. However, the UltraGrid seems to catch the exception and display it's own message box with the exception. How can I prevent the message box from being displayed, and catch that error within my application?
private static void Row_Changing( object sender, DataRowChangeEventArgs e )
{
if( <some logic to test the row values>)
throw new Exception("you can't do that");
}

I worked it out, but I thought I'd create this question anyway (since I have already typed it out).
You need to handle the Error event of the UltraGrid and set e.Cancel to true to prevent the dialog box from popping up:
public Form1()
{
...
this.ultraGrid1.Error += new Infragistics.Win.UltraWinGrid.ErrorEventHandler(ultraGrid1_Error);
}
void ultraGrid1_Error(object sender, Infragistics.Win.UltraWinGrid.ErrorEventArgs e)
{
//< deal with the error here>
// set Cancel to true to prevent the dialog box from showing.
e.Cancel = true;
}

Related

Closing a Windows Form, using the Close Window button, when a Validation Message is showing

I have a Windows form that has a validation event on a textBox so that if the value of that TextBox is a value that already exists it triggers a validation error.
private void txtUsername_Validating(object sender, CancelEventArgs e)
{
var alreadyExists = _logic.UserIdExists(txtUsername.Text.Trim());
if(alreadyExists)
{
errorProvider1.SetError(txtUsername, "This Userid already exists, please choose an alternative");
e.Cancel = true;
}
}
private void txtUsername_Validated(object sender, EventArgs e)
{
errorProvider1.SetError(txtUsername, "");
}
this.txtUsername.Validating += new System.ComponentModel.CancelEventHandler(this.txtUsername_Validating);
this.txtUsername.Validated += new System.EventHandler(this.txtUsername_Validated);
This results in an error image appearing next to that textBox along with a tooltip error message.
If I try and close the application, using the Close button at the top of the window, at this time I cannot as the above Event keeps firing even when I try and close the window (due to me taking focus away from the Text box).
Is there a way of closing the window, without resorting to creating an additional Close button on the form?
Based on your description, you want to maintain the default auto-validation behavior yet allow the Form to be closed using the title bar close button. I have observed that the Form.Closing event is raised in such a circumstance, however its argument Cancel property is preset to true. A simple solution is to handle this event and set e.Cancel = false. Implement any logic in the handler that you deem necessary.
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing) e.Cancel = false;
}

Winform Timer isn't running if statement when criteria is met

Hi I've come across either a weird bug or I'm not understanding something.
To cut a long story short I've had everything I'm wanting to work on my form working fine, I then decided to turn the form into an options menu so it was no longer the first form that appears when the application is launched and is shown after I click a button on a different form with the code
private void ShowOptionsButton_Click(object sender, EventArgs e)
{
formHomePage.Show();
}
And for some reason a timer if statement is no longer working:
private void StartSubCheckT_Tick(object sender, EventArgs e)
{
if (subliminalMessages.Count > 0)
{
MessageBox.Show("list greater than 0 if");
StartSubB.Enabled = true;
}
there are other if statements below but are irrelevant and the point of this is to make a button usable once a list is greater than 0. I've created another test button to display the value and it shows that the sublminalMessages list is greater than 0
private void testbutton3_Click(object sender, EventArgs e)
{
MessageBox.Show(subliminalMessages.Count.ToString());
}
Which outputs at 1 which it should be from some other code that adds a value in at the beginning. But for some reason even with the subliminalmessages.count being greater than 0 the if statement is no longer being called ever since I'm making the form appear being called from another form from the button code above.
The subliminalMessages list is being populated and created on the same form
public List<string> subliminalMessages = new List<string>();
private void Form1_Load(object sender, EventArgs e)
{
if (!String.IsNullOrEmpty(Settings.Default["Subliminal1"].ToString()))
{
subliminalMessages.Add(Settings.Default["Subliminal1"].ToString());
MessageBox.Show("If worked");
}
}
There is a value in the Setting.Default that is being added
The button and timer are on the same form and the timer in question is enabled.
Does anyone know why?
Thanks
I'll have a stab at giving you an answer. But it's a little swerve from what you're doing now.
From what I understand of your code you are using the timer to enable/disable the StartSubB button. Or maybe just enable it.
Instead of relying on the timer which appears to not work why not use a BindingList<string>. This has an event called ListChanged which you can handle and then enable/disable your button.
Here's a test form I created:
public partial class Form1 : Form
{
BindingList<string> items;
public Form1()
{
InitializeComponent();
button3.Enabled = false;
items = new BindingList<string>();
items.ListChanged += Items_ListChanged;
}
private void Items_ListChanged(object sender, ListChangedEventArgs e)
{
button3.Enabled = items.Count > 0;
}
private void btnAdd_Click(object sender, EventArgs e)
{
items.Add("a");
}
private void btnRemove_Click(object sender, EventArgs e)
{
if (items.Count > 0)
items.RemoveAt(items.Count - 1);
}
}
I have a BindingList<string> called items. This is analagous with your subliminalmessages list but it's a BindingList. This type of list has a ListChanged event that fires when items are added or removed from the list. In the constructor we new up the items list and subscribe to the ListChanged event.
When the Items_ListChanged event fires button3 is enabled or disabled based on whether items.Count > 0 or not.
In btnAdd_Click we just add an item to the list.
In btnRemove_Click we check that there are some items then remove the last one.
If you were to run this you'd see that when we click the Add button, button3 gets enabled. If we click the Remove button we'll see button3 get disabled.
The only limitation of BindingList is that you can't add a range by passing in another List.
If you implement this and your button still doesn't activate then you'll probably need to post some more code. Strip out all the irrelevant stuff and put it in a new project that demonstrates the failure of the condition and either copy the code here or provide a link to download the project. The only reason the if statement should fail is if the list is actually empty.

Autocomplete does not appear for WPF controls

I'm a newbie to WPF and can't seem to understand why the editor autocomplete popup works for my business objects but not for user interface controls. Any help would be appreciated. For example when I type '.' after txtUserName the auto list members does not display.
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
try
{
Cursor = Cursors.Wait;
controller.Login(txtUserName.Text, txtPassword.Password);
DialogResult = true;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Cursor = Cursors.Arrow;
}
Also I continue to get these errors although my code compiles.
Are you able to run your app successfully ?
Check name of the control viz txtUserName for existance.
Sometimes this is normal behavior of VS. Try closing your xaml page and re-open it, or close VS altogether and re-open and check.

How to reset a textbox in WPF in a button handler before doing something else?

I have a simple WPF button and a textbox in my WPF application(not using MVC or binding at all). I like to be able to do the following upon clicking the button:
1) Clear the textbox
2) Create the result
3) assign the result to the textbox
I used Textbox.Clear, TextBox.Text= String.Empty, delegates and a dispatcher approach like
private void button_Click(object sender, RoutedEventArgs e)
{
Application.Current.Dispatcher.BeginInvoke(new Action (ClearReportTxtBox), DispatcherPriority.Send);
System.Threading.Thread.Sleep(5000);
runTest();
}
private void ClearReportTxtBox()
{
Report_textBox.Text = string.Empty;
}
None of them working correctly for me. The dispatcher method is somehow working but not as I wish. It seems that the Clear task will be queued and when the all actions in the button click handler are finished, it will come into play and delete the textbox, but this causes that the generated report and already assigned to the textbox (created by runtest in the code above) will be deleted as well. Hence it is too late delete action and eliminate the whole result.
Currently it seems to me that by clicking on the button the UIthread blocks and takes the control. The Dispatcher will queue the delete action as next action after finishing the button click.
Is it possible to force delete at the beginning and then do the rest? What I like to reach is that I pause the button activity and do delete at first action and then continue with the rest of actions in the button handler.
Am I doing something wrong?
Thank you for your help in advance.
The "Dispatcher.BeginInvoke" is kinda weird for what you want to do
All the UI update has to be done on main thread. Since the "Button_click" event is executing on main thread, the delegate you push into Dispatcher can only be executed AFTER the button_click handle is completed. That's why the execution sequence becomes
1. The GUI freeze because Thread.Sleep
2. RunTest
3. ClearReportTextBox
Guess you can try sth like the following instead.
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
ClearReportTxtBox();
Task.Factory.StartNew(RunTest);
}
private void ClearReportTxtBox()
{
MyTextBox.Text = string.Empty;
}
private void RunTest()
{
System.Threading.Thread.Sleep(5000);
if (dispatcher != null && !dispatcher.CheckAccess())
{
dispatcher.Invoke(priority, ()=> MyTextBox.Text = "123");
}
else
{
MyTextBox.Text = "123";
}
}

WinForms TabControl validation: Switch to a tab where validation failed

I currently have a Form with a TabControl containing some TabPages. Each TabPage has several controls with validation logic and appropriate ErrorProviders. On my OK_Button_Clicked event, I call Form.ValidateChildren() in order to determine whether to save and close the form . Now, suppose I have a control in tab 1 that fails validation, but the currently visible tab is tab 2. When the user presses OK, he would get no visual indication as to why the form is not closing. I would like to be able to automatically switch to a tab where validation failed, so the user would see the ErrorProvider's indication of error.
One approach would be subscribing to the Validated and validating events of all appropriate controls, and knowing which tab each of them is in, whenever one fails validation, a list of tabs that failed validation could be built. Since no ValidationFailed event is generated as far as I know, this could be cumbersome (e.g. defining a boolean for each control, setting it to false before validation and to true on its Validated event). And even if I had such an event, I would be forced to listen to many validation events, one for each control that might fail validation, and maintain the list of unvalidated tabs in code. I should note here that subscribing directly to the TabPage's validation events doesn't work, because they pass as validated even if controls contained inside them fail validation.
Another approach could leverage the fact that the controls in my TabPage happen to be custom controls. I could then make them implement an interface such as:
interface ILastValidationInfoProvider
{
public bool LastValidationSuccessful {get; set;}
}
For example:
public MyControl : UserControl, ILastValidationInfoProvider
{
MyControl_Validing(object sender, object sender, CancelEventArgs e)
{
if (this.PassesValidation())
this.ErrorProvider.SetError(sender, null);
LastValidationSuccessful = true;
else
e.Cancel = true;
this.ErrorProvider.SetError("Validation failed!", null);
LastValidationSuccessful = false;
}
}
And then, after the call to ValidateChildren I could use code such as:
public void OK_Button_Click
{
if (form.ValidateChildren())
this.Close()
else
foreach (TabPage tab in this.TabControl)
foreach (Control control in tab.Controls)
{
ValidationInfo = control as ILastValidationInfoProvider
if (ValidationInfo != null && !ValidationInfo.LastValidationSuccessful)
{
this.TabControl.SelectTab(tab);
return;
}
}
}
I like this approach better but it doesn't cater to cases where the controls being validated are not custom.
I would gladly use a better approach. Any ideas?
EDIT I am using Form.AutoValidate = EnableAllowFocusChange (as recommended by Chris Sells in his WinForms book), So the focus can indeed change from controls that failed validation (including moving to other tabs). I have also updated the sample code for the custom control to emphasize the fact that the ErrorProvider resides internally inside it.
OK so I finally figured it out.
I keep a dictionary whose keys are the TabPages and the values are HashSets of unvalidated controls within the corresponding tab. This is easily done by subscribing to all the validating and validated events of the controls in each tab. Finally, in OK_BUtton_Click, if ValidateChildren fails, I know one of the hashsets will be none empty and I simply jump to the first unvalidated tab (only if the currently selected tab doesn't have any error itself).
Dictionary<TabPage, HashSet<Control>> _tabControls
= new Dictionary<TabPage, HashSet<Control>>();
public OptionsForm()
{
InitializeComponent();
RegisterToValidationEvents();
}
private void RegisterToValidationEvents()
{
foreach (TabPage tab in this.OptionTabs.TabPages)
{
var tabControlList = new HashSet<Control>();
_tabControls[tab] = tabControlList;
foreach (Control control in tab.Controls)
{
var capturedControl = control; //this is necessary
control.Validating += (sender, e) =>
tabControlList.Add(capturedControl);
control.Validated += (sender, e) =>
tabControlList.Remove(capturedControl);
}
}
}
private void Ok_Button_Click(object sender, EventArgs e)
{
if (this.ValidateChildren())
{
_settings.Save();
this.Close();
}
else
{
var unvalidatedTabs = _tabControls.Where(kvp => kvp.Value.Count != 0)
.Select(kvp => kvp.Key);
TabPage firstUnvalidated = unvalidatedTabs.FirstOrDefault();
if (firstUnvalidated != null &&
!unvalidatedTabs.Contains(OptionTabs.SelectedTab))
OptionTabs.SelectedTab = firstUnvalidated;
}
}
I think it's pretty sweet !

Resources