Bootstrap tilbyder en fleksibel og kraftfuld metode til at skabe responsive weblayouts gennem sit grid-system, der bygger på en række foruddefinerede klasser. Når man anvender en container med klassen container-lg, fungerer denne som en ramme, der ændrer sin bredde afhængigt af browserens størrelse. Under 992 pixels strækker containeren sig altid til 100% af browserens bredde, hvilket sikrer optimal brug af pladsen på små skærme. Over denne grænse låses bredden til faste breakpoint-værdier som 960, 1140 og 1320 pixels, hvilket skaber en ensartet visuel struktur på større skærme. I modsætning hertil fylder container-fluid altid hele bredden, uanset skærmstørrelse.

Inden for en container er layoutet organiseret i rækker og kolonner, hvor hver række er opdelt i 12 virtuelle kolonner. Dette system gør det muligt at styre kolonnebredden præcist ved hjælp af klasser som col-2, col-4 osv., hvor tallet angiver, hvor mange af de 12 dele kolonnen skal optage. Det muliggør komplekse og fleksible opstillinger, hvor kolonner kan have forskellige størrelser, men stadig justeres harmonisk. Selvom systemet er effektivt, kan det hurtigt blive komplekst, hvilket kræver en grundig forståelse af grid-principperne for at opnå det ønskede resultat.

Bootstrap indeholder også et sæt indbyggede farvetemaer, der kan anvendes til elementer som knapper, tabeller og badges. Temaerne spænder fra primær blå, sekundær grå, til farver som succes-grøn, fare-rød og advarsels-gul. Disse farver kan bruges både som baggrundsfarver med hvid tekst eller som tekstfarver med hvid baggrund og farvede konturer, hvilket giver designeren stor frihed til at skabe visuel hierarki og tydelighed.

Tabeller i Bootstrap kræver aktivering gennem klassen table, som kan kombineres med andre klasser for at tilføje ekstra funktioner som farvede rækker, kompakte afstande, stribede mønstre, hover-effekter og rammer omkring celler. Derudover kan man kontrollere lodret og vandret justering af indhold i cellerne ved hjælp af specifikke align-klasser, hvilket sikrer både æstetik og læsbarhed i tabeller med forskelligt indhold.

Knappens udseende kan nemt tilpasses med forskellige Bootstrap-klasser, som ændrer farve, størrelse og om teksten skal brydes. Der findes også knapper med outlines i farvetemaerne for et mere afdæmpet udtryk, som skifter til fyldfarve ved hover. Det gør det muligt at skabe både fremtrædende og subtile call-to-actions.

Badges bruges til at fremhæve små informationsstykker, for eksempel antallet af ulæste beskeder. De kan styles som firkantede eller som mere bløde “piller” ved brug af rounded-pill-klassen, hvilket ofte ses i moderne apps og giver et intuitivt og visuelt tiltalende udtryk.

Alerts er en anden vigtig komponent, der bruges til at vise beskeder til brugeren. Disse skal altid anvende et af farvetemaerne for at tiltrække opmærksomhed og kan indeholde links med særlig styling. Alerts kan udvides med ikoner og overskrifter for at skabe mere informative og visuelle beskeder.

Bootstrap's styrke ligger i dens tidsbesparende karakter, tilpasningsmuligheder, open-source natur og omfattende dokumentation. Samtidig kræver det indsigt i systemet for at udnytte dets fulde potentiale uden at blive overvældet af kompleksitet.

Det er vigtigt at forstå, at selvom Bootstrap tilbyder en stærk ramme for layout og styling, bør designbeslutninger baseres på en helhedsforståelse af brugeroplevelsen, tilgængelighed og performance. At bruge Bootstrap betyder ikke bare at anvende klasser, men også at tænke kritisk over, hvordan layout og visuelle elementer kommunikerer med brugeren på tværs af forskellige enheder og skærmstørrelser. Desuden kan overdreven brug af standardklasser uden tilpasning føre til ensformige og ufleksible designs, hvorfor en balance mellem frameworkets fordele og individuel kreativitet bør efterstræbes.

