Search code examples
c#wpfdata-bindingdatagridobservablecollection

Display ObservableCollection of class with string property in DataGrid


I have a ObservableCollection of my Movie class which has a string property name Name, I want it to be displayed in a DataGrid in WPF.

<Window x:Class="MovieList.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:MovieList"
    mc:Ignorable="d"
    Title="Movie List" Height="450" Width="800">
<Grid>
    <DataGrid ItemsSource="{Binding movies}" AlternatingRowBackground="Gray" >
    </DataGrid>
</Grid>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
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;

namespace MovieList
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        ObservableCollection<Movie> movies;
        public MainWindow()
        {
            movies = new ObservableCollection<Movie>();
            foreach (string dir in Directory.GetDirectories(@"F:\")) {
                movies.Add(new Movie(dir));
            }

            InitializeComponent();
        }
    }
    public class Movie
    {
        public string Name { get; set; }
        public Movie(string movieName)
        {
            Name = movieName;
        }

        
    }
}

Solution

  • You cannot bind to fields, make movies a public property and use pascal casing as per convention.

    public ObservableCollection<Movie> Movies { get; }
    

    You create your Movies collection in code-behind, but bindings work on the DataContext that is inherited from parent controls all the way up to your window, where it is unset. You can set it there:

    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    

    Or you can use a relative source with ancestor type to refer to the window in the ItemsSource binding.

    <DataGrid ItemsSource="{Binding Movies, RelativeSource={RelativeSource AncestorType={x:Type local:MainWindowWindow}}}"
              AlternatingRowBackground="Gray">
    

    Since the DataGrid auto-generates columns from properties of the bound type by default, you do not have to create columns yourself. However, if you want to, set AutoGenerateColumns to false.

    <DataGrid ItemsSource="{Binding Movies}"
              AlternatingRowBackground="Gray"
              AutoGenerateColumns="False">
       <DataGrid.Columns>
          <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
       </DataGrid.Columns>
    </DataGrid>
    

    If you miss that, you will have duplicate columns, the auto-generated ones and your own.