Как отменить событие ComboBox SelectionChanged?
Есть ли простой способ предложить пользователю подтвердить изменение выбора со списком и не обработать изменение, если пользователь не выбрал?
У нас есть поле со списком, в котором изменение выбора приведет к потере данных. В основном пользователь выбирает тип, тогда они могут вводить атрибуты этого типа. Если они меняют тип, мы очищаем все атрибуты, поскольку они больше не могут применяться. Проблема в том, что при выборе вы снова поднимаете событие SelectionChanged
.
Вот фрагмент:
if (e.RemovedItems.Count > 0)
{
result = MessageBox.Show("Do you wish to continue?",
"Warning", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.No)
{
if (e.RemovedItems.Count > 0)
((ComboBox)sender).SelectedItem = e.RemovedItems[0];
else
((ComboBox)sender).SelectedItem = null;
}
}
У меня есть два решения, ни один из которых мне не нравится.
-
После выбора пользователем "Нет" удалите обработчик события SelectionChanged
, измените выбранный элемент и затем зарегистрируйте обработчик событий SelectionChanged
снова. Это означает, что вам нужно удерживать ссылку обработчика события в классе, чтобы вы могли добавлять и удалять его.
-
Создайте ProcessSelectionChanged
boolean как часть класса. Всегда проверяйте его в начале обработчика событий. Установите значение false, прежде чем мы изменим выделение назад, а затем reset на значение true. Это будет работать, но я не люблю использовать флаги, чтобы в принципе скрыть обработчик событий.
У кого-то есть альтернативное решение или усовершенствование тех, о которых я упоминаю?
Ответы
Ответ 1
Я помню, что мне нужно было сделать это некоторое время назад. Мне потребовалось около недели исследований и попыток, прежде чем я нашел хорошее решение. Я разместил его здесь:
WPF: Отменить выбор пользователя в привязке к данным ListBox?
FYI, это решение на основе M-V-VM (если вы не используете шаблон M-V-VM, вы должны быть!)
Ответ 2
Я нашел эту хорошую реализацию.
private bool handleSelection=true;
private void ComboBox_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
if (handleSelection)
{
MessageBoxResult result = MessageBox.Show
("Continue change?", MessageBoxButton.YesNo);
if (result == MessageBoxResult.No)
{
ComboBox combo = (ComboBox)sender;
handleSelection = false;
combo.SelectedItem = e.RemovedItems[0];
return;
}
}
handleSelection = true;
}
источник: http://www.amazedsaint.com/2008/06/wpf-combo-box-cancelling-selection.html
Ответ 3
Возможно, создайте класс, полученный из ComboBox
, и переопределите OnSelectedItemChanged
(Или OnSelectionChangeCommitted
.)
Ответ 4
Проверка в обработчике событий SelectionChanged
позволяет отменить вашу логику, если выбор недействителен, но я не знаю, как легко отменить выбор события или элемента.
Мое решение заключалось в подклассе combo-box WPF и добавлении внутреннего обработчика для события SelectionChanged
. Всякий раз, когда срабатывает событие, мой внутренний внутренний обработчик вместо этого запускает пользовательское событие SelectionChanging
.
Если свойство Cancel
установлено в соответствующем SelectionChangingEventArgs
, событие не поднимается, а SelectedIndex
возвращается к его предыдущему значению. В противном случае создается новый SelectionChanged
, который затеняет базовое событие. Надеюсь, это поможет!
EventArgs и делегат обработчика для события SelectionChanging:
public class SelectionChangingEventArgs : RoutedEventArgs
{
public bool Cancel { get; set; }
}
public delegate void
SelectionChangingEventHandler(Object sender, SelectionChangingEventArgs e);
Внедрение класса ChangingComboBox:
public class ChangingComboBox : ComboBox
{
private int _index;
private int _lastIndex;
private bool _suppress;
public event SelectionChangingEventHandler SelectionChanging;
public new event SelectionChangedEventHandler SelectionChanged;
public ChangingComboBox()
{
_index = -1;
_lastIndex = 0;
_suppress = false;
base.SelectionChanged += InternalSelectionChanged;
}
private void InternalSelectionChanged(Object s, SelectionChangedEventArgs e)
{
var args = new SelectionChangingEventArgs();
OnSelectionChanging(args);
if(args.Cancel)
{
return;
}
OnSelectionChanged(e);
}
public new void OnSelectionChanged(SelectionChangedEventArgs e)
{
if (_suppress) return;
// The selection has changed, so _index must be updated
_index = SelectedIndex;
if (SelectionChanged != null)
{
SelectionChanged(this, e);
}
}
public void OnSelectionChanging(SelectionChangingEventArgs e)
{
if (_suppress) return;
// Recall the last SelectedIndex before raising SelectionChanging
_lastIndex = (_index >= 0) ? _index : SelectedIndex;
if(SelectionChanging == null) return;
// Invoke user event handler and revert to last
// selected index if user cancels the change
SelectionChanging(this, e);
if (e.Cancel)
{
_suppress = true;
SelectedIndex = _lastIndex;
_suppress = false;
}
}
}
Ответ 5
Я не верю, что диспетчер может отправлять (или задерживать) диспетчер для обновления свойств - это хорошее решение, это скорее обходной путь, который на самом деле не нужен. Следующее решение я полностью mvvm и не требует диспетчера.
- Сначала привяжите выбранный элемент с явным режимом привязки.//это позволяет нам решить, следует ли Зафиксировать с помощью метода UpdateSource() изменения в виртуальной машине или Восстановить с помощью метода UpdateTarget() в пользовательском интерфейсе.
- Затем добавьте метод для виртуальной машины, который подтверждает, разрешено ли изменение (этот метод может содержать службу, которая запрашивает подтверждение пользователя и возвращает bool).
В коде представления за крючком к событию SelectionChanged и обновлению источника (то есть виртуальной машины) или цели (то есть V) в соответствии с тем, возвращал ли метод VM.ConfirmChange(...) следующее значение:
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(e.AddedItems.Count != 0)
{
var selectedItem = e.AddedItems[0];
if (e.AddedItems[0] != _ViewModel.SelectedFormatType)
{
var comboBoxSelectedItemBinder = _TypesComboBox.GetBindingExpression(Selector.SelectedItemProperty); //_TypesComboBox is the name of the ComboBox control
if (_ViewModel.ConfirmChange(selectedItem))
{
// Update the VM.SelectedItem property if the user confirms the change.
comboBoxSelectedItemBinder.UpdateSource();
}
else
{
//otherwise update the view in accordance to the VM.SelectedItem property
comboBoxSelectedItemBinder.UpdateTarget();
}
}
}
}