Welcome to MSDN Blogs Sign in | Join | Help

Data See, Data Do

Mike Hillberg's Blog on Wpf (.Net and Silverlight)

Syndication

News

All postings are provided "As Is" with no warranties, and confers no rights. Opinions and views expressed here are not necessarily those of Microsoft Corporation.


Why is it called a "DependencyProperty"?

When we create new classes and members we spend a lot of time and effort to make them as usable, understandable, and discoverable as possible.  We follow the .Net Design Guidelines in general, and in particular we constantly look at how this new class relates to other classes, future plans, etc.

 

So we went through all that when naming DependencyProperty and DependencyObject.  All told we probably spent hours just on the name.  What dependency properties (DPs) boil down to in the end is property calculation and dependency tracking.  Property calculation isn’t terribly distinctive, lots of properties do that, so the essence of a DP is really the dependency tracking, thus “Dependency” properties.

 

 

Here’s an example of that, actually one piece of sample code that shows several examples of dependency tracking:

 

<StackPanel TextBlock.FontSize="22" DataContext="Hello, world">

    <StackPanel.Resources>

 

        <Style TargetType="TextBlock">

            <Setter Property="FontWeight" Value="Bold" />

            <Style.Triggers>

                <Trigger Property="IsMouseOver" Value="True">

                    <Setter Property="Background" Value="Red" />

                </Trigger>

            </Style.Triggers>

        </Style>

 

    </StackPanel.Resources>

 

    <TextBlock Text="{Binding}" />

   

</StackPanel>

 

 

The properties of this TextBlock have quite a few dependencies:

·         TextBlock.Text is dependent on the Binding, and the Binding is dependent on the DataContext in this case.  The DataContext is inheriting down from the StackPanel, so the Text property is therefore also dependent on the shape of the tree; if the TextBlock is removed from the StackPanel, it’s Text property will update.

·         TextBlock.FontSize is dependent on the tree as well.  Here, you can see it’s inheriting from the StackPanel.

·         All of the TextBlock properties depend on TextBlock.Style.  For example, here TextBlock.FontWeight is coming from the Style.

·         Similarly, TextBlock.Background depends on the Style, but here it’s being set in trigger.  So TextBlock.Background in this case also depends on TextBlock.IsMouseOver.

 

Sometimes, if you write your own DP, you need to help with the dependency tracking.  You do this by calling InvalidateProperty when a property needs to be re-calculated, usually because you reference it in a CoerceValueCallback.

 

For example, here’s a DP Foo and a companion (read-only) DP named FooPlus1.  FooPlus1 just has a CoerceValueCallback that calculates “Foo+1”.  Foo therefore has a PropertyChangedCallback that invalidates FooPlus1 whenever Foo changes.

 

//

// Foo property

//

 

public int Foo

{

    get { return (int)GetValue(FooProperty); }

    set { SetValue(FooProperty, value); }

}

 

public static readonly DependencyProperty FooProperty =

    DependencyProperty.Register("Foo", typeof(int), typeof(Window1),

                                new PropertyMetadata(FooChangedCallback));

 

static void FooChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs args)

{

    // Whenever Foo changes, we need to invalidate FooPlus1, so that

    // the DependencyProperty system knows to update it (call its

    // CoerceValueCallback again).

    (d as Window1).InvalidateProperty(Window1.FooPlus1Property);

}

 

 

//

// FooPlus1 Property

//

 

public int FooPlus1

{

    get { return (int)GetValue(FooPlus1Property); }

}

 

static readonly DependencyPropertyKey FooPlus1PropertyKey =

    DependencyProperty.RegisterReadOnly("FooPlus1", typeof(int), typeof(Window1),

                                new PropertyMetadata(0, null, CoerceFooPlus1Callback));

static readonly DependencyProperty FooPlus1Property = FooPlus1PropertyKey.DependencyProperty;

 

static object CoerceFooPlus1Callback(DependencyObject d, object baseValue)

{

    return (d as Window1).Foo + 1;

}

 

 

Posted Friday, March 27, 2009 10:04 AM by MikeHillberg | 5 Comments

Filed under:

Implementing SelectedValue with the Silverlight ComboBox

Here’s the scenario … You have a Customers collection and an Orders collection.  In the Orders collection, an Order has a CustomerID property; this is the key to an item in the Customers collection.  Your goal is a ComboBox that updates the CustomerID property of a Customer, but interacts with the end user in terms of customer names.  If that doesn’t make sense, code & pictures help …

 

Here’s how you can accomplish this in markup in WPF (the l:Collection here is just a Collection<object>):

 

<Grid>

    <Grid.Resources>

       

        <l:Collection x:Key="Customers"> <!-- Collection<object> -->

            <l:Customer ID="1" CustomerName="Wilma" />

            <l:Customer ID="2" CustomerName="Betty" />

            <l:Customer ID="3" CustomerName="Fred" />

            <l:Customer ID="4" CustomerName="Barney" />

        </l:Collection>

 

        <l:Collection x:Key="Orders"> <!-- Collection<object> -->

            <l:Order Description="Magic carpet" CustomerID="1" />

            <l:Order Description="Blue suede shoes" CustomerID="4" />

            <l:Order Description="Hanna Montana wig" CustomerID="1" />

        </l:Collection>

 

    </Grid.Resources>

 

    <StackPanel>

       

        <!-- Show a customer ID -->

        <StackPanel Orientation="Horizontal">

            <TextBlock Text="Customer ID:" Margin="5"/>

            <TextBlock Name="TextBlock1" Margin="5"  Text="1"/>

        </StackPanel>

 

        <!-- Pick the customer ID that will be shown above -->

        <ComboBox ItemsSource="{StaticResource Customers}"

                  SelectedValuePath="ID"

                  SelectedValue="{Binding Text, ElementName=TextBlock1}"

                  DisplayMemberPath="CustomerName" />

       

    </StackPanel>

 

 

</Grid>

 

More on that ComboBox after these pictures,

 

Initially we get this:

 

clip_image002

… and here I am opening the ComboBox, and changing the customer to Betty (her CustomerID is 2):

clip_image004

 

So that changed the “Customer ID” from 1 to 2, but I (the end user) interacted only with customer names via the ComboBox.

 

Now let’s analyze that ComboBox markup:

 

