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
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}"/>
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!
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");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With