Когда Binding.RelativeSource
не разрешается, вы всегда можете быть уверены, что Binding.Target
не является частью визуального дерева.
В вашем первом примере вы явно определяете древовидную структуру ToolTip
. Вы явно создаете контент, например, добавляя TextBlock
. Содержимое ToolTip
не является частью визуального дерева, и поэтому Binding.RelativeSource
не может быть разрешено.
Во втором примере вы позволяете FrameworkElement
неявно создавать содержимое ToolTip
.
Теперь FrameWorkElement
сначала разрешит Binding
, которое разрешается, поскольку FrameworkElement
все еще является частью визуального дерева. Берется разрешенное значение, вызывается ToString
, создается TextBlock
и строковое значение присваивается TextBlock.Text
.
Решение
Для решения проблемы привязки при реализации ToolTip
явно, вы можете реализовать Binding Proxy , как предложено в комментарии @Mark Feldman, который использует разметку StaticResource
для предоставления Binding.Source
элементам, которые не являются частью визуального дерева.
Это в основном связываемый ObjectDataProvider
.
Аналогичное решение для прокси-сервера привязки состоит в том, чтобы определить контент как ресурс Grid
, а затем ссылаться на него через DynamicResource
с помощью ContentPresnter
:
<UserControl>
<Grid>
<Grid.Resources>
<!-- The proxy -->
<TextBlock x:Key="ToolTipText"
Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=InfoTT}" />
<Grid.ToolTip>
<ToolTip>
<ContentPresenter Content="{DynamicResource ToolTipText}" />
</ToolTip>
</Grid.ToolTip>
</Grid>
</UserControl>
Но вы также можете использовать тот факт, что DataContext
все еще наследуется. Привязки к DataContext
по-прежнему разрешаются.
В вашем сценарии, где вы хотите привязать содержимое ToolTip
к свойству родительского UserControl
, вы можете привязать это свойство к свойству модели представления, которое является текущим DataContext
из Grid
(и, следовательно, для своего ToolTip
). Я рекомендую это только при привязке к бизнес-данным, а не к данным макета:
<UserControl InfoTT="{Binding ViewModelInfoTT}">
<UserControl.DataContext>
<ViewModel />
</UserControl.DataContext>
<Grid>
<Grid.ToolTip>
<ToolTip>
<TextBlock Text="{Binding ViewModelInfoTT}" />
</ToolTip>
</Grid.ToolTip>
</Grid>
</UserControl>
Если вы не используете модели просмотра и размещаете данные непосредственно в элементе управления, вы можете установить DataContext
в сам элемент управления. Таким образом вы упростите все привязки и, конечно же, теперь можете выполнять привязку к UserControl
из ToolTip
:
// Constructor
public MyUserControl()
{
InitializeComponent();
// Set the UserControl's DataContext to the control itself
this.DataContext = this;
}
<UserControl>
<Grid>
<Grid.ToolTip>
<ToolTip>
<TextBlock Text="{Binding InfoTT}" />
</ToolTip>
</Grid.ToolTip>
</Grid>
</UserControl>
В качестве альтернативы переопределить DataContext
. Конечно, вы потеряете доступ к текущему контексту:
<UserControl>
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestoType=UserControl}>
<Grid.ToolTip>
<ToolTip>
<TextBlock Text="{Binding InfoTT}" />
</ToolTip>
</Grid.ToolTip>
</Grid>
</UserControl>