c# - 了解 WPF 数据绑定(bind)和值转换器交互

标签 c# wpf xaml data-binding ivalueconverter

我试图通过下面的简化重现代码了解幕后实际发生的情况。

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

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

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

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

我不明白的是:

  1. 为什么第一个调用传递了 MainWindowViewModel 类型的值?
  2. 为什么一开始就打这个电话?

这是我的代码:

主窗口.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/

相关文章:

c# - XAML 中的动画按钮背景颜色

c# - ResourceDictionary 找不到 DataTemplate 的 DataType

c# - 从不同的类访问控件

c# - 使 [IsOneWay=true] WCF 服务异步与在客户端使用任务调用同步方法之间是否存在显着差异?

c# - NotifyIcon Windows 10 适用于调试但不适用于发布

c# - 如何获取 WPF 应用程序的发布版本

c# - 在 WPF 中绘制相对于其容器的对角线 Text/TextBlock/Label/Control

c# - 如何在 WPF MVVM 应用程序中组织接口(interface)和实现

c# - 使用 Power BI Rest API 嵌入非组工作区报告

c# - 如何为颜色选择器 (wpf) 创建颜色 Canvas