Search code examples
c#wpfmvvmdata-bindingwpfdatagrid

Binding a list/collection/IEnumerable of Model Class to a Data Grid and Selecting Columns


I can't seem to find this answer via searching and cannot connect to the dots from other examples to my scenario.

Using MVVM Pattern:

  1. How do I bind my model class via ViewModel in my view to a listbox/datagrid?
  2. How do I pick and choose which columns I want displayed?
  3. Question 2 Followup: Can I pick and choose via the visual Studio UI properties, if yes, how?
  4. If done with XAML, is there autocomplete with my model/viewmodel class?
  5. Using Observable Collection, what's the correct way to implement?
  6. What is the "preferred" XAML control(datagrid?, listbox?, etc )

I've simplified my example into this: Model Class:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;


namespace TestListBinding.Models
{
    public class ProjectModel
    {
        public string Id { get; set; }
        public string ProjectTitle { get; set; }
        public string ProjectDetails { get; set; }
    }
}

ViewModel Class:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Threading.Tasks;
using TestListBinding.Models;

namespace TestListBinding.ViewModels
{
    public class ProjectViewModel
    {
        private ProjectModel _project;
        private ObservableCollection<ProjectModel> _projects;

        public ProjectViewModel()
        {
            _projects = new ObservableCollection<ProjectModel>();
            var proj = new ProjectModel();
            for (int i = 1; i <= 3; i++)
            {
                proj.Id = "ID" + i.ToString();
                proj.ProjectTitle = "Project " + i.ToString();
                proj.ProjectDetails = "Details about this: " + i.ToString();
                _projects.Add(proj);
                proj = new ProjectModel();
            }
        }

        public IEnumerable<ProjectModel> Projects
        {
            get { return _projects; }
        }
    }
}

The View Part:

I know I need to have a DataContext for the view. Many examples show it being set in the code-behind(see below), and some example reference a "StaticResource" for a listbox. I've not found a way to setup the bindings via UI with the way my code is setup.

MainWindow.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using TestListBinding.ViewModels;

namespace TestListBinding
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new ProjectViewModel();//is this correct, or is there an alternative method via XAML/control properties?
        }
    }
}

XAML(via code or UI) Can you please help me complete the data grid columns/bindings: Updated per @EdPlunkett

    <Window x:Class="TestListBinding.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:TestListBinding"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid HorizontalAlignment="Left" Height="239" Margin="42,32,0,0" VerticalAlignment="Top" Width="435" ItemsSource="{Binding Projects}">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Project Title" Binding="{Binding ProjectTitle}"/>
                <DataGridTextColumn Header="Project Details" Binding="{Binding ProjectDetails}"/>
            </DataGrid.Columns>

        </DataGrid>

    </Grid>
</Window>

This yields a strange layout with extra columns: Extra Columns


Solution

  • This is correct in your window constructor:

    DataContext = new ProjectViewModel();
    

    And this should show you some stuff in the grid:

    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Id}" Header="ID"  />
        <DataGridTextColumn Binding="{Binding ProjectTitle}" Header="Project Title" />
        <DataGridTextColumn Binding="{Binding ProjectDetails}" Header="Project Details" />
    </DataGrid.Columns>
    

    WPF people generally don’t use the designer/Properties pane stuff. It’s not great and it hasn’t improved in over ten years. You’re out of luck on autocomplete in this case as well.

    Preferred control? Depends what you want: If you want multiple columns per row, use DataGrid or ListView. If you want one value displayed per row, or some kind of pretty templated UI per row, use ListBox.

    Quick ListBox:

    <ListBox
        ItemsSource=“{Binding Projects}”
        DisplayMemberPath=“ProjectTitle”
        />