90 votes

Objets de liaison définis dans le code-behind

J'ai un objet qui est instancié dans le code derrière, par exemple, le XAML est appelé window.xaml et dans le window.xaml.cs

protected Dictionary<string, myClass> myDictionary;

Comment puis-je lier cet objet à une vue de liste, par exemple, en utilisant uniquement des balises XAML ?

Mise à jour :

(C'est exactement ce que j'ai dans mon code de test) :

<Window x:Class="QuizBee.Host.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="{Binding windowname}" Height="300" Width="300"
    DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
    </Grid>
</Window>

Et dans codebehind

public partial class Window1 : Window
{
    public const string windowname = "ABCDEFG";

    public Window1()
    {
        InitializeComponent();
    }
}

Supposons que le titre devrait devenir "ABCDEFG", n'est-ce pas ? mais il ne montre rien.

1 votes

Bizarrement, si je change l'ordre d'affectation des propriétés de la fenêtre, cela ne fonctionne pas. Si je fixe la propriété "Title" suivie de la propriété "DataContext", la liaison ne se produit pas. Quelqu'un peut-il expliquer cela ? <Window x:Class="INotifyPropertyTest.MainWindow" xmlns=" schémas.microsoft.com/winfx/2006/xaml/présentation " xmlns:x=" schémas.microsoft.com/winfx/2006/xaml " xmlns:local = "clr-namespace:INotifyPropertyTest" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource self}}" Title="{Binding WindowName}" >

126voto

Saad Imran. Points 2918

Il y a un moyen beaucoup plus facile de le faire. Vous pouvez attribuer un nom à votre fenêtre ou à votre UserControl, puis lier par ElementName.

Window1.xaml

<Window x:Class="QuizBee.Host.Window1"
        x:Name="Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ListView ItemsSource="{Binding ElementName=Window1, Path=myDictionary}" />
</Window>

Window1.xaml.cs

public partial class Window1:Window
{
    // the property must be public, and it must have a getter & setter
    public Dictionary<string, myClass> myDictionary { get; set; }

    public Window1()
    {
        // define the dictionary items in the constructor
        // do the defining BEFORE the InitializeComponent();

        myDictionary = new Dictionary<string, myClass>()
        {
            {"item 1", new myClass(1)},
            {"item 2", new myClass(2)},
            {"item 3", new myClass(3)},
            {"item 4", new myClass(4)},
            {"item 5", new myClass(5)},
        }; 

        InitializeComponent();
    }
}

3 votes

J'ai dû modifier x:Name (erreur de compilation CS0542). Ensuite, ElementName doit être modifié en conséquence.

113voto

Guy Starbuck Points 14241

Vous pouvez définir le DataContext de votre contrôle, formulaire, etc. comme suit :

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

Clarification :

La définition du contexte de données à la valeur ci-dessus doit être effectuée au niveau de l'élément qui "possède" le code derrière - ainsi, pour une fenêtre, vous devez le définir dans la déclaration de la fenêtre.

J'ai fait fonctionner votre exemple avec ce code :

<Window x:Class="MyClass"
  Title="{Binding windowname}"
  DataContext="{Binding RelativeSource={RelativeSource Self}}"
  Height="470" Width="626">

Le DataContext défini à ce niveau est ensuite hérité par tous les éléments de la fenêtre (à moins que vous ne le modifiiez explicitement pour un élément enfant). Ainsi, après avoir défini le DataContext de la fenêtre, vous devriez être en mesure d'effectuer une liaison directe avec le CodeBehind. propriétés à partir de n'importe quel contrôle de la fenêtre.

1 votes

Le "Self" signifie ici le contrôle, plutôt que la classe entière de la fenêtre, n'est-ce pas ?

0 votes

Assez étrangement, voici le code que j'ai et qui ne fonctionne pas comme prévu : public partial class Window1 : Window { public const string windowname = "ABCDEFG" ; public Window1() { InitializeComponent() ; } }. } <Window x:Class="QuizBee.Host.Window1" xmlns=" schémas.microsoft.com/winfx/2006/xaml/présentation " xmlns:x=" schémas.microsoft.com/winfx/2006/xaml " Title="{Binding windowname}" Height="300" Width="300" DataContext="{Binding RelativeSource={RelativeSource Self}}"> </Window>

