Silverlight 2 中支持键盘选择的组合框






4.27/5 (9投票s)
目前,ComboBoxItems 无法使用键盘选择,只能使用鼠标。本文试图解决这个问题。
引言
目前,在 Silverlight 3 中,无法通过键盘选择 ComboBox
和 ComboBoxItems
,不像在任何主要语言中那样。 在 Windows Forms、HTML、Flash/Flex 应用程序等中,用户可以选择一个 ComboBox
/ListBox
/Select
框,并通过键入键来选择所需的输入。 这给可访问性带来了问题,并可能因此让一些开发者望而却步。
互联网上许多现有的解决方案都使用了第三方 DLL,对于一些开发者来说,这可能不是一个选择。 此解决方案包括编写一个简单的扩展方法。
背景
用户可以通过键盘访问所有 ComboBox
的条目已经成为一种普遍的期望,这是一个重要的辅助功能特性。 在 Tab 键定位到控件后,他们应该能够开始输入并检索所需的 ComboBoxItem
。 此功能在 Silverlight 中默认不存在,因此需要编写。
一些实现可以让用户选择到第一个匹配的条目,但仅此而已,并且仅当 ComboBox
关闭时。 因此,在包含“Alabama”、“Alaska”和“Arkansas”的列表中,键入字母“a”只会让用户选择到“Alabama”。 键入“L”不会产生“Alabama”或“Alaska”,而是“Louisiana”。 但是,由于保持用户的期望非常重要,因此用户应该能够通过键入以下内容来选择“Alaska”:a-l-a-s。
Using the Code
任何熟悉 C# 和 Silverlight 的中级开发者都应该能够轻松实现以下代码。
在列出代码之前,实现如下
string[] state = new string[]
{ "Alabama", "Alaska", "Arizona", "Arkansas", "Delaware", "Louisiana", "Maine" };
ComboBox comboBox = new ComboBox();
for (int i = 0; i < state.Length; i++)
{
ComboBoxItem comboBoxItem = new ComboBoxItem();
comboBoxItem.Content = state[i];
comboBox.Items.Add(comboBoxItem);
}
//Must enable keyboard selection **AFTER** it all items have bene added to the ComboBox
comboBox.SetKeyboardSelection(true);
//To disable keyboard selection...
//this is useful if the items of a selection box change -
//keyboard selection should be reset.
comboBox.SetKeyboardSelection(false);
如上述注释中所述,必须在所有 ComboBoxItems
添加到 ComboBox
控件之后才能调用 SetKeyboardSelection()
。
有两种方法允许键盘选择。 SetKeyboardSelection()
是启用键盘选择唯一需要调用的方法。 另一种方法 KeyPressSearch()
在 SetKeyboardSelection
内部声明,用于进行实际搜索
public static class Extensions
{
/*
* SetKeyboardSelection enables keyboard selection on all
* ComboBoxItems, as well as on the ComboBox itself (it has not already been added).
* In addition, it tracks the "search history" that is created as the user types.
* This is done to allow the user to type in more letters to narrow down
* results (ie. "Ala" = Alabama, Alaska; "Alab" = Alabama)
*/
public static void SetKeyboardSelection(this ComboBox comboBox, bool enable)
{
string searchStringEnabled = "KeyboardSelectionEnabled";
string comboBoxTag = comboBox.Tag==null? "" : comboBox.Tag.ToString();
//See if our search history control already exists
//by analyzing the combobox tag...
bool isKeyboardEnabled = comboBoxTag.Contains(searchStringEnabled);
/*
* KeyPressSearch is defined as an anonymous delegate,
* that SetKeyboardSelection delegates
* to the KeyUp events of ComboBoxItems and the parent ComboBox.
*/
#region KeyPressSearch
KeyEventHandler keyPressSearch = delegate(object sender, KeyEventArgs e)
{
//Since Key has only certain values, A-Z, D0-D9, NumPad0-9, Space, etc.
//let's just focus on letters, and numbers, and ignore all other keys...
//if they're pressed, clear the search history another option is to
//use PlatformKeyCode, but since it's platform specific, let's not.
string key = e.Key.ToString();
if (key.Length > 1 && (key.StartsWith("D") || key.StartsWith("NumPad")))
{ //remove the D/NumPad prefix to get the digit
key = key.Replace("NumPad", "").Replace("D", "");
}
else if (key.Length > 1)
{
comboBox.Tag = searchStringEnabled + "||";
return;
}
string searchHistoryPartsString = comboBox.Tag ==
null ? searchStringEnabled + "||" : comboBox.Tag.ToString();
string[] searchHistoryParts = (searchHistoryPartsString.Contains("|")) ?
searchHistoryPartsString.Split('|') : new string[0];
int historyExpiration = 1500; //In 1.5 seconds, clear the history,
//and start new...
string searchStringHistory = searchHistoryParts.Length == 3 ?
searchHistoryParts[1] : "";
string searchStringTimeStampString = searchHistoryParts.Length == 3 ?
searchHistoryParts[2] : "";
DateTime searchStringTimeStamp;
string searchString = key;
if (DateTime.TryParse(searchStringTimeStampString, out searchStringTimeStamp)
&& DateTime.Now.Subtract
(searchStringTimeStamp).TotalMilliseconds < historyExpiration)
{ //search history is valid and has not yet expired...
searchString = searchStringHistory + key;
}
for (int i = 0; i < comboBox.Items.Count; i++)
{
if (comboBox.Items[i].GetType() == typeof(ComboBoxItem) &&
((ComboBoxItem)comboBox.Items[i]).Content.ToString().StartsWith
(searchString, StringComparison.InvariantCultureIgnoreCase))
{
comboBox.SelectedIndex = i;
comboBox.Tag = searchStringEnabled + "|" +
searchString + "|" + DateTime.Now;
break;
}
}
};
#endregion
if (!isKeyboardEnabled && enable)
{
comboBox.Tag = searchStringEnabled + "||";
//Reset the search history on open and close
comboBox.DropDownOpened += delegate
{
comboBox.Tag = searchStringEnabled + "||";
};
comboBox.DropDownClosed += delegate
{
comboBox.Tag = searchStringEnabled + "||";
};
//Add handler to parent control, so that we search even
//when combobox is closed, yet focused
comboBox.KeyUp += keyPressSearch;
for (int i = 0; i < comboBox.Items.Count; i++)
{
if (comboBox.Items[i].GetType() == typeof(ComboBoxItem))
{
((ComboBoxItem)comboBox.Items[i]).KeyUp += keyPressSearch;
}
}
}
else if (isKeyboardEnabled && !enable)
{
//Remove handler
comboBox.KeyUp -= keyPressSearch;
for (int i = 0; i < comboBox.Items.Count; i++)
{
if (comboBox.Items[i].GetType() == typeof(ComboBoxItem))
{
((ComboBoxItem)comboBox.Items[i]).KeyUp -= keyPressSearch;
}
}
comboBox.Tag = "";
}
else
{
//Remove handler
comboBox.KeyUp -= keyPressSearch;
comboBox.Tag = "";
}
}
}
所有这些工作的方式是,每个 ComboBoxItem
都会获得一个 KeyUp
事件来执行搜索。 这样,无论 ComboBox
是打开还是关闭,都可以进行搜索。 当前搜索历史记录(保存在 ComboBox
Tag 属性中)会保存 1.5 秒(如果用户搜索“A-l-a”,然后暂停两秒并开始搜索“D”,他们将进行新的搜索。)
关注点
值得指出的是,KeyPressSearch()
方法使用 KeyEventArgs
对象,该对象公开两种获取按键的方式:enum KeyEventArgs.Key
(在 System.Windows.Input.Key
中定义)和 KeyEventArgs.PlatformKeyCode
(一个平台特定的整数)。
这里有一个权衡。 由于 System.Windows.Input.Key
在其公开的键方面相当有限,因此搜索仅限于字母数字,因此特殊字符(!、-、+、@、#、$ 等)将被忽略。 其中一些字符可以很容易地被接受,但并非所有字符。
使用 KeyEventArgs.PlatformKeyCode
允许更大的键范围,但是,由于键代码是平台特定的,因此在接受和拒绝范围时需要进行更多的考虑,因为它取决于用户正在使用的操作系统。
历史
- 更改了实现,将历史记录存储在 Tag 属性中以获得更好的结果
- 添加了删除键盘选择和重新添加它的能力。 这对于项目刷新或更改时很有用。 在这种情况下,应删除键盘选择并再次添加
- 解决了使用多个
ComboBoxes
时出现的错误 - 上次更改:2010 年 3 月 24 日