Home > Blockchain >  How to set Focus on new row after TAB Pressed in the last row in C# WPF DataGrid?
How to set Focus on new row after TAB Pressed in the last row in C# WPF DataGrid?

Time:01-14

I'm using C# WPF , I have a DataGrid that is bound to an ObservableCollection , I want to keep focus on DataGrid's row when user is entering value with keyboard on DataGrid, I mean when last row is focused after value entered , instead of lost focus on Datagrid let's go to focus on the next row

also I want to use enter instead of TAB

but these solutions in stack overflow doesn't work for me ! : WPF DataGrid - How to stay focused on the bottom of the DataGrid as new rows are added?

.

What have I tried :

1- First case:

private void DGR_SUB_INVOLST_LoadingRow(object sender, DataGridRowEventArgs e)
{
            
  
  if (YOUR_DATA_GRID.Items.Count > 0 && YOUR_DATA_GRID != null)
    {
         //___________This piece of code is to go to the next line with enter for a single line that is entered for the first time and not to focus on the new line.
             bool ISOK = true;
             //If there is no more than one line
             // The reason for minus 2 is that because we gave something to it in Add New Item, a new line is added, so if we see 1 line
             //It means that the number of items is 2, item zero, the first line, item 1, a new line named New Place Holder
            if (YOUR_DATA_GRID.Items.Count - 2 <= 2)
            {
               //Don't get null error from where you delete the line and then refresh
                if (!(YOUR_DATA_GRID.Items[0] as CUSTOM_MODEL is null))
                {
                   // if is new
                    if ((YOUR_DATA_GRID.Items[0] as CUSTOM_MODEL).id is null)
                    {
                        ISOK = false;
                        return;
                    }
                }
            }
           //If the above condition is not met and there is also the last line, let's put the focus on the new line
            if (ISOK)
            {
                if ((YOUR_DATA_GRID.Items.Count - 2) == CURRENT_ROW_INDEX)
                {
                    e.Row.Loaded  = Row_Loaded;
                }
            }
        }     
        
        }
        void Row_Loaded(object sender, RoutedEventArgs e)
        {
            YOUR_DATA_GRID.SelectedItem = YOUR_DATA_GRID.Items[YOUR_DATA_GRID.Items.Count - 1];
            YOUR_DATA_GRID.ScrollIntoView(YOUR_DATA_GRID.Items[YOUR_DATA_GRID.Items.Count - 1]);
            DataGridRow dgrow = (DataGridRow)YOUR_DATA_GRID.ItemContainerGenerator.ContainerFromItem(YOUR_DATA_GRID.Items[YOUR_DATA_GRID.Items.Count - 1]);
            dgrow.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
        }

2- Second case:

Using below method in these events :

  • RowEditEnding

  • AddingNewItem

interestingly this code doesn't work to focus on the next row !:

 var IIDEX = CURRENT_ROW_INDEX   1;

        if (IIDEX > YOUR_DATA_GRID.Items.Count - 1)
        {
            IIDEX = 0;
        }
        DataGridRow row = (DataGridRow)YOUR_DATA_GRID.ItemContainerGenerator.ContainerFromIndex(IIDEX);
        if (row is null)
        {
            YOUR_DATA_GRID.ScrollIntoView(YOUR_DATA_GRID.Items[IIDEX]);
            row = (DataGridRow)YOUR_DATA_GRID.ItemContainerGenerator.ContainerFromIndex(IIDEX);
            object item = YOUR_DATA_GRID.Items[IIDEX];
            YOUR_DATA_GRID.SelectedItem = item;
            row.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));

        }
        else
        {
            object item = YOUR_DATA_GRID.Items[IIDEX];
            YOUR_DATA_GRID.SelectedItem = item;
            YOUR_DATA_GRID.ScrollIntoView(item);
            row.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
        }

all of thing's that I've tried, didn't work

My Full Source code Database SQL Server: https://mega.nz/file/w5ZyzbzZ#26LW3lHyO72EJnVKlzldpCczB9ePQl7gmf0xQMKEV2k

NOTE:↑ in that source code I removed my tries for Focus On next row ,

please guide me how do I do that

CodePudding user response:

I found these answers, but I don't know if these are the best ways or not! base on these answers :

Method 1:

//Method 1
            private void MyGrid_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key is Key.Enter && Keyboard.Modifiers == ModifierKeys.None)
        {
            if (MyDataGrid1.SelectedIndex == MyDataGrid1.Items.Count - 2 && MyDataGrid1.CurrentColumn.DisplayIndex == MyDataGrid1.Columns.Count - 1)
            {
                // DataGridRow row = (DataGridRow)MyDataGrid1.ItemContainerGenerator.ContainerFromIndex(MyDataGrid1.Items.Count - 1);
                // if (row is null)
                // {
                // MyDataGrid1.UpdateLayout();
                // MyDataGrid1.ScrollIntoView(MyDataGrid1.Items[MyDataGrid1.Items.Count - 1]);
                // row = (DataGridRow)MyDataGrid1.ItemContainerGenerator.ContainerFromIndex(MyDataGrid1.Items.Count - 1);
                // }
                MyDataGrid1.SelectedIndex = MyDataGrid1.Items.Count - 1;
                MyDataGrid1.CurrentCell = new DataGridCellInfo(MyDataGrid1.SelectedItem, MyDataGrid1.Columns[0]);
                //MyDataGrid1.BeginEdit();
            }

            e.Handled = true;
            Send(Key.Tab);
        }
    }

Method 2:

