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
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?
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.
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.
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.
<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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With