Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

CeDoMain

Alter Hase

  • »CeDoMain« ist der Autor dieses Themas

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

1

22.12.2014, 21:40

[C# WPF] ItemsControl Templates Datenbindung in zwei Richtungen

Hallo Leute,

in letzter Zeit experimentiere ich ein wenig mit den Datenbindungen von WPF herum. Dabei bin ich auf die ganz interessante Möglichkeit gestoßen, ObservableCollections mit ItemTemplates an ein Control zu binden. So kann ich ein Array mit Klassen mit einer GUI darstellen. Das ganze funktioniert auch super in die eine Richtung, nur in die andre nicht... Ich kann eben nur anzeigen, was in der Collection steht, aber Änderungen in der GUI werden nicht wieder in den passenden Arrayeintrag geschrieben.

Ich erkläre mal mein bisheriges Vorgehen an EINER Eigenschaft.

Die Eigenschaft des Anzeigepanels ist als DependencyProperty definiert:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static readonly DependencyProperty KanalNummerProperty =
            DependencyProperty.Register("KanalNummer", typeof(int), typeof(KanalPanel));
public int KanalNummer
{
    get
    {
        return (int)GetValue(KanalNummerProperty);
    }
    set
    {
        SetValue(KanalNummerProperty, value);
        RaisePropertyChanged("KanalNummer");
    }
}

Die GUI ist in beide Richtungen an diese Eigenschaft gebunden:

C#-Quelltext

1
Content="{Binding KanalNummer, ElementName=userControl, Mode=TwoWay}

Die ObservableCollection besteht aus Kanal-Klassen. Diese implementiert das IPorpertyChanged-Interface. Die Eigenschaften implementieren dies auch:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public event PropertyChangedEventHandler PropertyChanged;
private int _Nummer = 0;
public int Nummer
{
    get
    {
        return _Nummer;
    }
    set
    {
        if (value != _Nummer)
        {
            _Nummer = value;
            RaisePropertyChanged("Nummer");
        }
    }
}
private void RaisePropertyChanged(String PropertyName)
{
    var Handler = PropertyChanged;
    if (Handler != null)
    {
        Handler(this, new PropertyChangedEventArgs(PropertyName));
    }
}

Schließlich wird die ObservableCollection, die Teil der ListBox ist, an diese gebunden:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// C# Code
public ObservableCollection<Kanal> Kanäle { get; set; }
// XAML Code
<ListBox ItemsSource="{Binding Kanäle}"
        ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectionMode="Extended">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <local:KanalPanel
                KanalNummer="{Binding Path=Nummer, Mode=TwoWay}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ListBox>


Ich hoffe ich konnte meinen Code auf das wesentliche runterbrechen. Das Internet habe ich auch schon durchforstet, aber nichts gefunden. :( Es kann aber auch an den mangelnden Ausdrücken liegen, weil ich noch neu bin in dem Gebiet. Ich freue mich, wenn mir jemand helfen kann! :)
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

Sylence

Community-Fossil

Beiträge: 1 663

Beruf: Softwareentwickler

  • Private Nachricht senden

2

23.12.2014, 05:04

Die GUI ist in beide Richtungen an diese Eigenschaft gebunden:

C#-Quelltext

1
Content="{Binding KanalNummer, ElementName=userControl, Mode=TwoWay}



Interessant wäre hier um was für eine Control es sich da handelt. Das sieht mir nämlich nach keiner aus, die dazu in der Lage wäre einen Integer zu bearbeiten.
Versuch es mal mit einer TextBox.

CeDoMain

Alter Hase

  • »CeDoMain« ist der Autor dieses Themas

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

3

23.12.2014, 12:06

Du hast die breite Auswahl: Das ganze gibt es auch noch für zwei TextBoxen, eine ComboBox und einen ToggleButton. Das alles ist auch genau wie oben beschrieben aufgebaut - nur eben andere Variablennamen und Typen. Für die Textboxen Strings, für die ComboBox einen Enum und für den ToggleButton einen Bool.
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

TrommlBomml

Community-Fossil

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

4

23.12.2014, 17:29

Erstmal ist in deinem DependencyPropertyProperty-Setter nicht richtig, dass du dort RaiseProperryChanged aufrufst. Das wird durch den Setter im ViewModell ausgelöst, wenn sich das DependencyProperty ändert und ein Objekt darauf bindet. Prüfe mal folgende Dinge:

1) Binding-Errors in der Visualstudio Ausgabe?
2) Eigenes UserControl durch TextBox mit demselben Binding + UpdateSourceTrigger=PropertyChanged verwenden.
3) Sind überall auch Objekte gebunden? (ObservableCollection != null und Items darin sind auch nicht null).

CeDoMain

Alter Hase

  • »CeDoMain« ist der Autor dieses Themas

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

5

23.12.2014, 21:22

Erstmal ist in deinem DependencyPropertyProperty-Setter nicht richtig,
Also entferne ich die RaisePropertyChanged Aufrufe alle!? Die Schnittstellenangabe von INotifyPropertyChanged im Klassenkopf und den PropertyChanged-Event auch?

