2

I have created a custom control to search a customer in a list or by entering the customer code directly:

enter image description here

When a user enters a customer code and the textbox loses focus, an event should trigger. I can get this to work on a standalone control but as soon as I put the XAML in a template, the LostFocus event doesn't trigger anymore.

I've read somewhere that the Interaction Triggers don't work in styles/templates. What is the correct way to create a reusable MVVM control with LostFocus functionality on an underlying TextBox?

CustomerSelectorField.cs

public class CustomerSelectorField : Control
{
  public ICommand Command
  {
     get { return (ICommand)GetValue(CommandProperty); }
     set { SetValue(CommandProperty, value); }
  }

  // Using a DependencyProperty as the backing store for Command.  This enables animation, styling, binding, etc...
  public static readonly DependencyProperty CommandProperty =
      DependencyProperty.Register("Command", typeof(ICommand), typeof(CustomerSelectorField), new PropertyMetadata(null));



  public ICommand LostFocusCommand
  {
     get { return (ICommand)GetValue(LostFocusCommandProperty); }
     set { SetValue(LostFocusCommandProperty, value); }
  }

  // Using a DependencyProperty as the backing store for LostFocusCommand.  This enables animation, styling, binding, etc...
  public static readonly DependencyProperty LostFocusCommandProperty =
      DependencyProperty.Register("LostFocusCommand", typeof(ICommand), typeof(CustomerSelectorField), new PropertyMetadata(null));
}

XAML

<Style TargetType="{x:Type custom_controls:CustomerSelectorField}">
    <Setter Property="FocusManager.IsFocusScope" Value="False" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type custom_controls:CustomerSelectorField}">
                <Grid wpf:MarginSetter.Margin="0,0,5,0">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="200"/>
                        <ColumnDefinition Width="50"/>
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <TextBox Text="{Binding CustomerCode}">
                        <i:Interaction.Triggers>
                            <i:EventTrigger EventName="LostFocus">
                                <i:InvokeCommandAction Command="{TemplateBinding LostFocusCommand}" CommandParameter="{Binding}" />
                            </i:EventTrigger>
                        </i:Interaction.Triggers>
                    </TextBox>
                    <custom_controls:IconButton 
                        Grid.Column="1" 
                        Command="{TemplateBinding Command}" 
                        CommandParameter="{Binding}" />
                    <TextBox IsReadOnly="True" Text="{Binding Name, Mode=OneWay}" Grid.Column="2" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Implementation

    <custom_controls:CustomerSelectorField DataContext="{Binding ParentAcc}" LostFocusCommand="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.ParentCustomerLostFocusCommand}"  Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.OpenParentCustomerSelectorCommand}" />
Bruno V
  • 1,721
  • 1
  • 14
  • 32
  • I'd use Snoop to examine the control at runtime and see what's up with the binding... –  May 19 '15 at 14:35
  • I've installed Snoop and checked the field but I don't see how I can troubleshoot this. In [this stackoverflow answer](http://stackoverflow.com/a/22322742/3357566) it is mentioned that `Interaction.Triggers` cannot be applied in styles. I'm considering using a UserControl instead of a CustomControl, this does however blur the line between the two. – Bruno V May 20 '15 at 07:42

1 Answers1

0

I've used a data template in these scenarios:-

<DataTemplate x:Key="foo"
              DataType="{x:Type custom_controls:CustomerSelectorField}">
    <Grid wpf:MarginSetter.Margin="0,0,5,0">
        ...
    </Grid>
</DataTemplate>

(You'll need to change the InvokeCommandAction binding from "{TemplateBinding ...}" to "{Binding ...}").

Your control's XAML stays the same but you need to wrap it in a ContentControl then apply the data template to that:-

<ContentControl ContentTemplate="{StaticResource foo}">
    <custom_controls:CustomerSelectorField ....
</ContentControl>

It's not ideal but it does the job.

Andrew Stephens
  • 9,413
  • 6
  • 76
  • 152
  • 1
    Thanks for the suggestion, I have tried it and the Interaction Triggers are executed but my bindings are messed up. The `CommandParameter={Binding}`points to the `CustomerSelectorField` after the modification. I'm considering changing the CustomControl to a UserControl where the `Interaction.Triggers` work. – Bruno V May 20 '15 at 07:46