Mengikat objek yang ditentukan di belakang kode

88

Saya memiliki beberapa objek yang dibuat dalam kode di belakang, misalnya, XAML disebut window.xaml dan di dalam window.xaml.cs

protected Dictionary<string, myClass> myDictionary;

Bagaimana cara mengikat objek ini ke, misalnya, tampilan daftar, hanya menggunakan markup XAML?

Memperbarui:

(Ini persis yang saya miliki di kode pengujian saya):

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

Dan di codebehind

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

    public Window1()
    {
        InitializeComponent();
    }
}

Misalkan judul harus menjadi "ABCDEFG" kan? tapi akhirnya tidak menunjukkan apa-apa.

xandy
sumber
1
Anehnya, Jika saya mengubah urutan penugasan properti jendela, Itu tidak berhasil. Jika saya menyetel Properti "Judul" diikuti dengan Properti "DataContext", pengikatan tidak terjadi. Adakah yang bisa menjelaskan ini? <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

Jawaban:

109

Anda dapat mengatur DataContext untuk kontrol Anda, formulir, dll. Seperti:

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

Klarifikasi :

Konteks data yang disetel ke nilai di atas harus dilakukan pada elemen apa pun yang "memiliki" kode di belakang - jadi untuk Window, Anda harus menyetelnya di deklarasi Window.

Saya memiliki contoh Anda bekerja dengan kode ini:

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

DataContext yang disetel pada level ini kemudian diwarisi oleh elemen apa pun di jendela (kecuali Anda secara eksplisit mengubahnya untuk elemen turunan), jadi setelah menyetel DataContext untuk Jendela Anda seharusnya dapat melakukan pengikatan langsung ke properti CodeBehind dari kontrol apa pun. di atas jendela.

Guy Starbuck
sumber
1
"Diri" di sini berarti kontrol, bukan kelas jendela secara keseluruhan, bukan?
xandy
Cukup aneh, Berikut ini adalah kode yang saya miliki dan tidak berfungsi seperti yang diharapkan: kelas parsial publik Window1: Jendela {public const string windowname = "ABCDEFG"; publik Window1 () {InitializeComponent (); }} <Window x: Class = "QuizBee.Host.Window1" xmlns = " schemas.microsoft.com/winfx/2006/xaml/presentation " xmlns: x = " schemas.microsoft.com/winfx/2006/xaml " Judul = "{Binding windowname}" Height = "300" Width = "300" DataContext = "{Binding RelativeSource = {RelativeSource Self}}"> </Window>
xandy
10
Oh, tidak apa-apa sekarang, saya mengubah nama angin menjadi properti daripada variabel publik murni dan dapat ditampilkan sekarang! Terima kasih!
xandy
1
Saya tidak dapat membayangkan mengapa ini tidak diatur secara default.
Okonomiyaki3000
122

Ada cara yang lebih mudah untuk melakukan ini. Anda dapat menetapkan Nama ke Window atau UserControl Anda, dan kemudian mengikat dengan 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.
sumber
3
Saya harus mengubah x: Nama (kesalahan kompiler CS0542). Kemudian ElementName perlu diubah sesuai.
Jack Miller
25

Meskipun jawaban Guy benar (dan mungkin cocok dengan 9 dari 10 kasus), perlu diperhatikan bahwa jika Anda mencoba melakukan ini dari kontrol yang sudah mengatur DataContext lebih lanjut, Anda akan menyetel ulang ini saat Anda menyetel DataContext kembali ke dirinya sendiri:

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

Ini tentu saja akan merusak binding yang ada.

Jika ini masalahnya, Anda harus menyetel RelativeSource pada kontrol yang Anda coba ikat, bukan induknya.

yaitu untuk mengikat properti UserControl:

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

Mengingat betapa sulitnya saat ini untuk melihat apa yang terjadi dengan data binding, ada baiknya mengingat hal ini bahkan jika Anda menemukan bahwa pengaturan RelativeSource={RelativeSource Self}saat ini berfungsi :)

CatBusStop
sumber
1
Silverlight 4 tidak mendukung FindAncestor. Namun jika Anda perlu melakukannya dengan cara ini, Anda dapat mengimplementasikan FindAncestor seperti yang dijelaskan di situs ini. http://blog.thekieners.com/2010/09/08/relativesource-binding-with-findancestor-mode-in-silverlight/
ShawnFeatherly
6

Sedikit penjelasan lagi: Properti tanpa 'get', 'set' tidak akan bisa diikat

Saya menghadapi kasus seperti kasus penanya. Dan saya harus memiliki hal-hal berikut agar pengikatan berfungsi dengan benar:

//(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.dll
sumber
9
Bagaimana tepatnya Anda bisa memiliki properti tanpa 'get' dan 'set'? Bukankah itu ladang dan bukan properti?
kjbartel
1

Tentukan konverter:

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

Mengikat ke definisi kustom dari sebuah Dictionary. Ada banyak penggantian yang telah saya hilangkan, tetapi pengindeks adalah yang terpenting, karena pengindeks memancarkan peristiwa properti yang diubah ketika nilainya diubah. Ini diperlukan untuk pengikatan sumber ke target.

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


}

Dalam file .xaml Anda, gunakan konverter ini. Referensi pertama itu:

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

Kemudian, misalnya, jika kamus Anda memiliki entri dengan kuncinya adalah "Nama", maka untuk mengikatnya: gunakan

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

Jadikan properti Anda "windowname" sebagai DependencyProperty dan pertahankan sisanya tetap sama.

viky
sumber
0

Di belakang kode Anda, setel DataContext jendela ke kamus. Di XAML Anda, Anda dapat menulis:

<ListView ItemsSource="{Binding}" />

Ini akan mengikat ListView ke kamus.

Untuk skenario yang lebih kompleks, ini akan menjadi bagian dari teknik di balik pola MVVM .

Szymon Rozga
sumber
0

Salah satu caranya adalah dengan membuat ObservableCollection (System.Collections.ObjectModel) dan memiliki data kamus Anda di sana. Maka Anda harus bisa mengikat ObservableCollection ke ListBox Anda.

Di XAML Anda, Anda harus memiliki sesuatu seperti ini:

<ListBox ItemsSource="{Binding Path=Name_of_your_ObservableCollection" />
Sebagian
sumber
0

Saya mengalami masalah yang sama persis tetapi masalah saya bukan karena saya menyetel variabel lokal ... Saya berada di jendela anak, dan saya perlu menyetel DataContext relatif yang baru saja saya tambahkan ke Jendela 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
sumber
0

Anda dapat mencoba x: Trik referensi

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

Itulah cara saya mengikat kode di belakang (lihat properti DataTemplateSelector)

public partial class MainWindow : Window
{
  public MainWindow()
  {
    this.DataTemplateSelector = new MyDataTemplateSelector();

    InitializeComponent();

    // ... more initializations ...
  }

  public DataTemplateSelector DataTemplateSelector { get; }

  // ... more code stuff ...
}

Di XAML akan direferensikan oleh RelativeSourcemelalui Ancestors hingga berisi Window, jadi saya berada di Windowkelas saya dan menggunakan properti melalui Pathdeklarasi:

<GridViewColumn Header="Value(s)"
                CellTemplateSelector="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataTemplateSelector}"/>

Pengaturan properti DataTemplateSelectorsebelum panggilan InitializeComponentbergantung pada implementasi yang hilang IPropertyChangedatau penggunaan implementasi DependencyPropertysehingga tidak ada komunikasi yang berjalan pada perubahan properti DataTemplateSelector.

VBWebProfi
sumber