RelativeSource de liaison à partir d'une info-bulle ou ContextMenu
-
01-10-2019 - |
Question
Qu'est-ce que je fais mal ici:
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button>
<Button.ToolTip>
<TextBlock Text="{Binding Path=Title, RelativeSource={RelativeSource AncestorType=Window}}" />
C'est juste un exemple simplifié, qui ne fonctionne pas de toute façon :) En fait, je dois obtenir une valeur d'une autre propriété qui est portée de DataContext de la fenêtre.
Aidez-moi svp.
La solution
Ceci est délicat car infobulle ne fait pas partie du VisualTree. vous voyez une solution fraîche pour le même problème avec ContextMenus. De la même façon, vous pouvez aller pour l'info-bulle.
UPDATE
Malheureusement, le lien a disparu et je n'ai pas trouvé le plus article référencé.
Pour autant que je me souvienne, le blog référencé a montré comment se lier à une DataContext d'un autre VisualTree, qui est souvent necessay lors de la liaison d'une info-bulle, un ContextMenu ou Popup.
Une façon agréable de ce faire, est de fournir l'instance désirée (par exemple ViewModel) dans le Tag-propriété du PlacementTarget. L'exemple suivant fait pour accéder à une instance de commande d'un 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>
Je l'ai pas testé et son depuis longtemps je l'ai fait la dernière fois. S'il vous plaît faire un commentaire si cela ne fonctionne pas pour vous.
MISE À JOUR 2
Comme le lien d'origine que cette réponse a été écrit au sujet est allé, je frappé archive.org et trouvé entrée de blog d'origine. Ici, il est, mot pour mot du blog:
Parce qu'un ContextMenu dans WPF n'existe pas dans l'arbre visuel de votre page / fenêtre / contrôle en soi, la liaison de données peut être un peu délicat. J'ai cherché haut et bas sur le Web pour cela, et le plus réponse commune semble être « juste le faire dans le code derrière ». FAUX! je ne sont pas venus dans le monde merveilleux de XAML pour y retourner à faire les choses dans le derrière de code.
Voici mon exemple qui vous permettra de se lier à une chaîne existe en tant que propriété de la fenêtre.
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>
La partie importante est la balise sur le bouton (bien que vous pourriez aussi facilement régler le DataContext du bouton). Cela stocke une référence à la fenêtre parent. Le ContextMenu est capable d'accéder à cette à travers elle la propriété de PlacementTarget. Vous pouvez ensuite passer ce contexte vers le bas par vos éléments de menu.
Je reconnais ce n'est pas la solution la plus élégante du monde. Cependant, il bat des trucs de réglage dans le derrière de code. Si quelqu'un a un même meilleure façon de le faire, je serais ravi de l'entendre.
Autres conseils
Par ci-dessous:
PlacementTarget est le contrôle qui est propriétaire du ContextMenu (ex: DataGrid). Pas besoin d'une propriété « tag ».
isEnabled se fixe à la valeur "myProperty" du DataGrid.
Je l'ai testé cela et il fonctionne. Ayant été question similaire à la liaison.
<ContextMenu
DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}"
IsEnabled="{Binding myProperty}"
>
Parce que ContextMenu
n'est pas dans l'arbre visuel, la liaison ne fonctionnera pas.
une solution simple, utilise modèle Proxy, vous pouvez créer une classe wrapper qui hérite de DependencyObject
et a une DependencyProperty
qui gardera un DataContext
de votre Window
, alors vous pouvez avoir une ressource de la procuration en XAML et enfin lier votre commande MenuItem
à votre souhaitée commande via l'objet proxy.
Sample Proxy:
Public class ProxyClass : DependencyObject
{
Public object Data {get; set;}
public static readonly DependencyProperty DataProperty = DependencyProperty.Register("DataProperty", typeof(object), typeof(ProxyClass), new FrameworkPropertyMetadata(null));
}
Comment utiliser en 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>
Qu'est-ce qui se passe?
propriété Data
de ProxyClass
se lie à DataContext
de Window
, il a tous vos comamnds et les propriétés de votre ViewModel
dans la ressource ProxyClass
.
Un autre avantage de cette approche est la portabilité et la réutilisation dans plusieurs vues et des projets.
Je pense que cela devrait être fait comme ceci:
{Binding Path=Title, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"