Понимание взаимодействия привязки данных WPF и преобразователя значений
Я пытаюсь понять, что на самом деле происходит за кулисами на упрощенном коде repro ниже.
У меня есть один Window
с ListBox
и TextBlock
, которые связаны вместе (т. е. мастер -> деталь). Затем у меня есть ViewModel с парой свойств-строка и дата. Для даты я реализовал преобразователь значений (LongDateConverter
).
У меня есть пара Debug.WriteLine()
вызовов в коде, которые приводят к следующему выходу:
- запуск апп
In converter: ConverterProblem.MainWindowViewModel
In converter: null
- щелкните по одному из двух пунктов в списке.
In converter: ConverterProblem.DataModel
Второй и третий вызовы метода IValueConverter
я думаю, что понимаю. Второй - null
, потому что у ListBox
еще нет выбранного элемента. Третий - для выбранного мною предмета.
Чего я не понимаю, так это:
- Почему при первом вызове передается значение типа
MainWindowViewModel
? - Почему этот звонок даже происходящее в самом начале?
Вот мой код:
Главное окно.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>
Главное окно.код 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 ответ:
Проблема в том, что вы связали
Text
зависимость до установкиDataContext
для текстового блока.XAML-файлы компилируются в BAML и при запуске приложения загружаются из BAML с помощью
XAMLLoader
который проанализируйте XAML сверху вниз и установите значение для DP соответственно.Поскольку Text DP встречается первым, поэтому он попытается сначала установить его значение, а DataContext еще не установлен для TextBlock, поэтому он унаследует от своего родительского окна, чье DataContext имеет значение MainWindowViewModel. Таким образом, Вы видите, что MainWindowViewModel напечатан в вашем конвертере. И когда DataContext установлен, все привязки DP будут повторно оценены в соответствии с новым DataContext.
Замените свой XAML на этот, и вы увидите
MainWindowViewModel
больше не буду печатать:<TextBlock DataContext="{Binding Data}" Text="{Binding Converter={StaticResource longDateConverter}}" />
вывод :
In converter: null In converter: ConverterProblem.DataModel