1

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!

PeterBuilt
  • 99
  • 1
  • 6
  • 1
    Any reason you need to do this since you aren't following MVVM by putting a load of code in the code-behind file? – Charleh Oct 05 '14 at 19:59
  • 1
    @Charleh: I know that I can do it in code behind but this is a sample application I am using to learn more about using a DataGrid with binding. As such, I am exploring all possible ways of doing this. – PeterBuilt Oct 05 '14 at 20:05

2 Answers2

3

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"));
    }
The One
  • 4,560
  • 5
  • 36
  • 52
  • Thank you! I didn't realize that I was initializing before populating my data source. Your prompt answer is much appreciated! – PeterBuilt Oct 05 '14 at 20:14
  • @PeterBuilt Also, your Person class needs to implement INotifyPropertyChanged – The One Oct 05 '14 at 20:18
  • Since I'm using an ObservableCollection for my list of Person objects I do not need to explicitly implement NotifyPropertyChange. I believe that is the advantage for using ObservableCollection. – PeterBuilt Oct 05 '14 at 20:34
  • 1
    @PeterBuilt Well ObservableCollection notifies changes in your collection, but not changes in your individual items, see this [post](http://stackoverflow.com/questions/10291339/if-observablecollection-is-used-do-i-still-need-to-implement-inotifypropertycha) – The One Oct 05 '14 at 20:39
  • Interesting. I wasn't aware of that distinction. Thank you for bringing it to my attentions. – PeterBuilt Oct 05 '14 at 20:54
2

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.
    }
}
AJ Richardson
  • 6,610
  • 1
  • 49
  • 59