WPF互斥列表框

时间:2010-11-27 18:42:41

标签: wpf xaml binding

我有一个包含ListBox的ListBox的应用程序。我想使InnerList框互斥。我的ViewModel有一个Foos集合,它有一个描述,一个IsSelected属性和一个具有名称和IsSelected属性的集合条。

public class MyViewModel : INotifyPropertyChanged
{
     public ObservableCollection<Foo> Foos { /* code removed for brevity */ }
}

public class Foo : INotifyPropertyChanged
{
     public string Description { /* code removed for brevity */ }
     public ObservableCollection<Bar> Bars { /* code removed for brevity */ }
     public bool IsSelected { /* code removed for brevity */ }
}

public class Bar : INotifyPropertyChanged
{
     public string Name { /* code removed for brevity */ }
     public bool IsSelected { /* code removed for brevity */ }
}

以下是我的MainWindow的一部分,其DataContext设置为MyViewModel。此ListBox的ItemsSource属性使用ItemsSource={Binding Path=Foos}绑定,并且在此ListBox的模板中是使用ItemsSource="{Binding Path=Bars}"绑定的内部ListBox。 A,B和C是Foos的描述。其中包含的项目是Bar的名称。

|--------------------------|
| A |--------------------| |
|   | Item 1             | |
|   | Item 2             | |
|   | Item 3             | |
|   |--------------------| |
|                          |
| B |--------------------| |
|   | Item X             | |
|   | Item Y             | |
|   | Item Z             | |
|   |--------------------| |
|                          |
| C |--------------------| |
|   | Item l             | |
|   | Item m             | |
|   |--------------------| |
|--------------------------|

我需要这样做才能让用户只能从任何一个条中选择一个项目。因此,如果用户从Foo A中选择项目1,则从Foo B中选择项目X,则应取消选择项目1。

我还需要将所选项目绑定到窗口上其他位置的TextBox控件,但我认为只有一件事。

在代码和选择更改事件中执行此操作不是一种选择。我更喜欢只使用XAML。

提前致谢。

更新
根据Moonshield的建议,我已经提出了这个建议,但它仍然没有完全发挥作用。

public class MyViewModel
{
     private Bar _selectedBar;

     public ObservableCollection<Foo> Foos { /* code removed for brevity */ }
     public Bar SelectedBar 
     { 
          get { return _selectedBar; }
          set 
          {
              _selectedBar = null;
              NotifyPropertyChanged("SelectedBar");

              _selectedBar = value;
              NotifyPropertyChanged("SelectedBar");
          }
     }    
}
<ListBox x:Name="lbFoos" ItemsSource="{Binding Path=Foos}" SelectedItem="{Binding Path=SelectedBar}">
    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <StackPanel>
                            <TextBlock Text="Foo: " />
                            <TextBlock Text="{Binding Path=Description}" />
                            <ListBox ItemsSource="{Binding Path=Bars}" SelectedItem="{Binding Path=SelectedItem RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type ListBox}}}">
                                <ListBox.ItemContainerStyle>
                                    <Style TargetType="ListBoxItem">
                                        <Setter Property="Template">
                                            <Setter.Value>
                                                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                                    <TextBlock Text="{Binding Path=Name}" />
                                                </ControlTemplate>
                                            </Setter.Value>
                                        </Setter>
                                        <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=OneWayToSource}" />
                                    </Style>
                                </ListBox.ItemContainerStyle>
                            </ListBox>
                        </StackPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

3 个答案:

答案 0 :(得分:8)

最简单的方法是将SelectedBar属性添加到MyViewModel类,并将列表框的SelectedItem属性绑定到该属性。这只允许一次选择一个项目,并为您提供将文本框绑定到以后的内容。

然后,您可以在每个ListBoxItem的IsSelected属性上设置绑定(OneWayToSource)(可能通过ItemContainerStyle)以更新每个条的IsSelected属性。要更新Foo对象的IsSelected属性,请使用valueconverter设置与列表框的SelectedItem的绑定,以检查它是否为null。

编辑:

SelectedItem属性(实现Dan的修复):

protected Bar selectedItem;
public Bar SelectedItem{
    get
    {
        return selectedItem;
    }
    set
    {
        selectedItem = null;
        NotifyPropertyChanged("SelectedItem");

        selectedItem = value;
        NotifyPropertyChanged("SelectedItem");
    }

带有Binding的ListBoxItem(假设ListBoxItem DataContext是Bar viewmodel):

<ListBoxItem IsSelected="{Binding Path=IsSelected, Mode=OneWayToSource}" />

修改 - 修复代码:

我设法让您的代码正常运行。我发现了两个问题:

  1. 项目未出现选择的原因是您重新模板化了用Bar对象填充的ListBoxItems,因此在选择项目时没有突出显示样式 - 通过设置ItemTemplate来修复此问题,它模拟项目的内容而不是覆盖整个模板。

  2. 我没有将其中一个嵌套ListBoxes的SelectedItem绑定到父类的SelectedItem索引,然后将其绑定到viewmodel,而是将绑定更改为直接绑定到viewmodel,修复了多选问题

    <ListBox x:Name="lbFoos" ItemsSource="{Binding Path=Foos}"> <!--Removed SelectedItem binding.-->
        <ListBox.ItemContainerStyle>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ListBoxItem}">
                            <StackPanel>
                                <TextBlock Text="Foo: " />
                                <TextBlock Text="{Binding Path=Description}" />
                                <ListBox ItemsSource="{Binding Path=Bars}" SelectionChanged="ListBox_SelectionChanged" SelectedItem="{Binding Path=DataContext.SelectedBar, RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type ListBox}}}"><!--Changed binding to bind directly to ViewModel-->
                                    <ListBox.ItemTemplate><!--Set ItemTemplated rather than ControlTemplate-->
                                        <DataTemplate>
                                            <TextBlock Text="{Binding Path=Name}" />
                                        </DataTemplate> 
                                    </ListBox.ItemTemplate>
                                    <ListBox.ItemContainerStyle>
                                        <Style TargetType="ListBoxItem">
                                            <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=OneWayToSource}" />
                                        </Style>
                                    </ListBox.ItemContainerStyle>
                                </ListBox>
                            </StackPanel>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>
    

答案 1 :(得分:1)

如果您只想随时选择一个项目,那么拥有IsSelected属性毫无意义。相反,你应该有一个容器属性,它保存当前所选的项目(根据Moonshield的建议)。此模型意味着只能选择一个,而现有模型意味着可以选择多个。最终,Foo的个体实例可能不需要知道它们已经被选中了。

答案 2 :(得分:0)

而不是绑定到SelectedItem,而是尝试绑定到SelectedValue。我有两个DataGrids类似的情况,它将ItemsSource绑定到我的ViewModel中的两个不同的ICollectionView属性。这些ICollectionView属性使用与其原始源相同的ObservableCollection,并且通过使用MyType属性的过滤器互斥。 (即,对属性的一个值进行过滤,对同一属性的不同值进行其他ICollectionView过滤。)

我的ViewModel中有一个名为MyType类型的SelectedMyType的属性,该属性绑定到每个DataGrid的SelectedValue属性。从DataGrid中选择一个项目时,将取消选择任何以前选择的项目。