10 votes

Oh, c'est bon maintenant, j'ai changé le windowname pour qu'il soit une propriété au lieu d'une pure variable publique et il peut s'afficher maintenant ! merci !

27voto

Tom Allen Points 1185

Bien que la réponse de Guy soit correcte (et corresponde probablement à 9 cas sur 10), il convient de noter que si vous essayez de faire cela à partir d'un contrôle dont le DataContext est déjà défini plus haut dans la pile, vous le réinitialiserez lorsque vous redéfinirez le DataContext sur lui-même :

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

Cela aura bien sûr pour effet de casser vos fixations existantes.

Si c'est le cas, vous devez définir le paramètre RelativeSource sur le contrôle que vous essayez de lier, plutôt que sur son parent.

c'est-à-dire pour lier les propriétés d'un UserControl :

Binding Path=PropertyName, 
        RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}

Étant donné qu'il est actuellement difficile de comprendre ce qui se passe avec la liaison de données, il est utile de garder cela à l'esprit même si vous trouvez que le réglage de la liaison de données n'est pas toujours facile. RelativeSource={RelativeSource Self} fonctionne actuellement :)

1 votes

Silverlight 4 ne prend pas en charge FindAncestor. Cependant, si vous avez besoin de le faire de cette façon, vous pouvez implémenter FindAncestor comme décrit sur ce site. http://blog.thekieners.com/2010/09/08/relativesource-binding-with-findancestor-mode-in-silverlight/

8voto

Jon Dinham Points 2314

Juste une petite clarification supplémentaire : Une propriété sans 'get' ou 'set' ne pourra pas être liée.

J'affronte le cas comme celui de l'auteur de la question. Et je dois avoir les éléments suivants pour que la liaison fonctionne correctement :

//(1) Declare a property with 'get','set' in code behind
public partial class my_class:Window {
  public String My_Property { get; set; }
  ...

//(2) Initialise the property in constructor of code behind
public partial class my_class:Window {
  ...
  public my_class() {
     My_Property = "my-string-value";
     InitializeComponent();
  }

//(3) Set data context in window xaml and specify a binding
<Window ...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
  <TextBlock Text="{Binding My_Property}"/>
</Window>

11 votes

Comment peut-on avoir une propriété sans "get" et "set" ? Ne serait-ce pas un champ et non une propriété ?

0voto

Phillip Ngan Points 4303

Définir un convertisseur :

public class RowIndexConverter : IValueConverter
{
    public object Convert( object value, Type targetType,
                           object parameter, CultureInfo culture )
    {
        var row = (IDictionary<string, object>) value;
        var key = (string) parameter;
        return row.Keys.Contains( key ) ? row[ key ] : null;
    }

    public object ConvertBack( object value, Type targetType,
                               object parameter, CultureInfo culture )
    {
        throw new NotImplementedException( );
    }
}

Lier à une définition personnalisée d'un dictionnaire. Il y a beaucoup de surcharges que j'ai omises, mais l'indexeur est le plus important, car il émet l'événement de changement de propriété lorsque la valeur est modifiée. Ceci est nécessaire pour la liaison source à cible.

public class BindableRow : INotifyPropertyChanged, IDictionary<string, object>
{
    private Dictionary<string, object> _data = new Dictionary<string, object>( );

    public object Dummy   // Provides a dummy property for the column to bind to
    {
        get
        {
            return this;
        }
        set
        {
            var o = value;
        }
    }

    public object this[ string index ]
    {
        get
        {
            return _data[ index ];
        }
        set
        {
            _data[ index ] = value;
            InvokePropertyChanged( new PropertyChangedEventArgs( "Dummy" ) ); // Trigger update
        }
    }

}

Dans votre fichier .xaml, utilisez ce convertisseur. Il faut d'abord le référencer :

<UserControl.Resources>
    <ViewModelHelpers:RowIndexConverter x:Key="RowIndexConverter"/>
</UserControl.Resources>

Ainsi, par exemple, si votre dictionnaire contient une entrée dont la clé est "Nom", pour se lier à elle, utilisez

<TextBlock  Text="{Binding Dummy, Converter={StaticResource RowIndexConverter}, ConverterParameter=Name}">

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X