2012-03-22 97 views
4

我目前正在执行,使用ListBoxes显示层次应用(请不要建议使用TreeViewListBoxes需要)。WPF MVVM层次中选择项目

它看起来像那个in the article: WPF’s CollectionViewSource (with source code)

enter image description here

类:

public class Mountains : ObservableCollection<Mountain> 
{ 
    public ObservableCollection<Lift> Lifts { get; } 

    public string Name { get; } 
} 

public class Lift 
{ 
    public ObservableCollection<string> Runs { get; } 
} 

的例子使用CollectionViewSource实例(见XAML)以简化设计。 Mountains类的实例是窗口的DataContext


的问题是:我想的是,Mountains类有SelectedRun财产,应以当前选定的运行进行设置。

public class Mountains : ObservableCollection<Mountain> 
{ 
    public ObservableCollection<Lift> Lifts { get; } 

    public string Name { get; } 

    public string SelectedRun { get; set; } 
} 

也许我错过了一些基本的原则,但我怎么能做到这一点?

回答

4

您可能想了解在绑定中使用'/'。请参阅MSDN article上的'当前项目指针'一节。

这里是我的解决方案:

的XAML

<Grid.RowDefinitions> 
     <RowDefinition Height="Auto"/> 
     <RowDefinition/> 
    </Grid.RowDefinitions> 

    <Grid.ColumnDefinitions> 
     <ColumnDefinition/> 
     <ColumnDefinition/> 
     <ColumnDefinition/> 
    </Grid.ColumnDefinitions> 

    <TextBlock Margin="5" Grid.Row="0" Grid.Column="0" Text="Mountains"/> 
    <TextBlock Margin="5" Grid.Row="0" Grid.Column="1" Text="Lifts"/> 
    <TextBlock Margin="5" Grid.Row="0" Grid.Column="2" Text="Runs"/> 

    <ListBox Grid.Row="1" Grid.Column="0" Margin="5" 
      ItemsSource="{Binding Mountains}" DisplayMemberPath="Name" 
      IsSynchronizedWithCurrentItem="True" /> 

    <ListBox Grid.Row="1" Grid.Column="1" Margin="5" 
      ItemsSource="{Binding Mountains/Lifts}" DisplayMemberPath="Name" 
      IsSynchronizedWithCurrentItem="True"/> 

    <ListBox Grid.Row="1" Grid.Column="2" Margin="5" 
      ItemsSource="{Binding Mountains/Lifts/Runs}" 
      IsSynchronizedWithCurrentItem="True" 
      SelectedItem="{Binding SelectedRun}"/> 
</Grid> 

C#(注意,你并不需要执行INotifyPropertyChanged除非属性将被改变,不只是选择)

public class MountainsViewModel 
{ 
    public MountainsViewModel() 
    { 
     Mountains = new ObservableCollection<Mountain> 
         { 
          new Mountain 
           { 
            Name = "Whistler", 
            Lifts = new ObservableCollection<Lift> 
               { 
                new Lift 
                 { 
                  Name = "Big Red", 
                  Runs = new ObservableCollection<string> 
                     { 
                      "Headwall", 
                      "Fisheye", 
                      "Jimmy's" 
                     } 
                 }, 
                new Lift 
                 { 
                  Name = "Garbanzo", 
                  Runs = new ObservableCollection<string> 
                     { 
                      "Headwall1", 
                      "Fisheye1", 
                      "Jimmy's1" 
                     } 
                 }, 
                new Lift {Name = "Orange"}, 
               } 

           }, 
          new Mountain 
           { 
            Name = "Stevens", 
            Lifts = new ObservableCollection<Lift> 
               { 
                new Lift {Name = "One"}, 
                new Lift {Name = "Two"}, 
                new Lift {Name = "Three"}, 
               } 

           }, 
          new Mountain {Name = "Crystal"}, 
         }; 
    } 

    public string Name { get; set; } 
    private string _selectedRun; 
    public string SelectedRun 
    { 
     get { return _selectedRun; } 
     set 
     { 
      Debug.WriteLine(value); 
      _selectedRun = value; 
     } 
    } 

    public ObservableCollection<Mountain> Mountains { get; set; } 
} 

public class Mountain 
{ 
    public string Name { get; set; } 

    public ObservableCollection<Lift> Lifts { get; set; } 
} 

public class Lift 
{ 
    public string Name { get; set; } 

    public ObservableCollection<string> Runs { get; set; } 
} 
+0

