Binding di oggetti definiti in code-behind


88

Ho un oggetto che viene istanziato nel codice dietro, ad esempio, XAML si chiama window.xaml e all'interno di window.xaml.cs

protected Dictionary<string, myClass> myDictionary;

Come posso associare questo oggetto, ad esempio, a una visualizzazione elenco, utilizzando solo markup XAML?

Aggiornare:

(Questo è esattamente quello che ho nel mio codice di prova):

<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>

E in codice dietro

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

    public Window1()
    {
        InitializeComponent();
    }
}

Supponiamo che il titolo diventi "ABCDEFG", giusto? ma finisce per non mostrare nulla.


1
Stranamente, se cambio l'ordine di assegnazione delle proprietà della finestra, non funziona. Se imposto la proprietà "Title" seguita dalla proprietà "DataContext", l'associazione non avviene. Qualcuno può spiegarlo? <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

Risposte:


109

Puoi impostare DataContext per il tuo controllo, modulo, ecc. In questo modo:

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

Chiarimento :

Il contesto dei dati che viene impostato sul valore sopra dovrebbe essere eseguito su qualsiasi elemento "possieda" il codice sottostante, quindi per una finestra, dovresti impostarlo nella dichiarazione della finestra.

Ho il tuo esempio che funziona con questo codice:

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

Il DataContext impostato a questo livello viene quindi ereditato da qualsiasi elemento nella finestra (a meno che non lo si modifichi esplicitamente per un elemento figlio), quindi dopo aver impostato il DataContext per la finestra dovresti essere in grado di eseguire semplicemente l'associazione diretta alle proprietà CodeBehind da qualsiasi controllo Sulla finestra.


1
Il "Sé" qui significa il controllo, piuttosto che l'intera classe della finestra, vero?
xandy

Abbastanza strano, di seguito è il codice che ho e non funziona come previsto: 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 " Titolo = "{Binding windowname}" Height = "300" Width = "300" DataContext = "{Binding RelativeSource = {RelativeSource Self}}"> </Window>
xandy

9
Oh, va bene ora, ho cambiato il windowname in proprietà invece che pura variabile pubblica e ora può essere visualizzato! Grazie!
xandy

1
Non riesco a immaginare perché questo non sia solo impostato di default.
Okonomiyaki3000

122

C'è un modo molto più semplice per farlo. È possibile assegnare un nome alla finestra o al controllo utente e quindi eseguire il binding tramite 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
Ho dovuto cambiare x: Name (errore del compilatore CS0542). Quindi ElementName deve essere modificato di conseguenza.
Jack Miller

25

Sebbene la risposta di Guy sia corretta (e probabilmente si adatta a 9 casi su 10), vale la pena notare che se stai tentando di farlo da un controllo che ha già il suo DataContext impostato più in alto nello stack, lo reimposterai quando imposti DataContext torna a se stesso:

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

Questo ovviamente interromperà i tuoi attacchi esistenti.

In questo caso, è necessario impostare RelativeSource sul controllo che si sta tentando di associare, anziché sul suo padre.

cioè per l'associazione alle proprietà di un UserControl:

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

Dato quanto può essere difficile attualmente vedere cosa sta succedendo con il data binding, vale la pena tenerlo presente anche se trovi che l'impostazione RelativeSource={RelativeSource Self}attualmente funziona :)


1
Silverlight 4 non supporta FindAncestor. Comunque sia necessario farlo in questo modo è possibile implementare FindAncestor come descritto in questo sito. http://blog.thekieners.com/2010/09/08/relativesource-binding-with-findancestor-mode-in-silverlight/
ShawnFeatherly

7

Ancora un po 'di chiarimento: una proprietà senza' get ',' set 'non potrà essere vincolata

Sto affrontando il caso proprio come il caso del richiedente. E devo avere le seguenti cose affinché il binding funzioni correttamente:

//(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>

9
Come puoi esattamente avere una proprietà senza "get" e "set"? Non sarebbe un campo e non una proprietà?
kjbartel

1

Definisci un convertitore:

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

Associa a una definizione personalizzata di un dizionario. Ci sono molte sostituzioni che ho omesso, ma l'indicizzatore è quello importante, perché emette l'evento di modifica della proprietà quando il valore viene modificato. Ciò è necessario per l'associazione tra origine e destinazione.

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
        }
    }


}

Nel tuo file .xaml usa questo convertitore. Primo riferimento:

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

Quindi, ad esempio, se il tuo dizionario ha una voce in cui la chiave è "Nome", per vincolarla: usa

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

1

Rendi la tua proprietà "windowname" una proprietà di dipendenza e mantieni lo stesso.


0

Nel codice sottostante, imposta il DataContext della finestra sul dizionario. Nel tuo XAML puoi scrivere:

<ListView ItemsSource="{Binding}" />

Questo legherà il ListView al dizionario.

Per scenari più complessi, questo sarebbe un sottoinsieme di tecniche dietro il modello MVVM .


0

Un modo sarebbe creare un ObservableCollection (System.Collections.ObjectModel) e inserire i dati del dizionario. Quindi dovresti essere in grado di associare ObservableCollection al tuo ListBox.

Nel tuo XAML dovresti avere qualcosa del genere:

<ListBox ItemsSource="{Binding Path=Name_of_your_ObservableCollection" />

0

Avevo lo stesso identico problema, ma il mio non era perché stavo impostando una variabile locale ... Ero in una finestra figlio e avevo bisogno di impostare un DataContext relativo che ho appena aggiunto a Window 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">

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.