What is the difference between WinForms and WPF/MVVM?

Fra WinForms til WPF: En Guide til Tankegangen

26/07/2012

Rating: 4.84 (16395 votes)

Mange udviklere, der kommer fra en baggrund i Windows Forms (WinForms), oplever en stejl læringskurve, når de forsøger at skifte til Windows Presentation Foundation (WPF). Internettet er fyldt med tutorials, men de fleste føles ufuldstændige. Nogle viser kun små kodestykker, andre benytter tredjeparts-frameworks uden at forklare grundprincipperne, og de færreste dækker den kritiske del: Hvordan henter og håndterer man data fra en database eller en fil? Hvor hører CRUD-operationerne (Create, Read, Update, Delete) hjemme? I Model, View, ViewModel, eller findes der et fjerde lag? Sandheden er, at den største forhindring ikke er syntaksen, men en fundamental ændring i tankegang. Denne artikel er den guide, du har manglet. Vi dykker ned i den filosofiske forskel, der gør WPF så kraftfuldt, og viser dig, hvordan du strukturerer din applikation korrekt.

How do I learn WPF?
Divide and conquer. Start with c# and write a few simple programs. Then try to put a fancy interface on it for WPF learning. Once you feel comfortable doing that, follow a tutorial on mvvm. Check out this pluralsight course
Indholdsfortegnelse

Den Grundlæggende Forskel: Fra UI-centreret til Datacentreret

For at mestre WPF er det afgørende at forstå det paradigmeskift, der adskiller det fra WinForms. Det kan bedst opsummeres således:

  • I WinForms: Dine formularer og UI-kontroller er din applikation. Logikken er tæt knyttet til UI-elementerne.
  • I WPF/MVVM: De klasseobjekter, du bygger (din ViewModel og Model), er din applikation. UI'en er blot en brugervenlig, udskiftelig skal, der visualiserer disse objekter.

I WinForms arbejder du imperativt. Du trækker en knap ind på en form, dobbeltklikker på den og skriver i `button1_Click`-eventhandleren: `string name = nameTextBox.Text;`. Du manipulerer direkte med UI-elementerne for at hente og sætte data. Logikken og præsentationen er uadskilleligt sammenfiltrede.

I WPF arbejder du deklarativt. Du definerer, hvad din UI skal vise, ikke hvordan den skal opdatere sig. Dette sker gennem det kraftfulde koncept databinding. Din UI observerer dine dataobjekter, og når dataene ændrer sig, opdaterer UI'en sig selv automatisk. Applikationslogikken har ingen viden om, hvorvidt der findes en `TextBox` eller ej; den arbejder udelukkende med data-properties.

Et Praktisk Eksempel: Registreringsformularen

Lad os illustrere forskellen med et simpelt eksempel: en registreringsformular, der indsamler navn, e-mail og et samtykke til at modtage tilbud.

WinForms-tilgangen

En WinForms-udviklers tankeproces ville være:

  1. Jeg skal bruge et `Form`-vindue.
  2. Jeg trækker to `TextBox`-kontroller (til navn og e-mail), en `CheckBox` (til tilbud) og en `Button` (til at indsende) ind på formen.
  3. Jeg dobbeltklikker på knappen for at oprette en `submitButton_Click`-event.
  4. I eventhandleren skriver jeg kode, der direkte læser værdierne fra kontrollerne: `string name = nameTextBox.Text;`, `string email = emailTextBox.Text;`, `bool wantsOffers = offersCheckBox.Checked;`.
  5. Derefter sender jeg disse værdier til en metode, der gemmer dem i databasen.

Her er UI'en og applikationen én og samme enhed. Uden `nameTextBox` kan du ikke få fat i navnet.

WPF/MVVM-tilgangen

En WPF-udviklers tankeproces er markant anderledes:

  1. Jeg skal bruge en `RegistrationViewModel`-klasse.
  2. Denne klasse skal have properties: `public string Name { get; set; }`, `public string EmailAddress { get; set; }` og `public bool WantsSpecialOffers { get; set; }`.
  3. Klassen skal også have en `ICommand`, f.eks. `public ICommand SubmitCommand { get; }`, som indeholder logikken for, hvad der skal ske, når formularen indsendes.
  4. Først bagefter tænker jeg på UI'en (View). Jeg opretter et `Window` eller `UserControl`.
  5. I XAML binder jeg UI-kontrollerne til min ViewModel: ``, ``, ``, ``.

Bemærk den totale adskillelse. `RegistrationViewModel` aner intet om `TextBox` eller `CheckBox`. Den indeholder kun data og adfærd. Du kunne i princippet køre hele din applikation gennem enhedstests eller en konsolapplikation uden nogensinde at have en grafisk brugerflade. UI'en er blot en visuel repræsentation af ViewModel'ens tilstand.

Hvor Hører CRUD-operationerne Hjemme?

Dette er et af de mest centrale spørgsmål, og svaret ligger i en velstruktureret arkitektur, der går ud over bare Model, View og ViewModel.

  • Model (M): Repræsenterer dine rene dataobjekter. En `User`-klasse med properties som `Id`, `Name` og `Email`. Modellen indeholder ingen logik om, hvordan den gemmes eller hentes. Den er bare data.
  • View (V): Din XAML-kode. Den er "dum" og indeholder ideelt set ingen kode-behind. Dens eneste opgave er at vise data fra ViewModel'en og videresende brugerhandlinger (via Commands) til ViewModel'en.
  • ViewModel (VM): Broen mellem Model og View. Den holder på applikationens tilstand (f.eks. den `User`, der redigeres i øjeblikket) og den logik, der er relateret til præsentationen (f.eks. hvad der sker, når man trykker på "Gem").

