1

I have a user control and I've been doing some experimentation with handling events of various controls in the underlying view model. In many cases I have found that using some basic xaml like the bit below works.

<i:Interaction.Triggers>
            <i:EventTrigger EventName="Event to be handled">
                <i:InvokeCommandAction Command="{Binding Path= I Command handling event}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>

However I have been trying this with a text box defined in my xaml like so:

 <TextBox x:Name="TxtInvNumber" Margin="5" Grid.Column="1" Width="80" Text="{Binding Path=InvoiceNumber}" >
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="TextChanged">
                <i:InvokeCommandAction Command="{Binding Path=InvoiceNumberChangedCommand}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TextBox>

When done like this and with a breakpoint set on the InvoiceNumberChangedCommand nothing happens, yet if I put a simple message box in the TextChanged handler in the code behind the view it gets fired every time.

Can anyone explain if this is something particular to text boxes or is it that using interactions to handle events in this way doesn't always work?

Incidentally I had wondered if this might have had something to do with the fact that the text box text property is bound to an underlying property that implements propertychanged so I tried it with the LostFocus event as well and still the same result.

Thanks

Dom Sinclair
  • 2,458
  • 1
  • 30
  • 47
  • Just for my understanding: have you set a breakpoint in the `InvoiceNumberChangedCommand` property getter or in the corresponding `Execute()` method implementation of that command? Are there any binding errors in the trace? – dymanoid Mar 14 '15 at 17:26
  • Hi. the breakpoint is in the actual execute method and there are no binding errors in the trace. I'm still relatively new to using interactions so it wouldn't surprise me if I have overlooked something obvious, just not sure what that might be. – Dom Sinclair Mar 14 '15 at 17:31

2 Answers2

1

Requirements for Interaction.Triggers to work:

  1. Add reference to 'System.Windows.Interactivity'
    a) Here's one way. https://stackoverflow.com/a/52110697/6010880
  2. Add this attribute to XAML.
    b) xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

    enter image description here

    Here's the complete solution that you can try from a new project. Just copy and paste the 4 files below:

    1. MainWindow.xaml
    2. MainWindow.xaml.cs
    3. MainViewModel
    4. DelegateCommand.cs

    XAML



Code Behind (MainWindow.xaml.cs)

using System.Windows;

namespace WpfApp1
{
   public partial class MainWindow : Window
   {
       private readonly MainViewModel _main = new MainViewModel();

    public MainWindow()
    {
        InitializeComponent();

        DataContext = _main;  //<-- It does not need to be before InitializeComponent()
    }
}

}


MainViewModel.cs

using System.Windows.Input;

namespace WpfApp1 { public class MainViewModel { protected ICommand _windowsLoadedVm;

    public ICommand WindowsLoadedVm
    {
        get
        {
            if (_windowsLoadedVm == null)
            {
                _windowsLoadedVm = new DelegateCommand(WindowsLoadedHandler);
            }
            return _windowsLoadedVm;
        }
    }

    public void WindowsLoadedHandler(object parameter)
    {
        //Do your thang!
    }
}

}


DelegateCommand.cs

using System;

using System.Windows.Input;

namespace WpfApp1
{
   public class DelegateCommand : ICommand
   {
     private readonly Predicate<object> _canExecute;
     private readonly Action<object> _execute;

    public event EventHandler CanExecuteChanged;

    public DelegateCommand(Action<object> execute)
                   : this(execute, null)
    {
    }

    public DelegateCommand(Action<object> execute,
                   Predicate<object> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        if (_canExecute == null)
        {
            return true;
        }

        return _canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

}

icernos
  • 395
  • 3
  • 6
0

It works for me... Few possible things to check:

  1. System.Windows.Interactivity are you using?
  2. Are you sure your command is raising a notify property changed, or being set before InitializeComponent()?

View.cs

public partial class InteractionTriggerNotFiring : Window
{
    public InteractionTriggerNotFiring()
    {
        DataContext = new SO29051551VM();
        InitializeComponent();

    }
}

View.xaml

<Window x:Class="WpfApplication1.InteractionTriggerNotFiring"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        Title="InteractionTriggerNotFiring" Height="300" Width="300">
    <Grid>
        <TextBox x:Name="TxtInvNumber" Margin="5" Grid.Column="1" Width="80" Text="{Binding Path=InvoiceNumber}">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="TextChanged">
                    <i:InvokeCommandAction Command="{Binding Path=InvoiceNumberChangedCommand}" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </TextBox>
    </Grid>
</Window>

View Model

public class SO29051551VM : ViewModelBase
{
    private RelayCommand _invoiceNumberChangedCommand;

    public SO29051551VM()
    {
        InvoiceNumberChangedCommand = new RelayCommand(() =>
        {
            Console.WriteLine("TEst");
        });
    }
    public RelayCommand InvoiceNumberChangedCommand
    {
        get { return _invoiceNumberChangedCommand; }
        set
        {
            if (_invoiceNumberChangedCommand == value) return;
            _invoiceNumberChangedCommand = value;
            RaisePropertyChanged(() => InvoiceNumberChangedCommand);
        }
    }
}
Michal Ciechan
  • 13,492
  • 11
  • 76
  • 118