Hvordan man integrerer filvalg og medie-picker funktioner i .NET MAUI-applikationer

Når man udvikler applikationer med .NET MAUI (Multi-platform App UI), er det ofte nødvendigt at give brugeren mulighed for at vælge filer eller medier fra enheden. Dette kunne være billeder, tekstfiler eller videoer, som de har optaget. At implementere sådanne funktioner kræver både en forståelse af de forskellige platforme og den måde, hvorpå .NET MAUI håndterer filvalg og medie-picking. I denne sammenhæng vil vi gennemgå, hvordan man aktiverer disse funktioner på tværs af forskellige platforme, herunder Windows, Android og iOS, samt hvordan man integrerer funktionerne i en app.

For at kunne bruge fil- og medie-pickere i en .NET MAUI-applikation, skal der først konfigureres platformsspecifikke indstillinger. Dette sikrer, at applikationen har den nødvendige adgang til systemets filer og medier.

Aktivering af filvalg på Windows
På Windows-platformen kræver det, at man tilføjer de nødvendige indstillinger i Package.appxmanifest filen. Denne fil findes under Platforms/Windows mappen i projektet. Det er vigtigt at åbne denne fil i XML-editoren, da Visual Studio som standard åbner en grafisk editor, som ikke giver mulighed for direkte XML-redigering. Når du har åbnet filen, skal du tilføje de nødvendige XML-elementer under <Capabilities> sektionen for at aktivere filvalg og medieadgang.

Aktivering af filvalg på Android
På Android skal man modificere AndroidManifest.xml filen, som findes under Platforms/Android. I denne fil skal du tilføje de relevante tilladelser i manifestet. På Android 11 og nyere (API 30 eller højere) skal du være opmærksom på de ekstra tilladelser, der kræves for at få adgang til systemets filsystem og medier. Du skal blandt andet bruge tilladelser som READ_EXTERNAL_STORAGE og WRITE_EXTERNAL_STORAGE for at få adgang til filer.

Aktivering af filvalg på iOS
På iOS skal Info.plist filen i Platforms/iOS mappen redigeres. Her skal du tilføje specifikke nøgler og beskrivelser for at anmode om adgang til kamera, mikrofon og fotobibliotek. For eksempel kræver funktionaliteten til at tage billeder, at du tilføjer NSCameraUsageDescription, som informerer brugeren om, hvorfor appen har brug for kameraadgang. Desuden skal du også tilføje nøgler som NSPhotoLibraryUsageDescription for at få adgang til brugerens fotoalbum.

Integrering af filvalg og medie-picker i appen

Når de nødvendige platformspecifikke indstillinger er på plads, kan du begynde at integrere filvalg- og medie-picking funktionerne i selve appen. Et eksempel på en sådan integration kan ses i EmployeesPage.xaml, hvor du tilføjer knapper og visuelle kontroller til at vælge en tekstfil eller et billede.

Her kan du f.eks. lave en knap, der giver brugeren mulighed for at vælge en tekstfil. Når knappen trykkes, åbnes filvælgeren, hvor brugeren kan vælge en fil. Filen bliver derefter læst og vist i en label i appen. På samme måde kan du lave en knap til at vælge billeder ved hjælp af medie-pickeren. Når et billede er valgt, vises det i et billedkontrol.

Kodeeksempel for at vælge en tekstfil

csharp
private async void PickTextFileButton_Clicked(object sender, EventArgs e)
{ try { FilePickerFileType textFileTypes = new( new Dictionary<DevicePlatform, string[]> { { DevicePlatform.iOS, new[] { "public.plain-text" } }, { DevicePlatform.Android, new[] { "text/plain" } }, { DevicePlatform.WinUI, new[] { ".txt" } } }); PickOptions options = new() { PickerTitle = "Vælg en tekstfil", FileTypes = textFileTypes }; FileResult result = await FilePicker.Default.PickAsync(options); if (result != null) { using var stream = await result.OpenReadAsync(); FileContentsLabel.Text = new StreamReader(stream).ReadToEnd(); } FilePathLabel.Text = result.FullPath; } catch (Exception ex) { await DisplayAlert("Fejl", ex.Message, "OK"); } }

