一次仅选择一项的多个 WPF 列表框

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12759507/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 05:41:40  来源:igfitidea点击:

Multiple WPF ListBoxes with only one item selected at a time

c#wpfmvvmbindinglistbox

提问by user1722960

I am using MVVM and am displaying two listboxes on one window. I am binding from both of those listboxes simultaneously to different fields call them A and B. A and B are then both modifying C. To make this work, I want to only have one item from the two listboxes IsSelected at once, so that A does not override C when B IsSelected. How can I restrict this?

我正在使用 MVVM 并在一个窗口上显示两个列表框。我同时从这两个列表框绑定到不同的字段,称为 A 和 B。然后 A 和 B 都在修改 C。为了完成这项工作,我只想一次从两个列表框 IsSelected 中获得一个项目,以便 A当 B IsSelected 时不覆盖 C。我怎样才能限制这个?

回答by Simon

I can think of a couple of ways to do this.

我可以想到几种方法来做到这一点。

One way is you could bind the ListBox.SelectedIndexof your 2 ListBoxes to change-notifying ViewModel properties.

一种方法是您可以将ListBox.SelectedIndex2 个 ListBox 中的绑定到更改通知 ViewModel 属性。

For example in your View:

例如在您的视图中:

<ListBox SelectedIndex="{Binding SelectedIndexA}">
     <ListBoxItem Content="Item 1"/>
     <ListBoxItem Content="Item 2"/>
</ListBox>
<ListBox SelectedIndex="{Binding SelectedIndexB}">
     <ListBoxItem Content="Item 1"/>
     <ListBoxItem Content="Item 2"/>
</ListBox>

And in your ViewModel:

在您的 ViewModel 中:

public int SelectedIndexA
{
    get { return _selectedIndexA; }
    set
    {
        _selectedIndexA = value;
        _selectedIndexB = -1;
        OnPropertyChanged("SelectedIndexB");
    }
}

public int SelectedIndexB
{
    get { return _selectedIndexB; }
    set
    {
        _selectedIndexB = value;
        _selectedIndexA = -1;
        OnPropertyChanged("SelectedIndexA");
    }
}

Another way would be with an attached property like 'GroupName' where you can group Selectors (ListBoxinherits from Selector) to ensure only one Selectorin the group has a selected item at any one time.

另一种方法是使用诸如“GroupName”之类的附加属性,您可以在其中对选择器(ListBox继承自Selector)进行分组,以确保Selector在任何时候组中只有一个具有所选项目。

For example:

例如:

public static class SingleSelectionGroup
{
    public static readonly DependencyProperty GroupNameProperty =
        DependencyProperty.RegisterAttached("GroupName", typeof(string), typeof(SingleSelectionGroup),
                                            new UIPropertyMetadata(OnGroupNameChanged));

    public static string GetGroupname(Selector selector)
    {
        return (string) selector.GetValue(GroupNameProperty);
    }

    public static void SetGroupName(Selector selector, string value)
    {
        selector.SetValue(GroupNameProperty, value);
    }

    private static void OnGroupNameChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        var selector = (Selector) dependencyObject;

        if (e.OldValue != null)
            selector.SelectionChanged -= SelectorOnSelectionChanged;
        if (e.NewValue != null)
            selector.SelectionChanged += SelectorOnSelectionChanged;
    }

    private static void SelectorOnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.AddedItems.Count == 0)
            return;

        var selector = (Selector) sender;
        var groupName = (string) selector.GetValue(GroupNameProperty);
        var groupSelectors = GetGroupSelectors(selector, groupName);

        foreach (var groupSelector in groupSelectors.Where(gs => !gs.Equals(sender)))
        {
            groupSelector.SelectedIndex = -1;
        }
    }

    private static IEnumerable<Selector> GetGroupSelectors(DependencyObject selector, string groupName)
    {
        var selectors = new Collection<Selector>();
        var parent = GetParent(selector);
        GetGroupSelectors(parent, selectors, groupName);
        return selectors;
    }

    private static DependencyObject GetParent(DependencyObject depObj)
    {
        var parent = VisualTreeHelper.GetParent(depObj);
        return parent == null ? depObj : GetParent(parent);
    }

    private static void GetGroupSelectors(DependencyObject parent, Collection<Selector> selectors, string groupName)
    {
        var childrenCount = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < childrenCount; i++)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
            var selector = child as Selector;
            if (selector != null && (string) selector.GetValue(GroupNameProperty) == groupName)
                selectors.Add(selector);

            GetGroupSelectors(child, selectors, groupName);
        }
    }
}

And in your View:

在您的视图中:

<ListBox my:SingleSelectionGroup.GroupName="Group A">
    <ListBoxItem Content="Item 1 (Group A)"/>
    <ListBoxItem Content="Item 2 (Group A)"/>
</ListBox>
<ListBox my:SingleSelectionGroup.GroupName="Group A">
    <ListBoxItem Content="Item 1 (Group A)"/>
    <ListBoxItem Content="Item 2 (Group A)"/>
</ListBox>

<ListBox my:SingleSelectionGroup.GroupName="Group B">
    <ListBoxItem Content="Item 1 (Group B)"/>
    <ListBoxItem Content="Item 2 (Group B)"/>
</ListBox>
<ListBox my:SingleSelectionGroup.GroupName="Group B">
    <ListBoxItem Content="Item 1 (Group B)"/>
    <ListBoxItem Content="Item 2 (Group B)"/>
</ListBox>

If you have to click an item twice before it is highlighted you can use a quick workaround like this:

如果您必须在突出显示之前单击项目两次,您可以使用如下快速解决方法:

<Style TargetType="ListBoxItem">
    <Style.Triggers>
        <EventTrigger RoutedEvent="GotKeyboardFocus">
            <BeginStoryboard>
                <Storyboard>
                    <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="(ListBoxItem.IsSelected)">
                        <DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="True" />
                    </BooleanAnimationUsingKeyFrames>
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Style.Triggers>
</Style>

回答by Vishal

If anybody has used ListBox/ListView inside ItemsControl and has the following problem after using the above answer:

如果有人在 ItemsControl 中使用过 ListBox/ListView 并且在使用上述答案后出现以下问题:

  1. When you click on any Item in listbox1 the item is selected.
  2. When you click on any item in listbox2 the selected item of listbox1 is unselected but no item in listbox2 is selected.
  3. When you click again on any item in listbox2 the item is selected.
  1. 当您单击 listbox1 中的任何项目时,该项目被选中。
  2. 当您单击 listbox2 中的任何项目时,listbox1 的选定项目未选中,但 listbox2 中的任何项目都未选中。
  3. 当您再次单击 listbox2 中的任何项目时,该项目被选中。

Just add below xaml to the style of your ListBoxItem:

只需将下面的 xaml 添加到 ListBoxItem 的样式中:

<Style  TargetType="ListBoxItem">
    <Style.Triggers>
        <Trigger Property="IsKeyboardFocusWithin" Value="True">
             <Setter Property="IsSelected" Value="True"></Setter>
        </Trigger>
    </Style.Triggers>
</Style>

Also, If anybody is getting the following error after using code in above answer:

另外,如果有人在使用上述答案中的代码后出现以下错误:

GroupName is already registered by Selector

Please change the third parameter typeof(......)in dependency property declaration to Name of your class.

请将third parameter typeof(......)依赖属性声明更改为Name of your class