单击选择列表框,单击时也取消选择...?

时间:2011-02-28 09:07:11

标签: wpf xaml listbox selection listboxitem

我需要一个在第一次点击时选择的列表框,在第二次点击时取消选择,这样任何时候都只能选择零个或一个项目。

当您按住crtl时,选择/取消选择在列表框中实现(使用SelectionMode =“Single”),但遗憾的是,我的所有用户都不可能知道这一点。

使用SelectionMode =“Multiple”,我们拥有我想要的确切功能,除了您可以选择多个项目...

更多背景: 我希望用户首先选择要登录的安装,然后提供凭据(以及其他一些选择)

为实现这一目标,我使用了一个包含扩展内容的列表框。为了帮助扩展,我在listboxitem的左侧创建了一个三角形,当你选择了列表框项目时,在未展开时指向右转,指向下方。

因此,首先用户看到安装列表,然后,当他通过选择它选择了他想要的项目时,listboxitem会扩展到他需要输入的其他信息。它非常好,而且运行良好,但测试报告他们希望第二次点击三角形以取消选择(从而折叠展开的部分)。而且我必须承认我点击了¤%&也是箭头,期待行动导致崩溃...... :-(

任何人都知道如何实现这一目标(最好没有代码)?

6 个答案:

答案 0 :(得分:29)

执行此操作的常用方法是将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);
                }
            }
        }
    }
}

答案 1 :(得分:11)

试试:

您使用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的ListBoxItem.IsSelected(ListBoxItem是一个围绕每个Item的内容的Control)。 由于selectionMode在选择另一个项目时是单一的,因此会发生以下情况:

  • 取消选择实际选择的项目
  • 通过绑定,Toggler也可以取消选中
  • 选择新项目
  • 通过绑定
  • 检查新项目切换器

如果实际选择的项目的切换器未被选中,则该项目通过绑定取消选择...

答案 2 :(得分:0)

我的解决方案是将ListBox SelectionMode设置为Multiple,在Click事件上添加方法 forbidSelectionButOne ,之后只允许选择一个项目,如下所示:

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

答案 3 :(得分:0)

我允许我自己补充Fredrik对于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);
                }
            }
        }
    }
}

答案 4 :(得分:0)

在uwp应用中, 使用SelectionMode =“ Multiple”

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

答案 5 :(得分:0)

再容易一点 只需添加带有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;


        }
    }
相关问题