Kodeeksempel for at vælge et billede

csharp
private async void PickImageButton_Clicked(object sender, EventArgs e) { FileResult photo = await MediaPicker.Default.PickPhotoAsync(); if (photo != null) { FileImage.Source = ImageSource.FromFile(photo.FullPath); FilePathLabel.Text = photo.FullPath; } else {
await DisplayAlert("Fejl", "Billedet var null.", "OK");
} }

Hvordan man tager et billede
En anden vigtig funktion er at give brugeren mulighed for at tage et billede direkte fra appen. Hvis enheden understøtter medieoptagelse, kan du bruge CapturePhotoAsync metoden til at tage et billede. Den optagne fil kan derefter vises i billedkontrollen, ligesom et valgt billede.

Vigtige overvejelser og anbefalinger

Når du implementerer funktioner som filvalg og medie-picking, skal du være opmærksom på forskellige platformsspecifikke restriktioner og tilladelser. Sørg for, at din app korrekt håndterer undtagelser og giver brugeren passende feedback, når noget går galt, som f.eks. manglende tilladelser eller enhedens inkompatibilitet.

Desuden bør du overveje at tilføje brugervenlige funktioner som previews af valgte billeder, eller en mulighed for at vise flere filtyper i filvælgeren for at gøre appen mere fleksibel og brugervenlig. Det er også vigtigt at optimere appen for forskellige skærmstørrelser og enheder for at sikre en god brugeroplevelse på tværs af platforme.

Hvordan migrerer man relationelle data til Cosmos DB?

Når man arbejder med NoSQL-databaser som Azure Cosmos DB, er det vigtigt at forstå, hvordan man kan migrere data fra relationelle databasemodeller, som SQL Server, til en NoSQL-struktur, der er mere fleksibel og skalerbar. I denne sammenhæng tager vi udgangspunkt i Northwind-databasen, en klassisk SQL Server-database, og demonstrerer, hvordan man kan tilpasse dens data for at opbevare det i Cosmos DB ved hjælp af EF Core.

I første omgang er det nødvendigt at forstå forskellen mellem relationelle og NoSQL-databaser. I relationelle databaser opbevares data i tabeller med faste relationer mellem dem, mens NoSQL-databaser som Cosmos DB typisk gemmer data i dokumenter (JSON-format), som kan indeholde indlejrede elementer og har en mere fleksibel struktur. For at tilpasse sig denne forskel er det nødvendigt at oprette nye klasser i projektet, der afspejler den embedderede struktur af Cosmos DB.

I Northwind.CosmosDb.SqlApi-projektet skal du først oprette en klasse, der repræsenterer kategorier i Cosmos DB. Denne klasse skal indeholde egenskaber som categoryId, categoryName og en valgfri beskrivelse af kategorien. Den næste klasse, som du opretter, skal repræsentere leverandører (suppliers) og indeholde en lang række oplysninger som companyName, contactName, phone og andre relevante detaljer. Endelig opretter du en klasse, der repræsenterer produkter i Cosmos DB, hvor du skal inkludere en række egenskaber som produktnavn, enhedspris og enheder på lager.

Alle disse klasser er designet til at afspejle den indlejrede dokumentstruktur i Cosmos DB. Det er især vigtigt at bemærke, at hver JSON-dokumentpost i Cosmos DB skal have en id-egenskab, som er nødvendig for korrekt identifikation og sporbarhed. Hvis denne ikke er eksplicit defineret i modellen, vil systemet automatisk tildele en GUID-værdi. For at sikre kontrol over denne værdi og undgå uforudsete problemer anbefales det at definere id-egenskaben eksplicit i modellen.