<ComboBox ItemsSource="{StaticResource Customers}"

          SelectedValuePath="ID"

          SelectedValue="{Binding Text, ElementName=TextBlock1}"

          DisplayMemberPath="CustomerName" />

 

The ItemsSource is set to the Customers list.  So when you open the ComboBox, you’ll see customers.  Specifically, for each customer in that list, you’ll see the Customer.CustomerName, because the DisplayMemberPath is set to the “CustomerName” property.

 

Now whatever is selected in the ComboBox, I want it to show up in TextBlock1.  If I just bind SelectedItem to TextBlock1, I’d be sending the whole Customer object over, but really I just want the TextBlock.Text to show the Customer.CustomerID.  So, instead of binding SelectedItem to the TextBlock, I bind SelectedValue.  SelectedValue behaves the same as SelectedItem, though, until you set a SelectedValuePath.  When you set SelectedValuePath, then SelectedValue becomes that path into the SelectedItem.  For example, if your SelectedItem is a Customer, and your SelectedValuePath is “ID”, then SelectedValue is going to be Customer.ID, which is just what we want.

 

(Note in this example I defaulted TextBlock1.Text to ‘1’.  Otherwise the SelectedValue binding gets confused.  Alternatively, I could have set the Binding.Mode to ‘OneWayToSource’.)

 

 

Next let’s do this with the Silverlight ComboBox, because it doesn’t have the SelectedValue or SelectedValuePath properties.  But we can still get it to work with a little bit of code.  Change the markup to this:

 

<ComboBox ItemsSource="{StaticResource Customers}"

          DisplayMemberPath="CustomerName"

          SelectionChanged="ComboBox_SelectionChanged" />

 

… and write a little code:

 

private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)

{

    var comboBox = sender as ComboBox;

    TextBlock1.Text = (comboBox.SelectedItem as Customer).ID.ToString();

}

 

 

 

It’d be nice if we could do this all in markup, though, and in fact there have been some posts on the Silverlight forums to do just that with a Binding value converter.  Also, I wish I could have this feature on a TextBlock too, not just on ComboBox.  So below is a handy value converter that can be used with a Binding.  (I created this on the Silverlight 3 beta, but most of this is applicable to Silverlight 2 as well.)  The idea of this is to mimic the ComboBox mechanism.  First, here’s an example of the converter being created:

 

<Grid.Resources>

    <l:Collection x:Key="Customers"> <!-- Collection<object> -->

      ...

    </l:Collection>

   

    <l:Collection x:Key="Orders"> <!-- Collection<object> -->

      ...

    </l:Collection>

 

    <l:ValueToItemConverter x:Key="CustomerID2Name"

                            ItemsSource="{StaticResource Customers}"

                            ValuePath="ID"

                            DisplayMemberPath="CustomerName" />

</Grid.Resources>

 

… notice that it looks much like the ComboBox.  The only thing it doesn’t have is a counterpart to the SelectedValue property, because the value is what comes into the IValueConverter methods.  Now let’s use it in a trivial master/detail:

 

<Grid >

    <Grid.ColumnDefinitions>

        <ColumnDefinition/>

        <ColumnDefinition/>

    </Grid.ColumnDefinitions>

 

    <!-- List of Orders -->

    <ListBox ItemsSource="{StaticResource Orders}" Name="ListBox1">

        <ListBox.ItemTemplate>

            <DataTemplate>

                <TextBlock Text="{Binding Description}" />

            </DataTemplate>

        </ListBox.ItemTemplate>

    </ListBox>

 

    <!-- Detail for the selected Order -->

    <StackPanel Grid.Column="1"

                DataContext="{Binding SelectedItem, ElementName=ListBox1}">

        <TextBlock Text="{Binding Description}" />

       

        <!-- Order.CustomerID convertered to a Customer.CustomerName -->

        <TextBlock Text="{Binding CustomerID,

                                  Converter={StaticResource CustomerID2Name}}" />

    </StackPanel>

</Grid>

 

… which gives us this (here I’ve selected the first order):

clip_image006

The key here is that in the detail, rather than showing CustomerID 1 from the Order, we see “Wilma”.

 

 

And finally, here’s an example using this value converter in the column of a DataGrid.  Again, the items are Orders, and the column shows a CustomerID as a CustomerName, using a TextBlock when the cell isn’t being edited, and as a ComboBox when it is. 

 

First, create a version of the value converter that doesn’t have DisplayMemberPath set (because ComboBox has its own DisplayMemberPath property):

 

<l:ValueToItemConverter x:Key="CustomerID2Customer"

                        ItemsSource="{StaticResource Customers}"

                        ValuePath="ID" />

 

… and then the DataGrid itself:

 

<d:DataGrid AutoGenerateColumns="False"

            ItemsSource="{StaticResource Orders}">

    <d:DataGrid.Columns>

        <d:DataGridTextColumn Binding="{Binding Description}" />

       

        <d:DataGridTemplateColumn>

           

            <d:DataGridTemplateColumn.CellTemplate>

                <DataTemplate>

                    <TextBlock Text="{Binding CustomerID,

                                              Converter={StaticResource CustomerID2Name}}"

                               Margin="4"/>

                </DataTemplate>

            </d:DataGridTemplateColumn.CellTemplate>

           

            <d:DataGridTemplateColumn.CellEditingTemplate>

                <DataTemplate>

                    <ComboBox

                         DisplayMemberPath="CustomerName"

                         ItemsSource="{StaticResource Customers}"

                         SelectedItem="{Binding CustomerID, Mode=TwoWay,

                                          Converter={StaticResource CustomerID2Customer}}" />

                </DataTemplate>

            </d:DataGridTemplateColumn.CellEditingTemplate>

           

        </d:DataGridTemplateColumn>

    </d:DataGrid.Columns>

</d:DataGrid>

 

 

 

Posted Thursday, March 26, 2009 10:53 PM by MikeHillberg | 7 Comments

Filed under:

An ICommand with IsEnabled

ICommand is a simple interface with three members – Execute, CanExecute, and CanExecuteChanged (more on those here).  You can write your own implementations of that interface, one for each command, but that gets a bit heavyweight.  So there are several implementations of ICommand that are pluggable and re-usable, like DelegateCommand, RelayCommand, and RoutedCommand.

 

