2012-10-06 105 views
7

我正在使用MVVM并在一个窗口中显示两个列表框。我将这两个列表框同时绑定到不同的字段上,称为A和B.然后A和B都修改C.为了实现这个功能,我只想从两个列表框中选择一个IsSelected,这样A当B IsSelected时不覆盖C.我怎样才能限制呢?一次只选择一个项目的多个WPF列表框

谢谢!

+1

当在另一个ListBox中进行选择时,您想要在一个ListBox中清除选择?我发现你的问题很难理解。 – Simon

+0

我基本上想知道如何将我的视图模型中的东西像IsFocused/IsSelected Listbox属性绑定到布尔。 – user1722960

+2

您可以将ListBox的SelectedIndex绑定到视图模型中的属性。因此,例如,当设置ListBoxA的SelectedIndexA时,可以将ListBoxB的SelectedIndexB设置为-1以清除ListBoxB的选择,反之亦然。这是你的意图吗? – Simon

回答

8

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

一种方法是您可以绑定您的2个ListBoxes的ListBox.SelectedIndex来更改 - 通知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> 

而在你的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"); 
    } 
} 

另一种方法是用像“组名”附加属性其中您可以选择器(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); 
     } 
    } 
} 

而在你的视野:

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

很好的答案!我已经对下面的答案进行了一些修正。我仍然有一个小问题。如果你可以尝试解决这个问题,那么它会对你很好。如果你变得免费,那么请看看我的问题在这里:http://stackoverflow.com/questions/22209877/movefocus-from-one-listbox-to-another – Vishal

6

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

  1. 当您单击listbox1中的任何项目时,该项目被选中。
  2. 当您单击列表框2中的任何项目时,所选的列表框1的项目未被选中,但列表框2中的项目未被选中。
  3. 当您再次点击listbox2中的任何项目时,该项目被选中。

以下XAML只需添加到您的ListBoxItem的风格:

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

而且,如果有人在上面的回答使用代码后发现了以下错误

GroupName is already registered by Selector 

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

+0

这是一个很好的后续行动。谢谢! – usefulBee

+0

@usefulBee不客气。 – Vishal

+0

感谢您的关注。直到今天,我还没有真正需要附加的财产,所以发现所选项目没有被突出显示的问题。作为一个快速修复,我使用了一个EventTrigger,而不是上面的,因为当值为假时,常规触发器会重置,这意味着当该项目丢失键盘焦点时清除选择。这在我的情况下是不需要的。 – Simon

相关问题