2010-11-27 84 views
5

我有一個具有ListBox列表框的應用程序。我想使InnerList框互斥。我的ViewModel有一個集合Foos,它有一個描述,一個IsSelected屬性和一個集合Bars,它們有一個名字和IsSelected屬性。WPF互斥列表框

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的模板中是一個內部ListBox,它使用ItemsSource="{Binding Path=Bars}"綁定。 A,B和C是Foos的描述。其中包含的項目是酒吧名稱。

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

我需要這樣做,因此用戶只能從任何酒吧中選擇一個項目。因此,如果用戶從Foo A中選擇Item 1,然後從Foo B中選擇Item X,則應取消選擇Item 1。

我還需要將選定的項目綁定到窗口上其他位置的TextBox控件,但是我認爲這是一次。

在代碼和選擇更改事件中執行此操作不是一種選擇。我寧願只使用XAML來保存它。

在此先感謝。

UPDATE
繼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> 

回答

8

要做到這一點,最簡單的方法可能是一個SelectedBar屬性添加到您的MyViewModel類和綁定列表框到的SelectedItem屬性。這隻允許一次選擇一個項目,併爲您提供一些將您的文本框綁定到以後的內容。

然後,您可以在每個ListBoxItem的IsSelected屬性(可能通過ItemContainerStyle)上設置綁定(OneWayToSource)來更新每個欄的IsSelected屬性。要更新Foo對象的IsSelected屬性,請使用valueconverter設置與列表框的SelectedItem綁定,以檢查它是否爲null。

編輯:

SelectedItem屬性(實施丹的修復):

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

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

ListBoxItem中與綁定(假設ListBoxItem的DataContext的是酒吧視圖模型):

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

編輯 - 修復了代碼:

我設法讓你的代碼工作。有兩個問題,我發現:

  1. 項目未出現,選擇是,你會重新模板填充與酒吧對象ListBoxItems,所以沒有結果時選定的項目沒有高亮風格的原因 - 這個固定通過設置ItemTemplate來代替項目的內容,而不是覆蓋整個模板。

  2. 不是將其中一個嵌套列表框的SelectedItem綁定到父項的SelectedItem索引,然後將其綁定到視圖模型,而是將綁定直接綁定到視圖模型,該視圖模型修復了多選問題。

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

我遇到過的一個問題是,如果列表框的SelectedItem屬性綁定到一個屬性,並且屬性帶有一個不在列表中的值,那麼它不會導致所選項目在視覺上被取消選擇。我最終通過修改SelectedObject ViewModel屬性的setter來解決這個問題,以便在更新之間產生一個更改爲'null'的通知屬性。這將正確地導致列表框取消選擇。 – 2010-11-27 20:01:06

1

如果你只想要一個項目隨時然後有一個IsSelected屬性是沒有意義的選擇。相反,您應該擁有一個容納當前選定項目的容器屬性(按照Moonshield的建議)。這個模型意味着只能選擇一個,而現有的模型意味着可以選擇多個模型。最終,Foo的個體實例可能不需要知道他們已被選中。

0

而不是綁定到SelectedItem,而是嘗試綁定到SelectedValue。我有兩個將ItemsSource綁定到ViewModel中的兩個不同的ICollectionView屬性的DataGrid,類似的情況。這些ICollectionView屬性使用與其原始源相同的ObservableCollection,並通過使用MyType屬性的過濾器互斥。 (即,在屬性的一個值,並在相同的屬性的不同值的其它ICollectionView過濾器的一種過濾器。)

我有稱爲類型的MyType的SelectedMyType綁定到每個SelectedValue屬性在我的視圖模型的屬性數據網格。從DataGrid中選擇一個項目時,將取消選擇之前選擇的項目。