有没有一种简单的方法可以提示用户确认组合框选择更改,并且如果用户选择“否”则不处理更改?
我们有一个组合框,更改选择将导致数据丢失。基本上,用户选择一种类型,然后他们就可以输入该类型的属性。如果他们更改类型,我们将清除所有属性,因为它们可能不再适用。问题是,在选择下,您再次引发
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
布尔值作为类的一部分。始终在事件处理程序开始时检查它。在我们更改回选择之前将其设置为 false,然后将其重置为 true。这会起作用,但我不喜欢使用标志来基本上使事件处理程序无效。
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 组合框进行子类化并为
SelectionChanged
事件添加内部处理程序。每当事件触发时,我的私有内部处理程序都会引发自定义
SelectionChanging
事件。如果在相应的
Cancel
上设置了
SelectionChangingEventArgs
属性,则不会引发该事件,并且
SelectedIndex
将恢复为其之前的值。否则,会引发一个新的
SelectionChanged
来遮蔽基本事件。希望这有帮助!
SelectionChanging 事件的EventArgs 和处理程序委托:
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;
}
}
}
动态设置对象
if (sender.IsMouseCaptured)
{
//perform operation
}
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();
}
}
}
}
public class ComboBoxHelper
{
private readonly ComboBox _control;
public ComboBoxHelper(ComboBox control)
{
_control = control;
_control.PreviewMouseLeftButtonDown += _control_PreviewMouseLeftButtonDown; ;
_control.PreviewMouseLeftButtonUp += _control_PreviewMouseLeftButtonUp; ;
}
public Func<bool> IsEditingAllowed { get; set; }
public Func<object, bool> IsValidSelection { get; set; }
public Action<object> OnItemSelected { get; set; }
public bool CloseDropDownOnInvalidSelection { get; set; } = true;
private bool _handledMouseDown = false;
private void _control_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var isEditingAllowed = IsEditingAllowed?.Invoke() ?? true;
if (!isEditingAllowed)
{
e.Handled = true;
return;
}
_handledMouseDown = true;
}
private void _control_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (!_handledMouseDown) return;
_handledMouseDown = false;
var fe = (FrameworkElement)e.OriginalSource;
if (fe.DataContext != _control.DataContext)
{
//ASSUMPTION: Click was on an item and not the ComboBox itself (to open it)
var item = fe.DataContext;
var isValidSelection = IsValidSelection?.Invoke(item) ?? true;
if (isValidSelection)
{
OnItemSelected?.Invoke(item);
_control.IsDropDownOpen = false;
}
else if(CloseDropDownOnInvalidSelection)
{
_control.IsDropDownOpen = false;
}
e.Handled = true;
}
}
}
它可以在自定义用户控件中使用,如下所示:
public class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
var helper = new ComboBoxHelper(MyComboBox); //MyComboBox is x:Name of the ComboBox in Xaml
helper.IsEditingAllowed = () => return Keyboard.Modifiers != Modifiers.Shift; //example
helper.IsValidSelection = (item) => return item.ToString() != "Invalid example.";
helper.OnItemSelected = (item) =>
{
System.Console.WriteLine(item);
};
}
}
这与
SelectionChanged
事件无关,事件触发频率超过要求不会产生副作用。这样其他人就可以安全地收听该事件,例如更新他们的用户界面。还避免了:通过将事件处理程序中的选择重置为有效项目而导致的“递归”调用。
上述关于DataContext
的假设可能并不完全适合所有场景,但可以轻松调整。一种可能的替代方法是检查
ComboBox
是否是 e.OriginalSource
的视觉父级,而在选择项目时则不是。
if (e.KeyCode == Keys.Control || e.KeyCode == Keys.C)
{
Clipboard.SetText(comboBoxPatternSearch.Text);
e.Handled = true;
e.SuppressKeyPress = true;
return;
}
我认为 e.Handled 没有做任何事情,是 SuppressKeyPress 做到了。抱歉没有更多时间,只是想贡献这个,因为我正是遇到这个问题。