Laden...

Spielpaarungen in ListView nebeneinander

Letzter Beitrag vor einem Monat 12 Posts 321 Views
Spielpaarungen in ListView nebeneinander

Hallo Leute,

es geht rein um eine spezielle Darstellung einer ListView in XAML. Wahrscheinlich hat jemand von Euch eine Idee. Im Forum habe ich unter dem Thema "Spielpaarungen" und "Paarungen" etwas gefunden, aber dort geht es nur um die Erstellung von Spielpaarungen, nicht um meine Fragestellung zur "Formatierung".

Ich habe eine Klasse Mannschaft erstellt. Öffentliche Eigenschaften: LandFlagge und LandName. Diese befüllt mit 4 Mannschaften. Im code listView.ItemsSource auf die Liste gesetzt.

Nun will ich die Spielpaarungen in einer ListView "listView" darstellen. Pro Zeile (also zur Zeit 2 Zeilen) im Format

LandFlagge - LandName (Team 1) - LandName - LandFlagge (Team2)
LandFlagge - LandName (Team 3) - LandName - LandFlagge (Team4)

Wie soll ich das in XAML programmieren? Ich kriege es einfach nicht hin. Ich schaffe es lediglich alle Einträge der Liste untereinander darzustellen oder aber nebeneinander, dann aber doppelt, also

Team1 - Team1
Team2 - Team2
(siehe unten)

Ich danke für eure Hilfe!

Klasse Mannschaft:

public partial class MainWindow : Window
{

public class Mannschaft
{
private string landFlagge, landName;

public string LandFlagge { get ⇒ landFlagge; set ⇒ landFlagge = value; }
public string LandName { get ⇒ landName; set ⇒ landName = value; }

}

Code:

.....

List<Mannschaft> teams = new List<Mannschaft>();

private void Start()
{
teams.Add(new Mannschaft()
{
LandName = "Deutschland",
LandFlagge = "Flaggen/FlagDeutschland.png"
});
teams.Add(new Mannschaft()
{
LandName = "Russland",
LandFlagge = "Flaggen/FlagRussland.png"
}); 
teams.Add(new Mannschaft()
{
LandName = "Polen",
LandFlagge = "Flaggen/FlagPolen.png"
});
teams.Add(new Mannschaft()
{
LandName = "Portugal",
LandFlagge = "Flaggen/FlagPortugal.png"
});

listView.ItemsSource = teams;
}

XAML:

<Grid>
<StackPanel>
<TextBlock Text="Spielpaarungen"/>
<ListView x:Name="listView" Height="auto" Width="auto">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding LandFlagge}" Height="16" Width="16"/>
<TextBlock Text="{Binding LandName}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding LandName}"/>
<Image Source="{Binding LandFlagge}" Height="16" Width="16"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</Grid>

Du brauchst dafür eine weitere Klasse Spielpaarung, in der du dann zwei Eigenschaften für die Mannschaften (z.B. Heimmannschaft und Auswaertsmannschaft- oder einfach, wie unten im Code benutzt, Team1 und Team2;-) erstellst.

Und dann davon eine Liste erstellen und an das ListView binden.

Das Binding sieht dann so aus:

<Image Source="{Binding Team1.LandFlagge}" Height="16" Width="16"/>
<TextBlock Text="{Binding Team2.LandName}"/>

<TextBlock Text="{Binding Team2.LandName}"/>
<Image Source="{Binding Team2.LandFlagge}" Height="16" Width="16"/>

Außerdem solltest du für WPF das MVVM-Pattern benutzen, s. [Artikel] MVVM und DataBinding.


PS: Du solltest deinen Code hier im Forum passend mit den Code-Blöcken formatieren.

Ich denke mal, dass Du eine Klasse Spielpaarung mit zwei Eigenschaften vom Typ Mannschaft erstellen musst.

Im CellTemplate / Stackpanel dann

<StackPanel Orientation="Horizontal">
	<TextBlock Text="{Binding Mannschaft1.LandName}"/>
	<Image Source="{Binding Mannschaft1.LandFlagge}" Height="16" Width="16"/>
	<TextBlock Text="{Binding Mannschaft2.LandName}"/>
	<Image Source="{Binding Mannschaft2.LandFlagge}" Height="16" Width="16"/>
</StackPanel>

@Th69 und @Caveman

Ich danke Euch beiden vielmals! Ihr habt beide eine identische Lösung vorgeschlagen. Ich habe es getestet, leider wird jetzt gar nichts angezeigt in der ListView.

Den XAML code habe ich gemäß eurem Vorschlag geändert, eine neue zweite Klasse erstellt und den DataContext für die beiden StackPanel auf die neuen Objekte von den beiden Klassen gesetzt.

Im XAML gibt es den Hinweis, "Für die Bindung ...... wurde kein DataContext gefunden.

