C# 一次只选择一个项目的多个WPF列表框

C# 一次只选择一个项目的多个WPF列表框,c#,wpf,mvvm,binding,listbox,C#,Wpf,Mvvm,Binding,Listbox,我使用MVVM,在一个窗口上显示两个列表框。我同时从这两个列表框绑定到不同的字段,称它们为A和B。A和B然后都在修改C。为了实现这一点,我希望一次只从两个列表框中选择一个项目,以便A在选择B时不会覆盖C。我该如何限制这一点?我可以想出几种方法来做到这一点 一种方法是可以绑定列表框。从两个列表框中选择dex,以更改通知视图模型属性 例如,在您看来: <ListBox SelectedIndex="{Binding SelectedIndexA}"> <ListBoxIt

我使用MVVM,在一个窗口上显示两个列表框。我同时从这两个列表框绑定到不同的字段,称它们为A和B。A和B然后都在修改C。为了实现这一点,我希望一次只从两个列表框中选择一个项目,以便A在选择B时不会覆盖C。我该如何限制这一点?

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

一种方法是可以绑定
列表框。从两个列表框中选择dex
,以更改通知视图模型属性

例如,在您看来:

<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>
<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>
另一种方法是使用诸如“GroupName”之类的附加属性,在该属性中,您可以对选择器进行分组(
ListBox
继承自
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);
        }
    }
}
公共静态类SingleSelectionGroup
{
公共静态只读DependencyProperty GroupNameProperty=
DependencyProperty.RegisterAttached(“GroupName”、typeof(string)、typeof(SingleSelectionGroup),
新UIPropertyMetadata(OnGroupNameChanged));
公共静态字符串GetGroupname(选择器)
{
返回(字符串)选择器.GetValue(GroupNameProperty);
}
公共静态void SetGroupName(选择器、字符串值)
{
selector.SetValue(GroupNameProperty,value);
}
私有静态无效OnGroupNameChanged(DependencyObject DependencyObject,DependencyPropertyChangedEventArgs e)
{
变量选择器=(选择器)dependencyObject;
如果(e.OldValue!=null)
selector.SelectionChanged-=SelectorOnSelectionChanged;
如果(如NewValue!=null)
selector.SelectionChanged+=SelectorOnSelectionChanged;
}
私有静态void选择器选择更改(对象发送方,SelectionChangedEventArgs e)
{
如果(e.AddedItems.Count==0)
返回;
变量选择器=(选择器)发送方;
var groupName=(string)selector.GetValue(GroupNameProperty);
var groupSelectors=GetGroupSelectors(选择器,groupName);
foreach(groupSelectors.Where(gs=>!gs.Equals(sender))中的var groupSelector)
{
groupSelector.SelectedIndex=-1;
}
}
私有静态IEnumerable GetGroupSelector(DependencyObject选择器,字符串groupName)
{
var选择器=新集合();
var parent=GetParent(选择器);
GetGroupSelectors(父级、选择器、组名);
返回选择器;
}
私有静态DependencyObject GetParent(DependencyObject depObj)
{
var parent=visualtreeheloper.GetParent(depObj);
返回父项==null?depObj:GetParent(父项);
}
私有静态void GetGroupSelectors(DependencyObject父对象、集合选择器、字符串groupName)
{
var childrenCount=visualtreeheloper.GetChildrenCount(父级);
for(int i=0;i
在你看来:

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

如果必须在高亮显示某个项目之前单击该项目两次,则可以使用以下快速解决方法:

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

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

GroupName is already registered by Selector
  • 单击列表框1中的任何项目时,该项目将被选中
  • 单击listbox2中的任何项目时,listbox1中的选定项目将取消选中,但listbox2中的任何项目均未选中
  • 再次单击listbox2中的任何项目时,该项目将被选中
  • 只需将以下xaml添加到ListBoxItem的样式中:

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

    请将依赖项属性声明中的
    第三个参数typeof(…)
    更改为类的名称

    当在另一个列表框中进行选择时,是否希望在一个列表框中清除选择?我发现你的问题很难理解。我主要想知道如何将IsFocused/IsSelected Listbox属性绑定到视图模型中的布尔值。可以将Listbox的SelectedIndex绑定到视图模型中的属性。例如,当设置ListBoxA的SelectedIndex时,可以将ListBoxB的SelectedIndex设置为-1以清除ListBoxB的选择,反之亦然。这是你的意图吗?这应该可以,谢谢!西蒙给了你一个很好的回答,不接受它是不礼貌的。。。你也应该投赞成票。善待其他用户,他们也会善待他人。答案很好!!!我对下面的答案做了一些更正。我仍然有一个小问题。如果你能设法解决它,那你就太好了。如果你有空,那么请看看我的问题:谢谢你调查。直到今天,我才真正需要附加属性,因此发现了所选项目未突出显示的问题。作为一个快速修复,我使用了EventTrigger而不是上面提到的,因为当值为false时,常规触发器会重置,这意味着当项目失去键盘焦点时,选择被清除。这在我的情况下是不需要的。@Simon感谢新的快速修复。