One tricky part of implementing ICommand is the CanExecuteChanged event, because you have to know when to raise it.  (The easy alternative is to raise it whenever the CommandManager.RequerySuggested event is raised, but you need to use that with caution, because it can happen frequently and get expensive.) 

 

Really, though, CanExecute and CanExecuteChanged look like a typical Foo/FooChanged property/event pair that we use in data binding.  The reason that CanExecute isn’t actually a property, though, is that it takes an optional parameter.  But if you’re not using that parameter, a property would work just as well.

 

So I’ve been playing with an ICommand implementation that takes a delegate for the Execute method (just like DelegateCommand and RelayCommand), but also has an IsEnabled property that wraps CanExecute/CanExecuteChanged.  And since it’s all a DependencyObject, you can put a binding on IsEnabled.

 

I wasn’t sure what to call it though.  The key is that it has an IsEnabled property, but DelegateCommandWithIsEnabled didn’t sound good.  And it turns out that Enabled-able isn’t a word.  The closest thing I could come up with that had “able” in it was AbilityCommand.  Good enough for a blog post.

 

Anyway, here’s what the public members look like:

 

public class AbilityCommand : DependencyObject, ICommand

{

    public AbilityCommand(

        Action<object> executeDelegate,

        Binding isEnabledBinding );

 

    public AbilityCommand( Action<object> executeDelegate )

        : this( executeDelegate, null )  {  }

   

    public bool IsEnabled {get; set; }

    public static readonly DependencyProperty IsEnabledProperty ...;

 

}

 

Note that you can set IsEnabled however you want, including with a binding, and in fact the constructor lets you pass in the Binding to use.  AbilityCommand’s ICommand.CanExecute simply returns IsEnabled.  And any time IsEnabled’s value changes, AbilityCommand raises ICommand.CanExecuteChanged.

 

And here it is in practice.  In this snippet, the (View)Model object is the first page of a wizard-type view (with a “Next” button on it).  The SubmitCommand property binds the command’s IsEnabled to the object’s IsValid property:

 

public class FirstPage : INotifyPropertyChanged

{

    public FirstPage()

    {

        SubmitCommand = new AbilityCommand(

            (x) => OnSubmit(),

            new Binding("IsValid") { Source=this } );

 

    }

    public ICommand SubmitCommand { get; private set; }

 

    private void OnSubmit()

    {

        // ...

    }

 

    // INotifyPropertyChanged fires for this IsValid property if

    // either the "FirstProperty" or "SecondProperty" property is updated

    public bool IsValid

    {

        get

        {

            return !String.IsNullOrEmpty(FirstProperty)

                && !String.IsNullOrEmpty(SecondProperty);

        }

    }

 

 

 

 

And finally, here’s the whole AbilityCommand:

 

public class AbilityCommand : DependencyObject, ICommand

{

    Action<object> _executeDelegate;

 

    public AbilityCommand( Action<object> executeDelegate )

        : this( executeDelegate, null )  {  }

   

    public AbilityCommand(

        Action<object> executeDelegate,

        Binding isEnabledBinding )

    {

        _executeDelegate = executeDelegate;

 

        if( isEnabledBinding != null )

            BindingOperations.SetBinding(this, IsEnabledProperty, isEnabledBinding);

    }

 

    void ICommand.Execute(object parameter)

    {

        _executeDelegate(parameter);

    }

 

    bool ICommand.CanExecute(object parameter)

    {

        return IsEnabled;

       

    }

    public event EventHandler CanExecuteChanged;

 

    private void RaiseCanExecuteChanged()

    {

        if( CanExecuteChanged != null )

            CanExecuteChanged( this, new EventArgs() );

    }

 

    public bool IsEnabled

    {

        get { return (bool)GetValue(IsEnabledProperty); }

        set { SetValue(IsEnabledProperty, value); }

    }

    public static readonly DependencyProperty IsEnabledProperty =

        DependencyProperty.Register("IsEnabled", typeof(bool), typeof(AbilityCommand),

                            new PropertyMetadata(OnIsEnabledChanged));

 

    static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)

    {

        (d as AbilityCommand).RaiseCanExecuteChanged();

    }

}

 

 

Posted Monday, March 23, 2009 10:13 PM by MikeHillberg | 4 Comments

Filed under:

ICommand is like a chocolate cake

(This has been updated with some information about the origin of a routed command’s route, and of focus scopes.)

 

ICommand in WPF is a pretty simple thing at its core.  But it gets more interesting and complicated as you build up functionality on top of it, and integrate it into the higher layers of the UI.  So it’s either like a layer cake, or layers of an onion.  But onion is an outside-in metaphor, and layer cake is a bottom-up metaphor, and ICommand is easier to think of bottom up, so I’m declaring ICommand to be a layer cake.

 

For example, commands provide a mechanism to abstract input (so “navigate back” means “navigate back”, whether it came from the keyboard’s back button or the mouse’s X1 button or anywhere else).  And commands provide a mechanism for the View to update the Model in a Model/View separated application.  And commands provide a way to search the element tree for a command handler, as well as a way for a command handler to say that it doesn’t want to be executed at the moment.

 

The mechanism for all those scenarios is all very similar, but the scenarios themselves seem so different that it all can get confusing.  So here’s a step-by-step description of how it all fits together …

 

 

Start with ICommand

 

ICommand itself is very straightforward:

 

public interface ICommand

{

    void Execute(object parameter);

 

    bool CanExecute(object parameter);

    event EventHandler CanExecuteChanged;

}

 

Given an instance of an ICommand, you just call Execute, and it does whatever it’s supposed to do.  Except you shouldn’t call it if it’s CanExecute is false.  If you want to know when CanExecute might be willing to give you a different answer, listen to the CanExecuteChanged event.

 

For example, here’s a super simple command:

 

public class HelloWorldCommand : ICommand

{

    public void Execute(object parameter)

    {

        Debug.WriteLine("Hello, world");

    }

 

    public bool CanExecute(object parameter)

    {

        return true;

    }

    public event EventHandler CanExecuteChanged;

}

 

If you use that like this:

 

new HelloWorldCommand().Execute(null);

 

… you’ll see “Hello, world” in the debug output window.

 

You can make it more interesting with a parameter and by checking CanExecute.  First change the command like this:

 

public class HelloWorldCommand : ICommand

{

    public void Execute(object parameter)

    {

        Debug.WriteLine(parameter);

    }

 

