ListBox с одним выбором, а также unselect on click...?
Мне нужен список, который выбирает первый клик и отбирает второй щелчок, чтобы в любой момент был выбран только один или один элемент.
Выбор/снятие выделения реализуется в списке (с помощью SelectionMode = "Single" ), когда вы удерживаете crtl, но, к сожалению, ни один из моих пользователей не должен знать об этом.
С SelectionMode = "Multiple" у нас есть точная функциональность, которую я хочу, за исключением того, что вы можете выбрать более одного элемента...
Больше фона:
Я хочу, чтобы пользователь сначала выбирал, какая установка должна войти в систему, а затем предоставить учетные данные (и некоторые другие варианты)
Для этого я использовал список с расширяющимся контентом. Чтобы помочь расширению, я на левой стороне listboxitem сделал треугольник, который указывает прямо, когда нерасширенный, который превращается в точку вниз, когда вы выбрали элемент списка.
Итак, сначала пользователь увидит список над установками, а затем, когда он выбрал элемент, который он хочет, выбрав его, listboxitem расширится до остальной информации, которую ему нужно ввести. Это довольно хорошо, и работает хорошо, но тестирование отчетов, что они хотят второй щелчок треугольника, чтобы снять выделение (и, таким образом, свернуть расширенный раздел). И я должен признать, что я нажал кнопку ¤% & стрелка тоже, ожидая, что действие приведет к краху...:-(
У любого есть идея, как это может быть достигнуто (желательно без кода)?
Ответы
Ответ 1
попробуйте следующее:
вы используете ToggleButton как "Расширитель" детального контента.
Свойство "IsChecked" кнопки переключения вы можете привязать к свойству IsSelected элемента
здесь код:
<ListBox SelectionMode="Single">
<ListBox.ItemsSource>
<x:Array Type="{x:Type sys:String}">
<sys:String>test1</sys:String>
<sys:String>test2</sys:String>
<sys:String>test3</sys:String>
<sys:String>test4</sys:String>
<sys:String>test5</sys:String>
<sys:String>test6</sys:String>
</x:Array>
</ListBox.ItemsSource>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ToggleButton IsChecked="{Binding
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListBoxItem}},
Path=IsSelected}"
>btn</ToggleButton>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
как это работает:
В списке можно выбрать только один элемент. Когда мы выбираем элемент, Toggler получает расширение, потому что его IsChecked привязан к ListBoxItem.IsSelected(ListBoxItem - это элемент управления, который обернут вокруг содержимого каждого элемента) его родительского ListBoxItem.
Поскольку SelectionMode является одиночным, как только выбирается другой элемент, происходит следующее:
- Отмените выбор выбранного элемента
- Через привязку Toggler также не проверяется
- Выберите новый элемент
- Новые элементы toggler проверяются через привязку
и если только тот, который был выбран выбранный элемент, не снят, элемент отменяет себя через привязку...
Ответ 2
Общий способ сделать это - установить режим SelectionMode
на Multiple
, а затем отменить выбор всех элементов, кроме вновь выбранного в событии SelectionChanged
.
Смотрите ссылки ниже.
Вот приложенное поведение, которое делает это, которое можно использовать как это
<ListBox local:ListBoxSelectionBehavior.ClickSelection="True"
...>
ListBoxSelectionBehavior
public static class ListBoxSelectionBehavior
{
public static readonly DependencyProperty ClickSelectionProperty =
DependencyProperty.RegisterAttached("ClickSelection",
typeof(bool),
typeof(ListBoxSelectionBehavior),
new UIPropertyMetadata(false, OnClickSelectionChanged));
public static bool GetClickSelection(DependencyObject obj)
{
return (bool)obj.GetValue(ClickSelectionProperty);
}
public static void SetClickSelection(DependencyObject obj, bool value)
{
obj.SetValue(ClickSelectionProperty, value);
}
private static void OnClickSelectionChanged(DependencyObject dpo,
DependencyPropertyChangedEventArgs e)
{
ListBox listBox = dpo as ListBox;
if (listBox != null)
{
if ((bool)e.NewValue == true)
{
listBox.SelectionMode = SelectionMode.Multiple;
listBox.SelectionChanged += OnSelectionChanged;
}
else
{
listBox.SelectionChanged -= OnSelectionChanged;
}
}
}
static void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0)
{
ListBox listBox = sender as ListBox;
var valid = e.AddedItems[0];
foreach (var item in new ArrayList(listBox.SelectedItems))
{
if (item != valid)
{
listBox.SelectedItems.Remove(item);
}
}
}
}
}
Ответ 3
Мое решение установлено ListBox SelectionMode на Multiple, добавляет метод forbidSelectionButOne в событие Click и после этого разрешает только один выбранный элемент следующим образом:
Private Sub forbidSelectionButOne(sender As Object, e As MouseButtonEventArgs)
Dim lv As ListView = TryCast(sender, ListView)
If lv IsNot Nothing Then
If lv.SelectedIndex <> getCausesListViewItemIndex(sender, e) Then
lv.SelectedIndex = getCausesListViewItemIndex(sender, e)
e.Handled = True
End If
lv.Focus()
End If
End Sub
И помогая функции найти ListViewItem, на который щелкнула мышь:
Private Function getCausesListViewItemIndex(ByVal sender As Object, e As RoutedEventArgs) As Integer
Dim dep As DependencyObject = TryCast(e.OriginalSource, DependencyObject)
Do While dep IsNot Nothing AndAlso Not TypeOf (dep) Is ListViewItem
dep = VisualTreeHelper.GetParent(dep)
Loop
If dep Is Nothing Then
Return -1
Else
Dim lv As ListView = TryCast(sender, ListView)
If lv IsNot Nothing Then
Dim i As Integer = lv.ItemContainerGenerator.IndexFromContainer(dep)
Return i
Else
Return -1
End If
End If
End Function
Ответ 4
Я позволил себе дополнить ответ Фредрика для UWP и.NET Framework 4.7:
public static class ListBoxSelectionBehavior
{
public static readonly DependencyProperty ClickSelectionProperty =
DependencyProperty.RegisterAttached("ClickSelection",
typeof(bool),
typeof(ListBoxSelectionBehavior),
new PropertyMetadata(false, OnClickSelectionChanged));
public static bool GetClickSelection(DependencyObject obj)
{
return (bool)obj.GetValue(ClickSelectionProperty);
}
public static void SetClickSelection(DependencyObject obj, bool value)
{
obj.SetValue(ClickSelectionProperty, value);
}
private static void OnClickSelectionChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs e)
{
if (dpo is ListBox listBox)
{
if ((bool)e.NewValue == true)
{
listBox.SelectionMode = SelectionMode.Multiple;
listBox.SelectionChanged += OnSelectionChanged;
}
else
{
listBox.SelectionChanged -= OnSelectionChanged;
}
}
}
static void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0)
{
ListBox listBox = sender as ListBox;
var valid = e.AddedItems[0];
foreach (var item in new ArrayList(listBox.SelectedItems.ToArray()))
{
if (item != valid)
{
listBox.SelectedItems.Remove(item);
}
}
}
}
}