5

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:

  1. user types something
  2. text changed gets fired
  3. command is fired
  4. bound field gets updated

but I need it to be more like:

  1. user types something
  2. text changed gets fired
  3. bound field gets updated
  4. command is fired

Is there a better event to fire off or way to accomplish what I am trying to do?

2 Answers 2

6

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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);
        }
    }
}

2 Comments

Thanks very much, I can see how this would work as well, but I went with the answer proposed by dymanoid in the end
ah yes ofc, will do

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.