Привязка RelativeSource из всплывающей подсказки или ContextMenu - PullRequest
29 голосов
/ 08 сентября 2010

Что я здесь не так делаю?:

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

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

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

Ответы [ 4 ]

69 голосов
/ 08 сентября 2010

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

UPDATE
К сожалению, ссылка исчезла, и я больше не нашел ссылочную статью.
Насколько я помню, упомянутый блог показал, как привязать к 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. Затем вы можете передать этот контекст вниз через ваши пункты меню.

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

3 голосов
/ 25 июня 2017

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

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

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

<ContextMenu
DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}"
IsEnabled="{Binding myProperty}"  
>
0 голосов
/ 06 января 2017

Поскольку 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.
еще одним преимуществом этого подхода является переносимость и повторное использование в нескольких представлениях и проектах.

0 голосов
/ 08 сентября 2010

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

{Binding Path=Title, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
...