问题
I have a dynamic WPF input form, whereby a list of input text boxes is displayed based on the contents of a collection of data. The text of each text box is bound to a Value field in the data, when any of these fields changes I am using firing a Command which then executes a calculation in a script (also dynamically provided) using the values of the input field.
My question is how I can get the Command to execute after the field value has changed. At the moment I am firing the Command based on an event trigger using the TextChanged property.
The issue is that the TextChanged property seems to fire before the value of the bound property is set. This means that although the command fires and executes the script, the field which is currently being editing will have the 'old' data in it still because of the sequence of events.
The relevant bit of XAML code I am using is:
<Label Visibility="{Binding HasInputFieldsSection, Converter={StaticResource visibilityConverter}}">Input parameters</Label>
<ItemsControl Visibility="{Binding HasInputFieldsSection, Converter={StaticResource visibilityConverter}}"
ItemsSource="{Binding Path=SelectedQualificationType.QualificationTypeInputFields.QualificationTypeFields}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Label}" Visibility="{Binding Label, Converter={StaticResource stringVisibilityConverter}}"/>
<TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged, Delay=250}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<cmd:EventToCommand Command="{Binding DataContext.ExecuteExpressionsCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Grid}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I was hoping there would be a different event I could trigger the command on but after looking through the documentation for a WPF textbox and trying some alternative none of them seem appropriate.
I could change the binding to be based on LostFocus rather than PropertyChanged but for a better user experience I would rather not. Likewise I could remove the delay on the input TextBox binding but ideally I would want this delay to remain, again for user experience.
So in short the current sequence seems to be:
- user types something
- text changed gets fired
- command is fired
- bound field gets updated
but I need it to be more like:
- user types something
- text changed gets fired
- bound field gets updated
- command is fired
Is there a better event to fire off or way to accomplish what I am trying to do?
回答1:
You can use the Binding.SourceUpdated Attached Event instead of the TextChanged
event.
Set the Binding.NotifyOnSourceUpdated property on your Binding
to true
and listen to the attached event:
<TextBox Text="{Binding Value, NotifyOnSourceUpdated=True}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SourceUpdated">
<!--...-->
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
With this approach, your command will be triggered just after the Binding
data transfer back to the model layer.
回答2:
My question is how I can get the Command to execute after the field value has changed. At the moment I am firing the Command based on an event trigger using the TextChanged property.
Fire the command in the view model when the Value
property of any item in the QualificationTypeFields
collection is set.
You simply could simply hook up an event handler for the PropertyChanged
event for each item in the collection like this:
foreach(var item in SelectedQualificationType.QualificationTypeInputFields.QualificationTypeFields)
{
item.PropertyChanged += item_PropertyChanged;
}
...
private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Value")
{
ExecuteExpressionsCommand.Execute(null);
}
}
If QualificationTypeFields
is an ObservableCollection<T>
you should also remember to add the event handler to each new item that you add to the collection. You could easily do this by handling the CollectionChanged
event:
private void QualificationTypeFields_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (object item in e.NewItems)
{
(item as INotifyPropertyChanged).PropertyChanged
+= new PropertyChangedEventHandler(item_PropertyChanged);
}
}
if (e.OldItems != null)
{
foreach (object item in e.OldItems)
{
(item as INotifyPropertyChanged).PropertyChanged
-= new PropertyChangedEventHandler(item_PropertyChanged);
}
}
}
来源:https://stackoverflow.com/questions/44238222/wpf-textbox-eventtrigger-sequence-of-events