我有一个包含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>
答案 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}" />
修改 - 修复代码:
我设法让您的代码正常运行。我发现了两个问题:
项目未出现选择的原因是您重新模板化了用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>
答案 1 :(得分:1)
如果您只想随时选择一个项目,那么拥有IsSelected
属性毫无意义。相反,你应该有一个容器属性,它保存当前所选的项目(根据Moonshield的建议)。此模型意味着只能选择一个,而现有模型意味着可以选择多个。最终,Foo的个体实例可能不需要知道它们已经被选中了。
答案 2 :(得分:0)
而不是绑定到SelectedItem,而是尝试绑定到SelectedValue。我有两个DataGrids类似的情况,它将ItemsSource绑定到我的ViewModel中的两个不同的ICollectionView属性。这些ICollectionView属性使用与其原始源相同的ObservableCollection,并且通过使用MyType属性的过滤器互斥。 (即,对属性的一个值进行过滤,对同一属性的不同值进行其他ICollectionView过滤。)
我的ViewModel中有一个名为MyType类型的SelectedMyType的属性,该属性绑定到每个DataGrid的SelectedValue属性。从DataGrid中选择一个项目时,将取消选择任何以前选择的项目。