WPF C# - DataGrid在用户按键上选择项目

时间:2015-11-25 17:53:16

标签: c# .net wpf datagrid keypress

我使用此功能在用户按键上选择DataGrid上的项目。如果用户密钥是" A"它将选择用户名以字母" A"开头的第一个项目。如果用户密钥又是" A"它将选择下一个用户名以字母#34开头的项目; A"等等。该功能运行良好,但我想要的是当没有其他项目的用户名以" A"要重新开始(选择第一项),它目前仍然是最后一个项目,其中用户名以字母#34开头; 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;
}

更新(使用解决方案):

受Danielle的想法的启发,我已经改变了我的代码,就像一个魅力。

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;
}

4 个答案:

答案 0 :(得分:1)

您可以将for-loop提取到一个单独的方法中,以确定是否找到了另一个项目。

public int FindRecordWithinRange(DataGrid dataGrid, int min, int max)
{
    for (int i = min; i < max; 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;

            return i;
        }
    }

    return -1;
}

并使用以下内容调用它:

lastFoundIndex = FindRecordWithinRange(dataGrid, lastFoundIndex, dataGrid.Items.Count);
if (lastFoundIndex == -1)
    lastFoundIndex = FindRecordWithinRange(dataGrid, 0, dataGrid.Items.Count);
if (lastFoundIndex == -1)
    dataGrid.SelectedItem = null;

这基本上会尝试从头开始搜索列表,并且还会通过清除选择来处理没有找到项目的情况。在这种情况下,您可能还想滚动到开头,此时的处理取决于您想要做什么。

您可能想要做的另一件事是提取ScrollIntoView和Selection逻辑,并在确定索引后处理它。

答案 1 :(得分:1)

你最后解决的解决方案是不必要的复杂并检查它不需要检查的行。也不需要维护状态的两个静态变量。试试这个:

    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;
    }

关于您的评论,只需添加此项检查:

        if (Keyboard.Modifiers.HasFlag(ModifierKeys.Control))
        {
            return;
        }

答案 2 :(得分:0)

你的代码说要从头开始再次搜索,lastFoundIndex应该等于dataGrid.Items.Count - 1(假设再次按下相同的键)。 但是您最后找到的帐户可能不在Items集合的末尾,因此lastFoundIndex未设置为dataGrid.Items.Count - 按下上一个键时为1。在这种情况下,你的条件不能重新开始。

尝试更改上次找到的检查和“for”循环,如下所示:

bool found = false; 
bool lastFound = true;

if (lastKey != e.Key || lastFound)
{
    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))
    {
        if (!found)
        {
            dataGrid.ScrollIntoView(account);
            dataGrid.SelectedItem = account;

            lastFoundIndex = i;
            found = true;
        }
        else
        {
            lastFound = false;
            break;
        }
    }
}

基本上它会尝试再找一个项目来查看它是否是最后一场比赛。

答案 3 :(得分:0)

如果我改变我的功能,如下所示,它将在最后一个项目上停留2次#34; A&#34;在去第一个字母&#34; A&#34; (假设用户密钥仍为&#34; A&#34;)。

    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 ((lastFoundIndex > 0) && (lastFoundIndex == i))
            {
                lastFoundIndex = 0;
            }

            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;
    }