Подсказка привязки в UserControl - PullRequest
1 голос
/ 27 мая 2020

Я попытался связать текст ToolTip в UserControl следующим образом:

                   <Grid.ToolTip>
                        <TextBlock
                            Text="{
                                    Binding Path=InfoTT,  
                                    RelativeSource={
                                        RelativeSource Mode=FindAncestor, 
                                        AncestorType={x:Type UserControl}
                                       }
                                 }" />
                    </Grid.ToolTip>

И это не сработало, Tooltip был пустым и в журналах я увидел:

System. Windows .Data Ошибка: 4: не удается найти источник для привязки со ссылкой 'RelativeSource FindAncestor, AncestorType =' System. Windows .Controls.UserControl ', AncestorLevel =' 1 '' . BindingExpression: Путь = InfoTT; DataItem = null; целевой элемент - TextBlock (Name = ''); целевым свойством является «Текст» (тип «Строка») *

Но когда я это сделал:

                    <Grid
                    ToolTip="{
                                    Binding Path=InfoTT,  
                                    RelativeSource={
                                        RelativeSource Mode=FindAncestor, 
                                        AncestorType={x:Type UserControl}
                                       }
                                 }">

                </Grid>

Это сработало. Кто-нибудь может объяснить, почему не работает первый способ?

1 Ответ

3 голосов
/ 27 мая 2020

Когда 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>
...