Friday, December 13, 2013

Binding a Window's DataContext to the Window

I have a window that displays information contained in list of objects. I want the window's datacontext to be the window itself, then have a grid with an itemssource that points to the list. The tricky part turns out to be setting the window's datacontext. The obvious way to do this is with a line of code like this...

    Private Sub MainWindow_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
        Me.DataContext = Me
    End Sub

But I want to do this in the XAML, not because I have to, but because I think I should be able to. I thought this fragment of xaml would do the trick...

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WPFTest"
    Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MainWindow/> 
    </Window.DataContext>
... 
But this throws a stack overflow error. The difference between the code behind and the markup is that the code behind sets the window's datacontext to the window whereas the markup sets the window's datacontext to a new window, and the new window has it's datacontext set to a new window, and the new window has... etc, etc.

The correct markup is...
DataContext="{Binding RelativeSource={RelativeSource Self}}"

This markup binds the window's datacontext to itself just like the code behind does. Here's an example of how to bind a window to itself, then bind a grid to a public property and list the contents.

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <DataGrid ItemsSource="{Binding Path=DC}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Key"  Binding="{Binding Path=Key}"/>
            <DataGridTextColumn Header="Name"  Binding="{Binding Path=Name}"/>
        </DataGrid.Columns>
    </DataGrid>
</Window>



Class MainWindow 

    Public Class cDC
        Public Property Key As Integer
        Public Property Name As String
        Public Sub New(Key As Integer, Name As String)
            Me._Key = Key
            Me._Name = Name
        End Sub
    End Class
    Public Property DC As New List(Of cDC) From {New cDC(1, "Alpha"), New cDC(2, "Beta"), New cDC(3, "Gamma")}

End Class

No comments:

Post a Comment