WPF TextBox value doesn't change on OnPropertyChanged
Solution 1
The problem is that you are updating the source for the Binding
while the Binding
is updating your property. WPF won't actually check your property value when it raises the PropertyChanged
event in response to a Binding
update. You can solve this by using the Dispatcher
to delay the propagation of the event in that branch:
set
{
int val = int.Parse(value);
if (_runAfter != val)
{
if (val < _order)
{
_runAfter = val;
OnPropertyChanged("RunAfter");
}
else
{
_runAfter = 0;
Dispatcher.CurrentDispatcher.BeginInvoke(
new Action<String>(OnPropertyChanged),
DispatcherPriority.DataBind, "RunAfter");
}
}
}
Update:
The other thing I noticed is that the Binding
on your TextBox
is using the default UpdateSourceTrigger
, which happens when the TextBox
loses focus. You won't see the text change back to 0 until after the TextBox
loses focus with this mode. If you change it to PropertyChanged
, you will see this happen immediately. Otherwise, your property won't get set until your TextBox
loses focus:
<TextBox Name="txtRunAfter" Grid.Column="4" Text="{Binding RunAfter, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource TestStepTextBox}"/>
Solution 2
A few things I noticed here.
Unless you have a compelling reason to expose the RunAfter property as a string, there's no reason why it can't be an int. That would save you the cast in the setter (as well as a lurking possible InvalidCastException if the user enters something non-integer in the field).
Secondly, the OnPropertyChanged() call should occur outside of the inner if statement, like the following:
if(_runAfter != val)
{
if(val < _order)
_runAfter = val;
else
_runAfter = 0;
OnPropertyChanged("RunAfter");
}
Since the _runAfter local is being updated in both paths of the conditional, the OnPropertyChanged() has to be called regardless of the branch taken. I hope that helps point you in the right direction!
Solution 3
I had the same situation. I wrote the following and it worked.
<TextBox Grid.Column="3" Grid.Row="0" HorizontalAlignment="Left" VerticalAlignment="Center" Width="100" Text="{Binding Path=FirstName}"></TextBox>
And
public string FirstName
{
get { return _client.FirstName; }
set
{
if (value == _client.FirstName)
return;
else
_client.FirstName = value;
OnPropertyChanged("FirstName");
}
}
Related videos on Youtube
jpsstavares
Working as QA Engineer at Blip, Portugal. An avid reader about everything tech related from inovation to software testing. Kindle fan.
Updated on April 25, 2022Comments
-
jpsstavares about 2 years
I have a TextBox whose Value is binded to a ViewModel property:
<TextBox Name="txtRunAfter" Grid.Column="4" Text="{Binding Mode=TwoWay, Path=RunAfter}" Style="{StaticResource TestStepTextBox}"/>
The set and get were working fine until I tried to add some validation when the Value is set:
private int _runAfter = 0; public string RunAfter { get { return _runAfter.ToString(); } set { int val = int.Parse(value); if (_runAfter != val) { if (val < _order) _runAfter = val; else { _runAfter = 0; OnPropertyChanged("RunAfter"); } } } }
Although the OnPropertyChanged is reached (I have dubugged that), the View is not changed. How can I make this work?
Thanks, José Tavares
-
Robaticus almost 14 years+1 for noting that you need to move the OnPropertyChanged outside of the else.
-
jpsstavares almost 14 yearsWell, the set is called by the View (via binding) so I thought it would only be necessary to call OnPropertyChanged if the value would be changed from the one setted in the View.
-
Eric Olsson almost 14 yearsRegardless of how the setter is called, any change must raise the PropertyChanged event to let listeners know that they need to refresh their value. Have you looked at using ValidationRules for doing validation rather than embedding it in your setter code? msdn.microsoft.com/en-us/library/…
-
jpsstavares almost 14 yearsI guess your assessment of the problem is correct, but the dispatcher call doesn't work. My UserControl is used inside a WinForm application using an ElementHost. May this be affecting the Dispatcher call?
-
Abe Heidebrecht almost 14 yearsOkay, I tested this out and it worked fine (when you tab away from the TextBox as the default mode of the binding won't update the property until the TextBox loses focus). I updated the answer to explain about the UpdateSourceTrigger on the bindings, in case that is the behavior that you are seeing. I don't deal a lot with WPF hosted in WinForms, but I don't see why that would affect the Binding or Dispatcher in your context.