我试图通过下面的简化重现代码了解幕后实际发生的情况。
我有一个 Window
,其中有一个 ListBox
和一个绑定(bind)在一起的 TextBlock
(即,master -> detail)。然后我有一个带有几个属性的 ViewModel——一个字符串和一个日期。对于日期,我实现了一个值转换器 (LongDateConverter
)。
我在代码中有几个 Debug.WriteLine()
调用导致以下输出:
- 启动应用
在转换器中:ConverterProblem.MainWindowViewModel
在转换器中:null
- 单击列表框中的两个项目之一
在转换器中:ConverterProblem.DataModel
IValueConverter
方法的第二次和第三次调用我想我明白了。第二个是 null
,因为 ListBox
还没有选定的项目。第三个是我选择的项目。
我不明白的是:
- 为什么第一个调用传递了
MainWindowViewModel
类型的值? - 为什么一开始就打这个电话?
这是我的代码:
主窗口.xaml:
<Window x:Class="ConverterProblem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:app="clr-namespace:ConverterProblem"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<app:LongDateConverter x:Key="longDateConverter"/>
</Window.Resources>
<StackPanel Orientation="Horizontal">
<ListBox SelectedItem="{Binding Data}" ItemsSource="{Binding DataList}"
DisplayMemberPath="Name"/>
<TextBlock Text="{Binding Converter={StaticResource longDateConverter}}"
DataContext="{Binding Data}" />
</StackPanel>
</Window>
MainWindow.xaml.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace ConverterProblem
{
public class LongDateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) {
Debug.WriteLine("In converter: null");
return "null";
}
Debug.WriteLine("In converter: " + value.GetType().ToString());
if (value.GetType() == typeof(MainWindowViewModel))
return "viewmodel";
return ((DataModel)value).Date.ToLongDateString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
public class DataModel
{
public string Name { get; set; }
public DateTime Date { get; set; }
}
public class MainWindowViewModel : INotifyPropertyChanged
{
private DataModel _data;
private List<DataModel> _dataList;
public MainWindowViewModel()
{
_dataList = new List<DataModel> {
new DataModel { Date = DateTime.Now, Name = "John" },
new DataModel { Date = DateTime.Now.AddDays(50), Name = "Sue" }
};
}
public DataModel Data
{
get { return _data; }
set
{
if (_data == value) return;
_data = value;
RaisePropertyChanged("Data");
}
}
public List<DataModel> DataList
{
get { return _dataList; }
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public partial class MainWindow : Window
{
private MainWindowViewModel _viewModel;
public MainWindow()
{
_viewModel = new MainWindowViewModel();
DataContext = _viewModel;
InitializeComponent();
}
}
}
最佳答案
问题是您在为 TextBlock 设置 DataContext
之前绑定(bind)了 Text
依赖项。
XAML 文件被编译成 BAML,在应用程序运行时,它通过 XAMLLoader
从 BAML 加载,从上到下解析 XAML 并设置DP 的相应值(value)。
因为,首先遇到 Text DP,所以它将首先尝试设置它的值,并且尚未为 TextBlock 设置 DataContext,因此它将继承其 DataContext 设置为 MainWindowViewModel 的父窗口。因此,您会在转换器中看到打印的 MainWindowViewModel。当设置 DataContext 时,所有 DP 的绑定(bind)都将根据新的 DataContext 重新评估。
将您的 XAML 替换为此,您将看到 MainWindowViewModel
将不再打印:
<TextBlock DataContext="{Binding Data}"
Text="{Binding Converter={StaticResource longDateConverter}}" />
输出:
In converter: null
In converter: ConverterProblem.DataModel
关于c# - 了解 WPF 数据绑定(bind)和值转换器交互,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24666935/