Search code examples
c#wpfmvvmcaliburn.microdatagridcomboboxcolumn

Binding a DataGridComboBoxCollumn to a IObservableCollection in my ViewModel


I'm at a learning project right now in which I have two models. User and Role. User has a reference to one Role object. I have a collection of both stored in my ViewModel as an IObservableCollection (Users and Roles). In my View, I have a DataGrid in which I set the DataContext/ItemsSource to the Users collection from my ViewModel using Caliburn.Micro, in the DataGrid I have a DataGridComboBoxColumn that is supposed to display all the entries of the Roles Collection from my ViewModel, but I just can't access them.

I've already tried everything I found on the Internet. RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}} and every variation I found of that, using the DataGridComboBoxColumn.ElementStyle / DataGridComboBoxColumn.EditingElementStyle method where I set the ItemsSource through them, DisplayMember etc. Nothing worked even remotely.

This is my current view

<UserControl x:Class="UserModule.Views.User.ListUsersView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:UserModule.Views.User"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <StackPanel>
        <DataGrid x:Name="Users" AutoGenerateColumns="False" CanUserAddRows="True">
            <DataGrid.Columns>
                <DataGridTextColumn x:Name="Username" Header="Username" Binding="{Binding Username}"/>
                <DataGridTextColumn x:Name="Role" Header="Role" Binding="{Binding Role.RoleName}"/> <!--This is the current Role the User has-->
                <DataGridComboBoxColumn Header="Group" 
                             ItemsSource="{Binding Path=DataContext.Roles, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" DisplayMemberPath="RoleName" />
            </DataGrid.Columns>
        </DataGrid>
    </StackPanel>
</UserControl>

And this is (the relevant part of) my current ViewModel where I get the data from

namespace UserModule.ViewModels.User
{
    using Caliburn.Micro;

    public class ListUsersViewModel : Screen
    {
        private IObservableCollection<User> users;
        private User selectedUser;

        /// <summary>
        /// Constructor for ListUserViewModel
        /// </summary>
        public ListUsersViewModel()
        {
            this.LoadData(); //Loads data from database into Users and Roles
        }

        /// <summary>
        /// Loaded User model-objects from database
        /// </summary>
        public IObservableCollection<User> Users
        {
            get => this.users;
            set
            {
                this.users = value;
                this.NotifyOfPropertyChange();
            }
        }

        public IObservableCollection<Role> Roles { get; set; }

    }
}

Both Collections are populated with data. The Username and RoleName are shown properly, but I can't get the ComboBox to show any information about the Roles, not even the Caliburn.Micro error message "No View found for x". If I use an ComboBox outside of my DataGrid on the other hand, while simply binding to Roles, of course I get this error message for each Role in Roles.


Solution

  • Okay, I kind of solved this now...by not using DataGridComboBoxColumn but rather making my own ComboBoxColumn through the DataGridTemplateColumn which has a TextBlock in the CellTemplate and a ComboBox in the CellEditingTemplate

    <DataGrid.Columns>
         <DataGridTextColumn Binding="{Binding Username}"/>
         <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Role.RoleName}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                        <ComboBox ItemsSource="{Binding Path=DataContext.Roles, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" DisplayMemberPath="RoleName"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>