immediate data bind transfer

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

I am currently writing a compact framework application with uses the data
binding capabilities of windows forms controls. In this application, I am
binding controls (mainly text boxes) to a custom object type, with properties
defined suitable for binding to (including the associated propertyChanged
event types defined so that changes on these properties will be propagated to
the control).

The problem I am experiencing is that if the value of a text box is changed,
this change is not propagated to the property on the custom object until this
control loses focus (the user selects another control, or presses tab, etc).
Is this a documented "feature" of data binding to text fields, or am I doing
something wrong?

The class I'm binding to is defined thus (with the irrelevant details
omitted):

public class Values {

private string curVal;

public string CurrentValue
{
get
{
return curVal;
}

set
{
Console.WriteLine("current value changing");
curVal = value;
OnCurrentValueChanged();
}
}

private void OnCurrentValueChanged()
{
if (CurrentValueChanged != null)
{
CurrentValueChanged(this, new EventArgs());
}
}

public Binding getCurrentValueBinding(String controlProp)
{
return new Binding(controlProp, this, "CurrentValue");
}
}

The control is bound to a Values instance as follows:

TextBox textBox = ...
Values values = ...
textBox.DataBindings.Add(values.getCurrentValueBinding("Text"));
 
You're not doing anything wrong. It's the behaviour by "design" that's BTW
works the same way in .NET as well. In order to implement the functionality
you require, you can subclass the TextBox and place the following code in the
OnTextChanged:

public class TextBoxBind : TextBox
{
protected override void OnTextChanged(EventArgs e)
{
Binding binding = this.DataBindings["Text"];
if (binding != null)
{
binding.BindingManagerBase.EndCurrentEdit();
}
base.OnTextChanged (e);
}
}

HTH... Alex
 
It is a documented behavior of the data bound text fields. The
CurrencyManager binds to the Validating event on the Control, which is by
default invoked from OnLostFocus. You could work around this by simulating
Validating event. It would work fine as long as you don't do your own
validation. If you do, you will have to discern between a "real" validating
event and the one you generated.

For every TextBox control on your form add the following function as the
TextChanged handler:
textBox1.TextChanged += new EventHandler(InvokeValidating);


private void InvokeValidating(object sender, EventArgs e)

{

Type t = typeof(Control);

FieldInfo fi = t.GetField("Validating",
BindingFlags.NonPublic|BindingFlags.Instance);

MulticastDelegate d = fi.GetValue(sender) as MulticastDelegate;

Delegate[] list = d.GetInvocationList();

// It is important to cast each member to an appropriate delegate type

// For example for the KeyDown event we would replace EventHandler

// with KeyEvenHandler and new EventArgs() with new
KeyHandlerEventArgs()

foreach(CancelEventHandler handler in list)

{

handler.Invoke(sender, new CancelEventArgs(false));

}

}
 
I have to admit - Alex's solution is cleaner. The only thing I would add is
instead of overriding OnTextChanged, put that code in TextChanged handler.
Using derived classes screws the designer

--
Alex Feinman
---
Visit http://www.opennetcf.org
Alex Feinman said:
It is a documented behavior of the data bound text fields. The
CurrencyManager binds to the Validating event on the Control, which is by
default invoked from OnLostFocus. You could work around this by simulating
Validating event. It would work fine as long as you don't do your own
validation. If you do, you will have to discern between a "real"
validating event and the one you generated.

For every TextBox control on your form add the following function as the
TextChanged handler:
textBox1.TextChanged += new EventHandler(InvokeValidating);


private void InvokeValidating(object sender, EventArgs e)

{

Type t = typeof(Control);

FieldInfo fi = t.GetField("Validating",
BindingFlags.NonPublic|BindingFlags.Instance);

MulticastDelegate d = fi.GetValue(sender) as MulticastDelegate;

Delegate[] list = d.GetInvocationList();

// It is important to cast each member to an appropriate delegate type

// For example for the KeyDown event we would replace EventHandler

// with KeyEvenHandler and new EventArgs() with new
KeyHandlerEventArgs()

foreach(CancelEventHandler handler in list)

{

handler.Invoke(sender, new CancelEventArgs(false));

}

}



--
Alex Feinman
---
Visit http://www.opennetcf.org
Iain McGinniss said:
I am currently writing a compact framework application with uses the data
binding capabilities of windows forms controls. In this application, I am
binding controls (mainly text boxes) to a custom object type, with
properties
defined suitable for binding to (including the associated propertyChanged
event types defined so that changes on these properties will be
propagated to
the control).

The problem I am experiencing is that if the value of a text box is
changed,
this change is not propagated to the property on the custom object until
this
control loses focus (the user selects another control, or presses tab,
etc).
Is this a documented "feature" of data binding to text fields, or am I
doing
something wrong?

The class I'm binding to is defined thus (with the irrelevant details
omitted):