1) Binding-Errors in der Visualstudio Ausgabe?
Nein keinen einzigen, wenn ich die Textboxen verändere oder irgendwas anderes ändere.

Sind überall auch Objekte gebunden? (ObservableCollection != null und Items darin sind auch nicht null).
Ja habe ich. Der Collection werden 6 Kanalklassen hinzugefügt, die auch alle andere Werte haben. Die werden auch korrekt angezeigt. Wenn ich vom Programm aus die Reihenfolge jede Sekunde ändere oder was hinzufüge, dann kann ich das in meiner GUI auch nachverfolgen. Diese Richtung der Bindung klappt also. Nur die andere nicht. Wenn ich einen Haltepunkt im Setter der Kanalklasse setze, bekomme ich auch nur beim Start (wenn Kanäle hinzugefügt werden) Haltepunkte aktiviert.

Ich habe eben mal Haltepunkte in die Setter der Eigenschaften des GUI-Element (KanalPanel) gemacht. (Da wo auch die Dependencyproperties stehen) Die werden ÜBERHAUPT NICHT (auch nicht beim erzeugen der GUI-Elemente) angesprungen... ist das richtig so? Werden die DependecyProperties direkt ohne den Weg über die Setter verändert?

2) Eigenes UserControl durch TextBox mit demselben Binding + UpdateSourceTrigger=PropertyChanged verwenden.
Kannst du das mit dem UpdateSourceTrigger anders erklären? Ich weiß nicht was du meinst. Mein KanalPanel ist ein UserControl.
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

TrommlBomml

Community-Fossil

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

6

23.12.2014, 22:50

Genau, dein UserControl, welches das DependencyProperty hat, implementiert nicht INotifyPropertyChanged. Wenn sich die Eigenschaft des UserControls ändert wird der Wert des DependencyProperty geändert. WPF kümmert sich über die Binding Engine darum, dass dann ein gebundenes Objekt dann Notifiziert und der Setter aufgerufen wird, was ja erstmal anscheinend nicht passiert.

Passe deinen Xaml-Code wie folgt an:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
<ListBox ItemsSource="{Binding Kanäle}"
        ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectionMode="Extended">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBox Text="{Binding Path=Nummer, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ListBox>


Ich habe hier dein Control durch eine TextBox ausgetauscht und das UpdateSourceTrigger eingebaut. Dadurch wird das Control sofort vom gebunden ViewModel aktualisiert und wartet nich auf FokusLost.

Das sollte, wenn sonst alles korrekt ist, funktionieren und beim Ändern in der Textbox solltest du beim Debugger im Setter von Nummer im ViewModel-Objekt landen. Sollte dies der Fall sein, dann funktioniert dein UserControl nicht richtig, das kriegt man dann schon raus ;)

Grundlegend gilt:
1) Controls: DependencyProperties
2) Gebundene Objekte: INotifyPropertyChanged mit C#-Properties.


Die Notifikation sieht wie folgt aus:

Control zu ViewModel:
DependencyProperty ändert sich -> Wpf Prüft, ob es ein Binding gibt -> Setzt ViewModelProperty, INotifyPropertyChanged.PropertyChanged wird aufgerufen.
ViewModel zu Control:
ViewModel-Setter wird aufgerufen + INotifyPropertyChanged.PropertyChanged -> Binding Engine findet gebundenes DependencyProperty eines Controls -> setzt Wert im DependencyProperty und ggf. auch Callbacks, die man bei DependencyProperty.Register optional angeben kann ( die brauchst du aber nicht!).

Sonst sehe ich auch keinen Fehler.

CeDoMain

Alter Hase

  • »CeDoMain« ist der Autor dieses Themas

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

7

24.12.2014, 12:15

GENIAL! Es funktioniert! :) Ich habe die INotifyPropertyChanged-Sachen weggemacht und im XAML-Code überall "Path=" vor die Variablen geschrieben... Vielen Dank :)
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

TrommlBomml

Community-Fossil

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

8

24.12.2014, 12:25

Das Path ist optional. Folgendes ist also äquivalent:

Quellcode

1
2
<TextBox Text="{Binding Path=Nummer, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Text="{Binding Nummer, UpdateSourceTrigger=PropertyChanged}" />

CeDoMain

Alter Hase

  • »CeDoMain« ist der Autor dieses Themas

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

9

24.12.2014, 13:03

Schön zu wissen ;) Ich habe noch eine andere Frage, die eben aufkam: Die Kanal-Elemente (GUI) müssen wissen, das wievielte es in der ObservableCollection ist. Geht sowas auch mit Datenbindung oder ist das einfacher, wenn ich die mit einer Schleife bei jeder Änderung manuell setze?
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

TrommlBomml

Community-Fossil

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

10

24.12.2014, 13:54

Was möchtest du denn machen? Normalerweise sollte dich der Index nicht interessieren, weil ja jedes Kanal-Element an ein GUI-Element gebunden ist.

Werbeanzeige