Привязка 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}}}"