User Control data binding
Hi!
I am trying to bind a UserControl Text property to SqlCeResultSet:
Binding b = new Binding("Text", myResultSet, "DateDataEntered");
b.Parse += new ConvertEventHandler(ParseCtrlValueToDate);
b.Format += new ConvertEventHandler(FormatToDateCtrlValue); myUserControl.DataBindings.Add(b);
I overridden Text property fro my UserControl, but could not specify [Bindable(true)] attribute - it looks like it is not supported by compact framework (yaks...). It looks like my control displays value from the first row in the data table, but not parsing value back to database.All other bound controls on the form transfer data back and force without problems.
How can I properly bind UserControl? I am almost loosing patience.......
Thank you,
Olga, frustrated developer.
[814 byte] By [
Olga99] at [2007-12-25]
There's no need to specify this attribute, your bindings are just fine.
As to automatically passing values back to data source, it's triggered by the control. Data binding engine can be set to update data source on either <PropertyName>Changed event (which you have to implement in your control) or on validation event by setting DataSourceUpdateMode as needed.
http://msdn2.microsoft.com/en-us/library/system.windows.forms.datasourceupdatemode.aspx
On your part you need to make sure your custom control supports these events.
Thank you for a quick reply. I added "OnValidating" event to my control in the parent form, but it does not trigger (I think this is a reason why Parse event does not trigger also). Text property of control changing, but the host (parent) does not know it. Data gets displayed from database, but nothing is written back.
How can I add User control's onPropertyChanged event notification to a host control (a form)? I googled a lot to find the answer, but it looks that I am missing some simple step in the process.
Help will be greatly appreciated.
Olga
I did some research today. My User Control name is 'TvlDatePicker'. It looks like this.BindingContext[this.myResultSet].Bindings collection does not have binding for my User Control.
But my control has Data Bindings:
myControl.DataBindings[0] I got the following output:
BindableComponent: {TVL.WiseTrack.PocketPC.Controls.TvlDatePicker}
BindingManagerBase: {System.Windows.Forms.CurrencyManager}
bindingManagerBase: {System.Windows.Forms.CurrencyManager}
BindingMemberInfo: {System.Windows.Forms.BindingMemberInfo}
bindToObject: {System.Windows.Forms.BindToObject}
BindToObject: {System.Windows.Forms.BindToObject}
bound: true
ComponentCreated: true
control: {TVL.WiseTrack.PocketPC.Controls.TvlDatePicker}
Control: {TVL.WiseTrack.PocketPC.Controls.TvlDatePicker}
ControlUpdateMode: OnPropertyChanged
controlUpdateMode: OnPropertyChanged
DataSource: {System.Data.SqlServerCe.SqlCeResultSet}
DataSourceNullValue: {}
DataSourceUpdateMode: OnPropertyChanged
dataSourceUpdateMode: OnPropertyChanged
dsNullValue: {}
dsNullValueSet: false
FormatInfo: null
formatInfo: null
FormatString: ""
formatString: ""
formattingEnabled: true
FormattingEnabled: true
inOnBindingComplete: false
inPushOrPull: false
inSetPropValue: false
IsBindable: true
IsBinding: true
modified: false
nullValue: null
NullValue: null
onComplete: {System.Windows.Forms.BindingCompleteEventHandler}
onFormat: {System.Windows.Forms.ConvertEventHandler}
onParse: {System.Windows.Forms.ConvertEventHandler}
PropertyName: "Text"
propertyName: "Text"
propInfo: {System.ComponentModel.ReflectPropertyDescriptor}
propIsNullInfo: null
validateInfo: {System.ComponentModel.ReflectEventDescriptor}
For the myControl.DataBindings[0].DataSource I got (it is bound to SqlCeResultSet):
{System.Data.SqlServerCe.SqlCeResultSet}
base {System.Data.SqlServerCe.SqlCeDataReader}: {System.Data.SqlServerCe.SqlCeResultSet}
BookmarkArray: {System.Collections.ArrayList}
bookmarkArray: {System.Collections.ArrayList}
ContainsListCollection: false
isInitialized: true
onResultSetChanged: {System.Data.SqlServerCe.ResultSetChangedEventHandler}
ResultSetView: {System.Data.SqlServerCe.ResultSetView}
Scrollable: true
Sensitivity: Sensitive
sqlUpdatableRecord: null
System.ComponentModel.IListSource.ContainsListCollection: false
Updatable: true
So it looks like the control has binding, but the form does not know about it. How can I insure that the control's binding added to the form BindingContext? May be the problem is in a way I am adding Binding to my control? As an example, I am using the following syntax to add binding to the text box on the form (and it is working fine):
myTextBox.DataBindings.Add(new Binding("Text", this.myResultSet, "NameColumn", true, DataSourceUpdateMode.OnValidation));
and for my user control:
Binding
b =
new Binding(
"Text",
this.myResultSet, "DateColumn",
true,
DataSourceUpdateMode.OnPropertyChanged);
b.Parse += new ConvertEventHandler(ParseCtrlValue);
b.Format += new ConvertEventHandler(FormatCtrlValue);
myControl.DataBindings.Add(b);
Please help,
Olga
I set AutoValidate of myControl to EnablePreventFocusChange (in control code and in the host form), and added Validating events:
private
void TvlDatePicker_Validating(
object sender,
CancelEventArgs e) - in control code
and
myControl.Validating += new System.ComponentModel.CancelEventHandler(this.myControl_Validating);
in the form code
Neither of this events get called....
Do I have to implement INotifyPropertyChanged interface in my control's code? Or better call delegate and connect to host form event to perform validation? Am I missing some steps? Another thing that bothering me is that this.BindingContext[this.myResultSet].Bindings collection does not have binding for my User Control. How can I find information about all form's bindings?
Thank you,
Olga
Here's a sample. Bind to 'SomeText' property, set DataSourceUpdateMode to DataSourceUpdateMode.OnPropertyChanged.
public partial class UserControl1 : UserControl
{
private int _cliked = 0;private String _someText = "Click me...";public event EventHandler SomeTextChanged = null;public String SomeText{
get {
return this._someText;}
set{
if (_someText != value) // New value?{
_someText =
value; // Yes, store it.this.Invalidate(); // And redraw. OnSomeTextChanged(
EventArgs.Empty); // Fire event to notify subscribers (e.g. data binding) about property change.}
}
}
protected virtual void OnSomeTextChanged(EventArgs e){
if (this.SomeTextChanged != null)this.SomeTextChanged(this, e);}
public UserControl1(){
InitializeComponent();
}
protected override void OnClick(EventArgs e){
this._cliked++;this.SomeText = String.Format("Clicked {0} times", this._cliked);base.OnClick(e);}
protected override void OnPaint(PaintEventArgs e){
using (Brush brush = new SolidBrush(Color.Black)){
e.Graphics.DrawString(SomeText,
this.Font, brush, 0, 0);}
base.OnPaint(e);}
}
Thank you for reply, but it is still not working.....
I implemented INotifyPropertyChanged interface for my control, and added
myControl.PropertyChanged += new PropertyChangedEventHandler(myControl_PropertyChanged);
delegate to host form. In this event handle I got a Current data row and updated its value
RowView rv = (RowView)this.BindingContext[this.myResultSet].Current;
SqlCeUpdatableRecord rec = rv.UpdatableRecord;
rec.SetDateTime(columnIndex, (myControlClass(sender)).Date);
Database was updated, but when I change position of this.BindingContext[this.myResultSet] - data from the first row in data table ALWAYS dispalyed in the control! This is VERY strange, because this.BindingContext[this.myResultSet].Count gives me the right number of bindings. I added handles to .Validating events to both control code and form, but they NEVER trigger.
I feel that using BindingContext is a right way to bind controls on the form, but may be I just have to keep truck of Position in my ResultSet and update rows manually? It will involve more code, but will avoid strange things happening...
Thank you,
Olga
I think you should separate your custom control from data binding. Make it work with, say, TextBox, and then proceed with custom control.
Code I posted updates data source (I used DataTable but it does not matter) and it does show data for different records as you change position:
this.BindingContext[this.table].Position = someRecord;
There’s no need to do it manually, but if you feel more comfortable this way go ahead and do it.
As to Validating, I belive it should fire as you close form. If that’s not what you want, use DataSourceUpdateMode.OnPropertyChanged.
Thank you for trying to answer my question. I think that binding not beheaving because I overridden "Text" property of the control. Bunch of other user controls on the form derived from TextBox and working well with form's binding. It looks like if I overriden poperty I have to inmpelment INotifyPropertyChanged interface for the control (because CFoes not support [System.ComponentModel.Bindable(true)] property). But I couldn't figure out how to connect PropertyChanged event to form's Binding object (this.BindingContext[myResultSet] ). Control's binding always thinks that .Position=0 and displays value from the first row. Because it does not know that the record was cahnged - it does not update it. The problem is that I don't know how many controls of this type I will have on the form ( I am rendering them dynamically).
The validation event occures when (according to MS documentation):
When you change the focus by using the keyboard (TAB, SHIFT+TAB, and so on), by calling the Select or SelectNextControl methods, or by setting the ContainerControl.ActiveControl property to the current form, focus events occur in the following order:
Enter
GotFocus
Leave
Validating
Validated
LostFocus
When you change the focus by using the mouse or by calling the Focus method, focus events occur in the following order:
Enter
GotFocus
LostFocus
Leave
Validating
Validated
So for now I will manually update my Result set. If I will find any other solutions, I will post it here.
Thank you again for your help,
Olga