    public bool CanExecute(object parameter)

    {

        return parameter != null;

    }

    public event EventHandler CanExecuteChanged;

}

 

… and call it like this:

 

var hwc = new HelloWorldCommand();

if (hwc.CanExecute(this))

    hwc.Execute(this);

 

… and in my case I see “CommandCake.Window1” in the debugger output window.

 

Piece o’ cake.

 

 

Button (heart) ICommand

 

Once you have an ICommand instance handy, you can give it to a Button (on the Button.Command property), and Button knows what to do with it.  As the simplest example, you can do this with the previous command:

 

<Grid>

  <Grid.Resources>

    <local:HelloWorldCommand x:Key="hwc"/>

  </Grid.Resources>

 

  <Button Command="{StaticResource hwc}">

    Click

  </Button>

</Grid>

 

But if you do that, you’ll notice that the Button is disabled.  That’s because Button knows to call CanExecute, but we haven’t specified a parameter, and recall from above that if you pass null as an argument to CanExecute it returns false.   So Button has a CommandParameter property that lets you specify what will be passed to CanExecute and Execute:

 

<Grid>

  <Grid.Resources>

    <local:HelloWorldCommand x:Key="hwc"/>

  </Grid.Resources>

 

  <Button CommandParameter="Hello, world"  Command="{StaticResource hwc}"  >

    Click

  </Button>

</Grid>

 

Now the button is enabled, and if you click on it, you’ll see “Hello, world” in the debug output window.

 

This actually isn’t just a Button feature, it’s actually in the base class ButtonBase.  And MenuItem is a ButtonBase.  So MenuItem (heart) ICommand too.

 

 

Update the Model from the View

 

Now we’ve got enough for the classic Model/View usage of ICommand.  In the Model/View practice, your View is a bunch of elements, which is data-bound to your Model, which has your actual content.  The View can modify the model with two-way bindings and with commands.

 

First, before showing an example of this, let’s make it easier to implement ICommand, by introducing a helper class (a more complete DelegateCommand helper can be found in Prism).  This just creates an ICommand instance that takes a delegate which will be called by ICommand.Execute:

 

public class SimpleDelegateCommand : ICommand

{

    Action<object> _executeDelegate;

 

    public SimpleDelegateCommand(Action<object> executeDelegate)

    {

        _executeDelegate = executeDelegate;

    }

 

    public void Execute(object parameter)

    {

        _executeDelegate(parameter);

    }

 

    public bool CanExecute(object parameter) { return true; }

    public event EventHandler CanExecuteChanged;

}

 

 

Now let’s define a Model of a simple Debug writer:

 

public class DebugWriter

{

    ICommand _indentCommand =    new SimpleDelegateCommand( (x) => Debug.Indent() );

    ICommand _unindentCommand =  new SimpleDelegateCommand( (x) => Debug.Unindent() );

    ICommand _writeLineCommand = new SimpleDelegateCommand( (x) => Debug.WriteLine(x) );

 

    public ICommand IndentCommand { get { return _indentCommand; } }

    public ICommand UnindentCommand { get { return _unindentCommand; } }

    public ICommand WriteLineCommand { get { return _writeLineCommand; } }

 

    public int IndentSize

    {

        get { return Debug.IndentSize; }

        set { Debug.IndentSize = value; }

    }

}

 

 

… and use it from a View:

 

<StackPanel>

  <StackPanel.DataContext>

    <local:DebugWriter />

  </StackPanel.DataContext>

 

  <Grid>

    <Grid.ColumnDefinitions>

      <ColumnDefinition Width="Auto"/><ColumnDefinition />

    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>

      <RowDefinition/><RowDefinition/>

    </Grid.RowDefinitions>

 

    <TextBlock Margin="3">Indent size:</TextBlock>

    <TextBlock Margin="4" Grid.Column="1" Text="{Binding IndentSize}" />

 

    <TextBlock Grid.Row="1" Margin="3">Output string:</TextBlock>

    <TextBox Text="s" Grid.Row="1" Grid.Column="1" Name="OutputString" />

   

  </Grid>

 

  <Button Command="{Binding IndentCommand}">Indent</Button>

  <Button Command="{Binding UnindentCommand}">Unindent</Button>

  <Button CommandParameter="{Binding Text,ElementName=OutputString}"

          Command="{Binding WriteLineCommand}">WriteLine</Button>

   

</StackPanel>

 

Notice here that the Button bound to the WriteLine command (the third Button) has its CommandParameter bound to a TextBox.Text.

 

 

Routed commands: an ICommand.Execute implementation that searches for an execute handler

 

For the above example I used the SimpleDelegateCommand as my implementation of ICommand, which maps ICommand.Execute to a delegate call. 

 

WPF similarly has a built-in ICommand implementation called RoutedCommand.  You don’t give a delegate directly to the RoutedCommand, though.  Instead, the RoutedCommand walks up the tree, looking for a delegate.  It’s similar to a routed event, in fact it’s implemented as a routed event internally.  So you can put your delegate anywhere higher in the tree, in the form of an event handler, using the CommandBinding class, and it will be called by Execute.  You specify your delegate with a CommandBinding object.

 

So I add this to my Window1.xaml.cs:

 

public static RoutedCommand HelloWorldRoutedCommand = new RoutedCommand();

 

… and this to my Xaml:

 

<Window.CommandBindings>

    <CommandBinding

          Command="{x:Static local:Window1.HelloWorldRoutedCommand}"

          Executed="CommandBinding_Executed" />

</Window.CommandBindings>

 

<Grid>

    ...

    <Button Command="{x:Static local:Window1.HelloWorldRoutedCommand}">Hello, world </Button>

    ...

</Grid>

 

… and then when I click the Button, my CommandBinding_Executed method gets called:

 

private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)

{

    if( e.Source is Button )

        Debug.WriteLine((e.Source as Button).Content);

    else

        Debug.WriteLine("Hello, world");

}

 

… and once again I see “Hello, world” in the debug output.

 

Note that the event handler of the CommandBinding.Executed event can look at the Source property of the event args to see where the RoutedCommand started.  You can also get the command itself, and the CommandParameter from the args.

 

In this example I put the CommandBinding in the tree ancestry of the Button.  Alternatively, you can also register your CommandBinding globally, using the CommandManager.RegisterClassCommandBinding method.  That CommandBinding’s CanExecute then gets called no matter where in the tree the routed command’s route is starting.

 

