Objets de liaison définis dans code-behind

88

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 à, par exemple, une vue de liste, en utilisant uniquement des balises XAML?

Mettre à 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 devienne "ABCDEFG", non? mais ça finit par ne rien montrer.

xandy
la source
1
Curieusement, si je change l'ordre d'attribution des propriétés de la fenêtre, cela ne fonctionne pas. Si je mets la propriété "Titre" suivie de la propriété "DataContext", la liaison ne se produit pas. Quelqu'un peut-il expliquer cela? <Window x: Class = "INotifyPropertyTest.MainWindow" xmlns = " schemas.microsoft.com/winfx/2006/xaml/presentation " xmlns: x = " schemas.microsoft.com/winfx/2006/xaml " xmlns: local = " clr-namespace: INotifyPropertyTest "Height =" 350 "Width =" 525 "DataContext =" {Binding RelativeSource = {RelativeSource self}} "Title =" {Binding WindowName} ">
Ramesh

Réponses:

109

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

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

Clarification :

Le contexte de données défini sur la valeur ci-dessus doit être effectué à n'importe quel élément "possède" le code derrière - donc pour une fenêtre, vous devez le définir dans la déclaration de fenêtre.

J'ai votre exemple fonctionnant 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 alors hérité par n'importe quel élément de la fenêtre (sauf si vous le modifiez explicitement pour un élément enfant), donc après avoir défini le DataContext pour la fenêtre, vous devriez pouvoir simplement faire une liaison directe aux propriétés CodeBehind à partir de n'importe quel contrôle sur la fenêtre.

Guy Starbuck
la source
1
Le "Soi" signifie ici le contrôle, plutôt que la classe de fenêtre entière, n'est-ce pas?
xandy
Assez étrange, voici le code que j'ai et il 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 = " schemas.microsoft.com/winfx/2006/xaml/presentation " xmlns: x = " schemas.microsoft.com/winfx/2006/xaml " Titre = "{Binding windowname}" Height = "300" Width = "300" DataContext = "{Binding RelativeSource = {RelativeSource Self}}"> </Window>
xandy
9
Oh, ça va maintenant, j'ai changé le nom de la fenêtre en propriété au lieu de pure variable publique et il peut s'afficher maintenant! Merci!
xandy
1
Je ne peux pas imaginer pourquoi ce n'est pas seulement défini par défaut.
Okonomiyaki3000
122

Il existe un moyen beaucoup plus simple de faire cela. Vous pouvez attribuer un nom à votre Window ou UserControl, puis la liaison 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();
    }
}
Saad Imran.
la source
3
J'ai dû changer x: Name (erreur du compilateur CS0542). Ensuite, ElementName doit être modifié en conséquence.
Jack Miller
25

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

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

Cela cassera bien sûr ensuite vos liaisons existantes.

Si tel est le cas, vous devez définir RelativeSource sur le contrôle que vous essayez de lier, plutôt que sur son parent.

c'est-à-dire pour la liaison aux propriétés d'un UserControl:

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

Étant donné à quel point il peut être actuellement difficile de voir ce qui se passe avec la liaison de données, cela vaut la peine de garder cela à l'esprit même si vous trouvez que ce paramètre RelativeSource={RelativeSource Self}fonctionne actuellement :)

CatBusStop
la source
1
Silverlight 4 ne prend pas en charge FindAncestor. Cependant, si vous devez 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/
ShawnFeatherly
7

Juste un peu plus de précision: une propriété sans 'get', 'set' ne pourra pas être liée

Je fais face au cas comme le cas du demandeur. 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>
Jondinham
la source
9
Comment pouvez-vous exactement avoir une propriété sans "get" et "set"? Ne serait-ce pas un champ et non une propriété?
kjbartel
1

Définissez 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( );
    }
}

Liez à une définition personnalisée d'un dictionnaire. Il y a beaucoup de remplacements que j'ai omis, mais l'indexeur est le plus important, car il émet l'événement de modification de propriété lorsque la valeur est modifiée. Ceci est requis 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. Référencez-le d'abord:

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

Ensuite, par exemple, si votre dictionnaire a une entrée où la clé est "Nom", alors pour vous y lier: utilisez

<TextBlock  Text="{Binding Dummy, Converter={StaticResource RowIndexConverter}, ConverterParameter=Name}">
Phillip Ngan
la source
1

Faites de votre propriété "windowname" une propriété DependencyProperty et conservez la propriété restante.

Viky
la source
0

Dans votre code derrière, définissez le DataContext de la fenêtre sur le dictionnaire. Dans votre XAML, vous pouvez écrire:

<ListView ItemsSource="{Binding}" />

Cela liera le ListView au dictionnaire.

Pour des scénarios plus complexes, il s'agirait d'un sous-ensemble de techniques derrière le modèle MVVM .

Szymon Rozga
la source
0

Une façon serait de créer une ObservableCollection (System.Collections.ObjectModel) et d'y avoir vos données de dictionnaire. Ensuite, vous devriez être en mesure de lier ObservableCollection à votre ListBox.

Dans votre XAML, vous devriez avoir quelque chose comme ceci:

<ListBox ItemsSource="{Binding Path=Name_of_your_ObservableCollection" />
Partiel
la source
0

J'avais exactement le même problème, mais le mien n'était pas parce que je définissais une variable locale ... J'étais dans une fenêtre enfant, et j'avais besoin de définir un DataContext relatif que je venais d'ajouter à la fenêtre XAML.

<Window x:Class="Log4Net_Viewer.LogItemWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    Title="LogItemWindow" Height="397" Width="572">
davesbrain
la source
0

Vous pouvez essayer x: Astuce de référence

<Window ... x:Name="myWindow"><ListBox ItemsSource="{Binding Items, Source={x:Reference myWindow}}" /></Window>
Николай Солдаткин
la source