Når disse klasser er oprettet, skal du i din applikation importere de nødvendige namespaces og definere metoder til at overføre data fra den relationelle database til Cosmos DB. Du skal bruge en metode som CreateProductItems, der henter data fra Northwind SQL-databasen, inkluderer relaterede kategorier og leverandører, og derefter opretter nye objekter i Cosmos DB. Det er vigtigt at huske, at hver post i Cosmos DB oprettes som et nyt element i en container, og du skal håndtere scenarier, hvor en post allerede eksisterer, så du ikke opretter duplikater.

I denne metode anvendes Entity Framework (EF) Core's Include-funktion til at hente de relaterede data fra kategorier og leverandører for hvert produkt. Herefter oprettes der en ny ProductCosmos-post, hvor alle relevante oplysninger om kategori og leverandør er indlejret i produktet. Hvis produktet allerede findes i Cosmos DB, udføres en læsning af elementet; ellers oprettes et nyt element. Når du bruger Cosmos DB, skal du også være opmærksom på de ressourcer, der bruges ved oprettelse og opdatering af data, og derfor kan det være nyttigt at logge den mængde ressourcer (Request Units - RUs), der forbruges under hver operation.

Ved at følge denne proces kan man effektivt migrere relationelle data til Cosmos DB, samtidig med at man bevarer den nødvendige struktur og relation mellem entiteter. Det er en god praksis at bruge eksplicitte id’er for at sikre entydig identifikation af data og undgå problemer med duplikater eller inkonsistente data.

Når man arbejder med NoSQL-databaser som Cosmos DB, er det også vigtigt at forstå, hvordan data opdateres og slettes, da disse operationer kan være langt mere komplicerede end i en relationel database, hvor referentiel integritet håndteres automatisk. I Cosmos DB skal udvikleren selv sørge for, at referencer og afhængigheder håndteres korrekt. Det kan også være nyttigt at overveje, hvordan man håndterer versionering af data og håndterer eventuelle uoverensstemmelser mellem den relationelle og den NoSQL-struktur, hvilket kan kræve ekstra lag af fejlhåndtering og logning i applikationen.

Hvordan kan man hurtigt komme i gang med .NET-apps og services?

At vælge de rette teknologier og arkitekturprincipper er afgørende for hurtigt at blive produktiv i udviklingen af apps og services med .NET. Koden til trin-for-trin opgaver og øvelser kan hentes direkte fra et GitHub-repositorium, hvilket giver en praktisk måde at følge bogens eksempler på. For dem, der er nye til GitHub, er der i bogens første kapitel klare instruktioner om, hvordan man downloader eller kloner koden.

Opsætningen af udviklingsmiljøet er grundlaget, og her er Visual Studio 2022 og Visual Studio Code de primære værktøjer. Moderne C# og .NET indeholder en række nye funktioner og biblioteker, der forbedrer udvikleroplevelsen og effektiviteten. Bogen lægger vægt på at skabe en solid forståelse af disse, samtidig med at man introduceres til relevante steder for hjælp og kontakt med forfatteren for feedback.

Datahåndtering er essentiel, og derfor gennemgås både relationelle og NoSQL-databaser detaljeret. SQL Server kan konfigureres på tværs af platforme, inklusive Docker-containere og Azure cloud, og datatilgang sker både på lavt niveau via ADO.NET og højniveau med Entity Framework Core. Eksempelvis arbejdes der med Northwind-databasen, som er et velkendt case, der bruges til at demonstrere opbygning af modeller og datatilgang.

Azure Cosmos DB præsenteres som en cloud-native NoSQL-løsning, hvor man både lærer at interagere med den oprindelige API og med det grafbaserede Gremlin API, hvilket giver indsigt i forskellige datamodeller og deres anvendelser.

Ydeevne og konkurrence er centrale emner for at sikre applikationers effektivitet og brugertilfredshed. Ved at anvende tråde og opgaver kan flere processer køre parallelt, og med værktøjer som Benchmark.NET kan man måle og optimere koden.

