web-dev-qa-db-fra.com

Liaison à soi/'this' en XAML

Simple question WPF/XAML. En XAML, comment référencer le Self/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 souhaite lier une propriété du contrôle à la propriété codée à la main de la fenêtre. 

En code, c'est très simple - dans le constructeur de Window, 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 telles que l'exemple suivant n'ont 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

46
Tom Davies

Commencez par utiliser une virgule entre RelativeSource et Path dans votre liaison:

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

Deuxièmement, le RelativeSource se lie au bouton. Button n'a pas de propriété appelée ButtonWidth. Je suppose que vous devez vous lier à votre contrôle parental.

Alors essayez cette liaison RelativeSource:

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

Je pense que ce que vous recherchez est ceci:

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

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

>
29
Clint StLaurent

Une façon de contourner RelativeSource est de nommer l’élément racine XAML:

<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 le faire:

<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 à retenir toutes les complexités de la liaison RelativeSource.

28
Damian

Le problème avec la désignation de l'élément racine XAML est que, si vous prenez l'habitude d'utiliser le même nom (c'est-à-dire "_this", "Racine", etc.) pour toutes les racines de votre projet, une liaison tardive dans imbriquée les modèles peuvent accéder au mauvais élément. En effet, lorsque {Binding}ElementName=... est utilisé dans une Template, les noms sont résolus à l'exécution en remontant dans 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 le définit dans sa propre variable DataContext, ce qui peut ne pas être une option si DataContext est nécessaire, par exemple, pour des données. Il semble également un peu lourd d’introduire une autre liaison sur un élément uniquement dans le but de lui donner accès. Plus tard, si l'accès n'est plus nécessaire, ce {Binding} deviendra un fouillis: la responsabilité de l'accès appartient correctement à la cible et à la liaison.

En conséquence, voici une simple extension de balisage permettant d'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é, aucun clr-namespace n'est utilisé, mais notez que le code XAML présenté ici fonctionne réellement pour accéder à l'espace de noms global (bien que le concepteur de VS2013 se plaint).

3
Glenn Slayden

Malheureusement, nommer l'élément racine avec "NomElément = .." semble être le seul moyen d'utiliser UWP, car {RelativeSource Self} n'est pas pris en charge ici.

Curieusement, cela fonctionne toujours lorsque le nom est remplacé dans la présentation, par exemple.

<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 corrigeait une erreur (présente dans Windows 8.1), lorsque le même nom interne était utilisé pour différents éléments dans la même présentation.

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

0
cyanide