非常感谢!这正是我想要的!顺便说一句,如果我有'ICollectionView'而不是'ObservableCollection'?我试过'ICollectionView',解决方案也很好。 我出于好奇,有没有什么办法可以在'MountainsViewModel'上没有'ListRunnel'' SelectedItem'绑定来获得'SelectedRun'?也许以某种方式使用'ICollectionView'属性/事件? – 2012-03-23 06:29:10

+0

WPF自动将任何集合包装在ICollectionView的内部适当实现中,因此最终没有太大区别。如果SelectedRun是控件(不是视图模型)上的依赖属性,你可以将它绑定到“Mountains/Lifts/Runs /”,注意尾随的'/',...也许我还没有尝试过。 – Phil 2012-03-23 07:32:27

+0

如果你喜欢,你可以接受答案:-) – Phil 2012-03-23 07:33:04

0

您的ViewModel不应该也是一个集合,它应该包含绑定到视图的集合和属性。 SelectedRun应该是这个ViewModel(MountainViewModel)而不是Mountains的一个属性。 MountainViewModel应该公开Mountains集合和SelectedRun,并且应该绑定到列表框的ItemsSource和SelectedItem。

+0

谢谢!但这是一个非常简单的例子。我所描述的类是ViewModel的非常简化的版本。我已经尝试了使用'SelectedItem'属性绑定到'SelectedRun'的解决方案:它**不记得升降类别**中最后一次选择的运行。 – 2012-03-22 18:45:19

+0

你的意思是一旦用户选择了另一座山或电梯,SelectedRun属性被清除了? – Slugart 2012-03-22 18:47:17

+0

@Serge你能告诉我们你如何绑定到xaml中的SelectedRun? – Slugart 2012-03-22 18:53:36

2

以下是我将如何做到这一点。您要确保在设置属性时激发INotifyPropertyChanged事件。要获得选定的运行,你必须得到MainViewModel.SelectedMountain.SelectedLift.SelectedRun。

public class MainViewModel: ViewModelBae 
{ 
    ObservableCollection<MountainViewModel> mountains 
    public ObservableCollection<MountainViewModel> Mountains 
    { 
     get { return mountains; } 
     set 
     { 
      if (mountains != value) 
      { 
       mountains = value; 
       RaisePropertyChanged("Mountains"); 
      } 
     } 
    } 
    MountainViewModel selectedMountain 
    public MountainViewModel SelectedMountain 
    { 
     get { return selectedMountain; } 
     set 
     { 
      if (selectedMountain != value) 
      { 
       selectedMountain = value; 
       RaisePropertyChanged("SelectedMountain"); 
      } 
     } 
    } 
} 

public class MountainViewModel: ViewModelBae 
{ 
    ObservableCollection<LiftViewModel> lifts 
    public ObservableCollection<LiftViewModel> Lifts 
    { 
     get { return lifts; } 
     set 
     { 
      if (lifts != value) 
      { 
       lifts = value; 
       RaisePropertyChanged("Lifts"); 
      } 
     } 
    } 
    LiftViewModel selectedLift 
    public LiftViewModel SelectedLift 
    { 
     get { return selectedLift; } 
     set 
     { 
      if (selectedLift != value) 
      { 
       selectedLift = value; 
       RaisePropertyChanged("SelectedLift"); 
      } 
     } 
    } 
} 

public class LiftViewModel: ViewModelBae 
{ 
    ObservableCollection<string> runs 
    public ObservableCollection<string> Runs 
    { 
     get { return runs; } 
     set 
     { 
      if (runs != value) 
      { 
       runs = value; 
       RaisePropertyChanged("Runs"); 
      } 
     } 
    } 
    string selectedRun 
    public string SelectedRun 
    { 
     get { return selectedLift; } 
     set 
     { 
      if (selectedLift != value) 
      { 
       selectedLift = value; 
       RaisePropertyChanged("SelectedLift"); 
      } 
     } 
    } 
} 

<ListBox ItemsSource="{Binding Mountains}" SelectedItem="{Binding SelectedMountain, Mode=TwoWay}"> 
<ListBox ItemsSource="{Binding SelectedMountain.Lifts}" SelectedItem="{Binding SelectedMountain.SelectedLift, Mode=TwoWay}"> 
<ListBox ItemsSource="{Binding SelectedMountain.SelectedLift.Runs}" SelectedItem="{Binding SelectedMountain.SelectedLift.SelectedRun, Mode=TwoWay}">