自身 DataContext 绑定在 XAML 中不起作用,但在代码隐藏中起作用
Self DataContext binding not working in XAML but working in code behind
当我试图通过 DataContext="{Binding RelativeSource={RelativeSource Self}}" 绑定 Window 的 Datacontext 时它不起作用,如果我在代码隐藏中做同样的事情,它会怎样工作得很好。
我的假设是错误的吗
与
this.DataContext=这个
在代码隐藏中 window 的构造函数中。
XAML 的完整代码是...
<Window x:Class="SampleDemoListBox.MainWindow"
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:SampleDemoListBox"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
>
<Window.Resources>
<DataTemplate x:Key="ModelItemTemplate" >
<StackPanel Margin="25" Orientation="Horizontal">
<Image VerticalAlignment="Top" x:Name="ModelPicture" Width="150"
Source="{Binding PicturePath}"></Image>
<Grid VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label VerticalAlignment="Center" FontWeight="Bold" Content="Name:" Grid.Row="0" Grid.Column="0"></Label>
<Label VerticalAlignment="Center" FontWeight="Bold" Grid.Row="1" Grid.Column="0" Content="LastName:"></Label>
<Label VerticalAlignment="Center" FontWeight="Bold" Grid.Row="2" Grid.Column="0" Content="Age:"></Label>
<TextBlock VerticalAlignment="Center" Grid.Row="0" Grid.Column="1" x:Name="Name" Width="120" Text="{Binding Name}" ></TextBlock>
<TextBlock VerticalAlignment="Center" Grid.Row="1" Grid.Column="1" x:Name="LastName" Width="120" Text="{Binding LastName}" ></TextBlock>
<TextBox VerticalAlignment="Center" Grid.Row="2" Grid.Column="1" x:Name="Age" Width="120" Text="{Binding Age}" ></TextBox>
</Grid>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<ListBox x:Name="LstModels" Margin="25" ItemsSource="{Binding
Models}" ItemTemplate="{Binding Source={StaticResource
ModelItemTemplate}}"></ListBox>
<Button Width="120" Height="40" Click="AddModel_OnClick"
Content="Add Model" ></Button>
</StackPanel>
</Grid>
</Window>
代码隐藏是...
public partial class MainWindow : Window
{
public ObservableCollection<Model> Models{ get; set; }
public MainWindow()
{
InitializeComponent();
Models= new ObservableCollection<Model> { new Model{ Name =
"Shabana", LastName = "Parveen", Age = 35, PicturePath =
@"Images\pic.bmp" },
new Model { Name = "Ada", LastName = "Lovelace", Age = 37,
PicturePath = @"Images\AdaLovelace.bmp" }};
// this.DataContext = this;
}
}
你的问题是因为你有一个空的 observablecollection。如果你移动你的代码,它将起作用:
public partial class MainWindow : Window
{
public ObservableCollection<Model> Models{ get; set; }
public MainWindow()
{
Models= new ObservableCollection<Model> { new Model{ Name =
"Shabana", LastName = "Parveen", Age = 35, PicturePath =
@"Images\pic.bmp" },
new Model { Name = "Ada", LastName = "Lovelace", Age = 37,
PicturePath = @"Images\AdaLovelace.bmp" }};
InitializeComponent();
}
}
您应该考虑使用单独的 class 作为视图模型和 MVVM。
您观察到的行为是由于您初始化视图和实例化 ObservableCollection
的顺序所致。
当您在 XAML 中分配 DataContext
时,当您在构造函数中调用 InitilizeComponent()
时,所有 XAML 都会被解析。问题是:在解析您的 XAML 时,Models
仍然是 null
,因为它是在您调用 InitilizeComponent()
之后实例化的。出于这个原因,所有绑定都会失败,因为当绑定被评估时,您绑定到的集合是 null
(这发生在 xaml 正在被解析时)。当您实例化 Models
时,View 并不知道这一点,因为您还没有实现任何通知机制。
另一方面,当您在后面的代码中分配 DataContext 时,您会在解析完所有 XAML 之后执行此操作,这会强制视图在分配时评估绑定。 .因此有效。
我至少能想到3种解决方案:
- 更改初始化顺序
正如安迪在他的回答中所建议的那样,如果您首先实例化 Models
集合然后调用 InitilizeComponent()
,它将起作用。由于正在解析 XAML,View 将具有非空 DataContext,因此所有数据都将通过 Bindings 传播到您的 View。
- 实现
INotifyPropertyChanged
接口。
这将允许您在方便时实例化 Models
,然后引发 PropertyChanged
事件以通知视图。这同样有效。但是如果你要遇到这么多麻烦,你真的应该考虑使用 MVVM 模式(很多在线教程)。我将把实施的细节留给你,因为那里有无数的例子和教程。您可以从以下几个开始:
From SO, nice and to the point
More elaborate explanation here
- 使
Models
成为依赖项 属性。
如果您坚持在后台代码(而不是 MVVM)中这样做,DependencyPropeties
会很好地工作。依赖属性有自己的通知机制。尽管非常冗长,但它们很容易实现。下面是一个很好的来源,但您可以通过谷歌搜索找到很多。
当我试图通过 DataContext="{Binding RelativeSource={RelativeSource Self}}" 绑定 Window 的 Datacontext 时它不起作用,如果我在代码隐藏中做同样的事情,它会怎样工作得很好。 我的假设是错误的吗 与 this.DataContext=这个 在代码隐藏中 window 的构造函数中。
XAML 的完整代码是...
<Window x:Class="SampleDemoListBox.MainWindow"
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:SampleDemoListBox"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
>
<Window.Resources>
<DataTemplate x:Key="ModelItemTemplate" >
<StackPanel Margin="25" Orientation="Horizontal">
<Image VerticalAlignment="Top" x:Name="ModelPicture" Width="150"
Source="{Binding PicturePath}"></Image>
<Grid VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label VerticalAlignment="Center" FontWeight="Bold" Content="Name:" Grid.Row="0" Grid.Column="0"></Label>
<Label VerticalAlignment="Center" FontWeight="Bold" Grid.Row="1" Grid.Column="0" Content="LastName:"></Label>
<Label VerticalAlignment="Center" FontWeight="Bold" Grid.Row="2" Grid.Column="0" Content="Age:"></Label>
<TextBlock VerticalAlignment="Center" Grid.Row="0" Grid.Column="1" x:Name="Name" Width="120" Text="{Binding Name}" ></TextBlock>
<TextBlock VerticalAlignment="Center" Grid.Row="1" Grid.Column="1" x:Name="LastName" Width="120" Text="{Binding LastName}" ></TextBlock>
<TextBox VerticalAlignment="Center" Grid.Row="2" Grid.Column="1" x:Name="Age" Width="120" Text="{Binding Age}" ></TextBox>
</Grid>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<ListBox x:Name="LstModels" Margin="25" ItemsSource="{Binding
Models}" ItemTemplate="{Binding Source={StaticResource
ModelItemTemplate}}"></ListBox>
<Button Width="120" Height="40" Click="AddModel_OnClick"
Content="Add Model" ></Button>
</StackPanel>
</Grid>
</Window>
代码隐藏是...
public partial class MainWindow : Window
{
public ObservableCollection<Model> Models{ get; set; }
public MainWindow()
{
InitializeComponent();
Models= new ObservableCollection<Model> { new Model{ Name =
"Shabana", LastName = "Parveen", Age = 35, PicturePath =
@"Images\pic.bmp" },
new Model { Name = "Ada", LastName = "Lovelace", Age = 37,
PicturePath = @"Images\AdaLovelace.bmp" }};
// this.DataContext = this;
}
}
你的问题是因为你有一个空的 observablecollection。如果你移动你的代码,它将起作用:
public partial class MainWindow : Window
{
public ObservableCollection<Model> Models{ get; set; }
public MainWindow()
{
Models= new ObservableCollection<Model> { new Model{ Name =
"Shabana", LastName = "Parveen", Age = 35, PicturePath =
@"Images\pic.bmp" },
new Model { Name = "Ada", LastName = "Lovelace", Age = 37,
PicturePath = @"Images\AdaLovelace.bmp" }};
InitializeComponent();
}
}
您应该考虑使用单独的 class 作为视图模型和 MVVM。
您观察到的行为是由于您初始化视图和实例化 ObservableCollection
的顺序所致。
当您在 XAML 中分配 DataContext
时,当您在构造函数中调用 InitilizeComponent()
时,所有 XAML 都会被解析。问题是:在解析您的 XAML 时,Models
仍然是 null
,因为它是在您调用 InitilizeComponent()
之后实例化的。出于这个原因,所有绑定都会失败,因为当绑定被评估时,您绑定到的集合是 null
(这发生在 xaml 正在被解析时)。当您实例化 Models
时,View 并不知道这一点,因为您还没有实现任何通知机制。
另一方面,当您在后面的代码中分配 DataContext 时,您会在解析完所有 XAML 之后执行此操作,这会强制视图在分配时评估绑定。 .因此有效。
我至少能想到3种解决方案:
- 更改初始化顺序
正如安迪在他的回答中所建议的那样,如果您首先实例化 Models
集合然后调用 InitilizeComponent()
,它将起作用。由于正在解析 XAML,View 将具有非空 DataContext,因此所有数据都将通过 Bindings 传播到您的 View。
- 实现
INotifyPropertyChanged
接口。
这将允许您在方便时实例化 Models
,然后引发 PropertyChanged
事件以通知视图。这同样有效。但是如果你要遇到这么多麻烦,你真的应该考虑使用 MVVM 模式(很多在线教程)。我将把实施的细节留给你,因为那里有无数的例子和教程。您可以从以下几个开始:
From SO, nice and to the point
More elaborate explanation here
- 使
Models
成为依赖项 属性。
如果您坚持在后台代码(而不是 MVVM)中这样做,DependencyPropeties
会很好地工作。依赖属性有自己的通知机制。尽管非常冗长,但它们很容易实现。下面是一个很好的来源,但您可以通过谷歌搜索找到很多。