Windows Develop Bookmark and Share   
 index > Windows Forms Data Controls and Databinding > Best way to do row validation with BindingSource/BindingNavigator?
 

Best way to do row validation with BindingSource/BindingNavigator?

After the user has entered values in the bound controls on a form, I need to hit the database with those values before they move to a newrow, and disallow movement if validation fails.The PositionChanged event of the BS occurs only afterthe position has changed, e.g., due to clicks on the BindingNavigator's movement buttons.

On another form I wrote, I changed the movement buttons on the BN to regular buttons, trapped the click event, and did row validation therein, before changing the Position of the BS. I had to manually code the behavior of themovement buttons, since they couldn't be set as the regular movement button properties of the BN. Is there a better way?

kaborka  Monday, November 26, 2007 7:43 PM
Ken Tucker  Tuesday, November 27, 2007 1:33 AM
Ken Tucker  Tuesday, November 27, 2007 1:33 AM
Guess I wasn't clear in my first post -- that's exactly what I'm doing now in some apps. I was hoping there was a better way, because you lose the built-in functionality,but I guess there isn't.

kaborka  Tuesday, November 27, 2007 2:04 AM

So I just spent quite a few hours in Reflector working out whether this is possible while still preserving behaviour such as keeping track of the position in the list etc.

When the ToolStripItems are set in the BindingNavigator(e.g. BindingNavigator.MoveNextItem) it automatically wires up an EventHandler (e.g. OnMoveNext) that doesn't quite do enough to allow cancelling, validation etc. Relevant reflectored code from BindingNavigator here.

Code Block
public ToolStripItem MoveNextItem
{
get
{
if ((this.moveNextItem != null) && this.moveNextItem.IsDisposed)
{
this.moveNextItem = null;
}
return this.moveNextItem;
}
set
{
this.WireUpButton(ref this.moveNextItem, value, new EventHandler(this.OnMoveNext));
}
}

private void WireUpButton(ref ToolStripItem oldButton, ToolStripItem newButton, EventHandler clickHandler)
{
if (oldButton != newButton)
{
if (oldButton != null)
{
oldButton.Click -= clickHandler;
}
if (newButton != null)
{
newButton.Click += clickHandler;
}
oldButton = newButton;
this.RefreshItemsInternal();
}
}

private void OnMoveNext(object sender, EventArgs e)
{
if (this.Validate() && (this.bindingSource != null))
{
this.bindingSource.MoveNext();
this.RefreshItemsInternal();
}
}

BindingNavigator.Validate()only handlesvalidating the control that is losing focus (this would be a nice method to make protected virtual).this.bindingSource is not exposed anywhere settable and doesn't have any easyway of intercepting and cancelling the position change that I could find even if it were. So to work around this, one way I can see how to do it is to subclassToolStripButton and override the OnClickEvent.

e.g.

Code Block

public class MyValidatingToolStripButton : ToolStripButton

{

protected override void OnClick(EventArgs e)

{

if (myCustomValidation())

{

base.OnClick();

}

}

}

Then just replace the existing buttons with these new ones and it should work (setting the various properties correctly).

The position stuff will likely be similar - the difference is that it's the OnKeyUp and OnLostFocus event handling that needs to be overriden.

One might be able to extend the BindingNavigator class overriding the AddStandardItems to provide this functionality in a reusable component. If that were the case, I would be inclined to hook up events like MovingNext(object sender, CancelEventArgs e) in the new BindingNavigator and possible Validating etc.

Josh McK  Saturday, January 12, 2008 4:12 PM

Interesting solution!

It's really too bad this wasn't designed in properly. For example, the TreeView class has a BeforeSelect event that takes a CancelEvent delegate. My app has a MMC-like structure with a TreeView on the left. I use the BeforeSelect event to do form-level validation in the client area before allowing the user to move to a new node in the treeview. The AfterSelect event loads the form for the newly selectednode into the client area. The BindingSource / BindingNavigator desparately needs a similar event for record-level validation, in addition to the PositionChanged after-the-fact event.

kaborka  Monday, January 14, 2008 1:44 AM

The following code will cause the Validating event to be invoked from the bindingNavigator before a click is handled.

If validation fails - the click is canceled (more like ignored...).

public class ValidatingBindingNavigator : BindingNavigator

{

public ValidatingBindingNavigator(IContainer container)

: base(container)

{

}

protected override void WndProc(ref Message m)

{

if (m.Msg == 0x201) //WM_LBUTTONDOWN

{

CancelEventArgs args = new CancelEventArgs();

OnValidating(args);

if (args.Cancel)

{

return;

}

}

base.WndProc(ref m);

}

}

  • Proposed As Answer byBonnieBMVPWednesday, December 31, 2008 3:20 PM
  •  
