Как отменить событие 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, прежде чем мы изменим выбор обратно, а затем сбросить его в true после этого. Это будет работать, но мне не нравится использовать флаги, чтобы в основном аннулировать обработчик событий.
у кого-нибудь есть альтернативное решение или улучшение по сравнению с теми, о которых я упоминаю?
6 ответов
Я помню, что мне нужно было сделать это некоторое время назад. Мне потребовалось около недели исследований и попыток, прежде чем я нашел хорошее решение. Я разместил его здесь:
WPF: отменить выбор пользователя в списке базы данных?
FYI, это решение на основе M-V-VM (если вы не используете шаблон M-V-VM, вы должны быть!)
Я нашел эту хорошую реализацию.
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
возможно, создайте класс, производный от ComboBox
и переопределить OnSelectedItemChanged
(или OnSelectionChangeCommitted
.)
проверки в SelectionChanged
обработчик событий позволяет отменить логику, если выбор недопустим, но я не знаю простого способа отменить выбор события или элемента.
моим решением было подкласс WPF combo-box и добавить внутренний обработчик для SelectionChanged
событие. Всякий раз, когда событие срабатывает, мой личный внутренний обработчик вызывает обычай событие.
если 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;
}
}
}
Я не считаю, что использование диспетчера для публикации (или задержки) обновления свойств является хорошим решением, это скорее обходной путь, который на самом деле не нужен. Следующее решение i полностью mvvm и не требует диспетчера.
- сначала свяжите SelectedItem с явным режимом привязки. //это позволяет нам решить, следует ли фиксация с помощью метода UpdateSource () изменения в VM или в возврат с помощью метода UpdateTarget () in пользовательский интерфейс.
- затем добавьте метод к виртуальной машине, который подтверждает, разрешено ли изменение (этот метод может содержать службу, которая запрашивает подтверждение пользователя и возвращает bool).
в коде представления за крючком к событию SelectionChanged и обновите источник (т. е. VM) или цель (т. е. 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();
}
}
}
}