Ответ 1
Это исправление решит эту проблему.
Скажем, у нас есть следующий код в приложении 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
ComboBox
.
Я получаю неожиданное поведение при вводе "Bucket" в ComboBox
и вкладке. По какой-то причине ComboBox
отображает "Лодка", но TextBox
отображает "Ведро". Я ожидаю, что оба они отобразят "Bucket" .
Он ведет себя как ожидалось, если я изменю DropDownStyle
на DropDown
, но я не хочу, чтобы пользователи могли вводить что угодно. Они должны иметь возможность вводить только те элементы, которые указаны в списке.
Еще интереснее то, что, набрав "Bucket" и уйдя, если я снова наберу "Bucket" , он отобразит "Bucket" в обоих. Если я сделаю третью попытку, она вернется к "Лодке" для ComboBox
и "Bucket" для "TextBox". Так что кажется, что он едет на велосипеде через все B.
Я не заметил этого, пока не обновился с XP до Windows 7. Я не понимаю, как это может быть связано с этим, но я все равно отказываюсь.
Если это правильно, может ли кто-нибудь сказать мне, что я должен делать для достижения ожидаемого поведения?
UPDATE Казалось бы, это связано с Windows 7. Все работает так, как ожидалось, в режиме Windows XP. Может ли кто-нибудь еще запустить Windows 7 попробовать мой код и убедиться, что я не сумасшедший?
Это исправление решит эту проблему.
Обходной путь может изменить DropDownStyle
на DropDown
и добавить следующее:
comboBox.Validating += new CancelEventHandler((o, e) =>
{
e.Cancel = (comboBox.DataSource as string[]).Contains(comboBox.Text) == false;
});
Это позволит пользователям вводить что-либо, но не позволяет им отходить от элемента управления, если только они не набрали действительный элемент.
Тем не менее, он не доволен изменением поведения с XP на Win 7.
Я столкнулся с этим сам и нашел несколько обходных решений. В конце концов я применил собственное решение как подкласс 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;
}
}
}
Я создал свое собственное решение, потому что не думал, что установка исправления для всех компьютеров пользователей была разумной или управляемой. Однако описание проблемы с исправлением описывает мою проблему. См. Список ниже, например:
Acorn
Apple,
Bad
Кровать
Кирпич
Сыр
Если я выбираю элемент, начинающийся с B, например Bed, он выбирает первый раз, начиная с B, который был бы плохим. Это если я только набираю первые два символа Be (не тестировал типизацию всей строки в качестве моего реального мира, это было бы необоснованным для пользователя).
У меня есть ComboBox со следующими настройками:
Автозапуск - SuggestAppend,
Автозаполнение - 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;
}
Кажется, что событие "Оставить" касается нажатия клавиши ввода. KeyUp (KeyDown не работал), похоже, исправил проблему с вкладками.
Я знаю, что этот ответ довольно старый, но мне нужно было ответить на эту ошибку Windows 7. Некоторое время я возился в духе Ecyrb и придумал следующую работу:
От InitForm() для приложения Добавьте это свойство:
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. Поэтому вам нужно будет подключить их для вашего собственного использования.
Переопределите метод OnTextChanged
и просто не передавайте сообщение на базу.
protected override void OnTextChanged(EventArgs e)
{
//Eat the message so it doesn't select an incorrect value.
}
Я все еще могу воспроизвести это в 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