In Code-Behind definierte Bindungsobjekte

88

Ich habe ein Objekt, das im Code instanziiert ist, zum Beispiel heißt die XAML window.xaml und innerhalb der window.xaml.cs

protected Dictionary<string, myClass> myDictionary;

Wie kann ich dieses Objekt beispielsweise mit nur XAML-Markups an eine Listenansicht binden?

Aktualisieren:

(Genau das habe ich in meinem Testcode):

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

Und im Codebehind

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

    public Window1()
    {
        InitializeComponent();
    }
}

Angenommen, der Titel sollte "ABCDEFG" werden, oder? aber es zeigt am Ende nichts.

xandy
quelle
1
Seltsamerweise funktioniert es nicht, wenn ich die Reihenfolge der Eigenschaftszuweisung des Fensters ändere. Wenn ich die Eigenschaft "Title" gefolgt von der Eigenschaft "DataContext" festlege, findet keine Bindung statt. Kann jemand das erklären? <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

Antworten:

108

Sie können den DataContext für Ihr Steuerelement, Formular usw. wie folgt festlegen:

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

Klarstellung :

Der Datenkontext, der auf den obigen Wert gesetzt wird, sollte an jedem Element erfolgen, das den Code dahinter "besitzt". Für ein Fenster sollten Sie ihn daher in der Window-Deklaration festlegen.

Ich habe Ihr Beispiel mit diesem Code arbeiten:

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

Der auf dieser Ebene festgelegte DataContext wird dann von jedem Element im Fenster geerbt (es sei denn, Sie ändern ihn explizit für ein untergeordnetes Element). Nach dem Festlegen des DataContext für das Fenster sollten Sie also in der Lage sein, von jedem Steuerelement aus direkt an CodeBehind- Eigenschaften zu binden auf dem Fenster.

Guy Starbuck
quelle
1
Das "Selbst" bedeutet hier eher die Kontrolle als die gesamte Fensterklasse, oder?
Xandy
Seltsamerweise folgt der Code, den ich habe und der nicht wie erwartet funktioniert: öffentliche Teilklasse Window1: Window {public const string windowname = "ABCDEFG"; public Window1 () {InitializeComponent (); }} <Fenster x: Class = "QuizBee.Host.Window1" xmlns = " schemas.microsoft.com/winfx/2006/xaml/presentation " xmlns: x = " schemas.microsoft.com/winfx/2006/xaml " Titel = "{Binding Windowname}" Height = "300" Width = "300" DataContext = "{Binding RelativeSource = {RelativeSource Self}}"> </
Windows
9
Oh, jetzt ist es in Ordnung, ich habe den Windown-Namen so geändert, dass er Eigenschaft statt reine öffentliche Variable ist, und er kann jetzt angezeigt werden! Vielen Dank!
Xandy
1
Ich kann mir nicht vorstellen, warum dies nicht nur standardmäßig eingestellt ist.
Okonomiyaki3000
121

Es gibt einen viel einfacheren Weg, dies zu tun. Sie können Ihrem Fenster oder UserControl einen Namen zuweisen und dann über ElementName binden.

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.
quelle
3
Ich musste x: Name ändern (Compilerfehler CS0542). Dann muss ElementName entsprechend geändert werden.
Jack Miller
25

Die Antwort von Guy ist zwar richtig (und passt wahrscheinlich zu 9 von 10 Fällen), aber es ist erwähnenswert, dass Sie dies zurücksetzen, wenn Sie versuchen, dies von einem Steuerelement aus zu tun, dessen DataContext bereits weiter oben im Stapel eingerichtet ist, wenn Sie DataContext festlegen zurück zu sich selbst:

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

Dies wird dann natürlich Ihre vorhandenen Bindungen brechen.

In diesem Fall sollten Sie die RelativeSource für das Steuerelement festlegen, das Sie binden möchten, und nicht für das übergeordnete Steuerelement.

dh zum Binden an die Eigenschaften eines UserControls:

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

Angesichts der Tatsache, wie schwierig es derzeit sein kann, zu sehen, was mit der Datenbindung los ist, sollten Sie dies berücksichtigen, auch wenn Sie feststellen, dass diese Einstellung RelativeSource={RelativeSource Self}derzeit funktioniert :)

CatBusStop
quelle
1
Silverlight 4 unterstützt FindAncestor nicht. Wenn Sie dies jedoch tun müssen, können Sie FindAncestor wie auf dieser Site beschrieben implementieren. http://blog.thekieners.com/2010/09/08/relativesource-binding-with-findancestor-mode-in-silverlight/
ShawnFeatherly
7

Nur ein bisschen mehr Klarstellung: Eine Eigenschaft ohne 'get', 'set' kann nicht gebunden werden

Ich stehe dem Fall genauso gegenüber wie dem Fall des Fragestellers. Und ich muss die folgenden Dinge haben, damit die Bindung richtig funktioniert:

//(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
quelle
9
Wie genau können Sie eine Immobilie ohne 'get' und 'set' haben? Wäre das nicht ein Feld und keine Immobilie?
Kjbartel
1

Konverter definieren:

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

Binden Sie an eine benutzerdefinierte Definition eines Wörterbuchs. Es gibt viele Überschreibungen, die ich weggelassen habe, aber der Indexer ist wichtig, da er das Ereignis "Eigenschaft geändert" ausgibt, wenn der Wert geändert wird. Dies ist erforderlich, damit die Bindung zwischen Quelle und Ziel erfolgt.

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


}

Verwenden Sie in Ihrer .xaml-Datei diesen Konverter. Zuerst darauf verweisen:

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

Wenn Ihr Wörterbuch beispielsweise einen Eintrag enthält, in dem der Schlüssel "Name" lautet, können Sie ihn binden: Verwenden Sie ihn

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

Machen Sie Ihre Eigenschaft "Windowname" zu einer DependencyProperty und behalten Sie diese bei.

viky
quelle
0

Stellen Sie in Ihrem Code dahinter den DataContext des Fensters auf das Wörterbuch ein. In Ihrer XAML können Sie schreiben:

<ListView ItemsSource="{Binding}" />

Dadurch wird die ListView an das Wörterbuch gebunden.

Für komplexere Szenarien wäre dies eine Teilmenge der Techniken hinter dem MVVM- Muster.

Szymon Rozga
quelle
0

Eine Möglichkeit wäre, eine ObservableCollection (System.Collections.ObjectModel) zu erstellen und Ihre Wörterbuchdaten dort zu haben. Dann sollten Sie in der Lage sein, die ObservableCollection an Ihre ListBox zu binden.

In Ihrer XAML sollten Sie ungefähr Folgendes haben:

<ListBox ItemsSource="{Binding Path=Name_of_your_ObservableCollection" />
Teilweise
quelle
0

Ich hatte genau das gleiche Problem, aber meines nicht, weil ich eine lokale Variable festgelegt habe ... Ich befand mich in einem untergeordneten Fenster und musste einen relativen Datenkontext festlegen, den ich gerade zur Fenster-XAML hinzugefügt habe.

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

Sie können x: Referenztrick versuchen

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