我试图了解下面简化的repro代码背后的实际情况.
我有一个单独的Window,其中ListBox和TextBlock绑定在一起(即master – > detail).然后我有一个带有几个属性的viewmodel – 一个字符串和一个日期.在这个日期,我实现了一个值转换器(LongDateConverter).
我在代码中有几个Debug.WriteLine()调用,导致以下输出:
>启动应用
>在转换器中:ConverterProblem.MainWindowviewmodel
>在转换器中:null
>单击列表框中的两个项目之一
>在转换器中:ConverterProblem.DataModel
第二次和第三次调用IValueConverter方法我想我明白了.第二个为null,因为ListBox还没有选定的项目.第三个是我选择的项目.
我不明白的是:
>为什么第一个调用传递了MainWindowviewmodel类型的值?
>为什么这个电话甚至首先发生?
这是我的代码:
MainWindow.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,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之前已绑定了Text依赖项.
XAML文件被编译成BAML,在应用程序运行时,它由BAML通过XAMLLoader加载,它从上到下解析XAML并相应地设置DP的值.
因为,首先遇到Text DP,所以它会尝试首先设置它的值,并且TextBlock尚未设置DataContext,因此它将继承其DataContext设置为MainWindowviewmodel的父窗口.因此,您会在转换器中看到MainWindowviewmodel.并且当设置DataContext时,将根据新的DataContext重新评估所有DP的绑定.
将您的XAML替换为此,您将看到MainWindowviewmodel将不再打印:
<TextBlock DataContext="{Binding Data}" Text="{Binding Converter={StaticResource longDateConverter}}" />
输出:
In converter: null In converter: ConverterProblem.DataModel