I'm following this question on how to access your DataContext
class from code-behind. Implemeting it the way it says doiesn't work for me (apparently I'm doing something wrong).
What I'm trying to do:
I have a button that is trying to read a Text
property from a class that is binded to TextBox
:
private void myButton_Click(object sender, RoutedEventArgs e)
{
var dataContext = myWindow.DataContext as myClass;
System.Windows.MessageBox.Show(dataContext.Text);
}
For some reason dataContext
is always null.
XAML:
<Window x:Class="TestApp.MainWindow"
x:Name="myWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestApp"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="300">
<Window.Resources>
<local:myClass x:Key="myDataSource"/>
</Window.Resources>
<Window.DataContext>
<Binding Source="myDataSource"/>
</Window.DataContext>
<Grid Margin="0,0,0,2">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox HorizontalAlignment="Stretch" Margin="5,5,5,5" TextWrapping="Wrap" Text="{Binding Path=Text, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" />
<Button x:Name="myButton" Content="Button" HorizontalAlignment="Stretch" Margin="5,5,5,5" Grid.Row="1" VerticalAlignment="Stretch" Click="myButton_Click"/>
</Grid>
</Window>
Code-behind:
namespace TestApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//var dataContext = myWindow.DataContext as myClass;
}
private void myButton_Click(object sender, RoutedEventArgs e)
{
var dataContext = myWindow.DataContext as myClass;
System.Windows.MessageBox.Show(dataContext.Text);
}
}
public class myClass : INotifyPropertyChanged
{
private string text;
public string Text
{
get { return text; }
set {
text = value;
OnPropertyChanged("Text");
}
}
public event PropertyChangedEventHandler? PropertyChanged;
// Create the OnPropertyChanged method to raise the event
// The calling member's name will be used as the parameter.
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
The full code is also in this question (that was closed since it was "answered" in the question linked above, but the solution still doesn't work for me). What am I doing wrong?
CodePudding user response:
The expression
<Window.DataContext>
<Binding Source="myDataSource"/>
</Window.DataContext>
binds the DataContext to the string "myDataSource"
. It should instead be
<Window.DataContext>
<Binding Source="{StaticResource myDataSource}"/>
</Window.DataContext>
You do however not have to declare the DataContext object as resource at all. Just write
<Window.DataContext>
<local:myClass/>
</Window.DataContext>
Also be aware that your conclusion that the "DataContext is always null" is incorrect. It is the result of the expression DataContext as myClass
which was null, because DataContext did not contain an object of type myClass
. In general, when you use the as
operator, you should always check the result for null
before accessing it. Or use the is
operator like
if (DataContext is myClass dataContext)
{
MessageBox.Show(dataContext.Text);
}
You may also notice that using the myWindow
field was redundant, since the code behind methods belong to the MainWindow class.