假设我们在Windows应用程序中有以下代码:
ComboBox comboBox = new ComboBox()
{
AutoCompleteMode = AutoCompleteMode.SuggestAppend,
AutoCompleteSource = AutoCompleteSource.ListItems,
DataSource = new string[] { "", "Ark", "Boat", "Bucket" },
DropDownStyle = ComboBoxStyle.DropDownList
};
this.Controls.Add(comboBox);
TextBox textBox = new TextBox()
{
Left = comboBox.Right,
Top = comboBox.Top,
ReadOnly = true
};
textBox.DataBindings.Add("Text", comboBox, "SelectedValue");
this.Controls.Add(textBox);
这里没有魔法,只有ComboBox
绑定到字符串列表。 TextBox
显示SelectedValue
的{{1}}。
当我在ComboBox
和标签栏中输入“Bucket”时,我遇到了意外行为。出于某种原因,ComboBox
显示“船”,但ComboBox
显示“Bucket”。我希望他们都能显示“Bucket”。
如果我将TextBox
更改为DropDownStyle
,它会按预期运行,但我不希望用户能够键入他们想要的任何内容。他们应该只能输入列表中的项目。
更有趣的是,在输入“Bucket”并跳出标签后,如果我再次输入“Bucket”,它将同时显示“Bucket”。如果我进行第三次尝试,它将返回“船只”以获取{TextBox'的DropDown
和“Bucket”。所以它似乎在所有B的循环中运行。
在我最近从XP升级到Windows 7之前,我没有注意到这一点。我不明白这与此有什么关系,但无论如何我都把它丢掉了。
如果这种行为是正确的,任何人都可以告诉我应该做些什么来实现我预期的行为吗?
更新似乎 与Windows 7相关。在Windows XP模式下,所有操作都符合预期。任何运行Windows 7的人都可以尝试我的代码并验证我不是疯了吗?
答案 0 :(得分:6)
解决方法可能是将DropDownStyle
更改为DropDown
并添加以下内容:
comboBox.Validating += new CancelEventHandler((o, e) =>
{
e.Cancel = (comboBox.DataSource as string[]).Contains(comboBox.Text) == false;
});
这将允许用户输入任何内容,但除非他们输入有效的项目,否则不会让他们远离控件。
仍然对从XP变为Win 7的行为感到不满。
答案 1 :(得分:2)
此hotfix将解决此问题。
答案 2 :(得分:1)
我自己遇到了这个并找到了一些解决方法。最后,我将自己的解决方案作为ComboBox子类推出:
//as noted here: https://connect.microsoft.com/VisualStudio/feedback/details/523272/combobox-does-not-display-selectedvalue-to-user-in-windows-7
//Windows 7 has issues with ComboBoxStyle.DropDownList mixed with AutoCompleteMode.Append or AutoCompleteMode.SuggestAppend
//this class seeks to address those problems
public class BetterComboBox : ComboBox
{
private int _windows7CorrectedSelectedIndex = -1;
private int? _selectedIndexWhenDroppedDown = null;
protected override void OnDropDown(EventArgs e)
{
_selectedIndexWhenDroppedDown = SelectedIndex;
base.OnDropDown(e);
}
private bool _onDropDownClosedProcessing = false;
protected override void OnDropDownClosed(EventArgs e)
{
if (_selectedIndexWhenDroppedDown != null && _selectedIndexWhenDroppedDown != SelectedIndex)
{
try
{
_onDropDownClosedProcessing = true;
OnSelectionChangeCommitted(e);
}
finally
{
_onDropDownClosedProcessing = false;
}
}
base.OnDropDownClosed(e);
if (SelectedIndex != _windows7CorrectedSelectedIndex)
{
SelectedIndex = _windows7CorrectedSelectedIndex;
OnSelectionChangeCommitted(e);
}
}
protected override void OnSelectionChangeCommitted(EventArgs e)
{
if (!_onDropDownClosedProcessing) _windows7CorrectedSelectedIndex = SelectedIndex;
_selectedIndexWhenDroppedDown = null;
base.OnSelectionChangeCommitted(e);
}
protected override void OnSelectedIndexChanged(EventArgs e)
{
bool alreadyMatched = true;
if (_windows7CorrectedSelectedIndex != SelectedIndex)
{
_windows7CorrectedSelectedIndex = SelectedIndex;
alreadyMatched = false;
}
base.OnSelectedIndexChanged(e);
//when not dropped down, the SelectionChangeCommitted event does not fire upon non-arrow keystrokes due (I suppose) to AutoComplete behavior
//this is not acceptable for my needs, and so I have come up with the best way to determine when to raise the event, without causing duplication of the event (alreadyMatched)
//and without causing the event to fire when programmatic changes cause SelectedIndexChanged to be raised (_processingKeyEventArgs implies user-caused)
if (!DroppedDown && !alreadyMatched && _processingKeyEventArgs) OnSelectionChangeCommitted(e);
}
private bool _processingKeyEventArgs = false;
protected override bool ProcessKeyEventArgs(ref Message m)
{
try
{
_processingKeyEventArgs = true;
return base.ProcessKeyEventArgs(ref m);
}
finally
{
_processingKeyEventArgs = false;
}
}
}
答案 3 :(得分:1)
我创建了自己的解决方案,因为我没有想到将修补程序部署到所有用户的计算机是合理的还是可管理的。但是,修补程序问题描述描述了我的问题。请参阅下面的列表,例如:
橡子
苹果
坏
床
砖
干酪
如果我选择以B开头的项目,例如Bed,则会选择第一次以B开头,这将是Bad。这是因为我只输入前两个字符Be(没有测试输入整个字符串作为我的真实案例,这对用户来说是不合理的。)
我有一个ComboBox,其中包含以下设置:
AutoCompleteMode - SuggestAppend,
AutoCompleteSource - ListItems,
DropDownStyle - DropDownList。
为了解决这个问题,我做了以下工作,因为我注意到在SelectedIndexChanged事件期间选择了我想要的值,但没有选择Leave事件。
int myDesiredIndexSelection;
private void myComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
myDesiredIndexSelection = myComboBox.SelectedIndex;
}
private void myComboBox_Leave(object sender, EventArgs e)
{
myComboBox.SelectedIndex = myDesiredIndexSelection;
}
private void myForm_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Tab)
myComboBox.SelectedIndex = myDesiredIndexSelection;
}
离开事件似乎解决了按Enter键的问题。 KeyUp(KeyDown没有工作)似乎解决了标签问题。
答案 4 :(得分:1)
我仍然可以在Windows 10上重现这一点,所以我想我会将我的解决方法添加到列表中。
Private Sub LeaveComboBox(s As Object, e As EventArgs) Handles ComboBox1.Leave
Dim sender as ComboBox = DirectCast(s, ComboBox)
If sender.DroppedDown Then
' Save the correct item
Dim correctSelection as Object = sender.SelectedItem
' The core of the issue seems to be that while searching, Leave is
' triggered before DropDownClosed when you hit the TAB.
' Instead, trigger that now:
sender.DroppedDown = False
' Restore the correct item.
sender.SelectedItem = correctSelection
End If
End Sub
答案 5 :(得分:0)
我知道这个回复很老了,但我要求回答这个Windows 7错误。我在Ecyrb的静脉中徘徊了一会儿,并提出了以下解决方法:
来自InitForm()for Application添加此属性:
Me.KeyPreview = True
ComboBox所在的位置:
Private mbTab As Boolean
Private miPrevIndex As Integer = -1
Private Sub DropDownListEx_Validating(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles Me.Validating
miPrevIndex = Me.SelectedIndex
MyBase.OnSelectedIndexChanged(e)
End Sub
Private Sub DropDownListEx_PreviewKeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.PreviewKeyDownEventArgs) Handles Me.PreviewKeyDown
mbTab = e.KeyCode = Windows.Forms.Keys.Tab
End Sub
Protected Overrides Sub OnDropDownClosed(ByVal e As System.EventArgs)
MyBase.OnDropDownClosed(e)
If Me.SelectedIndex <> miPrevIndex Then
If mbTab = True Then
Me.SelectedIndex = miPrevIndex
Else
miPrevIndex = Me.SelectedIndex
End If
MyBase.OnSelectedIndexChanged(e)
End If
End Sub
现在,在我的示例中,我使用的是继承comboBox的自定义控件。因此,您需要将它们连接起来供自己使用。
答案 6 :(得分:0)
覆盖OnTextChanged
方法,只是不要将消息传递给基础。
protected override void OnTextChanged(EventArgs e)
{
//Eat the message so it doesn't select an incorrect value.
}