Liebes Forum,
derzeit bin ich dabei, eine WinForms - Anwendung ein bißchen aufzuhübschen und auf WPF zu übertragen. Im alten Projekt habe ich mir im Hauptbildschirm Spielstände mitgeschrieben in eine dort lokale Liste die aus Einträgen meiner Klasse(das Types böse sind, habe ich hier im Forum gelernt 😄) "MatchHistory" besteht.
Dann das Ganze über die Schnittstelle in mein Ergebnisformular geladen und dort mit einer ListView angezeigt. So weit - so gut.
Der neue Plan ist das ganze mit einem DataGridView anzuzeigen, das aber eine Datenbindung zu dieser Klasse hat.
In diesem Beitrag wurde das Thema angeschnitten und ich habe nach diesem Muster eine MatchHistoryViewModel - Klasse erstellt.
Jetzt fehlt mir wohl nur noch ein kleines Stück:
Ich habe eine lokale Variable vom Typen dieser Klasse erstellt:
private MatchHistoryViewModel _myMatchView = new MatchHistoryViewModel();
public MatchHistoryViewModel myMatchView
{
get { return _myMatchView; }
set
{
_myMatchView = value;
}
}
Beim Starten meines MainWindows füttere ich das mit Daten:
private void Test2()
{
foreach (string s in Enum.GetNames(typeof(MatchNames)))
{
MatchViewModel mv = new MatchViewModel
{
ID=0,
GameName=s,
Sets2Play=3,
SetsPlayed=0,
Score0=0,
Score1=0,
Player1="Vika",
Player2="Roli"
};
_myMatchView.AddEntry(mv);
}
}
Anhand des Beispiels in dem o.a. Artikel habe ich dann versucht, die Bindung herzustellen.
<Window x:Class="ListViewBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ListViewBinding"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MatchHistoryViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<DataGrid x:Name="Match" Grid.Column="0" Grid.ColumnSpan="2" ItemsSource="{Binding _myMatchview}">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Width="50" Binding="{Binding ID}"/>
<DataGridTextColumn Header="Game" Width="150" Binding="{Binding GameName}"/>
<DataGridTextColumn Header="Sets" Width="100" Binding="{Binding Sets2Play}"/>
<DataGridTextColumn Header="Played" Width="100" Binding="{Binding SetsPlayed}"/>
</DataGrid.Columns>
</DataGrid>
Aber da kommt die Fehlermeldung: > Fehlermeldung:
Schweregrad Anzahl Datenkontext Bindungspfad Ziel Zieltyp Beschreibung Datei Zeile Projekt
Fehler 1 MatchHistoryViewModel _myMatchview DataGrid.ItemsSource, Name="Match" IEnumerable Die Eigenschaft "_myMatchview" wurde im Objekt vom Typ "MatchHistoryViewModel" nicht gefunden.
Es ist ja nicht so, daß es ich das dringend brauche. Aber verstehen möchte ich das Ganze endlich. Wahrscheinlich ist der Denkfehler riesig oder winzig.
Und nach der zweiten schlaflosen Nacht deswegen gehe ich jetzt heia.
Off Topic: Für alle ein gesundes neues Jahr.
2 stupid 4 chess? No way.
2 stupid 4 C#? It seems so X(
_myMatchview
kann nicht gefunden werden weil es private
ist.
Du musst an myMatchview
binden.
Das solltest du auch noch nach MyMatchview
umbenennen.
[EDIT]
Und so wie es aussieht suchst du im MatchHistoryViewModel
nach dem _myMatchview
. _myMatchview
ist aber selbst das MatchHistoryViewModel
. Da müsstest du das Binding für das Window
auch noch anpassen.
“Knowledge cannot replace friendship. I'd rather be an idiot than lose you.”
Ein Binding muss bei WPF immer auf eine Property gehen, niemals auf eine Member Variable.
Wenn trotzdem noch nichts angezeigt wird, benötigst du INotifyPropertyChanged in deinem ViewModel, wenn du Veränderungen im Code an der gebundenen Collection machen willst, sollte es eine ObservableCollection sein.
Hier gibt es eine ausführliche Einführung in MVVM mit Code-Beispielen: [Artikel] MVVM und DataBinding
Weeks of programming can save you hours of planning
Wenn trotzdem noch nichts angezeigt wird, benötigst du INotifyPropertyChanged in deinem ViewModel, wenn du Veränderungen im Code an der gebundenen Collection machen willst, sollte es eine ObservableCollection sein.
public class MatchHistoryViewModel : ViewModelBase
{
public ObservableCollection<MatchViewModel> MatchHistory { get; }
public MatchHistoryViewModel()
{
MatchHistory = new ObservableCollection<MatchViewModel>();
}
public void AddEntry(MatchViewModel i)
{
MatchHistory.Add(i);
}
}
So hab ich es ja auch gemacht. Auch ein PropertyChangedEventhandler ist vorhanden, der im MatchviewModel auch brav gefeuert wird.
Da müsstest du das Binding für das
Window
auch noch anpassen.
Sehr gerne - aber wie?
<Window x:Class="ListViewBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ListViewBinding"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MatchHistoryViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<DataGrid x:Name="Match" Grid.Column="0" Grid.ColumnSpan="2" ItemsSource="{Binding MatchHistory}" AutoGenerateColumns="True" Margin="-23,0,22.8,0" >
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Width="50" Binding="{Binding ID}"/>
<DataGridTextColumn Header="Game" Width="150" Binding="{Binding GameName}"/>
<DataGridTextColumn Header="Sets" Width="100" Binding="{Binding Sets2Play}"/>
<DataGridTextColumn Header="Played" Width="100" Binding="{Binding SetsPlayed}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
So, und nun zeigt's was an. Ich habe in mein Modul "Test2" einfach am Ende eingefügt
Match.DataContext = MyMatchView
. Jetzt zeigt mir das DGV mit Ausnahme der ID alles an, und wenn ich Einträge editiere, werden auch brav die Ereignisse gefeuert.
Erstmal herzlichen Dank für die Antworten. Hat wer eine Idee, warum die ID nicht angezeigt wird?
Hier nochmal die MatchHistory - Klasse:
public class MatchViewModel : ViewModelBase
{
private int _ID;
private string _GameName;
private int _Sets2Play;
private int _SetsPlayed;
private int _Score0;
private int _Score1;
private string _Player1;
private string _Player2;
public int ID
{
get { return _ID; }
set
{
_ID = value;
RaisePropertyChanged(nameof(ID));
}
}
public string GameName
{
get { return _GameName; }
set
{
_GameName = value;
RaisePropertyChanged(nameof(GameName));
}
}
public int Sets2Play
{
get { return _Sets2Play; }
set
{
_Sets2Play = value;
RaisePropertyChanged(nameof(_Sets2Play));
}
}
public int SetsPlayed
{
get { return _SetsPlayed; }
set
{
_SetsPlayed = value;
RaisePropertyChanged(nameof(SetsPlayed));
}
}
public int Score0
{
get { return _Score0; }
set
{
_Score0 = value;
RaisePropertyChanged(nameof(Score0));
}
}
public int Score1
{
get { return _Score1; }
set
{
_Score1 = value;
RaisePropertyChanged(nameof(Score1));
}
}
public string Player1
{
get { return _Player1; }
set
{
_Player1 = value;
RaisePropertyChanged(nameof(Player1));
}
}
public string Player2
{
get { return _Player2; }
set
{
_Player2 = value;
RaisePropertyChanged(nameof(Player2));
}
}
}
public class MatchHistoryViewModel : ViewModelBase
{
public ObservableCollection<MatchViewModel> MatchHistory { get; }
public MatchHistoryViewModel()
{
MatchHistory = new ObservableCollection<MatchViewModel>();
}
public void AddEntry(MatchViewModel i)
{
MatchHistory.Add(i);
}
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
// Führt das Event aus und gibt den Property-Namen als Argument mit, damit WPF reagieren kann.
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
[EDIT] Auch das hat sich erledigt. Es hat sich eine negative Margin eingeschlichen (-46). Das ganze auf Null und fertig war's [/EDIT]
2 stupid 4 chess? No way.
2 stupid 4 C#? It seems so X(
Hier gibt es eine ausführliche Einführung in MVVM mit Code-Beispielen:
>
Herzlichen Dank. Diese Einführung habe ich mir im Vorfeld zu Gemüse geführt und - offensichtlich - nicht alles verstanden. Also arbeite ich das noch ein zweites mal durch.
2 stupid 4 chess? No way.
2 stupid 4 C#? It seems so X(
Wenn es einen Bindingfehler gibt, steht der während der Ausführung im Ausgabefenster bzw. auch (in VS 2019) im Fenster für die WPF-Bindingfehler.
Ansonsten kannst du dir die Inhalte im Live Visual Tree anschauen bzw. mit einem DebugConverter. Siehe dazu den Abschnitt "Debugging" im MVVM-Artikel.
Weeks of programming can save you hours of planning
Das wonach ich tagelang gesucht habe, habe ich hier im Forum gefunden. Herzlichen Dank an alle.
Und jetzt stoße ich auf dieses Video. Ein paar Tage zu spät.
Super erklärt anhand eines Beispiels.
2 stupid 4 chess? No way.
2 stupid 4 C#? It seems so X(