WPF互斥列表框

Ada*_*dam 5 wpf xaml binding

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

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

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

|--------------------------|
| A |--------------------| |
|   | Item 1             | |
|   | Item 2             | |
|   | Item 3             | |
|   |--------------------| |
|                          |
| B |--------------------| |
|   | Item X             | |
|   | Item Y             | |
|   | Item Z             | |
|   |--------------------| |
|                          |
| C |--------------------| |
|   | Item l             | |
|   | Item m             | |
|   |--------------------| |
|--------------------------|
Run Code Online (Sandbox Code Playgroud)

我需要这样做才能让用户只能从任何一个条中选择一个项目.因此,如果用户从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");
          }
     }    
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

Moo*_*eld 9

最简单的方法是将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");
    }
Run Code Online (Sandbox Code Playgroud)

ListBoxItem with Binding(假设ListBoxItem DataContext是Bar viewmodel):

<ListBoxItem IsSelected="{Binding Path=IsSelected, Mode=OneWayToSource}" />
Run Code Online (Sandbox Code Playgroud)

编辑 - 修复代码:

我设法让你的代码工作.我发现了两个问题:

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

  • 我遇到的一个警告是,如果列表框的SelectedItem属性绑定到属性并且属性采用不在列表中的值,则不会导致在视觉上取消选择所选项.我最后通过修改SelectedObject ViewModel属性的setter来解决这个问题,这样就可以在更新之间将notify属性更改为'null'.这将正确地导致列表框取消选择. (2认同)