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);
                }
            }
        }
    }
}