Какой обработчик события использовать для выбранного компонента ComboBox (выбранный элемент необязательно изменен)
Цель: выдать событие, когда выбраны элементы в выпадающем списке combobox.
Проблема: Использование "SelectionChanged", однако, если пользователь выбирает тот же элемент, что и элемент, который в данный момент выбран, выбор не изменяется и, следовательно, это событие не будет запущено.
Вопрос: Какой другой обработчик событий (или другие способы), который я могу использовать для выдачи события независимо от выбранного элемента, изменяется или не до тех пор, пока щелчок мышью по этому элементу и этот элемент выбирается.
(Уточнение: проблема заключается в том, как вызывать "что-то", когда один и тот же элемент снова выбирается. В раскрывающемся списке нет дубликатов. Сценарий: первый раз выберите элемент 1, закройте раскрывающийся список. Затем снова откройте раскрывающийся список и выберите элемент 1 при срабатывании какой-либо функции.)
Решение: на данный момент, похоже, нет прямого решения для этого. Но в соответствии с каждым отдельным проектом могут быть способы обойти его. (Пожалуйста, обновите, если есть действительно хорошие способы сделать это). Спасибо.
Ответы
Ответ 1
У меня был тот же вопрос, и я наконец нашел ответ:
Вам нужно обработать BOTH событие SelectionChanged и DropDownClosed следующим образом:
В XAML:
<ComboBox Name="cmbSelect" SelectionChanged="ComboBox_SelectionChanged" DropDownClosed="ComboBox_DropDownClosed">
<ComboBoxItem>1</ComboBoxItem>
<ComboBoxItem>2</ComboBoxItem>
<ComboBoxItem>3</ComboBoxItem>
</ComboBox>
В С#:
private bool handle = true;
private void ComboBox_DropDownClosed(object sender, EventArgs e) {
if(handle)Handle();
handle = true;
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {
ComboBox cmb = sender as ComboBox;
handle = !cmb.IsDropDownOpen;
Handle();
}
private void Handle() {
switch (cmbSelect.SelectedItem.ToString().Split(new string[] { ": " }, StringSplitOptions.None).Last())
{
case "1":
//Handle for the first combobox
break;
case "2":
//Handle for the second combobox
break;
case "3":
//Handle for the third combobox
break;
}
}
Ответ 2
Вы можете использовать событие "ComboBoxItem.PreviewMouseDown". Поэтому каждый раз, когда мышь на каком-то элементе, это событие будет запущено.
Чтобы добавить это событие в XAML, используйте "ComboBox.ItemContainerStyle", как в следующем примере:
<ComboBox x:Name="MyBox"
ItemsSource="{Binding MyList}"
SelectedValue="{Binding MyItem, Mode=OneWayToSource}" >
<ComboBox.ItemContainerStyle>
<Style>
<EventSetter Event="ComboBoxItem.PreviewMouseDown"
Handler="cmbItem_PreviewMouseDown"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
и обрабатывать его как обычно
void cmbItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
//...do your item selection code here...
}
Благодаря MSDN
Ответ 3
Для меня ComboBox.DropDownClosed
Событие сделало это.
private void cbValueType_DropDownClosed(object sender, EventArgs e)
{
if (cbValueType.SelectedIndex == someIntValue) //sel ind already updated
{
// change sel Index of other Combo for example
cbDataType.SelectedIndex = someotherIntValue;
}
}
Ответ 4
Я надеюсь, что вы найдете полезный трюк.
Вы можете связать оба события
combobox.SelectionChanged += OnSelectionChanged;
combobox.DropDownOpened += OnDropDownOpened;
И заставьте выбранный элемент в значение null внутри OnDropDownOpened
private void OnDropDownOpened(object sender, EventArgs e)
{
combobox.SelectedItem = null;
}
И сделайте то, что вам нужно, с элементом внутри OnSelectionChanged.
OnSelectionChanged будет подниматься каждый раз, когда вы откроете combobox, но вы можете проверить, является ли SelectedItem нулевым внутри метода и пропустить команду
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (combobox.SelectedItem != null)
{
//Do something with the selected item
}
}
Ответ 5
Для приложений UWP (Windows Store) ни одно из вышеперечисленных действий не будет работать (PointerPressed не запускается, не существует событий Preview, DropDownClosed или SelectedIndexChanged)
Мне пришлось прибегнуть к прозрачной кнопке, накладывающейся на ComboBox (но не на стрелку вниз). Когда вы нажимаете на стрелку, список падает, как обычно, и происходит событие Combo Box SelectionChanged. Когда вы нажимаете в другом месте в Combo Box, прозрачная кнопка запускает событие, позволяя вам повторно выбрать текущее значение Combo Box.
Некоторые рабочие коды XAML:
<Grid x:Name="ComboOverlay" Margin="0,0,5,0"> <!--See comments in code behind at ClickedComboButValueHasntChanged event handler-->
<ComboBox x:Name="NewFunctionSelect" Width="97" ItemsSource="{x:Bind Functions}"
SelectedItem="{x:Bind ChosenFunction}" SelectionChanged="Function_SelectionChanged"/>
<Button x:Name="OldFunctionClick" Height="30" Width="73" Background="Transparent" Click="ClickedComboButValueHasntChanged"/>
</Grid>
Некоторый рабочий код С#:
/// <summary>
/// It is impossible to simply click a ComboBox to select the shown value again. It always drops down the list of options but
/// doesn't raise SelectionChanged event if the value selected from the list is the same as before
///
/// To handle this, a transparent button is overlaid over the ComboBox (but not its dropdown arrow) to allow reselecting the old value
/// Thus clicking over the dropdown arrow allows the user to select a new option from the list, but
/// clicking anywhere else in the Combo re-selects the previous value
/// </summary>
private void ClickedComboButValueHasntChanged(object sender, RoutedEventArgs e)
{
//You could also dummy up a SelectionChangedEvent event and raise it to invoke Function_SelectionChanged handler, below
FunctionEntered(NewFunctionSelect.SelectedValue as string);
}
private void Function_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
FunctionEntered(e.AddedItems[0] as string);
}
Ответ 6
Вы можете попробовать "SelectedIndexChanged
", это вызовет событие, даже если выбран тот же самый элемент.
Ответ 7
Эта проблема часто меня беспокоит, так как ни одна работа не работала для меня: (
Но хорошая новость заключается в следующем методе, который отлично подходит для моего приложения.
Основная идея - зарегистрировать EventManager
в App.xmal.cs
для sniff PreviewMouseLeftButtonDownEvent
для всех ComboBoxItem
, затем запустить SelectionChangedEvent
, если элемент выбора совпадает с выбранным элементом, то есть выбор выполняется без изменения индекса.
В App.xmal.cs
:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
// raise selection change event even when there no change in index
EventManager.RegisterClassHandler(typeof(ComboBoxItem), UIElement.PreviewMouseLeftButtonDownEvent,
new MouseButtonEventHandler(ComboBoxSelfSelection), true);
base.OnStartup(e);
}
private static void ComboBoxSelfSelection(object sender, MouseButtonEventArgs e)
{
var item = sender as ComboBoxItem;
if (item == null) return;
// find the combobox where the item resides
var comboBox = ItemsControl.ItemsControlFromItemContainer(item) as ComboBox;
if (comboBox == null) return;
// fire SelectionChangedEvent if two value are the same
if ((string)comboBox.SelectedValue == (string)item.Content)
{
comboBox.IsDropDownOpen = false;
comboBox.RaiseEvent(new SelectionChangedEventArgs(Selector.SelectionChangedEvent, new ListItem(), new ListItem()));
}
}
}
Затем для всех комбинированных полей зарегистрируйте SelectionChangedEvent
обычным способом:
<ComboBox ItemsSource="{Binding BindList}"
SelectionChanged="YourSelectionChangedEventHandler"/>
Теперь, если два индекса отличаются друг от друга, ничего особенного, кроме обычного процесса обработки событий; если два индекса совпадают, сначала будет обрабатываться событие мыши на элементе, и таким образом запускается SelectionChangedEvent
. Таким образом, обе ситуации вызовут SelectionChangedEvent
:)
Ответ 8
Это объект DependencyObject для присоединения к ComboBox.
Он записывает текущий выбранный элемент, когда раскрывающийся список открывается, а затем запускает событие SelectionChanged, если тот же индекс все еще выбран, когда выпадающее меню закрыто. Возможно, потребуется изменить его для работы с выбором клавиатуры.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Web.UI.WebControls;
namespace MyNamespace
{
public class ComboAlwaysFireSelection : DependencyObject
{
public static readonly DependencyProperty ActiveProperty = DependencyProperty.RegisterAttached(
"Active",
typeof(bool),
typeof(ComboAlwaysFireSelection),
new PropertyMetadata(false, ActivePropertyChanged));
private static void ActivePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var element = d as ComboBox;
if (element == null)
return;
if ((e.NewValue as bool?).GetValueOrDefault(false))
{
element.DropDownClosed += ElementOnDropDownClosed;
element.DropDownOpened += ElementOnDropDownOpened;
}
else
{
element.DropDownClosed -= ElementOnDropDownClosed;
element.DropDownOpened -= ElementOnDropDownOpened;
}
}
private static void ElementOnDropDownOpened(object sender, EventArgs eventArgs)
{
_selectedIndex = ((ComboBox) sender).SelectedIndex;
}
private static int _selectedIndex;
private static void ElementOnDropDownClosed(object sender, EventArgs eventArgs)
{
var comboBox = ((ComboBox) sender);
if (comboBox.SelectedIndex == _selectedIndex)
{
comboBox.RaiseEvent(new SelectionChangedEventArgs(Selector.SelectionChangedEvent, new ListItemCollection(), new ListItemCollection()));
}
}
[AttachedPropertyBrowsableForChildrenAttribute(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(ComboBox))]
public static bool GetActive(DependencyObject @object)
{
return (bool)@object.GetValue(ActiveProperty);
}
public static void SetActive(DependencyObject @object, bool value)
{
@object.SetValue(ActiveProperty, value);
}
}
}
и добавьте префикс пространства имен, чтобы сделать его доступным.
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ut="clr-namespace:MyNamespace" ></UserControl>
а затем вам нужно прикрепить его так
<ComboBox ut:ComboAlwaysFireSelection.Active="True" />
Ответ 9
Использовать событие SelectionChangeCommitted(object sender, EventArgs e)
здесь
Ответ 10
Каждый экземпляр ComboBoxItem имеет событие PreviewMouseDown. Если вы подпишете свой пользовательский обработчик на это событие для каждого ComboBoxItem, у вас будет возможность обрабатывать каждый щелчок в раскрывающемся списке.
// Subscribe on ComboBoxItem-s events.
comboBox.Items.Cast<ComboBoxItem>().ToList().ForEach(i => i.PreviewMouseDown += ComboBoxItem_PreviewMouseDown);
private void ComboBoxItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
// your handler logic...
}
Ответ 11
Для UWP я попробовал другой подход. Я расширил класс ComboBox и обработал события SelectionChanged и OnKeyUp в ComboBox, а также событие Tapped в ComboBoxItems. В тех случаях, когда я получаю событие Tapped или клавишу Enter или пробел без предварительного получения SelectionChanged, я знаю, что текущий элемент был повторно выбран, и я отвечаю соответствующим образом.
class ExtendedComboBox : ComboBox
{
public ExtendedComboBox()
{
SelectionChanged += OnSelectionChanged;
}
protected override void PrepareContainerForItemOverride(Windows.UI.Xaml.DependencyObject element, object item)
{
ComboBoxItem cItem = element as ComboBoxItem;
if (cItem != null)
{
cItem.Tapped += OnItemTapped;
}
base.PrepareContainerForItemOverride(element, item);
}
protected override void OnKeyUp(KeyRoutedEventArgs e)
{
// if the user hits the Enter or Space to select an item, then consider this a "reselect" operation
if ((e.Key == Windows.System.VirtualKey.Space || e.Key == Windows.System.VirtualKey.Enter) && !isSelectionChanged)
{
// handle re-select logic here
}
isSelectionChanged = false;
base.OnKeyUp(e);
}
// track whether or not the ComboBox has received a SelectionChanged notification
// in cases where it has not yet we get a Tapped or KeyUp notification we will want to consider that a "re-select"
bool isSelectionChanged = false;
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
isSelectionChanged = true;
}
private void OnItemTapped(object sender, TappedRoutedEventArgs e)
{
if (!isSelectionChanged)
{
// indicates that an item was re-selected - handle logic here
}
isSelectionChanged = false;
}
}