0

I am really struggling to understand binding. I know there are loads of other threads with much the same title as this one, but they're all trying to do something more complex than I am, and all the answers assume a whole pile of stuff that I just don't get :(

I'm trying to display a dynamically updated message log. I've defined a Message class:

public class Message
{
    public DateTime Timestamp { get; private set; }
    public string Value { get; private set; }
    public int Severity { get; private set; }
    public Message(string value, int severity)
    {
        Timestamp = DateTime.Now;
        Value = value;
        Severity = severity;
    }
}

I've defined a MessageLog class as simply:

public class MessageLog: ObservableCollection<Message>
{
    public MessageLog(): base()
    { }
}

In my MainWindow constructor I have a Log property:

public MessageLog Log { get; private set; }

In the MainWindow constructor I initialise Log:

public MainWindow()
{
    InitializeComponent();
    DataContext = this;
    Log = new Model.MessageLog();
    // and so on
}

In the XAML for the main window I have:

<ListBox Name="MessagePanel" Height="100" ItemsSource="{Binding MessageLog}" IsEnabled="False"/>

Now if I add Message instances to the MessageLog I expected to see them appear in the ListBox. They don't. What have I missed?

Thanks in advance (and if you can point me somewhere that explains bindings clearly -- especially the view that XAML has of the code and where it can look for things -- then many more thanks on top. At the moment I'm using Matthew McDonald's "Pro WPF 4.5 in C#" and I'm just not getting it.)

chriga
  • 798
  • 2
  • 12
  • 29
digitig
  • 1,989
  • 3
  • 25
  • 45

3 Answers3

0

Change your constructor:

public MainWindow()
{
    InitializeComponent();
    DataContext = this;
    Log = new Model.MessageLog();
}

to this:

public MainWindow()
{
    InitializeComponent();
    Log = new Model.MessageLog(); // <- This line before setting the DataContext
    DataContext = this;
}

Explanation:

Setting properties after having set the DataContext requires your class to implement INotifyPropertyChanged and raise change notifications after properties are set.

Since you're setting the DataContext before setting the property, the value of this.Log is null at the time of DataBinding, and WPF is never notified that it ever changed.

That being said, you don't usually put Data inside UI Elements (such as Window). The accepted and recommended approach to WPF is MVVM, where you usually create a ViewModel and set that as the Window's DataContext:

public class MyViewModel
{
    public MessageLog Log {get;set;}

    public MyViewModel()
    {
        Log = new MessageLog();
    }
}

Window Constructor:

public MainWindow
{
   DataContext = new MyViewModel();
}
Community
  • 1
  • 1
Federico Berasategui
  • 43,562
  • 11
  • 100
  • 154
0

Your collection property name is Log which is what you should be binding to in ItemsSource property; and if you have not done a typo in your question then you are binding wrongly to MessageLog, and change Binding as below:

<ListBox Name="MessagePanel" Height="100" ItemsSource="{Binding Log}" IsEnabled="False"/>

For more information and learning on Data Binding in WPF (4.5), see MSDN Data Binding Overview

S2S2
  • 8,322
  • 5
  • 37
  • 65
0

The datacontext of the view must be the viewmodel.

Cornel Marian
  • 2,443
  • 23
  • 28
  • 2
    The `DataContext` simply sets the data layer for the UI layer. When following the `MVVM design pattern` I would agree with you that the `DataContext` should point to a `ViewModel`. It would appear however that the OP is not following `MVVM` and therefore it is acceptable in this scenario to set the data layer of the view to thew XAMLs code-behind file. –  Oct 09 '13 at 09:53