1) Po co baza danych w aplikacji mobilnej?
W mobilce jest dokładnie to samo co w desktopie:
- lista produktów/klientów/zamówień,
- historia działań,
- dane użytkownika, ustawienia.
Gdyby wszystko było tylko w RAM, po zamknięciu aplikacji wszystko znika.
SQLite pasuje idealnie, bo:
- działa offline,
- nie potrzebuje serwera,
- baza to jeden plik,
- działa na Android/iOS/Windows.
2) Jak to działa w MAUI (łańcuch)
[UI – XAML] → [Code-behind strony – MainPage.xaml.cs] → [Logika bazy – BazaDanych.cs] → [plik sklep.db w AppDataDirectory]
- XAML – wygląd: Entry, Button, listy.
- MainPage.xaml.cs – obsługa kliknięć (Dodaj/Usuń/Zapisz/Szukaj/Dodaj do koszyka).
- BazaDanych.cs – tworzenie tabel i SQL (SELECT/INSERT/UPDATE/DELETE/JOIN).
sklep.db– plik w telefonie w:FileSystem.AppDataDirectory.
3) Pakiety NuGet (MAUI)
W projekcie MAUI doinstaluj:
- Microsoft.Data.Sqlite
- SQLitePCLRaw.bundle_e_sqlite3
Dzięki temu masz SQLite działające też na Android/iOS.
4) Struktura plików (czytelnie)
- Modele
Produkt.csKoszykWidok.cs
- Logika
BazaDanych.cs
MainPage.xamlMainPage.xaml.cs
5) MODELE
Modele/Produkt.cs
namespace BazaDanychMaui.Modele
{
public class Produkt
{
public int Id { get; set; }
public string Nazwa { get; set; } = "";
public double Cena { get; set; }
}
}
Modele/KoszykWidok.cs
namespace BazaDanychMaui.Modele
{
// To jest “widok” koszyka po JOIN (czyli gotowe do wyświetlenia)
public class KoszykWidok
{
public int Id { get; set; } // id wpisu w koszyku
public string Nazwa { get; set; } = "";
public double Cena { get; set; }
public int Ilosc { get; set; }
public double Suma { get; set; }
}
}
6) LOGIKA BAZY (SQLite + SQL)
Logika/BazaDanych.cs
using Microsoft.Data.Sqlite;
using BazaDanychMaui.Modele;
namespace BazaDanychMaui.Logika
{
public class BazaDanych
{
private readonly string _sciezkaBazy;
public BazaDanych()
{
// WAŻNE: inicjalizacja silnika SQLite (Android/iOS)
SQLitePCL.Batteries_V2.Init();
_sciezkaBazy = Path.Combine(FileSystem.AppDataDirectory, "sklep.db");
UtworzTabeleJesliNieIstnieja();
}
private string ConnectionString => $"Data Source={_sciezkaBazy}";
private void UtworzTabeleJesliNieIstnieja()
{
using var con = new SqliteConnection(ConnectionString);
con.Open();
string sqlProdukty =
"CREATE TABLE IF NOT EXISTS Produkty (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"nazwa TEXT NOT NULL, " +
"cena REAL NOT NULL);";
string sqlKoszyk =
"CREATE TABLE IF NOT EXISTS Koszyk (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"produkt_id INTEGER NOT NULL, " +
"ilosc INTEGER NOT NULL);";
using var cmd1 = con.CreateCommand();
cmd1.CommandText = sqlProdukty;
cmd1.ExecuteNonQuery();
using var cmd2 = con.CreateCommand();
cmd2.CommandText = sqlKoszyk;
cmd2.ExecuteNonQuery();
}
// -------------------- PRODUKTY --------------------
public async Task<List<Produkt>> PobierzProduktyAsync(string filtr = "")
{
var lista = new List<Produkt>();
await using var con = new SqliteConnection(ConnectionString);
await con.OpenAsync();
string sql = "SELECT id, nazwa, cena FROM Produkty";
if (!string.IsNullOrWhiteSpace(filtr))
sql += " WHERE nazwa LIKE @f";
await using var cmd = con.CreateCommand();
cmd.CommandText = sql;
if (!string.IsNullOrWhiteSpace(filtr))
cmd.Parameters.AddWithValue("@f", "%" + filtr + "%");
await using var r = await cmd.ExecuteReaderAsync();
while (await r.ReadAsync())
{
lista.Add(new Produkt
{
Id = r.GetInt32(0),
Nazwa = r.GetString(1),
Cena = r.GetDouble(2)
});
}
return lista;
}
public async Task DodajProduktAsync(string nazwa, double cena)
{
await using var con = new SqliteConnection(ConnectionString);
await con.OpenAsync();
await using var cmd = con.CreateCommand();
cmd.CommandText = "INSERT INTO Produkty(nazwa, cena) VALUES (@n, @c)";
cmd.Parameters.AddWithValue("@n", nazwa);
cmd.Parameters.AddWithValue("@c", cena);
await cmd.ExecuteNonQueryAsync();
}
public async Task UsunProduktAsync(int id)
{
await using var con = new SqliteConnection(ConnectionString);
await con.OpenAsync();
await using var cmd = con.CreateCommand();
cmd.CommandText = "DELETE FROM Produkty WHERE id=@id";
cmd.Parameters.AddWithValue("@id", id);
await cmd.ExecuteNonQueryAsync();
}
public async Task EdytujProduktAsync(int id, string nazwa, double cena)
{
await using var con = new SqliteConnection(ConnectionString);
await con.OpenAsync();
await using var cmd = con.CreateCommand();
cmd.CommandText = "UPDATE Produkty SET nazwa=@n, cena=@c WHERE id=@id";
cmd.Parameters.AddWithValue("@n", nazwa);
cmd.Parameters.AddWithValue("@c", cena);
cmd.Parameters.AddWithValue("@id", id);
await cmd.ExecuteNonQueryAsync();
}
// -------------------- KOSZYK --------------------
public async Task DodajDoKoszykaAsync(int produktId, int ilosc)
{
await using var con = new SqliteConnection(ConnectionString);
await con.OpenAsync();
await using var cmd = con.CreateCommand();
cmd.CommandText = "INSERT INTO Koszyk(produkt_id, ilosc) VALUES (@pid, @ilosc)";
cmd.Parameters.AddWithValue("@pid", produktId);
cmd.Parameters.AddWithValue("@ilosc", ilosc);
await cmd.ExecuteNonQueryAsync();
}
public async Task<List<KoszykWidok>> PobierzKoszykAsync()
{
var lista = new List<KoszykWidok>();
await using var con = new SqliteConnection(ConnectionString);
await con.OpenAsync();
string sql =
"SELECT Koszyk.id, Produkty.nazwa, Produkty.cena, Koszyk.ilosc, " +
"(Produkty.cena * Koszyk.ilosc) AS suma " +
"FROM Koszyk " +
"JOIN Produkty ON Koszyk.produkt_id = Produkty.id";
await using var cmd = con.CreateCommand();
cmd.CommandText = sql;
await using var r = await cmd.ExecuteReaderAsync();
while (await r.ReadAsync())
{
lista.Add(new KoszykWidok
{
Id = r.GetInt32(0),
Nazwa = r.GetString(1),
Cena = r.GetDouble(2),
Ilosc = r.GetInt32(3),
Suma = r.GetDouble(4)
});
}
return lista;
}
}
}
7) UI (MAUI XAML) – zamiast DataGrid używamy CollectionView
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="BazaDanychMaui.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Title="Produkty + Koszyk">
<ScrollView>
<VerticalStackLayout Padding="12" Spacing="12">
<!-- FORMULARZ -->
<Label Text="Dane produktu" FontAttributes="Bold"/>
<Entry x:Name="txtNazwa" Placeholder="Nazwa" />
<Entry x:Name="txtCena" Placeholder="Cena" Keyboard="Numeric"/>
<Entry x:Name="txtID" Placeholder="ID (edycja/usuwanie)" Keyboard="Numeric"/>
<HorizontalStackLayout Spacing="10">
<Button Text="Dodaj" Clicked="btnDodaj_Clicked"/>
<Button Text="Zapisz" Clicked="btnZapisz_Clicked"/>
<Button Text="Usuń" Clicked="btnUsun_Clicked"/>
</HorizontalStackLayout>
<!-- SZUKAJ -->
<Label Text="Wyszukiwanie" FontAttributes="Bold"/>
<Entry x:Name="txtSzukaj" Placeholder="Szukaj po nazwie..." />
<HorizontalStackLayout Spacing="10">
<Button Text="Szukaj" Clicked="btnSzukaj_Clicked"/>
<Button Text="Odśwież" Clicked="btnOdswiez_Clicked"/>
</HorizontalStackLayout>
<!-- LISTA PRODUKTÓW -->
<Label Text="Produkty (kliknij, żeby wypełnić pola)" FontAttributes="Bold"/>
<CollectionView x:Name="listaProdukty" SelectionMode="Single" SelectionChanged="listaProdukty_SelectionChanged">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="8" ColumnDefinitions="60,*,90">
<Label Text="{Binding Id}" Grid.Column="0"/>
<Label Text="{Binding Nazwa}" Grid.Column="1"/>
<Label Text="{Binding Cena}" Grid.Column="2" HorizontalTextAlignment="End"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<!-- KOSZYK -->
<Label Text="Koszyk" FontAttributes="Bold"/>
<Entry x:Name="txtIlosc" Placeholder="Ilość (do koszyka)" Keyboard="Numeric"/>
<Button Text="Dodaj do koszyka (używa ID z pola ID)" Clicked="btnDodajDoKoszyka_Clicked"/>
<CollectionView x:Name="listaKoszyk">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="8" ColumnDefinitions="60,*,70,60,90">
<Label Text="{Binding Id}" Grid.Column="0"/>
<Label Text="{Binding Nazwa}" Grid.Column="1"/>
<Label Text="{Binding Cena}" Grid.Column="2" HorizontalTextAlignment="End"/>
<Label Text="{Binding Ilosc}" Grid.Column="3" HorizontalTextAlignment="End"/>
<Label Text="{Binding Suma}" Grid.Column="4" HorizontalTextAlignment="End"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
8) Logika strony (MainPage.xaml.cs)
MainPage.xaml.cs
using BazaDanychMaui.Logika;
using BazaDanychMaui.Modele;
namespace BazaDanychMaui;
public partial class MainPage : ContentPage
{
private readonly BazaDanych _baza = new BazaDanych();
public MainPage()
{
InitializeComponent();
_ = OdswiezProduktyAsync();
_ = OdswiezKoszykAsync();
}
private async Task OdswiezProduktyAsync(string filtr = "")
{
listaProdukty.ItemsSource = await _baza.PobierzProduktyAsync(filtr);
}
private async Task OdswiezKoszykAsync()
{
listaKoszyk.ItemsSource = await _baza.PobierzKoszykAsync();
}
private async void btnDodaj_Clicked(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(txtNazwa.Text))
{
await DisplayAlert("Błąd", "Podaj nazwę!", "OK");
return;
}
if (!double.TryParse(txtCena.Text, out double cena))
{
await DisplayAlert("Błąd", "Cena musi być liczbą.", "OK");
return;
}
await _baza.DodajProduktAsync(txtNazwa.Text.Trim(), cena);
await OdswiezProduktyAsync();
}
private async void btnUsun_Clicked(object sender, EventArgs e)
{
if (!int.TryParse(txtID.Text, out int id))
{
await DisplayAlert("Błąd", "Podaj poprawne ID!", "OK");
return;
}
await _baza.UsunProduktAsync(id);
await OdswiezProduktyAsync();
}
private async void btnZapisz_Clicked(object sender, EventArgs e)
{
if (!int.TryParse(txtID.Text, out int id))
{
await DisplayAlert("Błąd", "Podaj ID!", "OK");
return;
}
if (string.IsNullOrWhiteSpace(txtNazwa.Text))
{
await DisplayAlert("Błąd", "Podaj nazwę!", "OK");
return;
}
if (!double.TryParse(txtCena.Text, out double cena))
{
await DisplayAlert("Błąd", "Cena musi być liczbą.", "OK");
return;
}
await _baza.EdytujProduktAsync(id, txtNazwa.Text.Trim(), cena);
await OdswiezProduktyAsync();
}
private async void btnSzukaj_Clicked(object sender, EventArgs e)
{
await OdswiezProduktyAsync(txtSzukaj.Text ?? "");
}
private async void btnOdswiez_Clicked(object sender, EventArgs e)
{
txtSzukaj.Text = "";
await OdswiezProduktyAsync();
}
private void listaProdukty_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.CurrentSelection?.FirstOrDefault() is Produkt p)
{
txtID.Text = p.Id.ToString();
txtNazwa.Text = p.Nazwa;
txtCena.Text = p.Cena.ToString();
}
}
private async void btnDodajDoKoszyka_Clicked(object sender, EventArgs e)
{
if (!int.TryParse(txtID.Text, out int produktId))
{
await DisplayAlert("Błąd", "Najpierw wybierz produkt (albo wpisz ID).", "OK");
return;
}
if (!int.TryParse(txtIlosc.Text, out int ilosc) || ilosc <= 0)
{
await DisplayAlert("Błąd", "Podaj poprawną ilość (>0).", "OK");
return;
}
await _baza.DodajDoKoszykaAsync(produktId, ilosc);
await OdswiezKoszykAsync();
}
}