RoutedCommand also has support for CanExecute and CanExecuteChanged.  That’s the most complicated part of routed commands, so I’m saving that for the end.

 

 

Mapping input to an ICommand (keyboard accelerators)

 

Beyond Button and MenuItem, there’s another way to get an ICommand to Execute, this one based on user input.  As an example, with the following Xaml, pressing <Control>H on the keyboard executes our HelloWorldCommand, again showing “Hello, world” in the debugger:

 

<Grid>

    <Grid.Resources>

        <local:HelloWorldCommand x:Key="hwc"/>

    </Grid.Resources>

 

    <Grid.InputBindings>

        <KeyBinding Gesture="Control+H" Command="{StaticResource hwc}" CommandParameter="Hello, world"/>

    </Grid.InputBindings>

    ...

 

 

Note that this key binding only takes effect if the keyboard focus is currently somewhere under that Grid, e.g. on a TextBox; otherwise the KeyBinding doesn’t see the <Control>H.

 

RoutedCommand also has an InputGestures property on it, where you can set the default gestures for a command.  So you can get this same behavior for the HelloWorldRoutedCommand we created earlier by defining it like this:

 

public static RoutedCommand HelloWorldRoutedCommand = new RoutedCommand()

    {

        InputGestures = { new KeyGesture(Key.H, ModifierKeys.Control) }

    };

 

… and then it will Execute no matter where your keyboard focus is.

 

 

Built-in routed commands in WPF

 

Anyone can create a RoutedCommand.  But WPF has a set of them built-in.  These are all defined as static fields in the ApplicationCommands, EditingCommands, MediaCommands, and NavigationCommands classes.  E.g. NavigationCommands has the BrowseBack command, ApplicationCommands has cut/copy/paste, EditingCommands has ToggleBold, MoveRightByWord, etc.  The Windows WM_APPCOMMAND commands get converted into these built-in routed commands automatically by the WPF input system. 

 

So, for example, this markup:

 

<Window.CommandBindings>

 

    <CommandBinding Command="{x:Static NavigationCommands.BrowseBack}"

                    Executed="BrowseBackExecuted" />

 

</Window.CommandBindings>

 

… will cause the BrowseBackExecuted method to be called when the NavigationCommands.BrowseBack command is executed, for example if you click the mouse X1 button (this is the button that makes a web browser navigate back).

 

 

From whence the command routes

 

If you call RoutedCommand’s ICommand.Execute, the route will start from the element that currently has keyboard focus.  But RoutedCommand itself has a special overload of Execute that lets you pick where the route starts:

 

public class RoutedCommand

{

    ...

    public void Execute( Object parameter, IInputElement target);

    ...

}

 

And in fact, RoutedCommand’s implementation of ICommand.Execute just calls this Execute, looking roughly like this:

 

void ICommand.Execute(object parameter)

{

    this.Execute(parameter, Keyboard.FocusedElement );

}

 

 

Button (and MenuItem) actually understands RoutedCommand in particular, in addition to ICommand in general.  So if Button.Command is a RoutedCommand, Button by default calls this RoutedCommand.Execute method, passing itself as the target.  You can override this behavior, though, with the Button.CommandTarget property.  For example, here is a Button that sends the Cut command to a TextBox:

 

<Button Command="{x:Static ApplicationCommands.Cut}"

        CommandTarget="{Binding ElementName=TextBox1}">Click</Button>

<TextBox Name="TextBox1" />

 

This gets more complicated if you’re implementing a toolbar.  Say you have a toolbar with cut/copy/paste buttons.  You want that toolbar to work against whatever the currently focused text box is, so you don’t want to have to keep setting CommandTarget.  E.g., in this:

 

<StackPanel>

    <ToolBar>

        <Button Command="{x:Static ApplicationCommands.Cut}">Cut</Button>

        <Button Command="{x:Static ApplicationCommands.Copy}">Copy</Button>

        <Button Command="{x:Static ApplicationCommands.Paste}">Paste</Button>

    </ToolBar>

    <TextBox />

    <TextBox />

</StackPanel>

 

… the Cut/Copy/Paste should route to whichever TextBox has focus.

 

It doesn’t look like this should work, but it actually does, and here’s why … ToolBar is a “focus scope”.  Focus scopes is a whole other post, but the important thing here is what happens with a routed command that’s executing and looking for a CommandBinding.  As the execute request bubbles up to the ToolBar, it sees that it’s leaving a focus scope.  At that point, rather than just continuing to walk up the visual tree, it goes next to the focused element in that focus scope.  In this example, that means that when you click on the Cut button, the search for a handler starts at the Button, then the ToolBar, then the currently-focused TextBox, which is what we want.

 

Menu is also a focus scope, so what works for ToolBar works for Menu as well.

 

 

CanExecute and CanExecuteChanged for routed commands

 

Just like RoutedCommand allows you to define a command that walks up the tree, looking for someone to handle Execute, you also want to find someone to handle CanExecute.  So RoutedCommand’s CanExecute implementation does that, and you can listen for it on CommandBinding, as you’d expect, e.g.:

 

<CommandBinding

    Command="{x:Static local:Window1.HelloWorldRoutedCommand}"

    CanExecute="CommandBinding_CanExecute"

    Executed="CommandBinding_Executed" />

 

But here’s the trick:  RoutedCommand similarly needs to raise CanExecuteChanged.  But how does it know when to raise that event, when it doesn’t know what you’re going to do in CommandBinding_CanExecute? 

 

The solution in WPF is a global event named CommandManager.RequerySuggested.  This event is fired whenever the state of the routed command world might have changed.  In fact, the implementation of ICommand.CanExecuteChanged in RoutedCommand is just a forwarder:

 

public event EventHandler CanExecuteChanged

{

    add { CommandManager.RequerySuggested += value; }

    remove { CommandManager.RequerySuggested -= value; }

}

 

Next problem:  When should the RequerySuggested event fire?  You make that happen by calling CommandManager.InvalidateRequerySuggested.  But usually you don’t have to call it, it’s called automatically in several places, mostly during keyboard/mouse input.

 

That all creates a couple of interesting implications. 

 