//Mtehod 2 :

   private void MyDataGrid1_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e)
    {
        if (MyDataGrid1.SelectedIndex == MyDataGrid1.Items.Count - 2)
        {
            MyDataGrid1.SelectedIndex = MyDataGrid1.Items.Count - 1;
            MyDataGrid1.CurrentCell = new DataGridCellInfo(MyDataGrid1.Items[MyDataGrid1.Items.Count - 1], MyDataGrid1.Columns[0]);
        }
    }
    private void MyDataGrid1_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Tab)
        {
            if (MyDataGrid1.SelectedIndex == MyDataGrid1.Items.Count - 2 && MyDataGrid1.CurrentColumn.DisplayIndex == MyDataGrid1.Columns.Count - 1)
            {
                MyDataGrid1.CommitEdit(DataGridEditingUnit.Row, false);
                e.Handled = true;
            }
        }
    }

Method 3: //If you want the enter key to act like the tab key in the entire window of your program

 //Method 3:
          private void MyGrid_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            //if (e.Key is Key.Enter && Keyboard.Modifiers == ModifierKeys.None)
            //{
            //    e.Handled = true;
            //    Send(Key.Tab);
            //}
            DataGrid dg = MyDataGrid1;
            UIElement uie = e.OriginalSource as UIElement;
            //Is Foucs really inside the DataGrid
            if (((FrameworkElement)uie).Parent is DataGridCell || uie is DataGridCell)
            {

            }
            if (dg != null)
            {
                if (e.Key == Key.Enter && Keyboard.Modifiers == ModifierKeys.None)
                {
                    if (dg.Columns.IndexOf(dg.CurrentColumn) == dg.Columns.Count - 1 && dg.SelectedIndex == dg.Items.Count - 2)
                    {
                        //change the selected item to the last row
                        dg.SelectedItem = dg.Items[dg.Items.Count - 1];

                        //change the current cell to the first cell in the last row
                        dg.CurrentCell = new DataGridCellInfo(dg.SelectedItem, dg.Columns[0]);
                    }
                    else
                    {
                        uie.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
                    }
                    e.Handled = true;
                    //dg.BeginEdit();
                }
            }
        }

Method 4 (Recommended):

//Mtehod 4 :
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

/// <summary>
/// An attached behavior that modifies the tab behavior for a <see cref="DataGrid"/>.
/// </summary>
public static class DataGridBehavior
{
    /// <summary>
    /// Identifies the <c>NewLineOnTab</c> attached property.
    /// </summary>
    public static readonly DependencyProperty NewLineOnTabProperty = DependencyProperty.RegisterAttached(
        "NewLineOnTab",
        typeof(bool),
        typeof(DataGridBehavior),
        new PropertyMetadata(default(bool), OnNewLineOnTabChanged));

    /// <summary>
    /// Sets the value of the <c>NewLineOnTab</c> attached property.
    /// </summary>
    /// <param name="element">The <see cref="DataGrid"/>.</param>
    /// <param name="value">A value indicating whether to apply the behavior.</param>
    public static void SetNewLineOnTab(DataGrid element, bool value)
    {
        element.SetValue(NewLineOnTabProperty, value);
    }

    /// <summary>
    /// Gets the value of the <c>NewLineOnTab</c> attached property.
    /// </summary>
    /// <param name="element">The <see cref="DataGrid"/>.</param>
    /// <returns>A value indicating whether to apply the behavior.</returns>
    public static bool GetNewLineOnTab(DataGrid element)
    {
        return (bool)element.GetValue(NewLineOnTabProperty);
    }

    /// <summary>
    /// Called when the value of the <c>NewLineOnTab</c> property changes.
    /// </summary>
    /// <param name="sender">The event sender.</param>
    /// <param name="e">The event arguments.</param>
    private static void OnNewLineOnTabChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        DataGrid d = sender as DataGrid;

        if (d == null)
        {
            return;
        }

        bool newValue = (bool)e.NewValue;
        bool oldValue = (bool)e.OldValue;

        if (oldValue == newValue)
        {
            return;
        }

        if (oldValue)
        {
            d.PreviewKeyDown -= AssociatedObjectKeyDown;
        }
        else
        {
            d.PreviewKeyDown  = AssociatedObjectKeyDown;
            KeyboardNavigation.SetTabNavigation(d, KeyboardNavigationMode.Contained);
        }
    }

    /// <summary>
    /// Handles the <see cref="UIElement.KeyDown"/> event for a <see cref="DataGridCell"/>.
    /// </summary>
    /// <param name="sender">The event sender.</param>
    /// <param name="e">The event arguments.</param>
    private static void AssociatedObjectKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key != Key.Tab)
        {
            return;
        }

        DataGrid dg = e.Source as DataGrid;

        if (dg == null)
        {
            return;
        }

        if (dg.CurrentColumn.DisplayIndex == dg.Columns.Count - 1)
        {
            var icg = dg.ItemContainerGenerator;

            if (dg.SelectedIndex == icg.Items.Count - 2)
            {
                dg.CommitEdit(DataGridEditingUnit.Row, false);
            }
        }
    }
}   

(method 4 ↑) Add this setting in your datagrid :xmlns:sampleDataGridApp="clr-namespace:DATAGRID_ADVANCE.DEPEND"

sampleDataGridApp:DataGridBehavior.NewLineOnTab="True"
                KeyboardNavigation.TabNavigation="Contained"
                IsSynchronizedWithCurrentItem="True"

I found these answers after spending a lot of time searching on the internet and YouTube, if anyone knows a better solution,

please guide me.

I don't know why implementing these simple issues in WPF is so hard!

  • Related