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 :
- Tab create new row and focus new row in DataGrid
- https://social.technet.microsoft.com/wiki/contents/articles/21202.wpf-programmatically-selecting-and-focusing-a-row-or-cell-in-a-datagrid.aspx
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!