First of all, if you’re using routed commands, you’ve got some work taking place on every keystroke.  On the one hand, even for a fast typist (I clocked in the other day at 88 words/minute on the high-difficulty test!), user input is very infrequent in CPU timeframes.  On the other hand, it still takes time to find those CanExecute handlers, and if those handlers do anything non-trivial, and you have a lot of them, it can add up.  We’ve seen that be a problem in some larger applications.  You can mitigate that by reducing use of routed commands (just use ICommand instead), and by keeping the CanExecute implementations fast.

 

The second interesting implication is that CommandManager.RequerySuggested is a static (global) event.  Usually such events can lead to leaks, because they hold the event handler delegate forever.  But RequerySuggested instead only keeps weak references to its handler delegates.  But now you’ve got a new problem; now the delegate can be garbage collected.  You probably don’t have to worry about that, because most applications don’t listen to RequerySuggested; it’s really the Button doing this on your behalf when you set Button.Command.  But Button (and MenuItem) deal with this problem by keeping their own strong reference on that delegate, so that it doesn’t get collected.

 

 

In summary

 

So the key points to remember here:

·         ICommand is a simple definition with Execute, CanExecute, and CanExecuteChanged.

·         You can point a Button  or MenuItem at any ICommand with the Command property.  The Button/MouseItem will then Execute that command if you click it, will disable itself if CanExecute is false, and will automatically listen for CanExecuteChanged.

·         Routed commands are an ICommand implementation that searches the tree (usually starting with the focused element) for a CommandBinding that provides Execute/CanExecute handlers.

·         You can also invoke an ICommand from input, e.g. using a KeyBinding.

·         WPF pre-defines a set of routed commands in ApplicationCommands, EditingCommands, NavigationCommands, and MediaCommands.

·         Be aware that routed commands can impact perf if you use a lot of them and/or your CanExecute handlers do non-trivial work.

 

 

 

Posted Friday, March 20, 2009 3:48 PM by MikeHillberg | 5 Comments

Filed under:

A master/detail view with navigation

I don’t remember what got me thinking about it, but somewhere along the line I wanted a master/detail view with a navigation bar.  E.g., when you change selection in the master view, you can navigate back to the previous selection. 

Here’s an example (here my selection started on “Wilma”, then I changed it to “Fred”, which is why the Back button of the navigation bar is enabled):

image

It looks like a Frame, and it is, but I just used the Frame for its navigation UI and its journaling functionality; I didn’t put anything (visible anyway) into the Content of the Frame.

Here’s the basic markup for the above Window sample:

<StackPanel>

   

    <navbar:NavigationBar

        JournaledValue="{Binding SelectedItem, ElementName=_listBox, Mode=TwoWay}"

        NavigationUIVisibility="Visible"/>

 

    <Grid>

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width="Auto" />

            <ColumnDefinition Width="*" />

        </Grid.ColumnDefinitions>

 

        <!-- Master view -->

        <ListBox ItemsSource="{Binding}" Name='_listBox' Margin="5">

            <ListBox.ItemTemplate>

                <DataTemplate>

                    <TextBlock Text="{Binding FirstName}" Margin="2"/>

                </DataTemplate>

            </ListBox.ItemTemplate>

        </ListBox>

 

        <!-- Details view (more on this later) -->

        ...

 

    </Grid>

</StackPanel>

What does this do?  The NavigationBar control has the navigation UI (forward/backward button).  It also journals whatever you put in the JournaledValue property.  That means:

  • If you change NavigationBar.JournaledValue, the old value will be added to the navigation service’s “BackStack”, and the Back button will become enabled.
  • If you then click on the Back button, NavigationBar.JournaledValue will be restored to its previous value.

So the way the properties are bound in this example, if the ListBox.SelectedItem changes, the old value gets journaled by the NavigationBar.  If you then click the Back button, the ListBox.SelectedItem gets restored to that previous value.  And of course, in the full example, the details view is similarly bound to the ListBox.SelectedItem, so it updates as well.

The NavigationBar control just has a couple of properties and a default ControlTemplate.  The properties are JournaledValue (which drives, and is driven by, the navigation bar UI), and NavigationUIVisibility (Automatic, Visible, or Hidden).  Here’s the control code:

public class NavigationBar : Control

{

    static NavigationBar()

    {

        DefaultStyleKeyProperty.OverrideMetadata(

                               typeof(NavigationBar),

                               new FrameworkPropertyMetadata(typeof(NavigationBar)));

    }

 

    // The JournaledValue property is the value that controls the navigation bar UI.

 

    public object JournaledValue

    {

        get { return (object)GetValue(JournaledValueProperty); }

        set { SetValue(JournaledValueProperty, value); }

    }

    public static readonly DependencyProperty JournaledValueProperty =

        DependencyProperty.Register("JournaledValue", typeof(object), typeof(NavigationBar));

 

 

 

    // The NavigationUIVisibility property is aliased to the

    // Frame.NavigationUIVisibility property.

 

    public NavigationUIVisibility NavigationUIVisibility

    {

        get { return (NavigationUIVisibility)GetValue(NavigationUIVisibilityProperty); }

        set { SetValue(NavigationUIVisibilityProperty, value); }

    }

    public static readonly DependencyProperty NavigationUIVisibilityProperty =

        Frame.NavigationUIVisibilityProperty.AddOwner(typeof(NavigationBar));

 

 

}

The NavigationBar’s ControlTemplate simplify forwards everything to a Frame.  Frame has the UI for the navigation controls, and journals its Content property.  So for journaling, I just bound the NavigationBar.JournaledValue to Frame.Content, and Frame takes care of the rest.  (Note that NavigationBar.NavigationUIVisibility is also bound to the corresponding property on Frame.)  The only trick is, I didn’t want the JournaledValue to actually get displayed by the Frame, so I put an empty ContentTemplate on it.  This leads to:

<ControlTemplate TargetType="{x:Type local:NavigationBar}">

 

    <Frame NavigationUIVisibility

              ='{TemplateBinding local:NavigationBar.NavigationUIVisibility}'

           Content='{Binding JournaledValue,

                             RelativeSource={RelativeSource TemplatedParent},

                             Mode=TwoWay}'

           Background="{TemplateBinding Background}"

           BorderBrush="{TemplateBinding BorderBrush}"

           BorderThickness="{TemplateBinding BorderThickness}" >

 

       <!-- Don't show the frame content, we're just using Frame

            for it's navigation UI and journaling -->

 

        <Frame.ContentTemplate>

            <DataTemplate />

        </Frame.ContentTemplate>

 

    </Frame>

 

