了解WPF数据绑定和值转换器交互

Gus*_*usP 9 c# data-binding wpf xaml ivalueconverter

我试图了解下面简化的repro代码背后的实际情况.

我有一个单独Window的a ListBox和a TextBlock绑定在一起(即master - > detail).然后我有一个带有几个属性的ViewModel - 一个字符串和一个日期.对于这个日期,我实现了一个值转换器(LongDateConverter).

Debug.WriteLine()在代码中有几个调用导致以下输出:

  • 启动应用
    • In converter: ConverterProblem.MainWindowViewModel
    • In converter: null
  • 单击列表框中的两个项目之一
    • In converter: ConverterProblem.DataModel

第二次和第三次调用IValueConverter我认为理解的方法.第二个是null因为ListBox还没有选定的项目.第三个是我选择的项目.

我不明白的是:

  1. 为什么第一个调用传递了类型的值MainWindowViewModel
  2. 为什么这个电话甚至首先发生?

这是我的代码:

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>
Run Code Online (Sandbox Code Playgroud)

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Roh*_*ats 15

问题是您Text在设置DataContextTextBlock 之前有绑定依赖关系.

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}}" />
Run Code Online (Sandbox Code Playgroud)

输出:

In converter: null
In converter: ConverterProblem.DataModel
Run Code Online (Sandbox Code Playgroud)