Brugen af tredjepartsbiblioteker som ImageSharp, Serilog, AutoMapper, FluentAssertions, FluentValidation og QuestPDF hjælper med at løse praktiske problemer effektivt. Dette gør det muligt at fokusere mere på forretningslogik og mindre på generiske funktioner som billedbehandling, logging, objektmapping, test og PDF-generering.

For avancerede teknikker bliver refleksion, attributter og dynamisk kodegenerering gennemgået, hvilket giver mulighed for at tilpasse programadfærd under kørslen og optimering under kompilering.

Håndtering af datoer, tidspunkter, tidszoner samt internationalisering er nødvendige færdigheder for globale applikationer, der skal tilpasses forskellige markeder og brugere.

Datasikkerhed er uundværlig. Kryptering, hashing, signering samt autentifikation og autorisation sikrer, at data ikke bliver læst eller manipuleret af uvedkommende, og at brugere kun får adgang til det, de er berettigede til.

Webservices kan bygges på en mere minimalistisk måde med ASP.NET Core Minimal APIs, der forenkler udviklingen ved at eliminere behovet for omfattende controller-klasser, samtidig med at sikkerhedsforanstaltninger som rate limiting og CORS implementeres.

OData og GraphQL giver forskellige metoder til at eksponere data via webtjenester, henholdsvis med flere HTTP-endpoints eller via et enkelt endpoint, der aggregerer data fra flere kilder.

Mikrotjenester kan effektivt opbygges med gRPC, der bruger Protobuf-formatet til kompakt meddelelsesudveksling og giver mulighed for webbrowseradgang via JSON-transkodning.

SignalR muliggør realtidskommunikation til applikationer som dashboards og notifikationssystemer, der kræver øjeblikkelig opdatering til flere klienter samtidigt.

Azure Functions giver en serverløs arkitektur, hvor kode kun kører på baggrund af bestemte trigger-aktiviteter, hvilket giver høj fleksibilitet og omkostningseffektivitet.

Web-brugerflader kan udvikles med ASP.NET Core MVC ved brug af Razor, tag helpers og Bootstrap, mens Blazor WebAssembly muliggør opbygning af UI-komponenter, der kører på klienten for krydsplatformsanvendelser.

Udvalgte open source Blazor-komponentbiblioteker tilbyder genanvendelige UI-elementer, hvilket fremmer hurtigere udvikling.

Med .NET MAUI kan man bygge mobile og desktop applikationer, der fungerer på Android, iOS, macOS og Windows, ved at bruge XAML til brugerfladedefinition. Integration af native platformfunktioner sikrer en bedre brugeroplevelse og udnyttelse af enhedens kapaciteter.

Et afsluttende projekt med en survey-applikation giver mulighed for praktisk anvendelse af læring og offentlig feedback.

For at udnytte denne bogs indhold fuldt ud, kræves kun en understøttet operativsystemplatform og internetadgang for at installere nødvendige udviklingsværktøjer. Valgfrit kan man bruge tredjepartsværktøjer som JetBrains Rider.

Farveillustrationer af bogens screenshots og diagrammer stilles til rådighed i en separat PDF, hvilket støtter læserens forståelse af visuelle ændringer i koden.

Udover det tekniske indhold er det vigtigt at forstå sammenhængen mellem de forskellige teknologier og deres plads i en moderne softwareudviklingsproces. Valg af arkitektur og værktøjer bør altid afspejle behovet for skalerbarhed, vedligeholdelse, performance og sikkerhed. At mestre både lavniveau dataadgang og højniveau abstraktioner sikrer fleksibilitet i komplekse løsninger. Samtidig må man ikke undervurdere værdien af community-ressourcer og kontinuerlig læring for at holde sig opdateret i et hurtigt udviklende miljø. Endvidere understøtter en dyb forståelse af samtidighed og asynkron programmering ikke blot ydelse, men også robustheden af applikationer i virkelige produktionsmiljøer. Endelig er integrationen af cloud-tjenester og serverløse funktioner en nøgle til moderne, omkostningseffektive løsninger, som hurtigt kan tilpasses forretningsbehov.