Enlazando un WPF DataGridComboBoxColumn con MVVM
(2)
Miré las respuestas a various questions , pero no logré asignar el contenido de las respuestas al problema que intento resolver. Lo he reducido al siguiente código (representativo del resultado que estoy tratando de lograr), y básicamente quiero poder Person.TitleId
el Person.TitleId
como su Title.TitleText
correspondiente cuando la fila no está siendo editada, y tiene el menú desplegable enlazado correctamente para que muestre el texto del TitleText
en el menú desplegable y escriba el TitleId
asociado de nuevo en el registro de la Person
cuando se TitleId
.
En resumen, ¿qué pongo en mi <DataGridComboBoxColumn>
para lograr esto?
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;
}
Aquí hay un código de trabajo. El punto clave aquí fue usar SelectedValueBinding
lugar de 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>
La respuesta de @ SnowBear funcionó bien para mí. Pero quiero aclarar un detalle de la unión.
En el ejemplo de @ Rob, las clases de Título y Persona usan TitleID. Por lo tanto, en la respuesta de @ SnowBear, en el enlace:
SelectedValueBinding="{Binding TitleId}"
no era inmediatamente obvio para mí a qué clase y propiedad se estaba destinado.
Como el atributo SelectedValueBinding apareció en DataGridComboBoxColumn, es vinculante para ItemsSource del DataGrid que lo contiene. En este caso, la colección Contactos de objetos Persona.
En mi caso, la colección DataSource de DataGrid fue atribuida con una propiedad que fue nombrada diferente de ValuePath de la colección ItemSource de ComboBox. Por lo tanto, mi valor de SelectedValueBinding estaba ligado a una propiedad diferente a la propiedad nombrada en SelectedValuePath de ComboBox.