public class Values {

private string curVal;

public string CurrentValue
{
get
{
return curVal;
}

set
{
Console.WriteLine("current value changing");
curVal = value;
OnCurrentValueChanged();
}
}

private void OnCurrentValueChanged()
{
if (CurrentValueChanged != null)
{
CurrentValueChanged(this, new EventArgs());
}
}

public Binding getCurrentValueBinding(String controlProp)
{
return new Binding(controlProp, this, "CurrentValue");
}
}

The control is bound to a Values instance as follows:

TextBox textBox = ...
Values values = ...
textBox.DataBindings.Add(values.getCurrentValueBinding("Text"));
 
V2 would allow data source update done on Validation, on PropertyChanged or
Never.
So, no more workarounds, just set what you need.

Best regards,

Ilya

This posting is provided "AS IS" with no warranties, and confers no rights.

--------------------
From: "Alex Feinman [MVP]" <[email protected]>
References: <[email protected]>
Subject: Re: immediate data bind transfer
Date: Fri, 14 Jan 2005 12:12:13 -0800
Lines: 122
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="Utf-8";
reply-type=response
Content-Transfer-Encoding: 7bit
X-Priority: 3
X-MSMail-Priority: Normal
X-Newsreader: Microsoft Outlook Express 6.00.2900.2180
x-mimeole: Produced By Microsoft MimeOLE V6.00.2900.2180
Message-ID: <#Me2xUn#[email protected]>
Newsgroups: microsoft.public.dotnet.framework.compactframework
NNTP-Posting-Host: 204.249.181.133
Path: cpmsftngxa10.phx.gbl!TK2MSFTFEED02.phx.gbl!TK2MSFTNGP08.phx.gbl!TK2MSFTNGP12
..phx.gbl
Xref: cpmsftngxa10.phx.gbl microsoft.public.dotnet.framework.compactframework:68749
X-Tomcat-NG: microsoft.public.dotnet.framework.compactframework

I have to admit - Alex's solution is cleaner. The only thing I would add is
instead of overriding OnTextChanged, put that code in TextChanged handler.
Using derived classes screws the designer

--
Alex Feinman
---
Visit http://www.opennetcf.org
Alex Feinman said:
It is a documented behavior of the data bound text fields. The
CurrencyManager binds to the Validating event on the Control, which is by
default invoked from OnLostFocus. You could work around this by simulating
Validating event. It would work fine as long as you don't do your own
validation. If you do, you will have to discern between a "real"
validating event and the one you generated.

For every TextBox control on your form add the following function as the
TextChanged handler:
textBox1.TextChanged += new EventHandler(InvokeValidating);


private void InvokeValidating(object sender, EventArgs e)

{

Type t = typeof(Control);

FieldInfo fi = t.GetField("Validating",
BindingFlags.NonPublic|BindingFlags.Instance);

MulticastDelegate d = fi.GetValue(sender) as MulticastDelegate;

Delegate[] list = d.GetInvocationList();

// It is important to cast each member to an appropriate delegate type

// For example for the KeyDown event we would replace EventHandler

// with KeyEvenHandler and new EventArgs() with new
KeyHandlerEventArgs()

foreach(CancelEventHandler handler in list)

{

handler.Invoke(sender, new CancelEventArgs(false));

}

}



--
Alex Feinman
---
Visit http://www.opennetcf.org
Iain McGinniss said:
I am currently writing a compact framework application with uses the data
binding capabilities of windows forms controls. In this application, I am
binding controls (mainly text boxes) to a custom object type, with
properties
defined suitable for binding to (including the associated propertyChanged
event types defined so that changes on these properties will be
propagated to
the control).

The problem I am experiencing is that if the value of a text box is
changed,
this change is not propagated to the property on the custom object until
this
control loses focus (the user selects another control, or presses tab,
etc).
Is this a documented "feature" of data binding to text fields, or am I
doing
something wrong?

The class I'm binding to is defined thus (with the irrelevant details
omitted):

public class Values {

private string curVal;

public string CurrentValue
{
get
{
return curVal;
}

set
{
Console.WriteLine("current value changing");
curVal = value;
OnCurrentValueChanged();
}
}

private void OnCurrentValueChanged()
{
if (CurrentValueChanged != null)
{
CurrentValueChanged(this, new EventArgs());
}
}

public Binding getCurrentValueBinding(String controlProp)
{
return new Binding(controlProp, this, "CurrentValue");
}
}

The control is bound to a Values instance as follows:

TextBox textBox = ...
Values values = ...
textBox.DataBindings.Add(values.getCurrentValueBinding("Text"));
 
Thanks for the help guys. I was aware that the bevaviour was the same in both
..NET and .NET CF; the reason I stated the problem in the context of .NET CF
was in case the general .NET solution wouldn't work there (due to unsupported
methods and the like).

Thanks again,
Iain
 
Unfortunately, it does not appear either solution works:

