Хорошо, друг на работе в основном понял это для меня. Этот способ очень чистый, не чувствуешь себя счастливым.
Вот основная проблема: как упоминалось пользователем 164184, всплывающие подсказки являются всплывающими окнами и, следовательно, не являются частью визуального дерева. Так что есть какая-то магия, которую делает WPF. DataContext для всплывающего окна получен из PlacementTarget, так как привязки работают большую часть времени, несмотря на то, что всплывающее окно не является частью дерева. Но при изменении PlacementTarget это переопределяет значение по умолчанию, и теперь DataContext исходит из нового PlacementTarget, каким бы он ни был.
Совершенно не интуитивно понятно. Было бы неплохо, если бы MSDN вместо того, чтобы тратить часы на создание всех этих симпатичных графиков, где появляются различные всплывающие подсказки, сказал бы одно предложение о том, что происходит с DataContext.
Во всяком случае, на решение! Как и во всех забавных трюках WPF, на помощь приходят прикрепленные свойства. Мы собираемся добавить два прикрепленных свойства, чтобы мы могли напрямую установить DataContext всплывающей подсказки при ее создании.
public static class BindableToolTip
{
public static readonly DependencyProperty ToolTipProperty = DependencyProperty.RegisterAttached(
"ToolTip", typeof(FrameworkElement), typeof(BindableToolTip), new PropertyMetadata(null, OnToolTipChanged));
public static void SetToolTip(DependencyObject element, FrameworkElement value) { element.SetValue(ToolTipProperty, value); }
public static FrameworkElement GetToolTip(DependencyObject element) { return (FrameworkElement)element.GetValue(ToolTipProperty); }
static void OnToolTipChanged(DependencyObject element, DependencyPropertyChangedEventArgs e)
{
ToolTipService.SetToolTip(element, e.NewValue);
if (e.NewValue != null)
{
((ToolTip)e.NewValue).DataContext = GetDataContext(element);
}
}
public static readonly DependencyProperty DataContextProperty = DependencyProperty.RegisterAttached(
"DataContext", typeof(object), typeof(BindableToolTip), new PropertyMetadata(null, OnDataContextChanged));
public static void SetDataContext(DependencyObject element, object value) { element.SetValue(DataContextProperty, value); }
public static object GetDataContext(DependencyObject element) { return element.GetValue(DataContextProperty); }
static void OnDataContextChanged(DependencyObject element, DependencyPropertyChangedEventArgs e)
{
var toolTip = GetToolTip(element);
if (toolTip != null)
{
toolTip.DataContext = e.NewValue;
}
}
}
А потом в XAML:
<Grid ...>
<TextBlock x:Name="ObjectText"
ToolTipService.Placement="Left"
ToolTipService.PlacementTarget="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
mystuff:BindableToolTip.DataContext="{Binding}">
<mystuff:BindableToolTip.ToolTip>
<ToolTip>
<StackPanel>
<TextBlock Text="{Binding DisplayName.Name}"/>
<TextBlock Text="{Binding Details}" FontStyle="Italic"/>
...
</StackPanel>
</ToolTip>
</mystuff:BindableToolTip.ToolTip>
</TextBlock>
...
Просто переключите ToolTip
на BindableToolTip.ToolTip
, а затем добавьте новый BindableToolTip.DataContext
, который указывает на то, что вы хотите. Я просто устанавливаю его в текущий DataContext, поэтому он наследует модель представления, привязанную к DataTemplate.
Обратите внимание, что я встроил всплывающую подсказку вместо использования StaticResource. Это была ошибка в моем первоначальном вопросе. Очевидно, должен быть создан уникальный для каждого элемента. Другим вариантом будет использование триггера в стиле ControlTemplate.
Одним из улучшений может быть регистрация BindableToolTip.DataContext для уведомлений об изменении всплывающей подсказки, а затем я мог бы избавиться от BindableToolTip.ToolTip. Задача на другой день!