-1

I'm working on a WPF MVVM project. A DataGrid shows the records found by a service. Each row has a button which executes an action with the record chosen.

public class ReferenceDossier
{
}

XAML

<GroupBox>
    <Grid>
        <StackPanel>
            <TextBox>
                <TextBox.InputBindings>
                    <KeyBinding Key="Enter" Command="{Binding SearchCommand}"/>
                </TextBox.InputBindings>
            </TextBox>
        </StackPanel>
    </Grid>
</GroupBox>

<GroupBox>
    <Grid>
        <StackPanel>
            <DataGrid ItemsSource="{Binding ReferenceDossiers}">
                <DataGrid.Columns>
                    <DataGridTemplateColumn>
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <Button Command="{Binding Command}"
                                                CommandParameter="{Binding}">>
                                    <iconPacks:PackIconFontAwesome Kind="PlusCircle" Foreground="#FF94bf00"/>
                                </Button>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
            </DataGrid>
        </StackPanel>
    </Grid>
</GroupBox>

When I make a search with the keyword in the TextBox and tap Enter, it performs a calculation and shows the results in DataGrid.

What I want to code:

If the user press ENTER again (after records shown on DataGrid) and the user hasn't selected yet a row, the Command of the Button of the first row must be invoked, not important how many records in the grid, the first one must be chosen.

If the user selects a row in DataGrid and press ENTER, the Command of the Button of that row is invoked.

If zero record, do nothing.

The goal is to reduce the time of chose on GUI.

But I can't find a way to do that by MVVM approach.

With some tries, when I press ENTER after tapping the keyword, TextBox is always focused. I press ENTER again, it reperforms the calculation.

Antoine V
  • 6,998
  • 2
  • 11
  • 34
  • Do you have any `ICommand` created for the button inside `CellTemplate`? How are you handling the *Tap* operation currently? – dhilmathy Jul 06 '18 at 10:52
  • @dhilmathy of course, it has. I updated the question. Actually, it can be executed with a click by mouse – Antoine V Jul 06 '18 at 11:38
  • If executing the `Command` is the only requirement on `Button` *Tap*, why can't you call `Command.Execute()` (after calculation) inside your `ViewModel` itself. – dhilmathy Jul 06 '18 at 11:43
  • I updated the question. Hope it more clear for you. – Antoine V Jul 06 '18 at 11:55
  • So if the user presses ENTER and there is only one record in the DataGrid, the Command of this record should be invoked? If there is zero records or more than one record, the SearchCommand should be invoked? Is that what you want? – mm8 Jul 06 '18 at 12:42
  • I updated the question. sorry for my English that makes the confuse. The goal of the question is very simple. – Antoine V Jul 06 '18 at 13:02
  • Why don't you invoke it from the viewmodel? Bind SelectedItem property of the DataGrid to an object property in the vm then check if selecteditem is null. If it's null fire the first element command from viewmodel – Gianluca Conte Jul 06 '18 at 13:17

2 Answers2

0

You can use EventTrigger to achieve this.

XAML

<Style TargetType="DataGridRow">
    <Style.Triggers>
        <local:EnterDownEventTrigger EventName="KeyDown">
            <i:InvokeCommandAction Command="{Binding Command}" CommandParameter="{Binding}"/>
        </local:EnterDownEventTrigger>
    </Style.Triggers>
</Style>

As you want the Command to get invoked only on Enter key, you need to write custom EventTrigger for this.

Custom Event Trigger for Enter Key

using System.Windows.Interactivity;
public class EnterDownEventTrigger : EventTrigger 
{    
    public EnterDownEventTrigger() : base("KeyDown") { }

    protected override void OnEvent(EventArgs eventArgs) 
    {
        var e = eventArgs as KeyEventArgs;
        if (e != null && e.Key == Key.Enter)
            this.InvokeActions(eventArgs);
    }
}

Reference the assembly: System.Windows.Interactivity.dll

dhilmathy
  • 2,800
  • 2
  • 21
  • 29
  • Looks at my updated question pls. I think your answer will never work because the DataGrid is never focused. – Antoine V Jul 06 '18 at 13:48
0

Solution found:

Following this answer, we can set focus to control by a custom Property IsFocused.

So I put this property to the button, set true for the property if that is the first row and bind the Enter InputBinding

public class ReferenceDossier : INotifyPropertyChanged
    {
        private bool _selected;
        public bool Selected
        {
            get
            {
                return _selected;
            }
            set
            {
                _selected = value;
                RaisePropertyChanged("Selected");
            }
        }
}

Set Selected for the first record to focus the button of the first row.

if(ReferenceDossiers.FirstOrDefault() != null)
                    ReferenceDossiers.First().Selected = true;

XAML

                     <DataGridTemplateColumn>
                            <DataGridTemplateColumn.CellStyle>
                                <Style TargetType="DataGridCell">
                                    <Setter Property="IsTabStop" Value="False" />
                                    <Setter Property="BorderThickness" Value="0"/>                                        
                                </Style>
                            </DataGridTemplateColumn.CellStyle>
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <Button behaviors:FocusExtension.IsFocused="{Binding Selected}"
                                         Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls:MetroWindow}}, Path=DataContext.AddCommand}"
                                            CommandParameter="{Binding}">
                                        <Button.InputBindings>
                                            <KeyBinding Key="Enter" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls:MetroWindow}}, Path=DataContext.AddCommand}"
                                            CommandParameter="{Binding}"/>
                                        </Button.InputBindings>
                                        <iconPacks:PackIconFontAwesome Kind="PlusCircle" Foreground="#FF94bf00"/>
                                    </Button>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>

To choose the button of others row by the Up/Down press, we should customize the Cell, if not, the CellTemplate is focused not the button and the ENTER press doesn't work.

Antoine V
  • 6,998
  • 2
  • 11
  • 34