Trying to wrap my old brain around WPF data binding. Starting with the simplest scenario I could think of: An example online that worked, and I then simplified it.
I bound a simple string
property to a TextBlock
. Didn't work. When I then encapsulated that string
inside a class of my own, it worked just fine!
Question: Why doesn't my string
property work, but my class does?
<Window x:Class = "DataBindingOneWay.MainWindow"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
Height = "350" Width = "600">
<Grid>
<StackPanel Orientation = "Vertical" HorizontalAlignment="Center" Margin="0,100,0,0">
<TextBlock Text="{Binding Name1}" />
</StackPanel>
</Grid>
</Window>
using System.Windows;
namespace DataBindingOneWay
{
public partial class MainWindow : Window
{
public class Employee
{
public string? Name1 { get; set; } = "Paul";
}
public string? Name1 { get; set; } = "Peter";
public MainWindow()
{
InitializeComponent();
DataContext = new Employee();
// DataContext = Name1;
}
}
}
CodePudding user response:
Bindings operate on a data context that is set on elements deriving from FrameworkElement
through the DataContext
property. A data context is inherited in the tree of elements.
When data binding is declared on XAML elements, they resolve data binding by looking at their immediate DataContext property. The data context is typically the binding source object for the binding source value path evaluation. You can override this behavior in the binding and set a specific binding source object value. If the DataContext property for the object hosting the binding isn't set, the parent element's DataContext property is checked, and so on, up until the root of the XAML object tree. In short, the data context used to resolve binding is inherited from the parent unless explicitly set on the object.
If there is no data context set or you need to refer to other elements as bindings source, you need to parameterize a Binding
with Source
, ElementName
or RelativeSource
.
Bindings can be configured to resolve with a specific object, as opposed to using the data context for binding resolution.
Let use review how to bind a property defined in your MainWindow
. There is no data context assigned initially. Therefore, all bindings to Name1
will fail. What can we do? Here are a few options (there are more).
Set the
DataContext
ofMainWindow
tothis
in code-behind.public MainWindow() { InitializeComponent(); DataContext = this; }
This means all of the controls inherit the
MainWindow
as binding source. Consequently, we can bind theName1
property like this:<TextBlock Text="{Binding Name1}"/>
Here
Name1
is the name (or property path) of the property to bind on the binding source. So if we simply assignedName1
directly asDataContext
of the window, then we would need to refer to the binding source itself, which is what{Binding}
without a property path does.public MainWindow() { InitializeComponent(); DataContext = Name1; }
<TextBlock Text="{Binding}"/>
If you do not set a
DataContext
, you can still refer to elements usingElementName
(requires anx:Name
assigned to the target control) orRelativeSource
which refers to an element in the element tree from the current element traversing parents until the root is reached.<TextBlock Text="{Binding Name1, RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}}}"/>
This example translates to: Search a binding source of type
MainWindow
up the element tree and if you find it, bind to itsName1
property. As you can see, this does not necessarily need a data context if the property is defined on the control itself like in your example. Nevertheless, generally you would use theDataContext
property if nothing prevents it, since it is conveniently inherited.
I hope you saw that there is nothing really magical about it. Regarding the binding sytax (which can be confusing), there is a good documentation that should take away lots of your question marks:
I got to think: WHAT field, or, rather, property in the String Class actually holds the 'value' of the string?
Bonus round: You do not need to worry about that at all. You bind to the string
property and WPF knows how to display it, no need to use internal structures, but for educational purposes, the documentation states:
A string is a sequential collection of characters that's used to represent text. A
String
object is a sequential collection ofSystem.Char
objects that represent a string; aSystem.Char
object corresponds to a UTF-16 code unit. The value of theString
object is the content of the sequential collection ofSystem.Char
objects, and that value is immutable (that is, it is read-only).
The string
type exposes this character array with its indexer property Chars
.
CodePudding user response:
Because there is no Name1
inside the object that you are binding to, when you bind to the string. That is, there is no Name1
inside the Name1
(what would be the result of Name1.Name1
?). There is, however, a Name1
inside the Employee
object (what would be the result of Employee.Name1
?)
CodePudding user response:
If you want to access Name1
property of MainWindow
you should set DataContext
to this
(which is MainWindow instance) instead of Name1
.
using System.Windows;
namespace DataBindingOneWay
{
public partial class MainWindow : Window
{
public class Employee
{
public string? Name1 { get; set; } = "Paul";
}
public string? Name1 { get; set; } = "Peter";
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
}
}
or from the other side - when you want the Name1
property to be set as DataContext
of MainWindow
your binding should look like this:
<Window x:Class = "DataBindingOneWay.MainWindow"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
Height = "350" Width = "600">
<Grid>
<StackPanel Orientation = "Vertical" HorizontalAlignment="Center" Margin="0,100,0,0">
<TextBlock Text="{Binding}" />
</StackPanel>
</Grid>
</Window>
CodePudding user response:
Thanks to Jonathan's inspiring response, I got to think: WHAT field, or, rather, property in the String Class actually holds the 'value' of the string?
I played with everything, but nothing seemed to hold the actual string! (Weird). That's where I got a crazy idea and tried simply
<TextBlock Text="{Binding}" />
And
DataContext = Name1;
And ... that worked!