我有一个具有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)
最简单的方法是将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)
编辑 - 修复代码:
我设法让你的代码工作.我发现了两个问题:
项目未出现选择的原因是您重新模板化了用Bar对象填充的ListBoxItems,因此在选择项目时没有突出显示样式 - 通过设置ItemTemplate来修复此问题,其中模板的内容为项而不是覆盖整个模板.
我没有将其中一个嵌套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)