</ControlTemplate>

 

That’s it for the NavigationBar control.  Here’s the rest of the sample app too.  For the details view, I used an ItemsControl to list the “fields” of the selected object, where each field is a HeaderedContentControl.  To get the right look, I then templated HeaderedContentControl.  Easier to show the markup.  First, the sample data:

 

<Window.DataContext>

    <x:Array Type="local:Person">

        <local:Person FirstName="Wilma" LastName="Flintstone" Age="41" />

        <local:Person FirstName="Fred" LastName="Flintstone" Age="42" />

        <local:Person FirstName="Betty" LastName="Rubble" Age="36" />

        <local:Person FirstName="Barney" LastName="Rubble" Age="35" />

    </x:Array>

</Window.DataContext>

… and then the details view:

<!-- Details view -->

<ItemsControl

         DataContext="{Binding SelectedItem, ElementName=_detailsView}"

         Grid.IsSharedSizeScope="True"

         Grid.Column="1"

         Margin="5" >

 

    <!-- For the selected item, show the first name, last name, and age -->

    <HeaderedContentControl Header="First name:" Content="{Binding FirstName}" />

    <HeaderedContentControl Header="Last name:" Content="{Binding LastName}" />

    <HeaderedContentControl Header="Age:" Content="{Binding Age}" />

 

    <!-- Create a HeaderedContentControl template to display each item

         as "Label: Value", e.g. "Age: 42" -->

    <ItemsControl.Resources>

        <Style TargetType="HeaderedContentControl">

            <Setter Property="Template">

                <Setter.Value>

                    <ControlTemplate TargetType="HeaderedContentControl">

                        <Grid>

                            <Grid.ColumnDefinitions>

                                <ColumnDefinition SharedSizeGroup="FirstColumn"

                                                  Width="Auto" />

                                <ColumnDefinition SharedSizeGroup="SecondColumn" />

                            </Grid.ColumnDefinitions>

 

                            <TextBlock Text="{TemplateBinding Header}" Grid.Column="0"

                                       Margin="0,0,5,0" FontWeight="Bold"/>

                            <TextBlock Text="{TemplateBinding Content}" Grid.Column="1"

                                       Margin="0,0,5,0"/>

                        </Grid>

                    </ControlTemplate>

                </Setter.Value>

            </Setter>

        </Style>

    </ItemsControl.Resources>

 

</ItemsControl>

 

Posted Friday, January 09, 2009 10:50 AM by MikeHillberg | 3 Comments

Filed under:

A Comparable DataTrigger

Property triggers today only check for equality.  We’d like to add support for other comparison operators, but that hasn’t happened yet.  But I needed them for a project, and wrote a workaround for it.  It’s a bit hacky in a couple of places, but if you can get past that, it’s a handy way to simplify some coding.

 

Here’s a sample of what I ended up with:

 

<DataTrigger Binding="{l:ComparisonBinding Age, LT, 65}" Value="{x:Null}" >

 

The basics:

·         You have to set the DataTrigger.Value to null.  That’s the main hack.

·         The supported comparison operators are GT, GTE, LT, LTE, and EQ.

·         The comparand (“65” in the above example) is converted from string to the type of the target value (presumably Age is an int in the above example), using Compare.ChangeType or the target’s TypeConverter.

 

That’s all there is to use it.  You have to remember to set DataTrigger.Value to null, otherwise it’s relatively straightforward.

 

And here’s the implementation:

 

//

// ComparisonBinding is a Binding that should be used in a DataTrigger.Binding.

// It supports a comparison operator and a comparand, so that you can use it as a

// conditional DataTrigger.  The trick is to set {x:Null} as the DataTrigger.Value.

// E.g.:

//

//  <DataTrigger Value={x:Null}

//               Binding={h:ComparisonBinding Width, EQ, 100}"

//

// The operator can be EQ, LT, LTE, GT, GTE.

//

 

public class ComparisonBinding : Binding

{

    // Default constructor

 

    public ComparisonBinding()

        : this(null, ComparisonOperators.EQ, null)

    {

    }

 

    // Construction with an operator & comparand

 

    public ComparisonBinding(string path, ComparisonOperators op, object comparand)

        : base(path)

    {

        RelativeSource = RelativeSource.Self;

        Comparand = comparand;

        Operator = op;

        Converter = new ComparisonConverter( this );

    }

 

    // Operator and comparand

 

    public ComparisonOperators Operator { get; set; }

    public object Comparand { get; set; }

 

}

 

// Supported types of comparisons

 

public enum ComparisonOperators

{

    EQ = 0,

    GT,

    GTE,

    LT,

    LTE

}

 

//

// Thie IValueConverter is used by the StyleBinding to

// implement the logical comparisson.  ConvertBack isn't supported.

// Convert returns null if the condition is met, non-null otherwise.

//

 

internal class ComparisonConverter : IValueConverter

{

    // Keep a back reference to the StyleBinding

    ComparisonBinding _styleBinding;

 

    // Return this if the condition isn't met

    static object _notNull = new Object();

 

    // In construction, get a reference to the StyleBinding

    public ComparisonConverter(ComparisonBinding styleBinding)

    {

        _styleBinding = styleBinding;

    }

 

 

    //

    //  IValueConverter.Convert

    //

    //  Return null of the condition is met, non-null if not.

    //

 

