用MVVM绑定WPF DataGridComboBoxColumn

问题描述:

我已经看过variousquestions的答案,但还没有设法将答案中的内容映射到我试图解决的问题上。我已经将它缩减为下面的代码(代表我尝试实现的结果),并且基本上希望能够在行未被编辑时呈现Person.TitleId作为其对应的Title.TitleText,并且具有下降正确绑定,以便它在下拉列表中显示TitleText,并在关联的TitleId记录更改后将其写回Person记录。用MVVM绑定WPF DataGridComboBoxColumn

总之,我为了达到这个目的在<DataGridComboBoxColumn>中放什么?

App.xaml.cs

protected override void OnStartup(StartupEventArgs e) 
{ 
    base.OnStartup(e); 

    var viewModel = new ViewModels.MainWindowViewModel(); 
    var mainWindow = new MainWindow(); 
    mainWindow.DataContext = viewModel; 
    mainWindow.ShowDialog(); 
} 

MainWindow.xaml

<Grid> 
    <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Path=Contacts}"> 
     <DataGrid.Columns> 
      <DataGridComboBoxColumn Header="Title" SelectedItemBinding="{Binding Person}"> 
       <DataGridComboBoxColumn.ElementStyle> 
        <Style TargetType="ComboBox"> 
         <Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Titles}"/> 
         <Setter Property="IsReadOnly" Value="True"/> 
        </Style> 
       </DataGridComboBoxColumn.ElementStyle> 
       <DataGridComboBoxColumn.EditingElementStyle> 
        <Style TargetType="ComboBox"> 
         <Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Titles}"/> 
         <Setter Property="DisplayMemberPath" Value="TitleText" /> 
        </Style> 
       </DataGridComboBoxColumn.EditingElementStyle> 
      </DataGridComboBoxColumn> 
     </DataGrid.Columns> 
    </DataGrid> 
</Grid> 

Person.cs

public class Person 
{ 
    public int TitleId { get; set; } 
    public string LastName { get; set; } 
    public string FirstName { get; set; } 
} 

Title.cs

public struct Title 
{ 
    public Title(int titleId, string titleText) 
     : this() 
    { 
     TitleId = titleId; 
     TitleText = titleText; 
    } 

    public string TitleText { get; private set; } 
    public int TitleId { get; private set; } 

    public static List<Title> GetAvailableTitles() 
    { 
     var titles = new List<Title>(); 

     titles.Add(new Title(1, "Mr")); 
     titles.Add(new Title(2, "Miss")); 
     titles.Add(new Title(3, "Mrs")); 

     return titles; 
    } 
} 

MainWindowViewModel.cs

public class MainWindowViewModel : ViewModelBase 
{ 
    private ObservableCollection<Person> contacts; 
    private List<Title> titles; 

    public MainWindowViewModel() 
    { 
     titles = Title.GetAvailableTitles(); 

     Contacts = new ObservableCollection<Person>(); 
     Contacts.Add(new Person() { FirstName = "Jane", LastName = "Smith", TitleId = 2 }); 
    } 

    public List<Title> Titles 
    { 
     get { return titles; } 
    } 

    public ObservableCollection<Person> Contacts 
    { 
     get { return contacts; } 
     set 
     { 
      if (contacts != value) 
      { 
       contacts = value; 
       this.OnPropertyChanged("Contacts"); 
      } 
     } 
    } 
} 

ViewModelBase.cs

public class ViewModelBase : INotifyPropertyChanged 
{ 
    protected void OnPropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
} 
+0

如果你能概述那个“计划”的哪些部分不起作用/哪些实际上做了,它将会有所帮助。例如你能找到ComboBox的ItemsSource列表吗? – 2011-04-01 19:32:49

+0

另外:从看起来你如何使用'Title'这个类似乎是多余的,并且最好由'Dictionary '替代。 – 2011-04-01 19:38:51

+0

@ H.B。 - 如代码所示,网格不显示初始值的文本表示(即Person.TitleId的相应TitleText),下拉列表中正确填充{Mr,Mrs,Miss}并选择一个项目下拉式结果在TestMVVM.Models中。标题显示在网格中(TestMVVM.Models是解决方案中的名称空间,为简洁起见,我将其删除)。 – Rob 2011-04-01 19:39:26

这里是一个工作的代码。这里的关键是使用SelectedValueBinding而不是SelecteItemBinding

<DataGridComboBoxColumn Header="Title" 
         SelectedValueBinding="{Binding TitleId}" 
         SelectedValuePath="TitleId" 
         DisplayMemberPath="TitleText" 
         > 
    <DataGridComboBoxColumn.ElementStyle> 
     <Style TargetType="ComboBox"> 
      <Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Titles}"/> 
     </Style> 
    </DataGridComboBoxColumn.ElementStyle> 
    <DataGridComboBoxColumn.EditingElementStyle> 
     <Style TargetType="ComboBox"> 
      <Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Titles}"/> 
     </Style> 
    </DataGridComboBoxColumn.EditingElementStyle> 
</DataGridComboBoxColumn> 
+0

令人难以置信的是,我只是写了几乎完全相同的东西,好吧,这一轮去找你。 – 2011-04-01 20:20:42

+0

这在我的简化情况下完美运行,我将花费一些时间将其整合到我的实际代码中,以查看它是否在那里工作(我确信它会!)。任何你可以解释的机会*它是如何/为什么会起作用的? :) – Rob 2011-04-01 20:36:32

+2

'SelectedValuePath'设置选定对象中代表组合框选定项目的成员的路径,另一方面'DisplayMemberPath'设置应该显示的类内成员的路径。 “SelectedValueBinding”会将选定的值绑定到绑定中指定的属性。这有点令人困惑,但如果你使用它几次,这是有道理的。 E:http://*.com/questions/3797034/confused-with-wpf-combobox-displaymemberpath-selectedvalue-and-selectedvaluepath – 2011-04-01 20:48:59

@ SnowBear的回答对我很好。但我想澄清绑定的细节。

在@ Rob的例子中,Title和Person类都使用TitleID。 因此,在@ SnowBear的回答,在结合:

SelectedValueBinding="{Binding TitleId}" 

它不是立即明显,我哪个阶级和财产的束缚。

因为SelectedValueBinding属性出现在DataGridComboBoxColumn上,所以它绑定到包含DataGrid的ItemsSource。在这种情况下,Person对象的Contacts集合。

就我而言,DataGrid的DataSource集合被赋予了一个与ComboBox的ItemSource集合的ValuePath命名不同的属性。所以我的SelectedValueBinding的值被绑定到一个不同于ComboBox的SelectedValuePath中指定的属性的属性。