Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does binding the MainWindow datacontext in XAML fail to act the same as binding in the codebehind with this.datacontext=this?

I am trying to use Data binding to bind an ObservableCollection to the ItemsSource of a DataGrid, as I learn about WPF and stuff.

In the code-behind I can set the DataContext with this.DataContext = this; or bloopDataGrid.DataContext = this;. That's fine and dandy.

I thought I could try something like

<Window.DataContext>
    <local:MainWindow/>
</Window.DataContext>

in my main window, but this causes a Stack Overflow Exception as explained in this question. Fine, that makes some sense.

After reading this and other questions/answers that say to try DataContext="{Binding RelativeSource={RelativeSource Self}}" in the window's XAML code, I thought I could actually do this. Apparently I cannot. Or at least, the IDE lets me and it's syntactically correct, but does not do what I want (ie, exactly what this.DataContext = this; does).

Then I read this about using "{Binding ElementName=, Path=}" and tried to use it like so:

<DataGrid
    Name="bloopDataGrid"
    Grid.Row="1"
    ItemsSource="{Binding ElementName=testWin, Path=OutputCollection}">
</DataGrid>

Which also doesn't work. Maybe not for the same reason, but I can't figure out the problem with it.

Oddly, I can't replicate the rebinding example shown in Rachel Lim's blog post.

XAML:

<Window
    x:Class="DataBinding.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"
    x:Name="testWin">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>

        <Label Grid.Row="0" Content="{Binding text}">   
        </Label>

        <DataGrid
            Name="bloopDataGrid"
            Grid.Row="1"
            ItemsSource="{Binding Path=OutputCollection}">
        </DataGrid>
    </Grid>
</Window>

C#:

using System;
using System.Collections.ObjectModel; //For ObservableCollection<T>
using System.Windows;

namespace DataBinding
{
    public partial class MainWindow : Window
    {
        public String text { get; set; }
        public ObservableCollection<testStruct> OutputCollection { get; set; }

        public struct testStruct
        {
            public testStruct(String x, String y) : this()
            {
                Col1 = x;
                Col2 = y;
            }
            public String Col1 { get; set; }
            public String Col2 { get; set; }
        }

        public MainWindow()
        {
            InitializeComponent();

            testA t1 = new testA();
            this.DataContext = this;
            //this.DataContext = t1;
            //bloopDataGrid.DataContext = this;
            text = "bound \"this\"";
            t1.text = "bound a class";

            OutputCollection = new ObservableCollection<testStruct>();
            OutputCollection.Add(new testStruct("1", "2"));
            OutputCollection.Add(new testStruct("3", "4"));
        }

        public class testA
        {
            public String text { get; set; }
        }

    }
}

The above code is what I'm using to test this, and is currently using the code-behind version which correctly gives me

In codebehind

What am I doing wrong, which is preventing me from getting the same results as the above picture but by using XAML for the DataContext handling? Am I not connecting the dots properly? ...am I missing some dots?

like image 561
Hydronium Avatar asked Mar 04 '13 15:03

Hydronium


People also ask

How does binding work in XAML?

Data binding is a mechanism in XAML applications that provides a simple and easy way for Windows Runtime Apps using partial classes to display and interact with data. The management of data is entirely separated from the way the data is displayed in this mechanism.

How do I bind a ViewModel in XAML?

There are 3 ways to bind the View with ViewModel. One way is to bind DataContext Property within a XAML, Second is to assign DataContext within Code-Behind and last using ViewModelLocator.

What is the data binding concept and how binding works in WPF?

Data binding is a mechanism in WPF applications that provides a simple and easy way for Windows Runtime apps to display and interact with data. In this mechanism, the management of data is entirely separated from the way data. Data binding allows the flow of data between UI elements and data object on user interface.


1 Answers

<Window.DataContext>
    <local:MainWindow/>
</Window.DataContext>

is not the same as

this.DataContext = this;

The first one is creating a new instance of the MainWindow class and assigning that to the DataContext property of the Window, while the second is assigning the very same instance of the Window to its DataContext property.

In order to achieve that in XAML, you need to use a RelativeSource Binding:

<Window DataContext="{Binding RelativeSource={RelativeSource Self}}">
</Window>

Edit:

The difference in behavior between defining the DataContext in XAML and in code behind is caused by the fact that the XAML is actually parsed when the constructor finishes executing, because the Dispatcher waits for the user code (in the constructor of the Window) to finish before executing its pending operations.

This causes the actual property values to be different in these different moments, and since there is no INotifyPropertyChanged, WPF has no way of updating the UI to reflect the new values.

You could implement INotifyPropertyChanged in the Window itself, but I suggest creating a ViewModel for this, as I don't like the fact of mixing INotifyPropertyChanged (which is more of a ViewModel concept) with DependencyObject-derived classes (UI elements).

like image 127
Federico Berasategui Avatar answered Oct 08 '22 16:10

Federico Berasategui