In a WPF, if I use AutoGenerateColumns and bind it to my collection of class objects, it displays enums as a combobox automatically where I can choose between all options available in the enum:
<DataGrid ItemsSource="{Binding SettingsList}"/>
However, if I explicitly declare it in XAML:
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Mirroring" SelectedValuePath="{Binding Mirroring}"/>
</DataGrid.Columns>
It seems to not recognize the options available in the enum. I am certain I am missing something minor, like setting the item source to something, but I can't figure out what to set it to to make it work out of the box like when using auto generate, of course I can write a custom converter to get all values to set the item source, but it feels like I am missing a builtin way to make it work.
You said:
I am certain I am missing something minor, like setting the item source to something.
For setting the item source (and in general) what works best for me is managing the columns in code-behind, and since column definitions are part of the UI's presentation logic and don’t include business logic or state management, this doesn’t violate MVVM.
XAML: DataGrid
AutoGenerateColumns=False
<Window ...>
<Window.DataContext>
<local:MainWindowViewModel x:Name="MainWindowViewModel"/>
</Window.DataContext>
<Grid>
<DataGrid
Name="dataGrid" ItemsSource="{Binding Items}" AutoGenerateColumns="False"
CanUserAddRows="False" FontSize="14" RowHeight="30">
</DataGrid>
</Grid>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += (sender, e) =>
{
dataGrid.Columns.Add(new DataGridComboBoxColumn
{
Header = nameof(Item.Mirroring),
SelectedValueBinding = new Binding(nameof(Item.Mirroring)),
ItemsSource = Enum.GetValues<Mirroring>(),
Width = 150,
});
dataGrid.Columns.Add(new DataGridTextColumn
{
Header = nameof(Item.Name),
Binding = new Binding(nameof(Item.Name)),
Width = new DataGridLength(1, DataGridLengthUnitType.Star)
});
};
}
}
XAML Approach
If you prefer to stick with the XAML column declarations, one way to do it is have the view model provide the items source (here I made a singleton to do the enumeration one time). But now we strain a little to express in XAML what is so straightforward in code-behind.
class MainWindowViewModel
{
public ObservableCollection<Item> Items { get; } =
new(Enumerable.Range(0,5).Select(_=>new Item(_)));
public Array ComboBoxValues
{
get
{
if (_comboBoxValues is null)
{
_comboBoxValues = Enum.GetValues<Mirroring>();
}
return _comboBoxValues;
}
}
static Array? _comboBoxValues = default;
}
<Window ...>
<Window.DataContext>
<local:MainWindowViewModel x:Name="MainWindowViewModel"/>
</Window.DataContext>
<Grid>
<DataGrid
Name="dataGrid" ItemsSource="{Binding Items}" AutoGenerateColumns="False"
CanUserAddRows="False" FontSize="14" RowHeight="30">
<DataGrid.Columns>
<DataGridComboBoxColumn
Header="Mirroring"
SelectedValueBinding="{Binding Mirroring}"
ItemsSource="{Binding ComboBoxValues, Source={x:Reference MainWindowViewModel}}"
Width="150" />
<DataGridTextColumn
Header="Name"
Binding="{Binding Name}"
Width="*"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Either Way