我试图了解下面简化的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