Men ViewModel'en bør ikke tale direkte med databasen. Hvorfor? Fordi det gør den svær at teste og tæt koblet til en specifik datateknologi. Her introducerer vi et fjerde lag:

Det Fjerde Lag: Service- eller Repository-laget

For at holde din arkitektur ren og testbar, bør din ViewModel delegere alt dataarbejde til et separat lag. Dette kaldes ofte et Service-lag eller et Repository Pattern.

Processen ser således ud:

  1. Brugeren klikker på "Gem"-knappen i View'en.
  2. Knappens `Command` i ViewModel'en bliver eksekveret.
  3. ViewModel'en kalder en metode på et service-objekt, f.eks. `_userService.SaveUser(currentUser)`. ViewModel'en ved ikke, hvordan brugeren gemmes, kun at den skal gemmes.
  4. `UserService` implementerer den konkrete logik. Det er her, din `DbContext` fra Entity Framework, dine SQL-kald eller din kode til at skrive til en JSON-fil bor.

Denne adskillelse er guld værd. Når du skal enhedsteste din ViewModel, kan du blot give den en "falsk" (mock) `IUserService`, der ikke taler med en rigtig database. Du kan også udskifte din database fra SQL Server til en web-API uden at skulle ændre en eneste linje kode i din ViewModel eller View.

Sammenligningstabel: WinForms vs. WPF/MVVM

For at opsummere forskellene er her en direkte sammenligning:

AspektWinFormsWPF/MVVM
Grundlæggende FilosofiUI-elementer er applikationen. Tæt kobling mellem UI og logik.Dataobjekter (klasser) er applikationen. Løs kobling via databinding.
DatahåndteringManuel opdatering af UI fra kode-behind (`textBox.Text = ...`).Automatisk opdatering af UI via databinding og `INotifyPropertyChanged`.
UI-logikPlaceret i event-handlere i kode-behind-filen (f.eks. `button_Click`).Placeret i `Commands` i ViewModel'en, adskilt fra UI.
TestbarhedSvær at enhedsteste, da logik er afhængig af UI-elementer.Let at enhedsteste ViewModel'en, da den er en ren C#-klasse uden UI-afhængigheder.
Design vs. LogikDesigner og udvikler arbejder ofte i de samme filer.Klar adskillelse. En designer kan arbejde på XAML (View), mens en udvikler arbejder på C# (ViewModel).

Ofte Stillede Spørgsmål (FAQ)

Er WPF/MVVM ikke meget mere kompliceret end WinForms?

Den indledende læringskurve er uden tvivl stejlere. Du skal forstå koncepter som databinding, commands, dependency properties og XAML. Men for enhver applikation, der har mere end simpel kompleksitet, bliver MVVM-mønsteret en gave. Det tvinger dig til at skrive struktureret, testbar og vedligeholdelsesvenlig kode. Den tid, du investerer i at lære det, betaler sig mange gange tilbage i det lange løb.

Skal jeg bruge et framework som Prism eller MVVM Light?

Når du starter, er det en rigtig god idé at bygge dine første par applikationer uden et framework. Implementer selv `INotifyPropertyChanged` og `ICommand` for at forstå, hvordan mekanismerne fungerer. Når du har styr på det grundlæggende, kan frameworks som Prism, MVVM Light Toolkit (nu en del af Windows Community Toolkit) eller ReactiveUI spare dig for en masse gentagende (boilerplate) kode og give dig avancerede funktioner som en event aggregator og dependency injection container.

Hvad er en DataTemplate?

En DataTemplate er et af de mest geniale koncepter i WPF. Den fortæller WPF, hvordan et C#-objekt skal tegnes. Forestil dig, at du har en `ObservableCollection` i din `MainViewModel`. I XAML kan du binde en `ListBox` til denne samling. Men hvordan ved `ListBox`'en, hvordan den skal vise et `ProductViewModel`-objekt? Det fortæller du den med en `DataTemplate`. Du kan definere, at for hvert `ProductViewModel`-objekt skal der vises et billede, et navn i fed skrift og en pris. Dette gør det muligt at bygge komplekse og dynamiske brugerflader med meget lidt kode.

Konklusion

At skifte fra WinForms til WPF handler mindre om at lære et nyt UI-bibliotek og mere om at adoptere en ny måde at tænke på applikationsarkitektur. Hold op med at tænke på knapper og tekstbokse som din applikation. Begynd i stedet at tænke på de klasser og data, der repræsenterer din applikations kerne. Byg din `ViewModel`, definer dens properties og commands, og betragt derefter UI'en som en skal, du bygger ovenpå. CRUD-operationer hører hjemme i et dedikeret service-lag, som din ViewModel kalder. Når du først har internaliseret denne adskillelse af ansvar, vil du opdage, at WPF ikke bare er et alternativ til WinForms, men en langt mere kraftfuld, fleksibel og moderne måde at bygge robuste desktop-applikationer på.

Hvis du vil læse andre artikler, der ligner Fra WinForms til WPF: En Guide til Tankegangen, kan du besøge kategorien Træ.

Go up