ListBox с одним выбором, а также отменить выбор по клику ...? - PullRequest
15 голосов
/ 28 февраля 2011

Мне нужен список, который выбирает при первом щелчке и отменяет выбор при втором щелчке, чтобы в любой момент был выбран только ноль или один элемент.

Выбор / отмена выбора реализуется в списке (с SelectionMode = "Single"), когда вы удерживаете crtl, но, к сожалению, никто из моих пользователей не может этого знать.

С SelectionMode = "Multiple" у нас есть точная функциональность, которую я хочу, за исключением того, что вы можете выбрать более одного элемента ...

Больше фона: Я хочу, чтобы пользователь сначала выбрал, в какую установку войти, затем предоставил учетные данные (и некоторые другие варианты)

Для этого я использовал список с расширением контента. Чтобы помочь расширению, я с левой стороны элемента списка создал треугольник, который указывает вправо, когда он не развернут, и поворачивает, чтобы указать вниз, когда вы выбрали элемент списка.

Итак, сначала пользователь видит список поверх установок, а затем, когда он выбрал элемент, который он хочет, выбрав его, элемент listbox расширяется до остальной информации, которую он должен ввести. Это довольно хорошо, и работает хорошо, но тестирование сообщает, что они хотят, чтобы второй щелчок треугольника отменил выбор (и, таким образом, свернул расширенный раздел). И я должен признать, что я тоже нажал на стрелку ¤% &, ожидая, что действие приведет к краху ... :-(

Кто-нибудь имеет представление, как этого можно достичь ( желательно без кода )?

Ответы [ 6 ]

29 голосов
/ 28 февраля 2011

Обычный способ сделать это - установить для режима 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);
                }
            }
        }
    }
}
11 голосов
/ 28 февраля 2011

попробуйте:

вы используете 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 также не проверяется
  • Выберите новый элемент
  • . Переключатель новых элементов проверяется через его привязку

, и если отключен только переключатель фактически выбранного элемента, элемент отменяет выбор через привязку ...

0 голосов
/ 27 февраля 2019

Еще проще, чем это просто добавьте флаг комбайн с SelectionMode = "Multiple"

 private bool _ignoreSelectionFlag = false;
    private void LbHistory_OnSelectionChanged(object sender,SelectionChangedEventArgs e)
    {
        if (_ignoreSelectionFlag)
            return;

        if (e.AddedItems.Count > 0)
        {
            ListBox listBox = sender as ListBox;
            var valid = e.AddedItems[0];

            _ignoreSelectionFlag = true;
            LbHistory.UnselectAll();
            LbHistory.SelectedItems.Add(e.AddedItems[0]);
            e.Handled = true;
            _ignoreSelectionFlag = false;


        }
    }
0 голосов
/ 26 декабря 2018

в приложениях uwp, с SelectionMode = "Несколько"

private void MyListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (MyListBox.SelectedItems.Count <= 1)
    {
        // code related to SelectionChanged
    }
    while (MyListBox.SelectedItems.Count > 1)
    {
        MyListBox.SelectedItems.RemoveAt(0);
    }
}
0 голосов
/ 10 декабря 2018

Я позволил себе дополнить ответ Фредрика для 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);
                }
            }
        }
    }
}
0 голосов
/ 03 декабря 2016

Мое решение для 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
...