Reputation: 2741
I have a DatagridTemplateColumn
using a ComboBox
, but the ItemSource
will not fill with the bound collection.
I should mention that the DataGrid
is being correctly bound and any other collections in the veiwModel is working, it is just this ComboBox
in the datagrid does not work.
This is the MCVE sample code:
<UserControl
d:DataContext="{d:DesignInstance d:Type=viewModels:StaffInfoDetailViewModel, IsDesignTimeCreatable=False}">
<DataGrid Grid.Column="0" Grid.ColumnSpan="6" AutoGenerateColumns="False" ItemsSource="{Binding SectionStaffMasterDisplay}" Grid.Row="4" Grid.RowSpan="2" AlternationCount="2" CanUserAddRows="True" CanUserDeleteRows="True" GridLinesVisibility="None" VerticalAlignment="Top" CanUserSortColumns="False">
<DataGridTemplateColumn Width="190" Header="資格">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
DisplayMemberPath="ItemName"
SelectedValuePath="ItemName"
SelectedItem="{Binding Path=Name, UpdateSourceTrigger=LostFocus}"
ItemsSource="{Binding Path=LicenceComboBox}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
.....more XAML
And the ViewModel
public class StaffInfoDetailViewModel : CollectionViewModel<StaffInfoDetailWrapper>
{
public StaffInfoDetailViewModel()
{
LicenceComboBoxItems();
MasterDataDisplay();
}
public void LicenceComboBoxItems()
{
foreach (var item in DataProvider.StartUpSection)
{
LicenceComboBox.Add(item);
}
}
private ObservableCollection<Licence> _licenceComboBox = new ObservableCollection<Licence>();
public ObservableCollection<Licence> LicenceComboBox
{
get { return _licenceComboBox; }
set
{
_licenceComboBox = value;
OnPropertyChanged();
}
}
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
The Model class:
public partial class Licence
{
public System.Guid Id { get; set; } // ID (Primary key)
public string ItemName { get; set; } // ItemName (length: 50)
public string Section { get; set; } // Section (length: 50)
public Licence()
{
InitializePartial();
}
partial void InitializePartial();
}
The datagrid collection.
private ObservableCollectionEx<StaffInfoDetail> _sectionStaffMasterDisplay = new ObservableCollectionEx<StaffInfoDetail>();
public ObservableCollectionEx<StaffInfoDetail> SectionStaffMasterDisplay
{
get { return _sectionStaffMasterDisplay; }
set
{
if (value != _sectionStaffMasterDisplay)
{
_sectionStaffMasterDisplay = value;
OnPropertyChanged();
}
}
}
The Entity class that the collection is filled by,
public partial class StaffInfoDetail
{
public System.Guid Id { get; set; } // ID (Primary key)
public byte[] Image { get; set; } // Image (length: 2147483647)
public int? StaffNo { get; set; } // StaffNo
public string SecondName { get; set; } // SecondName (length: 50)
public string FirstName { get; set; } // FirstName (length: 50)
public string Section { get; set; } // Section (length: 50)
public string SubSection { get; set; } // SubSection (length: 50)
public string Licence { get; set; } // Licence (length: 50)
public System.DateTime? StartDate { get; set; } // StartDate
public System.DateTime? EndDate { get; set; } // EndDate
public long? NightShiftOne { get; set; } // NightShiftOne
public long? NightShiftTwo { get; set; } // NightShiftTwo
public long? Lunch { get; set; } // Lunch
public long? Unplesant { get; set; } // Unplesant
public string JobType { get; set; } // JobType (length: 50)
public bool Kaizen { get; set; } // Kaizen
public int KaizenPercentage { get; set; } // KaizenPercentage
public bool? CurrentStaff { get; set; } // CurrentStaff
public string Notes { get; set; } // Notes (length: 4000)
public StaffInfoDetail()
{
InitializePartial();
}
partial void InitializePartial();
}
And the method that fills the collection, I added the caller to the original code from public StaffInfoDetailViewModel()
:
public void MasterDataDisplay()
{
SectionStaffMasterDisplay.AddRange(DataProvider.StaffInfos.Where(p =>
p.CurrentStaff == true &&
DataProvider.StartUpSection.Contains(p.Section)).ToObservable());
}
I can't see an issue with the DataContext
,but why won't this bind correctly when every other property is working?
And stepping through the code shows LicenceComboBox
to be correctly filled.
Upvotes: 0
Views: 1264
Reputation: 169200
Since the DataContext
of the ComboBox
in the DataGrid
is a StaffInfoDetail
object and the LicenceComboBox
property belongs to the StaffInfoDetailViewModel
class, you need to use a RelativeSource
to be able bind to this property.
Try this:
<DataGridTemplateColumn Width="190" Header="資格">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
DisplayMemberPath="ItemName"
SelectedValuePath="ItemName"
SelectedValue="{Binding Path=Licence, UpdateSourceTrigger=LostFocus}"
ItemsSource="{Binding Path=DataContext.LicenceComboBox, RelativeSource={RelativeSource AncestorType=DataGrid}}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
Upvotes: 1
Reputation: 19106
This issue is about the DataContext
. Each row in a DataGrid
has its own DataContext
- the item of the collection from DataGrid.ItemsSource
.
Lets have a very simple example on this
using System.Collections.ObjectModel;
using GalaSoft.MvvmLight;
namespace WpfApp4.ViewModel
{
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
BarCollection = new ObservableCollection<BarModel>
{
new BarModel { Id = 1, Name = "Bar 1", },
new BarModel { Id = 2, Name = "Bar 2", },
new BarModel { Id = 3, Name = "Bar 3", },
};
FooCollection = new ObservableCollection<FooViewModel>
{
new FooViewModel{ Id = 1, },
new FooViewModel{ Id = 2, },
new FooViewModel{ Id = 3, },
};
}
public ObservableCollection<BarModel> BarCollection { get; set; }
public ObservableCollection<FooViewModel> FooCollection { get; set; }
}
public class FooViewModel : ViewModelBase
{
private BarModel _bar;
public int Id { get; set; }
public BarModel Bar { get => _bar; set => Set( ref _bar, value ); }
}
public class BarModel
{
public int Id { get; set; }
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
}
We present a collection of FooViewModel
(MainViewModel.FooCollection
) in a DataGrid
and want to edit the Bar
property with a ComboBox
. The possible values are located in MainViewModel.BarCollection
.
And here is the XAML for the binding
<Window x:Class="WpfApp4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp4"
mc:Ignorable="d"
DataContext="{Binding Source={StaticResource Locator}, Path=Main}"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<CollectionViewSource x:Key="BarCollectionSource" Source="{Binding Path=BarCollection}"/>
</Window.Resources>
<Grid>
<DataGrid ItemsSource="{Binding Path=FooCollection}" AutoGenerateColumns="True">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Bar">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Bar}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
DisplayMemberPath="Name"
SelectedItem="{Binding Bar, UpdateSourceTrigger=LostFocus}"
ItemsSource="{Binding Source={StaticResource BarCollectionSource}}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
The magic is done by declaring a CollectionViewSource
and bind the BarCollection
to it
<Window.Resources>
<CollectionViewSource
x:Key="BarCollectionSource"
Source="{Binding Path=BarCollection}"/>
</Window.Resources>
and bind that to the ComboBox.ItemsSource
<ComboBox
DisplayMemberPath="Name"
SelectedItem="{Binding Bar, UpdateSourceTrigger=LostFocus}"
ItemsSource="{Binding Source={StaticResource BarCollectionSource}}"/>
Upvotes: 1
Reputation: 1620
I see here that Combobox ItemsSource is not part of DataGrid ItemsSource. So both of them are sharing different DataContext. DataContext for Combobox ItemsSource is ViewModel and I assume that ViewModel is DataContext of the datagrid. If my assumption is correct then you need to add relative source to binding of ItemsSource of Combobox.
Use below syntax for this :
<ComboBox
DisplayMemberPath="ItemName"
SelectedValuePath="ItemName"
ItemsSource="{Binding Path=PathToProperty,
RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}"
</ComboBox>
Use proper value for typeOfAncestor and your Combobox should get populated.
To read more about RelativeSource and AncestorType, go through this SO post.
Upvotes: 1