PeterBuilt
PeterBuilt

Reputation: 99

Set DataContext in XAML Rather Than in Code Behind

I've read in other posts that in place of specifying the DataContext in code behind it can be specified in XAML.

I have an ObservableCollection declared and populated in the constructor of my main class where I also set the DataContext:

using System;
using System.Windows;
using System.Collections.ObjectModel;

namespace ItemsControlDemo
{
    public partial class MainWindow : Window
    {
        public ObservableCollection<Person> PersonList { get; set; }

        public MainWindow()
        {
            InitializeComponent();

            PersonList = new ObservableCollection<Person>();

            PersonList.Add(new Person(16, "Abraham", "Lincoln"));
            PersonList.Add(new Person(32, "Franklin", "Roosevelt"));
            PersonList.Add(new Person(35, "John", "Kennedy"));
            PersonList.Add(new Person(2, "John", "Adams"));
            PersonList.Add(new Person(1, "George", "Washington"));
            PersonList.Add(new Person(7, "Andrew", "Jackson"));

            DataContext = this;
        }

        private void Button_Add_Click(object sender, RoutedEventArgs e)
        {
            PersonList.Add(new Person(3, "Thomas", "Jefferson"));
        }

        private void Button_Remove_Click(object sender, RoutedEventArgs e)
        {
            PersonList.Remove(TheDataGrid.SelectedItem as Person);
        }
    }

    public class Person
    {
        public Person() { }

        public Person(int id, string firstName, string lastName)
        {
            ID = id;
            FirstName = firstName;
            LastName = lastName;
        }

        public int ID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
}

If I do this, all works well in my application.

However, if I remove "DataContext = this;" from the constructor and instead set the DataContext in the Window element of my application

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

I get no data.

This is the XAML where I set the DataContext after I have removed it from code behind:

<Window x:Class="ItemsControlDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ItemsControlDemo"
        Title="Items Control Demo" Height="350" Width="400"
        WindowStartupLocation="CenterScreen"
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <DataGrid Grid.Row="0" Name="TheDataGrid" SelectedValuePath="ID"
                  AutoGenerateColumns="False"
                  AlternatingRowBackground="Bisque"
                  ItemsSource="{Binding PersonList}">
            <DataGrid.Columns>
                <DataGridTextColumn Header="ID"         Binding="{Binding Path=ID}"/>
                <DataGridTextColumn Header="First Name" Binding="{Binding Path=FirstName}"/>
                <DataGridTextColumn Header="Last Name"  Binding="{Binding Path=LastName}"/>
            </DataGrid.Columns>
        </DataGrid>
        <StackPanel Grid.Row="1" Orientation="Horizontal"
                    HorizontalAlignment="Left" VerticalAlignment="Bottom">
            <Button Content="Add Item"    Margin="5" Click="Button_Add_Click"/>
            <Button Content="Remove Item" Margin="5" Click="Button_Remove_Click"/>
        </StackPanel>
    </Grid>
</Window>

Any help in what I am doing incorrectly would be greatly appreciated.

Thanks!

Upvotes: 1

Views: 1184

Answers (2)

The One
The One

Reputation: 4706

You need to setup your source before calling InitializeComponent

Edit: Actually you just need to instantiate it before InitializeComponent, since it's an ObservableCollection it implements INotifyCollectionChanged, so if you modify the collection, your grid will be updated with the changes.

public MainWindow()
    {
        PersonList = new ObservableCollection<Person>();
        InitializeComponent();
        PersonList.Add(new Person(16, "Abraham", "Lincoln"));
        PersonList.Add(new Person(32, "Franklin", "Roosevelt"));
        PersonList.Add(new Person(35, "John", "Kennedy"));
        PersonList.Add(new Person(2, "John", "Adams"));
        PersonList.Add(new Person(1, "George", "Washington"));
        PersonList.Add(new Person(7, "Andrew", "Jackson"));
    }

Upvotes: 3

AJ Richardson
AJ Richardson

Reputation: 6830

216's answer works. Alternatively, you can implement INotifyPropertyChanged so the XAML is notified when you do set PersonList.

ObservableCollection only notifies when items in the collection change. If you want to notify when you assign a new value to the collection (e.g. PersonList = new ObservableCollection<Person>()), you need to implement INotifyPropertyChanged.

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private ObservableCollection<Person> personList;

    public ObservableCollection<Person> PersonList
    {
        get { return personList; }
        set
        {
            if (value == personList)
                return;
            personList = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("PersonList"));
        }
    }

    public MainWindow()
    {
        InitializeComponent();

        PersonList = new ObservableCollection<Person>();

        // etc.
    }
}

Upvotes: 2

Related Questions