多个WPF列表框,一次只选择一个项目

use*_*960 7 c# wpf binding listbox mvvm

我正在使用MVVM并在一个窗口中显示两个列表框.我同时从这两个列表框绑定到不同的字段,称它们为A和B.然后A和B都修改C.为了使这个工作,我想只有两个列表框中的一个项目一次被选中,所以A B IsSelected时不会覆盖C. 我怎么能限制这个?

Sim*_*mon 9

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

一种方法是您可以绑定ListBox.SelectedIndex2个ListBoxes以更改通知ViewModel属性.

例如在您的视图中:

<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>
Run Code Online (Sandbox Code Playgroud)

在您的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");
    }
}
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用附加属性,例如'GroupName',您可以在其中对选择器进行分组(ListBox继承自Selector)以确保Selector组中只有一个在任何时候都有选定的项目.

例如:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并在您的视图中:

<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>
Run Code Online (Sandbox Code Playgroud)

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

<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>
Run Code Online (Sandbox Code Playgroud)


Vis*_*hal 6

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

  1. 单击列表框1中的任何项目时,将选择该项目.
  2. 当您单击listbox2中的任何项目时,将取消选择listbox1的所选项目,但不会选择listbox2中的项目.
  3. 当您再次单击listbox2中的任何项目时,将选择该项目.

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

<Style  TargetType="ListBoxItem">
    <Style.Triggers>
        <Trigger Property="IsKeyboardFocusWithin" Value="True">
             <Setter Property="IsSelected" Value="True"></Setter>
        </Trigger>
    </Style.Triggers>
</Style>
Run Code Online (Sandbox Code Playgroud)

此外,如果在使用上述答案中的代码后有任何人收到以下错误:

GroupName is already registered by Selector
Run Code Online (Sandbox Code Playgroud)

请将third parameter typeof(......)in依赖项属性声明更改为您的类的Name.