2010-10-01 7 views
42

Question WPF/XAML simple. En XAML, comment puis-je référencer le Soi/cet objet dans un contexte donné? Dans une application très basique avec une fenêtre principale, un contrôle et une propriété C# codée de la fenêtre, je veux lier une propriété du contrôle à la propriété codée à la main de la fenêtre.Liaison à soi/'this' dans XAML

Dans le code, ce qui est très facile - dans le constructeur de la fenêtre, j'ai ajouté ceci:

Binding bind = new Binding(); 
bind.Source = this; 
bind.Path = new PropertyPath("ButtonWidth"); 
button1.SetBinding(WidthProperty, bind); 

De toute évidence, j'ai une propriété appelée ButtonWidth, et un contrôle appelé button1. Je n'arrive pas à comprendre comment faire cela en XAML. Diverses tentatives comme l'exemple suivant n'a pas fonctionné:

<Button x:Name="button1" Width="{Binding Source=Self Path=ButtonWidth}"/> 

<Button x:Name="button1" Width="{Binding RelativeSource={RelativeSource Self} Path=ButtonWidth}"/> 

etc

Merci

Répondre

66

d'abord utiliser une virgule entre le RelativeSource et le chemin dans votre Reliure:

<Button x:Name="button1" Width="{Binding RelativeSource={RelativeSource Self}, 
           Path=ButtonWidth}"/> 

En second lieu, la RelativeSource se lie au bouton. Le bouton n'a pas de propriété appelée ButtonWidth. Je devine que vous devez vous lier à votre contrôle parent.

Donc, essayez ce RelativeSource contraignant:

<Button x:Name="button1" Width="{Binding RelativeSource= 
    {RelativeSource FindAncestor, AncestorType={x:Type YourNamespace:YourParentControl}}, 
    Path=ButtonWidth}"/> 
+0

Merci beaucoup pour cet article. Cela m'a beaucoup aidé! J'ai cherché une bonne solution 3 heures maintenant. –

+0

J'ai un DataGrid où si l'utilisateur accède à l'une des commandes MenuItem de ContextMenu en ligne via un KeyBinding d'InputBinding dont CommandParameter = "{Liaison ElementName = MyDataGrid, Path = SelectedItems}", il passera les SelectedItems à l'ICommand lié. Cependant, null est passé s'il est accédé via le ContextMenu. J'ai essayé CommandParameter = "{Binding SelectedItems}", "{Liaison ElementName = MyDataGrid, Chemin = SelectedItems}", "{Liaison RelativeSource = {RelativeSource FindAncestor, AncestorType = {x: Type DataGrid}}, Chemin = SelectedItems}" . J'ai défini le paramètre CommandParameter avant la commande. – Tom

26

Une façon de me avoir autour de traiter RelativeSource et autres est de nommer l'élément XAML racine:

<Window x:Class="TestApp2.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" Height="350" Width="525" 
    x:Name="_this" 
    > 
    <Grid> 
     <Button x:Name="button" Width="{Binding ElementName=_this,Path=ButtonWidth}" /> 
    </Grid> 
</Window> 

Si vous souhaitez définir le DataContext vous pouvez également faire ceci:

<Window x:Class="TestApp2.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" Height="350" Width="525" 
    x:Name="_this" 
    > 
    <Grid DataContext="{Binding ElementName=_this}">   
     <Button x:Name="button" Width="{Binding Path=ButtonWidth}" /> 
    </Grid> 
</Window> 

Je trouve que c'est un bon truc pour ne pas avoir à reme mber toutes les complexités de la liaison RelativeSource.

23

Je pense que ce que vous cherchez est le suivant:

<Window x:Class = "blah blah all the regular stuff" 

DataContext="{Binding RelativeSource={RelativeSource Self}}" 

> 
+0

Vérifié fonctionne toujours sur l'application Windows 10 dans l'élément principal . – Lavamantis

3

Le problème de nommer l'élément racine XAML est que, si vous avez l'habitude d'utiliser le même nom (par exemple, « _this », "Root", etc.) pour toutes les racines de votre projet, puis la liaison tardive dans les modèles imbriqués peut accéder au mauvais élément. En effet, lorsque {Binding}ElementName=... est utilisé dans un Template, les noms sont résolus à l'exécution en remontant l'arborescence NameScope jusqu'à ce que la première correspondance soit trouvée.

La solution de Clint évite de nommer l'élément racine, mais elle place l'élément racine dans son propre DataContext, ce qui peut ne pas être une option si le DataContext est nécessaire pour, disons, des données. Il semble également un peu sévère d'introduire une autre liaison sur un élément uniquement dans le but d'y donner accès. Plus tard, si l'accès n'est plus nécessaire, {Binding} deviendra fouillis: la responsabilité de l'accès appartient à la cible et à la liaison.

En conséquence, voici une simple extension de balisage pour accéder à l'élément racine XAML sans le nommer:

using System.Xaml; 
using System.Windows.Markup; 

public sealed class XamlRootExtension : MarkupExtension 
{ 
    public override Object ProvideValue(IServiceProvider sp) 
    { 
     var rop = sp.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider; 
     return rop == null ? null : rop.RootObject; 
    } 
}; 

XAML:

<Window x:Class="MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:global="clr-namespace:"> 

    <TextBlock Text="{Binding Source={global:XamlRoot},Mode=OneTime}" /> 

</Window> 

Résultat:

enter image description here


n.b.

pour plus de clarté, pas clr-namespace est utilisé, mais notez que le XAML montré ici ne fonctionne réellement pour accéder à l'espace de noms global (bien que le concepteur de VS2013 se plaint)

0

Malheureusement, en nommant l'élément racine avec « ElementName =. "semble être le seul moyen avec UWP car {RelativeSource Self} n'est pas supporté ici.

Curieusement, cela fonctionne toujours lorsque le nom est ignoré dans la mise en page, par ex.

<UserControl x:Class="Path.MyClass" x:Name="internalName"> 
    <Border Background={Binding Path=Background, ElementName=internalName}" ... 

puis

<Page> 
    <local:MyClass x:Name=externalName /> 

</Page> 

BTW, Windows 10 Correction d'une erreur (présent avec Windows 8.1), lorsque même nom interne est utilisé pour différents éléments dans la même disposition.

Néanmoins, je préférerais utiliser {RelativeSource Self}, car il me semble plus logique et plus sûr.