    public object Convert(

        object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

    {

        // Simple check for null

 

        if (value == null || _styleBinding.Comparand == null)

        {

            return ReturnHelper( value == _styleBinding.Comparand );

        }

 

        // Convert the comparand so that it matches the value

 

        object convertedComparand = _styleBinding.Comparand;

        try

        {

            // Only support simple conversions in here. 

            convertedComparand = System.Convert.ChangeType(_styleBinding.Comparand, value.GetType());

        }

        catch (InvalidCastException)

        {

            // If Convert.ChangeType didn't work, try a type converter

            TypeConverter typeConverter = TypeDescriptor.GetConverter(value);

            if (typeConverter != null)

            {

                if (typeConverter.CanConvertFrom(_styleBinding.Comparand.GetType()))

                {

                    convertedComparand = typeConverter.ConvertFrom(_styleBinding.Comparand);

                }

            }

        }

 

        // Simple check for the equality case

 

        if (_styleBinding.Operator == ComparisonOperators.EQ)

        {

            // Actually, equality is a little more interesting, so put it in

            // a helper routine

 

            return ReturnHelper(

                        CheckEquals(value.GetType(), value, convertedComparand) );

        }

 

        // For anything other than Equals, we need IComparable

 

        if (!(value is IComparable) || !(convertedComparand is IComparable))

        {

            Trace(value, "One of the values was not an IComparable");

            return ReturnHelper(false);

        }

 

        // Compare the values

 

        int comparison = (value as IComparable).CompareTo(convertedComparand);

 

        // And return the comparisson result

 

        switch (_styleBinding.Operator)

        {

            case ComparisonOperators.GT:

                return ReturnHelper( comparison > 0 );

 

            case ComparisonOperators.GTE:

                return ReturnHelper( comparison >= 0 );

 

            case ComparisonOperators.LT:

                return ReturnHelper( comparison < 0 );

 

            case ComparisonOperators.LTE:

                return ReturnHelper( comparison <= 0 );

        }

 

        return _notNull;

    }

 

    //

    // This helper produces the return value; null if the values

    // match, non-null otherwise.

    //

 

    object ReturnHelper(bool result)

    {

        return result ? null : _notNull;

    }

 

    //

    // Trace output to the debugger

    //

 

    void Trace(object value, string message)

    {

        if (Debugger.IsAttached)

        {

            Debug.WriteLine("StyleBinding couldn't convert '"

                             + value.GetType()

                             + "' to '"

                             + _styleBinding.Comparand.GetType()

                             + "'");

            Debug.WriteLine("(" + message + ")");

        }

    }

 

    //

    // Check for equality of two values

    //

 

    private bool CheckEquals(Type type, object value1, object value2)

    {

        if (type.IsValueType || type == typeof(string))

        {

            return Object.Equals(value1, value2);

        }

 

        else

        {

            return Object.ReferenceEquals(value1, value2);

        }

    }

 

    //

    //  IValueConverter.ConvertBack isn't supported.

    //

 

    public object ConvertBack(

        object value,

        Type targetType,

        object parameter,

        System.Globalization.CultureInfo culture)

    {

        throw new NotImplementedException();

    }

 

}

 

 

Posted Tuesday, September 30, 2008 12:04 AM by MikeHillberg | 6 Comments

Compact CheckBox Sample

I was creating a view of an object that had a bunch of boolean properties, but I wanted to keep the visual representation small.  So I created a look for a compact CheckBox that I liked enough to post.

As an example scenario, say I’m visualizing a the attributes of a file (hidden, system, archive, and read-only).  I could do it with a TextBlock and some CheckBoxes easy enough, to create a look like this:

 

image

 

… but I want it more compact.  I want just a letter for each attribute, and I just want the letter to change when it’s checked.  That got me this:

 

image

 

Here, the light gray italicized boxes are un-checked, and the un-italicized black letters with a gray background are checked.  (So before capturing this screenshot, I had checked the read-only and archive bits on the “readme.txt” file.)   This image doesn’t show it, but there’s also a tooltip for each CheckBox that shows the full name of the attribute.

 

Here’s the ControlTemplate for the CheckBox:

 

<ControlTemplate TargetType="CheckBox">

  <Border BorderThickness="{TemplateBinding BorderThickness}"

          BorderBrush="Gray" Background="LightGray" Padding="2"

          Name="_border" >

    <Grid Margin="1">

      <TextBlock Text="{TemplateBinding Content}"

                 FontStyle="Italic"

                 Foreground="LightGray"/>

      <TextBlock Text="{TemplateBinding Content}"

                 Name="_checkedTextBlock" />

    </Grid>

  </Border>

 

  <ControlTemplate.Triggers>

    <Trigger Property="IsChecked" Value="False">

      <Setter TargetName="_checkedTextBlock"

              Property="Opacity" Value="0" />

      <Setter TargetName="_border"

              Property="Background" Value="Transparent" />

    </Trigger>

  </ControlTemplate.Triggers>

</ControlTemplate>

 

The interesting thing here is that I created two TextBlocks to show the content of the CheckBox (e.g. the “R” for the read-only attribute CheckBox).  The first one shows the content in “unchecked” form, and the second one shows it in “checked” form.  The Trigger then picks which one shows up.  I had started with a single TextBlock, and had the Trigger set the FontStyle and Foreground.  The problem with this is that the italicized text is slightly wider than the normal text, and that caused the checkboxes to jiggle a little bit when you click on them.  There’s probably other ways to solve this, but a simple solution was to have both TextBlocks there and taking up space in layout, and then let the Trigger decide which one wins for render.

 

And here’s a full template with sample usage, again with some of the more interesting parts highlighted.  (I changed the trigger so that instead of a Setter to update the Background when IsChecked changes, it animates the change.)

 

<Page

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

 

  <Grid>

   

    <Grid.ColumnDefinitions>

      <ColumnDefinition Width="Auto" />

    </Grid.ColumnDefinitions>

 

    <Grid.Resources>

     

      <!-- Implicit style to be applied to all CheckBoxs in this Window

           (this sets the new template) -->

 

      <Style TargetType="CheckBox">

       

        <Setter Property="BorderThickness" Value="1,0,0,0" />

        <Setter Property="Template">

          <Setter.Value>

           

            <ControlTemplate TargetType="CheckBox">

              <Border BorderThickness="{TemplateBinding BorderThickness}"

                      BorderBrush="Gray" Background="LightGray" Padding="2"

                      Name="_border" >

                <Grid Margin="1">

                  <TextBlock Text="{TemplateBinding Content}"

                             FontStyle="Italic"

                             Foreground="LightGray"/>

                  <TextBlock Text="{TemplateBinding Content}"

                             Name="_checkedTextBlock" />

                </Grid>

              </Border>

 

              <ControlTemplate.Triggers>

                <Trigger Property="IsChecked" Value="False">

                 

                  <Setter TargetName="_checkedTextBlock"

                          Property="Opacity" Value="0" />

                 

                  <!-- Instead of a Setter to update the background color,

                       use Trigger.EnterActions/ExitActions to animate it

                  <Setter TargetName="_border"

                          Property="Background" Value="Transparent" /> -->

 

                  <Trigger.EnterActions>