Habe es auch mit nur einem StackPanel in einem GridViewColumn versucht, brachte aber nichts.

Würdet Ihr freundlicherweise noch einmal prüfen, was falsch ist?

Ich habe diesmal wegen des Formatierungsproblems hier im Editor code und XAML als Datei angehängt.

@Th69: Bezüglich der Formatierung mit den Code-Blöcken: in meinem XAML und Code ist alles korrekt formatiert, aber beim Einfügen in diesen Editor ist die Formatierung nach dem Klicken des Button "Beitrag erstellen" flöten gegangen. MVVM-Pattern: Das werde ich in Zukunft nutzen. Es wird hier im Forum immer wieder erwähnt.

Du hast dort immer noch keine Klasse Spielpaarung erstellt.

Und dann, wie schon geschrieben, davon eine Liste erstellen:

List<Spielpaarung> pairs = new()
{
  new Spielpaarung(teams[0], teams[1]),
  new Spielpaarung(teams[2], teams[3])
};

listView.ItemsSource = pairs;

Mit einem passenden Konstruktor (mit zwei Mannschaft-Objekten), sowie den zwei zugehörigen Eigenschaften.

Alternativ kann man auch record benutzen, z.B.

public record Spielpaarung(Mannschaft Team1, Mannschaft Team2);

Dabei werden automatisch die zugehörigen Eigenschaften Team1und Team2 vom Compiler erzeugt. Und im XAML-Code dann diese Eigenschaften benutzen (Groß-/Kleinschreibung beachten).

PS: Für die Code-Formatierung gibt es extra einen Button im Forumseditor (rechts neben der Schriftfarbe)!

Vielen Dank Th69! Habe alles gemacht, wie Du gesagt hast (hoffe ich). Bekomme aber eine Fehlermeldung bezüglich

new Spielpaarung(teams[0], teams[1]),
new Spielpaarung(teams[2], teams[3])

"Ein Feldinitialisierer kann nicht auf das nicht statische Feld bzw. die nicht statische Methode oder Eigenschaft "MainWindow.teams" verweisen". Habe die Liste auf private static gesetzt, bekomme dann aber "Index out of range", obwohl definitiv 4 Elemente in der Liste teams sind.

Würdest Du bitte noch einmal den code und XAML prüfen?

Vielen Dank!

Den Button zur Formatierung habe ich jetzt gesehen!

Okay, da sind mehrere Fehler enthalten. Beispielsweise Klassen in Klassen, usw.

Außerdem verwendest Du nicht das MVVM-Pattern.

Hier mal mein Ansatz:

Im ersten Schritt erstellen wir ein UserControl SpielpaarungControl.xaml, das das Aussehen einer Spielpaarung enthält

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="25" />
        <ColumnDefinition Width="100" />
        <ColumnDefinition Width="30" />
        <ColumnDefinition Width="100" />
        <ColumnDefinition Width="25" />
    </Grid.ColumnDefinitions>
    
    <Image x:Name="Team1Flagge"
           Grid.Column="0"
           Height="16"
           Width="16"
           HorizontalAlignment="Center"
           VerticalAlignment="Center"
           Source="{Binding Team1.LandFlagge}"/>

    <TextBlock x:Name="Team1Name"
               Grid.Column="1"
               HorizontalAlignment="Left"
               Text="{Binding Team1.LandName}"/>

    <TextBlock x:Name="MatchSeparator"
               Grid.Column="2"
               HorizontalAlignment="Center"
               Text="vs."/>

    <TextBlock x:Name="Team2Name"
               Grid.Column="3"
               HorizontalAlignment="Right"
               Text="{Binding Team2.LandName}"/>

    <Image x:Name="Team2Flagge"
           Grid.Column="4"
           Height="16"
           Width="16"
           HorizontalAlignment="Center"
           VerticalAlignment="Center"
           Source="{Binding Team2.LandFlagge}"/>
</Grid>

In MainWindow.xaml wird dieses UserControl in die ListView als Vorlage definiert. Zusätzlich definieren wir den DataContext, also eine Klasse, wo MainWindow die benötigten Daten findet.

    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    
    <Grid>
        <StackPanel>
            <TextBlock Text="Spielpaarungen"/>
            <ListView x:Name="listView" 
                      Height="auto" 
                      Width="auto"
                      ItemsSource="{Binding Spielpaarungen}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <local:SpielpaarungControl />
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </StackPanel>
    </Grid>

Es werden noch drei Klassen benötigt - jede Klasse in eine separate Datei. Das sind MainWindowViewModel.cs, Mannschaft.cs und Spielpaarung.cs.

Die Klasse Mannschaft:

    public class Mannschaft
    {
        #region Eigenschaften
        public string LandFlagge { get; set; }

        public string LandName { get; set; } 
        #endregion
    }