Alex Yakhin's solution (when implemented as an overridden version of
OnTextChanged or as a TextChangedHandler) has no effect: setting a breakpoint
on my property's set code block shows that the change is still only being
propagated when the control loses focus.

Alex Feinman's solution gives a NullReferenceException on the line

MulticastDelegate d = fi.GetValue(sender) as MulticastDelegate;

as the variable fi is still null after the call to GetField on the preceding
line. I did some fishing on google and I understand the principle of what the
code is trying to do (the MS compiler creates a field on the class with the
same name as the event) - however for some reason it does not find the field.
I also tried

Type t = actualControl.GetType();
EventInfo ev = t.GetEvent("Validating");
MethodInfo raiseValidating = ev.GetRaiseMethod(true);
raiseValidating.Invoke(actualControl, new object[] { sender, new
CancelEventArgs(false) });

However, similarly the call to getRaiseMethod returns null.
 
It is possible to make Alex Yahknin's solution work by simply providing a
stub implementation of IEditableObject on the data source which you bind the
control to (in my case, the Values class which I partially defined in the
original post). As I did not need support for rollback and so on with my
Values class, I implemented the IEditableObject with empty methods. Once this
was done, calls to BindingManagerBase.EndCurrentEdit() would successfully
transfer the value of the control to the bound data source. Implementing
IEditableObject in order to get this to work is documented in the
BindingManagerBase.EndCurrentEdit() method description.

Iain McGinniss said:
Unfortunately, it does not appear either solution works:

Alex Yakhin's solution (when implemented as an overridden version of
OnTextChanged or as a TextChangedHandler) has no effect: setting a breakpoint
on my property's set code block shows that the change is still only being
propagated when the control loses focus.

Alex Feinman's solution gives a NullReferenceException on the line

MulticastDelegate d = fi.GetValue(sender) as MulticastDelegate;

as the variable fi is still null after the call to GetField on the preceding
line. I did some fishing on google and I understand the principle of what the
code is trying to do (the MS compiler creates a field on the class with the
same name as the event) - however for some reason it does not find the field.
I also tried

Type t = actualControl.GetType();
EventInfo ev = t.GetEvent("Validating");
MethodInfo raiseValidating = ev.GetRaiseMethod(true);
raiseValidating.Invoke(actualControl, new object[] { sender, new
CancelEventArgs(false) });

However, similarly the call to getRaiseMethod returns null.

Iain McGinniss said:
Thanks for the help guys. I was aware that the bevaviour was the same in both
.NET and .NET CF; the reason I stated the problem in the context of .NET CF
was in case the general .NET solution wouldn't work there (due to unsupported
methods and the like).

Thanks again,
Iain
 
Iain McGinniss said:
Unfortunately, it does not appear either solution works:

Alex Feinman's solution gives a NullReferenceException on the line

MulticastDelegate d = fi.GetValue(sender) as MulticastDelegate;

as the variable fi is still null after the call to GetField on the
preceding
line. I did some fishing on google and I understand the principle of what
the
code is trying to do (the MS compiler creates a field on the class with
the
same name as the event) - however for some reason it does not find the
field.

Since I actually have built a working sample before posting (most of the
time I do), I beg to differ. It does work, The reason you might have gotten
a null there is InvokeValidating is attached before the control is
databound. Either add a check for null to that line (good idea overall) or
simply make sure that you attach InvokeValidating as TextChanged event
handler *after* you add the binding and the form is loaded.
 
BTW a working sample can be found at
http://www.alexfeinman.com/download.asp?doc=TextBoxImmediateBinding.zip

--
Alex Feinman
---
Visit http://www.opennetcf.org
Iain McGinniss said:
Unfortunately, it does not appear either solution works:

Alex Yakhin's solution (when implemented as an overridden version of
OnTextChanged or as a TextChangedHandler) has no effect: setting a
breakpoint
on my property's set code block shows that the change is still only being
propagated when the control loses focus.

Alex Feinman's solution gives a NullReferenceException on the line

MulticastDelegate d = fi.GetValue(sender) as MulticastDelegate;

as the variable fi is still null after the call to GetField on the
preceding
line. I did some fishing on google and I understand the principle of what
the
code is trying to do (the MS compiler creates a field on the class with
the
same name as the event) - however for some reason it does not find the
field.
I also tried

Type t = actualControl.GetType();
EventInfo ev = t.GetEvent("Validating");
MethodInfo raiseValidating = ev.GetRaiseMethod(true);
raiseValidating.Invoke(actualControl, new object[] { sender, new
CancelEventArgs(false) });

However, similarly the call to getRaiseMethod returns null.

Iain McGinniss said:
Thanks for the help guys. I was aware that the bevaviour was the same in
both
.NET and .NET CF; the reason I stated the problem in the context of .NET
CF
was in case the general .NET solution wouldn't work there (due to
unsupported
methods and the like).

Thanks again,
Iain
 
Back
Top