Привязка RelativeSource из всплывающей подсказки или ContextMenu

Что я тут не так делаю?

 <GridViewColumn>
    <GridViewColumn.CellTemplate>
       <DataTemplate>
          <Button>
            <Button.ToolTip>
              <TextBlock Text="{Binding Path=Title, RelativeSource={RelativeSource AncestorType=Window}}" />

Это просто упрощенный пример, который все равно не работает:) На самом деле мне нужно получить значение из другого свойства, которое находится в области действия DataContext окна.

Помоги мне, пожалуйста.

4 ответа

Решение

Это сложно, потому что ToolTip не является частью VisualTree. Здесь вы видите классное решение той же проблемы с ContextMenus. Точно так же вы можете использовать подсказку.

ОБНОВИТЬ
К сожалению, ссылка исчезла, и я больше не нашел ссылочную статью.
Насколько я помню, упомянутый блог показал, как привязать к DataContext другого VisualTree, что часто бывает необходимо при связывании из всплывающей подсказки, ContextMenu или Popup.

Хороший способ сделать это - предоставить требуемый экземпляр (например, ViewModel) в свойстве Tag свойства PlacementTarget. В следующем примере это делается для доступа к экземпляру Command модели ViewModel:

<Button Tag="{Binding DataContext,RelativeSource={RelativeSource Mode=Self}}">
  <Button.ContextMenu>
    <ContextMenu>
       <MenuItem Command="{Binding PlacementTarget.Tag.DesiredCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContextMenu}}" .../>
    <ContextMenu>
  </Button.ContextMenu>
</Button>

Я не проверял это, и долгое время я делал это в последний раз. Пожалуйста, оставьте комментарий, если он не работает для вас.

ОБНОВЛЕНИЕ 2

Поскольку исходная ссылка, о которой был написан этот ответ, исчезла, я нажал на archive.org и нашла оригинальную запись в блоге. Вот оно, дословно из блога:

Поскольку ContextMenu в WPF не существует внутри визуального дерева вашей страницы / окна / элемента управления как такового, привязка данных может быть немного сложнее. Я искал в Интернете все выше и ниже, и наиболее распространенный ответ, похоже, "просто сделайте это в коде позади". НЕПРАВИЛЬНО! Я не вошел в удивительный мир XAML, чтобы вернуться к тому, чтобы что-то делать в коде.

Вот мой пример, который позволит вам связать строку, которая существует как свойство вашего окна.

public partial class Window1 : Window
{
    public Window1()
    {
        MyString = "Here is my string";
    }

    public string MyString
    {
        get;
        set;

    }
}


<Button Content="Test Button" 
     Tag="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}">
  <Button.ContextMenu>
    <ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, 
          RelativeSource={RelativeSource Self}}" >
      <MenuItem Header="{Binding MyString}"/>
    </ContextMenu>
  </Button.ContextMenu>   
</Button>

Важной частью является тег на кнопке (хотя вы также можете легко установить DataContext для кнопки). Здесь хранится ссылка на родительское окно. ContextMenu может получить к нему доступ через свойство PlacementTarget. Затем вы можете передать этот контекст через ваши пункты меню.

Я признаю, что это не самое элегантное решение в мире. Тем не менее, это лучше, чем установка кода в коде позади. Если у кого-то есть еще лучший способ сделать это, я бы хотел это услышать.

Согласно ниже:
PlacementTarget - это элемент управления, которому принадлежит ContextMenu (например, DataGrid). Нет необходимости в теге

IsEnabled связывается со значением myProperty в DataGrid.

Я проверил это, и это работает. Была похожая проблема с привязкой.

<ContextMenu
DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}"
IsEnabled="{Binding myProperty}"  
>

Так как ContextMenu отсутствует в визуальном дереве, привязка не будет работать. простое решение - использование Proxy Pattern, вы можете создать класс-оболочку, который наследуется от DependencyObject и имеет DependencyProperty это будет держать DataContext вашей Window, тогда вы можете иметь ресурс прокси в XAML и, наконец, связать свой MenuItem Команда для желаемой команды через прокси-объект.
Пример прокси:

Public class ProxyClass : DependencyObject
{
    Public object Data {get; set;}
   public static readonly DependencyProperty DataProperty = DependencyProperty.Register("DataProperty", typeof(object), typeof(ProxyClass), new FrameworkPropertyMetadata(null));

}

Как использовать в XAML:

<Window DataContext="{Binding MyViewModel}">
...
<Window.Resources>
    <ProxyClass Data={Binding} x:Key="BindingProxy"/>

</Window.Resources>
...  
<MenuItem Command="{Binding Source={StaticResource BindingProxy}, Path=Data.MyDesiredCommand"/>
...
</Window>

Что происходит?
Data собственностью ProxyClass будет привязан к DataContext из Window, тогда у него есть все ваши комманды и свойства вашего ViewModel внутри ProxyClass ресурс.
Еще одним преимуществом этого подхода является переносимость и повторное использование в нескольких представлениях и проектах.

Я думаю, что это должно быть сделано так:

{Binding Path=Title, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Другие вопросы по тегам