Die Klasse Spielpaarung:

    public class Spielpaarung
    {
        #region Eigenschaften
        public Mannschaft Team1 { get; set; }
        public Mannschaft Team2 { get; set; }
        #endregion

        #region Konstruktoren
        public Spielpaarung(Mannschaft team1, Mannschaft team2)
        {
            Team1 = team1;
            Team2 = team2;
        } 
        #endregion
    }

Die Klasse MainWindowViewModel, das die Logik enthält:

    public class MainWindowViewModel
    {
        #region Felder
        private List<Mannschaft> teams;
        #endregion

        #region Eigenschaften
        public List<Spielpaarung> Spielpaarungen { get; set; }
        #endregion

        #region Konstruktoren
        public MainWindowViewModel()
        {
            Start();
        }
        #endregion

        #region Methoden
        private void Start()
        {
            teams =
            [
                new Mannschaft()
            {
                LandName = "Deutschland",
                LandFlagge = "Flaggen/FlagDeutschland.png"
            },
            new Mannschaft()
            {
                LandName = "Russland",
                LandFlagge = "Flaggen/FlagRussland.png"
            },
            new Mannschaft()
            {
                LandName = "Polen",
                LandFlagge = "Flaggen/FlagPolen.png"
            },
            new Mannschaft()
            {
                LandName = "Portugal",
                LandFlagge = "Flaggen/FlagPortugal.png"
            },
        ];

            Spielpaarungen =
                [
                    new Spielpaarung(teams[0], teams[1]),
                new Spielpaarung(teams[2], teams[3])
               ];
        }
        #endregion
    }

Ergänzend sei noch angemerkt, dass keine Benachrichtigungen erfolgen, wenn sich Eigenschaften ändern. Dafür kannst Du das CommunityToolkit MVVM verwenden oder INotifyPropertyChanged, oder oder oder.

Guten Morgen Caveman,

vielen Dank für Deine Hilfe. Es läuft jetzt, natürlich!

MVVM: sollte oder muss ich das MVVM-Toolkit in VS installieren oder kann ich bei der Erstellung neuer Projekte einfach nach der Logik vorgehen, wie Du es gemacht hast (Klasse MainWindowViewModell erstellen, alles raus aus der MainWindow code Datei, gesonderte Klasse für ein UserControl erstellen...).

Ich werde zunächst mal mit dem Artikel MVVM und DataBinding beschäftigen, oder was sollte ich als erstes tun?

Noch zu deinem letzten Code: du hättest, genauso wie du es bei den teams gemacht hast, die Spielpaarungen auch in der Start-Methode initialisieren müssen (genau so wie es auch Caveman in seinem Code gemacht hat).

Das MVVM-Toolkit (s. Einführung in das MVVM-Toolkit) nimmt dir einiges an Code ab, welchen du ansonsten selber für deine MVVM-Projekte erstellen (bzw. aus anderen Projekten kopieren) müßtest, z.B. die ObservableObject-Klasse als Implementierung der INotifyPropertyChanged-Schnittstelle (bei anderen MVVM-Projekten heißt diese Klasse häufig ViewModelBase bzw. BaseViewModel) sowie RelayCommand für die ICommand-Schnittstelle.

Zusätzlich zu dem MVVM-Artikel solltest du dir ein paar MVVM-Tutorials (am besten auf Grundlage des WPF MVVM-Toolkits) anschauen, um die Zusammenhänge besser zu verstehen (am Anfang kann dies alles verwirrend sein).


Noch bzgl.

Den Button zur Formatierung habe ich jetzt gesehen!

Aber nicht für deinen Code angewendet...

Hallo Th69,

okay, ich sehe das mit der Initialisierung der Spielpaarungen in der Startmethode.

Ich werde mir dann mal MVVM näher ansehen. Alle alten Hasen hier auf der Platform arbeiten offenbar damit.
Jo, den Button habe ich ab jetzt aber drauf!

Eine komplett andere Frage: wie finde ich schnell und auf möglichst direktem Wege eigentlich meine eigenen Posts wieder? In meine Account sind diese nicht zu finden.

Ich danke Dir auch sehr für Deine Unterstützung! 
Kann ich eigentlich etwas für die Platform spenden?

Zitat von Claus

Ich werde mir dann mal MVVM näher ansehen. Alle alten Hasen hier auf der Platform arbeiten offenbar damit.

Das hat damit zutun, dass WPF so konzipiert ist, dass Du MVVM nutzt.


Jo, den Button habe ich ab jetzt aber drauf!

Eine komplett andere Frage: wie finde ich schnell und auf möglichst direktem Wege eigentlich meine eigenen Posts wieder? In meine Account sind diese nicht zu finden.

Über Dein Profil.

Kann ich eigentlich etwas für die Platform spenden?

Danke, aber diese Plattform finanziert sich vor allem durch Freizeit und der finanziellen Unterstützung von Partnern, aktuell von Medialesson.

Hallo Abt,

alles klar.

Oh Mann, bin ich blind!