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

时间:2014-07-10 03:17:19

标签: c# wpf xaml data-binding ivalueconverter

我试图了解下面简化的复制代码背后的实际情况。

我有一个WindowListBoxTextBlock绑定在一起(即主 - >详细)。然后我有一个带有几个属性的ViewModel - 一个字符串和一个日期。对于这个日期,我实现了一个值转换器(LongDateConverter)。

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

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

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

我不明白的是:

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

    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, 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();
            }
        }
    }
    

1 个答案:

答案 0 :(得分:12)

问题是,在为TextBlock设置 Text 之前,您已绑定 DataContext 依赖关系。

XAML文件被编译为BAML,在应用程序运行时,它由 XAMLLoader 从BAML加载, 从上到下解析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