user9969
user9969

Reputation: 16040

Datagrid Set focus on newly added row

I have a datagrid bound to an observable collection. When the user press the "add button" it adds a new row and I do this by adding a new element to the observablecollection.

I cannot figure out how to make the newly added row with the first cell in focus as if we were editing. I am using a MVVM pattern.

Any ideas or suggestions?

Upvotes: 8

Views: 8010

Answers (6)

Mario Chapa
Mario Chapa

Reputation: 65

Solved it in my case thanks to Jake Berger's answer.

Binding the DataGrid's SelectedItem and handling the LoadingRow event works beautifully.

e.g.:

<DataGrid SelectionUnit="FullRow" SelectionMode="Single" ItemsSource="{Binding Source=MyList}" SelectedItem="{Binding SelectedDataItem, Mode=TwoWay}" LoadingRow="MyDataGrid_LoadingRow"></DataGrid>
private void MyDataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
          SelectedDataItem = (MyDataObject)e.Row.Item;
}

Upvotes: 0

Maxence
Maxence

Reputation: 13286

The answer given by Gauss is the right approach, but with some code, it is clearer:

void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
    e.Row.Loaded += Row_Loaded;
}

void Row_Loaded(object sender, RoutedEventArgs e)
{        
    var row = (DataGridRow) sender;
    row.Loaded -= Row_Loaded;
    DataGridCell cell = GetCell(dataGrid, row, 0);
    if (cell != null) cell.Focus();
    dataGrid.BeginEdit();        
}

static DataGridCell GetCell(DataGrid dataGrid, DataGridRow row, int column)
{
    if (dataGrid == null) throw new ArgumentNullException("dataGrid");
    if (row == null) throw new ArgumentNullException("row");
    if (column < 0) throw new ArgumentOutOfRangeException("column");

    DataGridCellsPresenter presenter = FindVisualChild<DataGridCellsPresenter>(row);
    if (presenter == null)
    {
        row.ApplyTemplate();
        presenter = FindVisualChild<DataGridCellsPresenter>(row);
    }
    if (presenter != null)
    {
        var cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
        if (cell == null)
        {
            dataGrid.ScrollIntoView(row, dataGrid.Columns[column]);
            cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
        }
        return cell;
    }
    return null;
}

static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
        var visualChild = child as T;
        if (visualChild != null)
            return visualChild;
        var childOfChild = FindVisualChild<T>(child);
        if (childOfChild != null)
            return childOfChild;
    }
    return null;
}

You retrieve the row in the DataGrid.LoadingRow event, but the cell is not yet available. So you put an handler on the Loaded event in order to wait for this event to occur and then you can retrieve the cell and put the focus on it.

The handler is removed to avoid a new triggering.

The editing session can also be started with dataGrid.BeginEdit().

All of this is in the code-behind, because it belongs to the view.

Upvotes: 4

Gauss
Gauss

Reputation: 1148

What worked for me was a combination of LoadingRow, e.Row.Loaded and the GetCell() method of the following link:

http://social.technet.microsoft.com/wiki/contents/articles/21202.wpf-programmatically-selecting-and-focusing-a-row-or-cell-in-a-datagrid.aspx

The DataGrid.LoadingRow event is called before the cell is available through GetCell. But in the DaraGridRow.Loaded event, the cell is available.

After that, you can use cell.Focus() and DataGrid.BeginEdit().

Upvotes: 0

Avi ba
Avi ba

Reputation: 461


Hopes that will help others.
i'll show the MVVM way, in the View:

<DataGrid Name="dg1" ItemsSource="{Binding Table}" AutoGenerateColumns="False" Margin="3" SelectionMode="Single" IsReadOnly="True" SelectedIndex="{Binding Path=SelectedIndex, Mode=TwoWay}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Date"  Binding="{Binding mydate}" MinWidth="100"></DataGridTextColumn>
        <DataGridTextColumn Header="Count" Binding="{Binding Count}" MinWidth="100"></DataGridTextColumn>
    </DataGrid.Columns>

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged" SourceName="dg1" >
            <i:InvokeCommandAction Command="{Binding DgSelectionChanged}" CommandParameter="{Binding ElementName=dg1}"/>
        </i:EventTrigger>

    </i:Interaction.Triggers>          
</DataGrid>

now in the View-Model:
you need to set the SelectedIndex after : add, remove, etc...and

 private ICommand dgSelectionChanged;
    /// <summary>
    /// when the datagrid Selection Changes
    /// </summary>
    public ICommand DgSelectionChanged
    {
        get
        {
            return dgSelectionChanged ??
            (dgSelectionChanged = new RelayCommand<DataGrid>(dg1 =>
            {
              // whatever you want when the Selection Changes


                SelectedIndex= d
                //select the item 
                if (dg1.SelectedIndex > -1)
                {
                    dg1.Focus();
                    dg1.CurrentCell = new DataGridCellInfo(dg1.Items[dg1.SelectedIndex], dg1.Columns[0]);
                }
            }));
        }
    }  

by the way, to select the first row after control loaded, add this to the view:

        <!-- select the row after control loaded -->
        <i:EventTrigger EventName="Loaded" SourceName="dg1" >
            <i:InvokeCommandAction Command="{Binding DgSelectionChanged}" CommandParameter="{Binding ElementName=dg1}"/>  
        </i:EventTrigger>
    </i:Interaction.Triggers> 

thanks, Avi.

Upvotes: 0

Tod
Tod

Reputation: 8232

You want to do something like this:

DataGridCell cell = GetCell(rowIndex, colIndex);
cell.Focus;

Of course you need that elusive GetCell() method. Vincent Sibal from MSFT wrote up that method (and the required GetRow as well) in this forum post. I was searching for something else when I came across this so I haven't used it but others seem to have had good luck with it. You'll notice there are links to his blog which you might also find helpful for all things DataGrid related.

Upvotes: 0

Jake Berger
Jake Berger

Reputation: 5357

Try capturing the DataGrid's LoadingRow (or similar) event. Do a SetFocus(e.Row) (or similar) on the row. This is purely View-oriented, so it conforms to MVVM.

Upvotes: 1

Related Questions