具有单一选择的列表框,并且单击时也取消选择...?

发布于 2024-10-19 09:51:47 字数 534 浏览 2 评论 0原文

我需要一个在第一次单击时选择并在第二次单击时取消选择的列表框,以便任何时候只选择零个或一个项目。

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

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

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

为了实现此目的,我使用了一个具有扩展内容的列表框。为了帮助展开,我在列表框项的左侧制作了一个三角形,未展开时该三角形指向右侧,当您选择列表框项时,该三角形变为向下指向。

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

任何人都知道如何实现这一点(最好没有背后的代码)?

I need a listbox that selects on first click and un-selects on second click, so that only zero or one item is selected at any time.

The select/unselect is implemented in the listbox (with SelectionMode="Single") when you hold down crtl, but unfortunately, none of my users can be expected to know that.

With SelectionMode="Multiple" we have the exact functionality I want, except that you can select more than one item...

More background:
I want the user to first choose which installation to log into, then to give credentials (and some other choices)

To achieve this I have used a listbox with expanding content. To aid the expansion I have on the left side of the listboxitem made a triangle that points right when unexpanded that turns to point down when you have selected the listbox item.

So, first the user see the list over the installations, and then, when he has chosen the item he wants by selecting it, the listboxitem expands to the rest of the info he need to enter. It's quite nice, and works well, but testing reports that they want a second click to the triangle to unselect (and thus collapse the expanded section). And I must admit that I have clicked the ¤%& arrow too, expecting the action to result in a collapse... :-(

Anyone has an idea how this can be achieved (preferably without code behind)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

叹梦 2024-10-26 09:51:48

尝试一下:

您使用 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 是一个围绕每个项目的内容的控件)。
由于选择模式是单一的,一旦选择了另一个项目,就会发生以下情况:

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

,如果只是实际选定项目的切换器,则 未选中的项目会通过绑定取消选择自身...

try that:

you use a ToggleButton as the "Expander" of the detailed content.
The "IsChecked" Property of the toggle Button you can bind to the IsSelected Property of the item

here the code:

<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>

how it works:
In the Listbox can only one item be selected. As we select an item the Toggler gets expanded cause his IsChecked is bound to ListBoxItem.IsSelected (ListBoxItem is a Control which gets wrapped around the Content of each Item) of his parent ListBoxItem.
As the selectionMode is single as soon as another item gets selected the following happens:

  • Deselect the actually selected item
  • Through the binding the Toggler gets unchecked too
  • Select the new item
  • the New items toggler gets checked through its binding

and if just the actually selected item's toggler gets unchecked the item deselects itself through the binding...

毁我热情 2024-10-26 09:51:48

我的解决方案是将 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

My solution is set ListBox SelectionMode to Multiple, add method forbidSelectionButOne on Click event and after that allow only one selected item as follows:

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

And helping function to find ListViewItem that was clicked by mouse:

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
梅倚清风 2024-10-26 09:51:48

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

I allowed myself to supplement Fredrik's answer for UWP and .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);
                }
            }
        }
    }
}
情绪失控 2024-10-26 09:51:48

比这个更容易
只需添加带有 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;


        }
    }

Even easier then this
just add flag combain with 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;


        }
    }
少钕鈤記 2024-10-26 09:51:47

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

The common way to do this is to set SelectionMode mode to Multiple and then unselect all items but the newly selected one in the SelectionChanged event.

See the follow links

Here is an Attached Behavior that does this which can be used like this

<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);
                }
            }
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文