I made this function to select the item on DataGrid on user keypress. If the user key is "A" it will select the first item where username starts with letter "A". If the user key is again "A" it will select the next item where username starts with letter "A" and so on. The function works great, but what I want is when there are no more items where username starts with "A" to start over (select the first item), it currently remains on the last item where username start with letter "A".
private static Key lastKey;
private static int lastFoundIndex = 0;
public static void AccountsDataGrid_SearchByKey(object sender, KeyEventArgs e)
{
DataGrid dataGrid = sender as DataGrid;
if ((dataGrid.Items.Count == 0) && !(e.Key >= Key.A && e.Key <= Key.Z))
{
return;
}
if ((lastKey != e.Key) || (lastFoundIndex == dataGrid.Items.Count - 1))
{
lastFoundIndex = 0;
}
for (int i = lastFoundIndex; i < dataGrid.Items.Count; i++)
{
if (dataGrid.SelectedIndex == i)
{
continue;
}
Account account = dataGrid.Items[i] as Account;
if (account.Username.StartsWith(e.Key.ToString(), true, CultureInfo.CurrentCulture))
{
dataGrid.ScrollIntoView(account);
dataGrid.SelectedItem = account;
lastFoundIndex = i;
break;
}
}
lastKey = e.Key;
}
Update (with solution):
Inspired by Danielle's idea, I have changed my code like below, works like a charm.
private static Key lastKey;
private static int lastFoundIndex = 0;
public static void AccountsDataGrid_SearchByKey(object sender, KeyEventArgs e)
{
DataGrid dataGrid = sender as DataGrid;
if ((dataGrid.Items.Count == 0) && !(e.Key >= Key.A && e.Key <= Key.Z))
{
return;
}
if ((lastKey != e.Key) || (lastFoundIndex == dataGrid.Items.Count - 1))
{
lastFoundIndex = 0;
}
Func<object, bool> itemCompareMethod = (item) =>
{
Account account = item as Account;
if (account.Username.StartsWith(e.Key.ToString(), true, CultureInfo.CurrentCulture))
{
return true;
}
return false;
};
lastFoundIndex = FindDataGridRecordWithinRange(dataGrid, lastFoundIndex, dataGrid.Items.Count, itemCompareMethod);
if (lastFoundIndex == -1)
{
lastFoundIndex = FindDataGridRecordWithinRange(dataGrid, 0, dataGrid.Items.Count, itemCompareMethod);
}
if (lastFoundIndex != -1)
{
dataGrid.ScrollIntoView(dataGrid.Items[lastFoundIndex]);
dataGrid.SelectedIndex = lastFoundIndex;
}
if (lastFoundIndex == -1)
{
lastFoundIndex = 0;
dataGrid.SelectedItem = null;
}
lastKey = e.Key;
}
private static int FindDataGridRecordWithinRange(DataGrid dataGrid, int min, int max, Func<object, bool> itemCompareMethod)
{
for (int i = min; i < max; i++)
{
if (dataGrid.SelectedIndex == i)
{
continue;
}
if (itemCompareMethod(dataGrid.Items[i]))
{
return i;
}
}
return -1;
}
The solution you wound up with is needlessly complex and checks rows it doesn't need to check. The two static variables to maintain state are not needed either. Try this instead:
public void MainGrid_SearchByKey(object sender, KeyEventArgs e)
{
DataGrid dataGrid = sender as DataGrid;
if (dataGrid.Items.Count == 0 || e.Key < Key.A || e.Key > Key.Z)
{
return;
}
Func<object, bool> doesItemStartWithChar = (item) =>
{
Account account = item as Account;
return account.Username.StartsWith(e.Key.ToString(), true, CultureInfo.InvariantCulture);
};
int currentIndex = dataGrid.SelectedIndex;
int foundIndex = currentIndex;
// Search in following rows
foundIndex = FindMatchingItemInRange(dataGrid, currentIndex, dataGrid.Items.Count - 1, doesItemStartWithChar);
// If not found, search again in the previous rows
if (foundIndex == -1)
{
foundIndex = FindMatchingItemInRange(dataGrid, 0, currentIndex - 1, doesItemStartWithChar);
}
if (foundIndex > -1) // Found
{
dataGrid.ScrollIntoView(dataGrid.Items[foundIndex]);
dataGrid.SelectedIndex = foundIndex;
}
}
private static int FindMatchingItemInRange(DataGrid dataGrid, int min, int max, Func<object, bool> doesItemStartWithChar)
{
for (int i = min; i <= max; i++)
{
if (dataGrid.SelectedIndex == i) // Skip the current selection
{
continue;
}
if (doesItemStartWithChar(dataGrid.Items[i])) // If current item matches the string, return index
{
return i;
}
}
return -1;
}
Regarding your comment, just add this check:
if (Keyboard.Modifiers.HasFlag(ModifierKeys.Control))
{
return;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With