Grafikus C# – Windows Forms tanfolyam › 15.3 lecke Továbbtanulás · 15.3 lecke · WPF Windows Formsban a felület és az adat szinkronban tartása a te dolgod: az eseménykezelő beolvassa a Adatkötés és MVVM fogalma
TextBoxot, és beírja az eredményt a Labelbe. Ha az adat sok helyen látszik, minden módosítás után mindenhol frissíteni kell: ez hibaforrás. A WPF adatkötése (data binding) ezt automatizálja: a felületi elem és az adat össze van kötve, és ha az egyik változik, a másik követi. Erre épül az MVVM minta (Model–View–ViewModel), amely a WPF programok szokásos szerkezete: a View (XAML) csak megjelenít, a ViewModel az állapotot és a műveleteket tartja, a Model az adat. Ez rokona annak a rétegzésnek, amelyet a 12. fejezetben megtanultál: itt a felület és a logika szétválasztása a keretrendszer része.
A lecke végére
- használod a
{Binding}kifejezést (Path,Mode,DataContext); - érted, mikor frissül automatikusan a felület:
INotifyPropertyChanged,ObservableCollection; - listát jelenítesz meg
ItemsSourceésDataTemplatesegítségével; - megírod az
ICommandegyszerű megvalósítását (RelayCommand), és gombhoz köted; - elmagyarázod az MVVM szerkezetét, és összeveted a 12. fejezet rétegeivel és a presenterrel (12.9);
- a ViewModelt felület nélkül teszteled.
Mielőtt elkezded
Ehhez a leckéhez a 15.1–15.2 lecke, a 12.6–12.9 lecke (modell, szolgáltatás, rétegek, presenter) és a 6. fejezet gyűjteményei szükségesek.
Az adatkötés alapjai
<!-- 1. Elemek közötti kötés: a címke a csúszka értékét mutatja (nincs C# kód!) -->
<Slider x:Name="sldMeret" Minimum="10" Maximum="40" Value="16"/>
<TextBlock Text="Ez a szöveg a csúszkával nagyítható" FontSize="{Binding Value, ElementName=sldMeret}"/>
<!-- 2. Adatobjektumhoz kötés: a DataContext tulajdonságait érjük el -->
<StackPanel>
<TextBox Text="{Binding Nev, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="{Binding Nev, StringFormat=Szia, {0}!}"/>
<TextBlock Text="{Binding Atlag, StringFormat=Átlag: {0:0.00}}"/>
</StackPanel>public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new Diak { Nev = "Anna", Atlag = 4.5 }; // az ablak (és minden gyereke) ebből az objektumból olvas
}
}DataContext az az objektum, amelynek tulajdonságaira a {Binding Nev} hivatkozik. Az ablakon beállítva örökli minden gyerek elem. A kötés Path-ja (Nev) a DataContext tulajdonsága; a {Binding Nev} a {Binding Path=Nev} rövidítése.OneWay az adatból a felületre (csak megjelenít); a TwoWay mindkét irányba (a TextBox módosítja az adatot); a OneTime csak egyszer olvas. A TextBox.Text alapból TwoWay, a TextBlock.Text OneWay.LostFocus); a PropertyChanged minden billentyűleütésnél: élő szűréshez, ellenőrzéshez ez kell.{0:0.00}, dátum: {0:yyyy.MM.dd}): nincs ToString a kódban. Összetettebb átalakításra az IValueConverter (például bool → Visibility) való.Jósolj!
A felület TextBlock Text="{Binding Nev}", a DataContext egy Diak objektum. A program futás közben átírja: diak.Nev = "Béla";. Megváltozik-e a felületen a név?
Jóslat ellenőrzése
Nem, ha a Diak egyszerű osztály: a kötés nem tudja, hogy az érték megváltozott. A tulajdonságnak értesítenie kell a kötést: ehhez kell az INotifyPropertyChanged (következő rész).
INotifyPropertyChanged: a „szólj, ha változtam”
// A közös alap: minden ViewModel ebből származik
public abstract class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void Szol([CallerMemberName] string nev = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nev)); // a nev: a tulajdonság neve (automatikusan)
}
protected bool Allit<T>(ref T mezo, T ertek, [CallerMemberName] string nev = null)
{
if (Equals(mezo, ertek)) { return false; } // nem változott: nem kell értesíteni
mezo = ertek;
Szol(nev);
return true;
}
}
public class DiakViewModel : ObservableObject
{
private string nev = "";
private double atlag;
public string Nev { get { return nev; } set { Allit(ref nev, value); Szol("Udvozles"); } } // a Nev változásakor az Udvozles is új
public double Atlag { get { return atlag; } set { Allit(ref atlag, value); } }
public string Udvozles { get { return "Szia, " + Nev + "!"; } } // számított tulajdonság
}INotifyPropertyChanged egyetlen eseményt (PropertyChanged) ír elő. A tulajdonság setterje kiváltja, a tulajdonság nevével: a WPF kötése feliratkozik rá, és frissíti a felületet. Enélkül a kötés csak az első olvasáskor látja az értéket.Szol és az Allit segéd (CallerMemberName: a fordító beírja a tulajdonság nevét) kiküszöböli az ismétlődő kódot. A CommunityToolkit.Mvvm csomag ezt még rövidebbre veszi: [ObservableProperty] attribútum generálja a tulajdonságot.Udvozles a Nev-ből számolódik, a Nev setterje a Szol("Udvozles") hívással külön jelzi, hogy az is változott. Ezt gyakran elfelejtik.Dispatcher.Invoke kell (a WPF megfelelője az Invoke-nak).Jósolj!
Az Udvozles számított tulajdonság ("Szia, " + Nev), de a Nev setterje nem hívja a Szol("Udvozles")-t. A felhasználó átírja a nevet. Mi látszik az üdvözlő címkén?
Jóslat ellenőrzése
A régi üdvözlés: a kötés csak azt tudja, hogy a Nev változott, az Udvozles nem. A számított tulajdonságról is értesíteni kell (Szol("Udvozles")).
Listák: ObservableCollection és DataTemplate
// ViewModel: ObservableCollection értesít, ha elemet adsz hozzá vagy törölsz (a List<T> nem!)
public ObservableCollection<DiakViewModel> Diakok { get; } = new ObservableCollection<DiakViewModel>();
private DiakViewModel kivalasztott;
public DiakViewModel Kivalasztott { get { return kivalasztott; } set { Allit(ref kivalasztott, value); } }
<!-- A nézet: ItemsSource + DataTemplate: egy elem kinézete -->
<ListBox ItemsSource="{Binding Diakok}" SelectedItem="{Binding Kivalasztott}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<!-- itt a DataContext az ELEM (egy DiakViewModel) -->
<Grid>
<Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="Auto"/></Grid.ColumnDefinitions>
<TextBlock Text="{Binding Nev}" FontWeight="SemiBold"/>
<TextBlock Grid.Column="1" Text="{Binding Atlag, StringFormat={}{0:0.00}}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>ListBox, ComboBox, DataGrid) elemeit nem egyenként adod hozzá (Items.Add), hanem ItemsSource-ra kötöd a gyűjteményt: a vezérlő minden elemhez létrehozza a megjelenítést.ObservableCollection<T> értesít a hozzáadásról, törlésről: a lista a felületen magától frissül (a Windows Forms BindingList<T> (12.6 lecke) megfelelője). Egyszerű List<T> esetén a felület nem követi a változást.DataTemplate azt írja le, hogyan nézzen ki egy adatelem. Az elem DataContext-je az adott objektum, ezért a {Binding Nev} az elem Nev tulajdonságára vonatkozik. Windows Formsban ehhez saját rajzolás vagy külön vezérlő kellett.SelectedItem="{Binding Kivalasztott}" a kiválasztott elemet a ViewModel tulajdonságába köti (TwoWay): a ViewModel tudja, mi van kijelölve, a nélkül, hogy a felületet ismerné.Jósolj!
A Diakok tulajdonság List<DiakViewModel> típusú, és a program gombnyomásra hozzáad egy elemet (Diakok.Add(...)). Megjelenik-e az új elem a ListBoxban?
Jóslat ellenőrzése
Nem (a lista nem értesít a változásról): a felület csak az első betöltéskor olvassa a listát. ObservableCollection<T> kell. Ugyanaz a probléma, mint a Windows Forms List<T> és DataSource esetében (12.6 lecke).
Parancsok és az MVVM minta
public class RelayCommand : ICommand
{
private readonly Action<object> vegrehajt;
private readonly Func<object, bool> lehet;
public RelayCommand(Action<object> vegrehajt, Func<object, bool> lehet = null) { this.vegrehajt = vegrehajt; this.lehet = lehet; }
public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } }
public bool CanExecute(object p) { return lehet == null || lehet(p); }
public void Execute(object p) { vegrehajt(p); }
}
// a ViewModelben
public ICommand TorolCommand { get; }
TorolCommand = new RelayCommand(_ => Diakok.Remove(Kivalasztott), _ => Kivalasztott != null); // a gomb csak kijelöléssel aktív
<!-- a nézetben: nincs Click eseménykezelő, nincs code-behind -->
<Button Content="Törlés" Command="{Binding TorolCommand}"/>Model
Az adat és a szabályai (Diak, FeladatLista, szolgáltatások, tárolók: 12.6–12.8 lecke). Nem ismeri a felületet.
ViewModel
A felület állapota és műveletei: a megjelenített adatok (tulajdonságok, ObservableCollection), a kijelölés, a parancsok. Nem ismer vezérlőt, ablakot, MessageBoxot.
View
A megjelenés: XAML (kötésekkel), minimális vagy semmi code-behind. Csak megjelenít és továbbít.
A kapcsolat
A View DataContext-je a ViewModel; a ViewModel a Modellt használja. A kötés és a parancs a „ragasztó”: a ViewModel és a View nem hivatkozik egymásra közvetlenül.
new MainViewModel(), vm.Diakok.Add(...), vm.TorolCommand.Execute(null), és ellenőrizheted az eredményt ablak nélkül (13.8 lecke). Ez az MVVM legnagyobb gyakorlati előnye.ICommand.CanExecute azt is eldönti, hogy a gomb aktív-e: a Command-hoz kötött Button automatikusan tiltódik, ha nem lehet végrehajtani. Windows Formsban ezt kézzel kellett (btn.Enabled = ..., minden változásnál).Jósolj!
A TorolCommand CanExecute feltétele Kivalasztott != null. A felhasználó törli az utolsó kijelölt elemet, és a kijelölés megszűnik. Mi történik a Törlés gombbal?
Jóslat ellenőrzése
A gomb automatikusan letiltódik (a CommandManager újraértékeli a CanExecute-t), kézi Enabled beállítás nélkül. Ezért olyan kényelmes a parancs a kézi btn.Enabled kezeléshez képest.
Mit tegyél ide: a diáklista MVVM-mel: a lista, a mezők, a letiltott/aktív Törlés gomb.Javasolt fájlnév: winforms-15-3-mvvm.webp · Alt szöveg: A diáklista MVVM mintával
Hibakeresés: te vagy a hibadetektív
Mi a baj, és hogyan javítod?
Gondold végig az okot, és csak utána nyisd le a megoldást.
1. A felület nem frissül
A tulajdonság értéke változik, a felület mégsem. A setter nem váltja ki a Ok és javítás
PropertyChanged eseményt (nincs INotifyPropertyChanged, vagy elfelejtetted a Szol() hívást). A számított tulajdonságokról is értesíts.
2. Az új elem nem jelenik meg a listában
A Diakok.Add(...) után a ListBox változatlan.Ok és javítás
List<T> helyett ObservableCollection<T>. Ha a teljes gyűjteményt cseréled (Diakok = new ...), a tulajdonságnak értesítenie kell.
3. A kötés semmit nem mutat
A Nincs beállítva a TextBlock üres, kivétel nincs.Ok és javítás
DataContext, vagy elírás van a Path-ban (a kötés csendben elhallgat). Nézd a Visual Studio Output ablakát: „BindingExpression path error: 'Nv' property not found”. A tulajdonság legyen public (nem mező).
4. A gomb mindig aktív/inaktív
A A Command CanExecute értéke nem frissül.Ok és javítás
CanExecuteChanged nincs bekötve (a CommandManager.RequerySuggested a felületi eseményekre frissít). Egyedi állapotváltozásnál hívd a CommandManager.InvalidateRequerySuggested() metódust.
5. A TextBox csak a mező elhagyásakor ad adatot
Az élő szűrés késik. Az Ok és javítás
UpdateSourceTrigger alapból LostFocus: UpdateSourceTrigger=PropertyChanged kell a kötésben.
6. A ViewModel MessageBox-ot hív
public void Mentes() { MessageBox.Show("Mentve!"); } // a ViewModelbenHiba és javítás
A ViewModel nem ismerheti a felületet (nem tesztelhető). Állítson be egy Uzenet tulajdonságot (kötött TextBlock), vagy egy szolgáltatás-interface-en (IParbeszedSzolgaltatas) át kérjen megjelenítést (12.8 lecke elve).
Mini projekt: diáklista MVVM-mel
Építsd újra a diáknyilvántartó egy kis részét WPF-ben, MVVM szerkezettel:
- Projekt: WPF Application (
DiakMvvm); mappák:Models,ViewModels,Commands; aViewsaMainWindow.xaml. - Model:
Diakosztály (név, jegyek listája, átlag számítása): felülettől független, ellenőrzéssel (13.5 lecke). - Alapok:
ObservableObject(INotifyPropertyChanged) ésRelayCommanda leckéből. - ViewModelek:
DiakViewModel(Nev,Atlag,Megjelenites) ésMainViewModel:ObservableCollection<DiakViewModel> Diakok,Kivalasztott,UjNev,UjJegy,Szuro,Allapot, parancsok:HozzaadCommand,JegyCommand,TorolCommand(CanExecute: van kijelölés). - Nézet:
Grid: bal oldalonListBox(ItemsSource,ItemTemplate), jobb oldalon a mezők és gombok (mind kötéssel); alul állapotsor aAllapot-ra kötve;code-behindüres (csakDataContext). - Szűrés: a
SzuroTextBox(UpdateSourceTrigger=PropertyChanged) a lista nevekre szűr (CollectionViewSource/ICollectionView.Filtervagy külön szűrt gyűjtemény). - Ellenőrzés: az üres név, a hibás jegy (1–5 kívül) az
Allapotüzenetben jelenik meg (nemMessageBox). - Teszt: a
MainViewModelegységtesztje ablak nélkül (hozzáadás, törlés, jegy, átlag, parancsokCanExecuteértéke). - Összehasonlítás: írd le (5–6 mondat): mi lett egyszerűbb, mint a Windows Forms változatban (lista és gomb állapot frissítés), és mi lett nehezebb vagy szokatlan?
Megoldás megnyitása (próbáld ki előbb önállóan!)
// ---- Models/Diak.cs ----
public class Diak
{
public string Nev { get; private set; }
public List<int> Jegyek { get; } = new List<int>();
public Diak(string nev)
{
if (string.IsNullOrWhiteSpace(nev)) { throw new ArgumentException("A név kötelező."); }
Nev = nev.Trim();
}
public void JegyetAd(int jegy)
{
if (jegy < 1 || jegy > 5) { throw new ArgumentOutOfRangeException("jegy", "A jegy 1 és 5 között lehet."); }
Jegyek.Add(jegy);
}
public double? Atlag { get { return Jegyek.Count == 0 ? (double?)null : Jegyek.Average(); } }
}
// ---- ViewModels/DiakViewModel.cs ----
public class DiakViewModel : ObservableObject
{
public Diak Model { get; }
public DiakViewModel(Diak model) { Model = model; }
public string Nev { get { return Model.Nev; } }
public string Megjelenites { get { return Model.Nev + (Model.Atlag.HasValue ? " (" + Model.Atlag.Value.ToString("0.00") + ")" : " (nincs jegy)"); } }
public void Frissul() { Szol("Megjelenites"); }
}
// ---- ViewModels/MainViewModel.cs ----
public class MainViewModel : ObservableObject
{
private DiakViewModel kivalasztott;
private string ujNev = "", ujJegy = "", allapot = "Kész.", szuro = "";
public ObservableCollection<DiakViewModel> Diakok { get; } = new ObservableCollection<DiakViewModel>();
public ICollectionView Lista { get; }
public DiakViewModel Kivalasztott { get { return kivalasztott; } set { Allit(ref kivalasztott, value); } }
public string UjNev { get { return ujNev; } set { Allit(ref ujNev, value); } }
public string UjJegy { get { return ujJegy; } set { Allit(ref ujJegy, value); } }
public string Allapot { get { return allapot; } set { Allit(ref allapot, value); } }
public string Szuro { get { return szuro; } set { if (Allit(ref szuro, value)) { Lista.Refresh(); } } }
public ICommand HozzaadCommand { get; }
public ICommand JegyCommand { get; }
public ICommand TorolCommand { get; }
public MainViewModel()
{
Lista = CollectionViewSource.GetDefaultView(Diakok);
Lista.Filter = o => ((DiakViewModel)o).Nev.IndexOf(Szuro ?? "", StringComparison.CurrentCultureIgnoreCase) >= 0;
HozzaadCommand = new RelayCommand(_ => Hozzaad());
JegyCommand = new RelayCommand(_ => JegyetAd(), _ => Kivalasztott != null);
TorolCommand = new RelayCommand(_ => { Diakok.Remove(Kivalasztott); Allapot = "Törölve."; }, _ => Kivalasztott != null);
}
private void Hozzaad()
{
try
{
Diakok.Add(new DiakViewModel(new Diak(UjNev)));
UjNev = "";
Allapot = "Diák felvéve (összesen: " + Diakok.Count + ").";
}
catch (ArgumentException ex) { Allapot = ex.Message; } // üzenet kötéssel, nincs MessageBox
}
private void JegyetAd()
{
int jegy;
if (!int.TryParse(UjJegy, out jegy)) { Allapot = "A jegy egész szám kell legyen."; return; }
try { Kivalasztott.Model.JegyetAd(jegy); Kivalasztott.Frissul(); UjJegy = ""; Allapot = "Jegy rögzítve."; }
catch (ArgumentOutOfRangeException ex) { Allapot = ex.Message; }
}
}
// ---- MainWindow.xaml ----
<Window x:Class="DiakMvvm.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Diáklista" Height="380" Width="620">
<DockPanel>
<StatusBar DockPanel.Dock="Bottom"><TextBlock Text="{Binding Allapot}"/></StatusBar>
<Grid Margin="12">
<Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="240"/></Grid.ColumnDefinitions>
<DockPanel Margin="0,0,12,0">
<TextBox DockPanel.Dock="Top" Text="{Binding Szuro, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,8"/>
<ListBox ItemsSource="{Binding Lista}" SelectedItem="{Binding Kivalasztott}" DisplayMemberPath="Megjelenites"/>
</DockPanel>
<StackPanel Grid.Column="1">
<TextBlock Text="Új diák neve:"/>
<TextBox Text="{Binding UjNev, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,6"/>
<Button Content="Hozzáad" Command="{Binding HozzaadCommand}" Margin="0,0,0,16"/>
<TextBlock Text="Jegy a kijelölt diáknak (1–5):"/>
<TextBox Text="{Binding UjJegy, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,6"/>
<Button Content="Jegyet rögzít" Command="{Binding JegyCommand}" Margin="0,0,0,6"/>
<Button Content="Törlés" Command="{Binding TorolCommand}"/>
</StackPanel>
</Grid>
</DockPanel>
</Window>
// ---- MainWindow.xaml.cs: ennyi a teljes code-behind ----
public MainWindow() { InitializeComponent(); DataContext = new MainViewModel(); }
// ---- Teszt (ablak nélkül) ----
MainViewModel vm = new MainViewModel();
vm.UjNev = "Anna"; vm.HozzaadCommand.Execute(null);
Debug.Assert(vm.Diakok.Count == 1);
Debug.Assert(!vm.TorolCommand.CanExecute(null)); // nincs kijelölés
vm.Kivalasztott = vm.Diakok[0];
Debug.Assert(vm.TorolCommand.CanExecute(null));
vm.UjJegy = "5"; vm.JegyCommand.Execute(null);
Debug.Assert(vm.Kivalasztott.Model.Atlag == 5.0);
vm.UjJegy = "7"; vm.JegyCommand.Execute(null);
Debug.Assert(vm.Allapot.Contains("1 és 5"));Megjegyzés: a using System.ComponentModel;, System.Windows.Data; (CollectionViewSource), System.Collections.ObjectModel; és System.Windows.Input; névterek kellenek. A DiakViewModel.Megjelenites a jegy rögzítése után a Frissul() hívással értesít, így a lista sora magától frissül. A Debug.Assert teszt csak szemléltet: éles projektben MSTest/xUnit teszt kell (13.8 lecke). A kinézet és a pontos viselkedés WPF-verziófüggően kissé eltérhet; futtasd és ellenőrizd.
Mit tegyél ide: a kész diáklista (szűrés, jegy rögzítése, letiltott Törlés gomb kijelölés nélkül).Javasolt fájlnév: winforms-15-3-kesz.webp · Alt szöveg: A kész MVVM diáklista
Három önálló gyakorlófeladat
Egyszerre csak egyet csinálj: tervezd meg, írd meg, futtasd, ellenőrizd, és csak utána nyisd ki a megoldást.
1. Converter
Írj A WPF-ben már van beépített IValueConvertert, amely egy bool értéket Visibility-re alakít (igaz: látszik, hamis: eltűnik).Egy lehetséges megoldás
BooleanToVisibilityConverter: erőforrásként (<BooleanToVisibilityConverter x:Key="BoolVis"/>) és Visibility="{Binding VanKivalasztas, Converter={StaticResource BoolVis}}". Saját változat: Convert(...) (bool)value ? Visibility.Visible : Visibility.Collapsed, a ConvertBack throw new NotSupportedException().
2. DataTrigger
Színezd pirosra a lista elemét, ha az átlag 2 alatti. A Egy lehetséges megoldás
DataTemplate.Triggers alatt <DataTrigger Binding="{Binding Gyenge}" Value="True"><Setter TargetName="szoveg" Property="Foreground" Value="Red"/></DataTrigger> (a TextBlock x:Name="szoveg"); a ViewModelben bool Gyenge számított tulajdonság. A szín nem csak jelzés: a szöveg is mutassa (akadálymentesség).
3. Mentés a ViewModelből
A ViewModel mentsen fájlba a 12.8 lecke tárolójával. Hogyan adod át? A Egy lehetséges megoldás
MainViewModel konstruktora egy IDiakTarolo-t kap (függőség átadása); a MentCommand a tárolón hív; az App.xaml.cs OnStartup metódusa (composition root, 12.9 lecke) állítja össze: new MainWindow { DataContext = new MainViewModel(new JsonDiakTarolo(ut)) }.
Gyakorlótár: 6 további feladat
Három szinten gyakorolhatsz: alap, közepes és kihívás. Csak a próbálkozás után nyisd le a megoldást.
Alap szint
1. Mi az adatkötés?
Fogalmazd meg egy mondatban. A felületi elem és az adat összekapcsolása, hogy az egyik változását a másik automatikusan kövesse.Megoldás megnyitása
2. DataContext
Mi a DataContext? Az az objektum, amelynek tulajdonságaira a Megoldás megnyitása
{Binding ...} hivatkozik; a gyerek elemek öröklik.
Közepes szint
3. Lista és értesítés
Miért Az ObservableCollection és nem List?Megoldás megnyitása
ObservableCollection értesít az elemek hozzáadásáról/törléséről, a felület magától frissül.
4. Mode
Mikor TwoWay, mikor OneWay?Megoldás megnyitása
TwoWay, ha a felhasználó módosíthatja az adatot (TextBox); OneWay, ha csak megjelenítés (TextBlock).
Kihívás szint
5. MVVM és MVP
Miben különbözik a ViewModel a presentertől (12.9 lecke)? A presenter a nézetet interface-en át hívja és állítja; a ViewModel nem ismeri a nézetet: a kötés és a parancs kapcsolja össze.Megoldás megnyitása
6. Parancs
Mi a Megadja, hogy a parancs végrehajtható-e: a hozzákötött gomb automatikusan tiltódik/engedélyeződik.CanExecute haszna?Megoldás megnyitása
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. Model (adat és szabály), View (XAML megjelenítés), ViewModel (felület-állapot és parancsok). Az interface, amellyel a tulajdonság jelzi a kötésnek, hogy változott. Egy adatelem megjelenésének leírása (listaelemhez, kártyához). A felületi művelet absztrakciója ( Nem ismer felületet: sima osztályként példányosítható és ellenőrizhető. A Visual Studio Output ablakában (BindingExpression path error). Kis, rövid életű programnál: a code-behind elég; a minta a nagyobb, tesztelendő alkalmazásoknál térül meg.1. Mit tartalmaz az MVVM három része?
2. Mi az INotifyPropertyChanged?
3. Mi a DataTemplate?
4. Mi az ICommand?
Execute, CanExecute); gombhoz kötve.5. Miért tesztelhető a ViewModel?
6. Hol látod a kötési hibákat?
7. Mikor nem kell MVVM?
Mit tudsz már?
- Adatkötést használsz (
Binding,DataContext,Mode,StringFormat). - ViewModelt írsz
INotifyPropertyChanged,ObservableCollectionésRelayCommandsegítségével. - Listát jelenítesz meg
ItemsSourceésDataTemplatesegítségével. - Érted az MVVM szerkezetét, és összekötöd a 12. fejezet rétegeivel; a ViewModelt felület nélkül teszteled.
Következő lecke: .NET MAUI és többplatformos alkalmazások
Hogyan készíthetsz egyetlen programot, amely Windowson, Androidon, iOS-en és macOS-en is fut.