Yoni.Shalom  Tuesday, October 21, 2008 1:52 PM
Hi Yoni. Good Code. But I am a biginner in C# and can't undestand the contex to place your class. Could you explain with a brief sample?. thank you very much.
Arielr  Monday, December 15, 2008 2:51 PM

Yoni has sub-classed the BindingNavigator and is using the sub-class for navigation rather than the base .NET BindingNavigator class. So instead of using

BindingNavigator bn = new BindingNavigator(bs);

You'd use

ValidatingBindingNavigator bn = new ValidatingBindingNavigator(bs);


~~Bonnie Berent [C# MVP]
BonnieB  Tuesday, December 16, 2008 5:28 AM
Forgive me for my ignorencia,....
My binding navigator is declared like this in InitializeComponent...

this.myBindingNavigator = new System.Windows.Forms.BindingNavigator(this.components);

Which is the correct way to replace it by,...

ValidatingBindingNavigator bn = new ValidatingBindingNavigator(bs);

Thank you for your patience..
Arielr  Tuesday, December 16, 2008 11:34 AM
You'd have to replace it in two places in your InitializeComponent:

Replace these two lines:

System.Windows.Forms.BindingNavigator myBindingNavigator;

this.myBindingNavigator = new System.Windows.Forms.BindingNavigator(this.components);

With these two lines:

ValidatingBindingNavigator myBindingNavigator;

this.myBindingNavigator = new ValidatingBindingNavigator(this.components);


This is, of course, after you've created that ValidationgBindingNavigator class as Yoni described above.
~~Bonnie Berent [C# MVP]
BonnieB  Wednesday, December 17, 2008 4:41 AM
Thank you Bonnie. I've implemented it. But now very good. But,... How to capture the click to validate it....

example...

private void myNavigator_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
if (e.ClickedItem = bnNext)
{

if( Validating )
.....
else
.....

}
}

This validation happens after binding move to the next reccord. Yoni class capture the click before, fine, very good, I need that. But how to validate? and How to check what button was pressed?

Thank you VERY MUCH. Really I estimate very much your help.
Arielr  Wednesday, December 17, 2008 12:04 PM
But that's the point of Yoni's class. It fires the Validating event before the button click causes the navigator to move to the next row. All you need is to handle the Validating event.
~~Bonnie Berent [C# MVP]
BonnieB  Thursday, December 18, 2008 1:40 AM
private void myValidatingNavigator_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
if (e.ClickedItem = bnNext)
{

if( !MyValidating() )
I need to cancel the click here, how?

}
}

Does It's posible?. I think isn´t, because Yoni´s class capture the click before. Probably I am wrong,

thank you very much....




Arielr  Thursday, December 18, 2008 11:50 AM
I haven't tried Yoni's class at all, but by the looks of it (the way it's intended to work anyway), the Click event won't even fire if the Validating fails. So you don't need to put any Validating in your click handler AT ALL!

~~Bonnie Berent [C# MVP]
BonnieB  Thursday, December 18, 2008 4:35 PM
Hi Bonnie, I'm back again.
May be I really don't understand the real purpose the Yoni's code.
In my case,

if the user perform a binding nav. back click i must to check if data binding has null values at fields that should not has its value.(null).

I can check it at:

private void myValidatingNavigator_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
if (e.ClickedItem = bnPrev )
{
if( myds.mydt[ipos].isMyFieldDbNull )

{
Here mus't continue with the click....

}

}
}

Yoni's Code Act before myValidatingNavigator_ItemClicked, you'r right. But Isn't solve my problem.... because I can't validate my binding data.....

thanks to be so patient.

Hou!!, Merry Christmas and Happy New Year. That all your desires will fulfil


Arielr  Friday, December 26, 2008 5:49 PM

Yoni left out an important part of his class. That's the implementation of the overridden OnValidating() method. Sorry that I didn't catch this sooner (and why didn't Yoni?), but as I mentioned I hadn't even tried Yoni's code. Until now. And discovered what he was missing.

In the ValidatingBindingNavigator class, add this code:

protectedoverridevoidOnValidating(CancelEventArgse)
{
if(this.ParentisForm)
e.Cancel=!((Form)this.Parent).ValidateChildren();
base.OnValidating(e);
}


If your parent isn't a Form, you can spin through each Parent's .Parent until you find the Form.

This ValidateChildren method of a Form will go through each control on the Form and perform its Validating and Validated methods. On the Form, this is a virtual method, so you can override it to suit your needs.

Sorry that I missed this earlier. =0(


~~Bonnie Berent [C# MVP]
  • Proposed As Answer byBonnieBMVPWednesday, December 31, 2008 3:20 PM
  • Edited byBonnieBMVPSunday, February 08, 2009 8:59 PM
  •  
BonnieB  Friday, December 26, 2008 10:13 PM
Thank you, very, very much. That was so usefull to me.....


Arielr  Tuesday, December 30, 2008 5:23 PM
You're very, very welcome Ariel! Glad I could help! =0)
~~Bonnie Berent [C# MVP]
BonnieB  Wednesday, December 31, 2008 3:18 PM
Hello Bonnie.

I want to make use of the built in code for the buttons of the bindingnavigator,and to be able of cancel a bindingnavigator event. But what I needis not to validate all the children on the form. I need to makedisctinct validations depending on the button of the bindingnavigator the user has pressed. For example, if the AddNewItem button is pressed, I need to check a property to check if there are changes, ask the user for save changes and then, depending on the answer: Yes --> save the data, add and move,or No --> cancel the adding of new datarow (now i must cancel, not earlier). For other buttons, the actions are not the same, you know.

Any idea to do it?. The method proposed by Yoni doesn't give the buttonthat was pressed. Perhaps a possibility is make something similar over the ToolStripButton class of the buttons, but how? And with PositionItem?

Thanks a lot for your work here.
Bob.
BobSoft  Thursday, February 05, 2009 6:52 AM
I did something like this, following Bonnie instructions and Yoni's Code.

first, derive your bar navig from Yoni's Class. At Designer replace..
this.myBindingNavigator = new ValidatingBindingNavigator(this.components);

and

ValidatingBindingNavigator myBindingNavigator; as show Bonnie previus post,....

after that I add this code to myBindingNavigator validating event,..

private void myBindingNavigator_Validating(object sender, CancelEventArgs e)
{

// this code prevent null errors validations//
DataRowView drvCal = cmyBindingSource.Current as DataRowView;

if (drvCal != null && drvCal.IsNew)
{

if (m_bnGrabar.Enabled)
{

............
}
else
{
drvCal.Delete();
}

e.Cancel = true;
}
else
{
e.Cancel = false;
}

base.OnValidating(e);
}
Arielr  Thursday, February 05, 2009 11:05 AM
Thanks Ariel. But I don´t understand how can you know which button was clicked, using the Enabled property. All the buttons have this property to True, at the moment of clicking.

Any suggestion? Thanx again.
BobSoft  Thursday, February 05, 2009 2:46 PM
Humm, sorry, but I solve this problem validating buttons at separated toolstrip. Yoni class help me to cancel the action when user try to move other row with null values at fields that musn't have it.



Arielr  Thursday, February 05, 2009 6:13 PM
You can try adding to mybinding_validating....

ValidatingBindingNavigator obj = sender as ValidatingBindingNavigator;

if (obj.MovePreviousItem.Selected)
{
MessageBox.Show("prev");
myBindingSource.MovePrevious();
}
if (obj.MoveNextItem.Selected)
MessageBox.Show("Next");

If you can solve this problem, please share it..... thank!
  • Proposed As Answer byBobSoft Friday, February 06, 2009 3:19 AM
  •  
Arielr  Thursday, February 05, 2009 7:11 PM
Mmmm... It seems to be a good idea. Let me try it and I tell you about it. Thanks a lot.
BobSoft  Thursday, February 05, 2009 7:29 PM
Ok Ariel, it works fine! I can know the item that fired the event. But now I have another big problem, that is a bug of .NET. In the navigator validating event (that fires at least!!),I must show a MessageBox, as the user can confirm changes or not (if not,I should set e.cancel to true). I'veread in many many forums thatif you show a MessageBox into a Validating event,after the message, the mouseup event is dissapeared and the focus is out of the navigator, an then it has an unexpected behaviour.This happens on Validating on every form controls. It seems that it's impossible to workaround this issue, then finally Ihave tomake a programmatic behaviour for each item of the navigator and loose the built in functionality=0(Grrrrrrr...

Anyway thanks a lot for your helpful post, andthanks to Yoni too.Chao.

BobSoft  Friday, February 06, 2009 2:55 AM
I've read all these great contributions and sidestepped the issue completely by disabling the navigation buttons, except for delete, when there is an error on the form. I used to be a Cobol programmer, so I'm used to using blunt objects! Is that an OO joke?

Regards

Mike Allen
MikeAinOZ  Tuesday, August 04, 2009 1:51 AM

You can use google to search for other answers

Custom Search

More Threads

• Problems with a DataGridVew with a DataGridViewComboBoxColumn
• How to set the TAB Control visible
• Data binding the text field of a TreeNode used in a tree view
• DataGridView + ComboBox Error leaving cell - I'm stuck!
• It wasn't a change to my DataTableAdapter after all
• OnValidating event not firing in DataGridView
• UInt32 and Int32 - Why are Data Types of columns different?
• Selecting new value from ComboBox in edit mode doesn't work
• C#:How to change a bound column's style to a checkbox
• What event fires when you click away from a new row?