Not a member of GistPad yet?
Sign Up,
it unlocks many cool features!
- ================================================================================
- КАК ВЫУЧИТЬ ЗА ~2 ЧАСА (ПОРЯДОК ПАМЯТИ)
- ================================================================================
- Час 1 — «скелет»
- 1) SQL: две таблицы users + products, поля как в schema.sql.
- 2) Модели User и Product (имена полей = как в SELECT).
- 3) DbService: строка подключения, Login (одна строка из users), GetProducts (цикл while Read).
- Час 2 — «окна»
- 4) App: старт с LoginWindow как MainWindow.
- 5) LoginWindow: два TextBox, кнопки, вызов db.Login, OpenByRole — if по Role,
- ОБЯЗАТЕЛЬНО desktop.MainWindow = новое окно, иначе после Close() приложение умрёт.
- 6) BaseProductsWindow: UserName + Products, DataContext = this, ItemsControl ItemsSource.
- 7) Картинка: строка ImagePath из БД → конвертер → Bitmap (avares://ИМЯ_СБОРКИ/...).
- 8) Admin / Manager / Client — три класса, только Title и : base(user).
- Запомнить наизусть: порядок колонок в reader (0,1,2,3), SQL с @параметрами,
- и три роли: admin, manager, client.
- ================================================================================
- БИБЛИОТЕКИ (NuGet) — КОРОТКО
- ================================================================================
- Avalonia — UI
- Avalonia.Desktop — настольный запуск
- Avalonia.Themes.Fluent — тема оформления
- Npgsql — PostgreSQL
- Все Avalonia.* — одна версия. Подробнее: файл Packages.txt
- ================================================================================
- ВЕРСИЯ AVALONIA
- ================================================================================
- Код рассчитан на Avalonia 11.x. На экзамене поменяйте только номера пакетов
- в .csproj на те, что в шаблоне (главное — одинаковые между собой).
- В XAML: ItemsSource у списка (не Items). Имя сборки в конвертере должно совпадать
- с именем проекта (здесь: AvaloniaApplication1).
- ================================================================================
- ФАЙЛ: AvaloniaApplication1.csproj
- ================================================================================
- (см. реальный файл в проекте — там PackageReference и AvaloniaResource для Assets)
- ================================================================================
- ФАЙЛ: Program.cs
- ================================================================================
- using System;
- using Avalonia;
- namespace AvaloniaApplication1;
- internal static class Program
- {
- [STAThread]
- public static void Main(string[] args) => BuildAvaloniaApp()
- .StartWithClassicDesktopLifetime(args);
- public static AppBuilder BuildAvaloniaApp()
- => AppBuilder.Configure<App>()
- .UsePlatformDetect()
- .LogToTrace();
- }
- ================================================================================
- ФАЙЛ: App.axaml
- ================================================================================
- <Application xmlns="https://github.com/avaloniaui"
- x:Class="AvaloniaApplication1.App"
- RequestedThemeVariant="Default">
- <Application.Styles>
- <FluentTheme />
- </Application.Styles>
- </Application>
- ================================================================================
- ФАЙЛ: App.axaml.cs
- ================================================================================
- using Avalonia;
- using Avalonia.Controls.ApplicationLifetimes;
- using Avalonia.Markup.Xaml;
- using AvaloniaApplication1.Views;
- namespace AvaloniaApplication1;
- public partial class App : Application
- {
- public override void Initialize()
- {
- AvaloniaXamlLoader.Load(this);
- }
- public override void OnFrameworkInitializationCompleted()
- {
- if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- desktop.MainWindow = new LoginWindow();
- base.OnFrameworkInitializationCompleted();
- }
- }
- ================================================================================
- ФАЙЛ: Models/User.cs
- ================================================================================
- namespace AvaloniaApplication1.Models;
- public sealed class User
- {
- public int Id { get; set; }
- public string Login { get; set; } = "";
- public string Password { get; set; } = "";
- public string Role { get; set; } = "";
- public string FullName { get; set; } = "";
- }
- ================================================================================
- ФАЙЛ: Models/Product.cs
- ================================================================================
- namespace AvaloniaApplication1.Models;
- public sealed class Product
- {
- public string Name { get; set; } = "";
- public decimal Price { get; set; }
- public int Discount { get; set; }
- public string ImagePath { get; set; } = "";
- public string FinalPrice =>
- (Price * (100 - Discount) / 100m).ToString("F2");
- }
- ================================================================================
- ФАЙЛ: Services/DbService.cs (папка может называться services — без разницы)
- ================================================================================
- using System.Collections.Generic;
- using AvaloniaApplication1.Models;
- using Npgsql;
- namespace AvaloniaApplication1.Services;
- public sealed class DbService
- {
- private readonly string connString =
- "Host=localhost;Port=5432;Username=postgres;Password=ВАШ_ПАРОЛЬ;Database=ВАША_БД";
- public User? Login(string login, string password)
- {
- using var conn = new NpgsqlConnection(connString);
- conn.Open();
- using var cmd = new NpgsqlCommand(
- "SELECT id, login, password, role, fullname FROM users WHERE login=@l AND password=@p",
- conn);
- cmd.Parameters.AddWithValue("l", login);
- cmd.Parameters.AddWithValue("p", password);
- using var reader = cmd.ExecuteReader();
- if (reader.Read())
- {
- return new User
- {
- Id = reader.GetInt32(0),
- Login = reader.GetString(1),
- Role = reader.GetString(3),
- FullName = reader.GetString(4)
- };
- }
- return null;
- }
- public List<Product> GetProducts()
- {
- var list = new List<Product>();
- using var conn = new NpgsqlConnection(connString);
- conn.Open();
- using var cmd = new NpgsqlCommand(
- "SELECT name, price, discount, image FROM products", conn);
- using var reader = cmd.ExecuteReader();
- while (reader.Read())
- {
- var img = reader.IsDBNull(3) ? "" : reader.GetString(3);
- if (string.IsNullOrWhiteSpace(img))
- img = "Assets/no_image.png";
- else
- img = "Assets/" + img;
- list.Add(new Product
- {
- Name = reader.GetString(0),
- Price = reader.GetDecimal(1),
- Discount = reader.GetInt32(2),
- ImagePath = img
- });
- }
- return list;
- }
- }
- ================================================================================
- ФАЙЛ: Converters/AssetPathToBitmapConverter.cs
- ================================================================================
- using System;
- using System.Globalization;
- using Avalonia.Data.Converters;
- using Avalonia.Media.Imaging;
- using Avalonia.Platform;
- namespace AvaloniaApplication1.Converters;
- public sealed class AssetPathToBitmapConverter : IValueConverter
- {
- public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
- {
- var relative = value as string;
- if (string.IsNullOrWhiteSpace(relative))
- relative = "Assets/no_image.png";
- relative = relative.Replace('\\', '/');
- var uri = new Uri("avares://AvaloniaApplication1/" + relative);
- try
- {
- using var stream = AssetLoader.Open(uri);
- return new Bitmap(stream);
- }
- catch
- {
- var fallback = new Uri("avares://AvaloniaApplication1/Assets/no_image.png");
- using var stream = AssetLoader.Open(fallback);
- return new Bitmap(stream);
- }
- }
- public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
- => throw new NotSupportedException();
- }
- ================================================================================
- ФАЙЛ: Views/LoginWindow.axaml
- ================================================================================
- <Window xmlns="https://github.com/avaloniaui"
- x:Class="AvaloniaApplication1.Views.LoginWindow"
- Width="400" Height="280"
- Title="Вход">
- <StackPanel Margin="20" Spacing="10">
- <TextBox x:Name="loginBox" Watermark="Логин"/>
- <TextBox x:Name="passwordBox" PasswordChar="*"/>
- <TextBlock x:Name="errorText" Foreground="Red" TextWrapping="Wrap"/>
- <Button Content="Войти" Click="Login_Click"/>
- <Button Content="Гость" Click="Guest_Click"/>
- </StackPanel>
- </Window>
- ================================================================================
- ФАЙЛ: Views/LoginWindow.axaml.cs
- ================================================================================
- using System;
- using Avalonia.Controls;
- using Avalonia.Controls.ApplicationLifetimes;
- using Avalonia.Interactivity;
- using AvaloniaApplication1.Models;
- using AvaloniaApplication1.Services;
- namespace AvaloniaApplication1.Views;
- public partial class LoginWindow : Window
- {
- private readonly DbService db = new DbService();
- public LoginWindow()
- {
- InitializeComponent();
- }
- private void Login_Click(object? sender, RoutedEventArgs e)
- {
- errorText.Text = "";
- var login = loginBox.Text?.Trim() ?? "";
- var password = passwordBox.Text ?? "";
- try
- {
- var user = db.Login(login, password);
- if (user == null)
- {
- errorText.Text = "Неверный логин или пароль.";
- return;
- }
- OpenByRole(user);
- }
- catch (Exception ex)
- {
- errorText.Text = "Ошибка БД: " + ex.Message;
- }
- }
- private void Guest_Click(object? sender, RoutedEventArgs e)
- {
- errorText.Text = "";
- OpenByRole(new User { Role = "client", FullName = "Гость" });
- }
- private void OpenByRole(User user)
- {
- Window w;
- if (user.Role == "admin")
- w = new AdminWindow(user);
- else if (user.Role == "manager")
- w = new ManagerWindow(user);
- else
- w = new ClientWindow(user);
- if (Avalonia.Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- desktop.MainWindow = w;
- w.Show();
- Close();
- }
- }
- ================================================================================
- ФАЙЛ: Views/BaseProductsWindow.axaml
- ================================================================================
- <Window xmlns="https://github.com/avaloniaui"
- xmlns:conv="clr-namespace:AvaloniaApplication1.Converters"
- x:Class="AvaloniaApplication1.Views.BaseProductsWindow"
- Width="900" Height="600">
- <Window.Resources>
- <conv:AssetPathToBitmapConverter x:Key="ImgConv"/>
- </Window.Resources>
- <DockPanel>
- <Border DockPanel.Dock="Top" Padding="12" Background="#E8E8E8">
- <Grid ColumnDefinitions="*,Auto">
- <TextBlock VerticalAlignment="Center" Text="{Binding UserName, StringFormat='Пользователь: {0}'}"/>
- <Button Grid.Column="1" Content="Выйти" Click="Logout_Click"/>
- </Grid>
- </Border>
- <ScrollViewer>
- <ItemsControl Margin="10" ItemsSource="{Binding Products}">
- <ItemsControl.ItemsPanel>
- <ItemsPanelTemplate>
- <WrapPanel/>
- </ItemsPanelTemplate>
- </ItemsControl.ItemsPanel>
- <ItemsControl.ItemTemplate>
- <DataTemplate>
- <Border Margin="10" Padding="10" BorderBrush="Gray" BorderThickness="1" Width="200">
- <StackPanel Spacing="6">
- <Image Width="120" Height="120" HorizontalAlignment="Center"
- Source="{Binding ImagePath, Converter={StaticResource ImgConv}}"/>
- <TextBlock Text="{Binding Name}" FontWeight="Bold" TextWrapping="Wrap"/>
- <TextBlock Text="{Binding Price, StringFormat='Цена: {0}'}"/>
- <TextBlock Text="{Binding FinalPrice, StringFormat='Со скидкой: {0}'}"/>
- </StackPanel>
- </Border>
- </DataTemplate>
- </ItemsControl.ItemTemplate>
- </ItemsControl>
- </ScrollViewer>
- </DockPanel>
- </Window>
- ================================================================================
- ФАЙЛ: Views/BaseProductsWindow.axaml.cs
- ================================================================================
- using System.Collections.Generic;
- using Avalonia;
- using Avalonia.Controls;
- using Avalonia.Controls.ApplicationLifetimes;
- using Avalonia.Interactivity;
- using AvaloniaApplication1.Models;
- using AvaloniaApplication1.Services;
- namespace AvaloniaApplication1.Views;
- public partial class BaseProductsWindow : Window
- {
- private readonly DbService db = new DbService();
- public string UserName { get; set; } = "";
- public List<Product> Products { get; set; } = new List<Product>();
- public BaseProductsWindow()
- {
- InitializeComponent();
- }
- public BaseProductsWindow(User user) : this()
- {
- UserName = user.FullName;
- Products = db.GetProducts();
- DataContext = this;
- }
- public void Logout()
- {
- var login = new LoginWindow();
- if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- desktop.MainWindow = login;
- login.Show();
- Close();
- }
- private void Logout_Click(object? sender, RoutedEventArgs e)
- {
- Logout();
- }
- }
- ================================================================================
- ФАЙЛ: Views/AdminWindow.cs
- ================================================================================
- using AvaloniaApplication1.Models;
- namespace AvaloniaApplication1.Views;
- public sealed class AdminWindow : BaseProductsWindow
- {
- public AdminWindow(User user) : base(user)
- {
- Title = "Администратор";
- }
- }
- ================================================================================
- ФАЙЛ: Views/ManagerWindow.cs
- ================================================================================
- using AvaloniaApplication1.Models;
- namespace AvaloniaApplication1.Views;
- public sealed class ManagerWindow : BaseProductsWindow
- {
- public ManagerWindow(User user) : base(user)
- {
- Title = "Менеджер";
- }
- }
- ================================================================================
- ФАЙЛ: Views/ClientWindow.cs
- ================================================================================
- using AvaloniaApplication1.Models;
- namespace AvaloniaApplication1.Views;
- public sealed class ClientWindow : BaseProductsWindow
- {
- public ClientWindow(User user) : base(user)
- {
- Title = "Клиент";
- }
- }
- ================================================================================
- ФАЙЛ: Database/schema.sql
- ================================================================================
- CREATE TABLE users (
- id SERIAL PRIMARY KEY,
- login TEXT,
- password TEXT,
- role TEXT,
- fullname TEXT
- );
- INSERT INTO users (login, password, role, fullname) VALUES
- ('admin','123','admin','Администратор'),
- ('manager','123','manager','Менеджер'),
- ('user','123','client','Пользователь');
- CREATE TABLE products (
- id SERIAL PRIMARY KEY,
- name TEXT,
- price NUMERIC,
- discount INT,
- image TEXT
- );
- INSERT INTO products (name, price, discount, image) VALUES
- ('Телефон', 50000, 10, 'phone.png'),
- ('Ноутбук', 90000, 20, 'laptop.png'),
- ('Мышка', 1500, 0, ''),
- ('Клавиатура', 3000, 5, NULL);
- ================================================================================
- КОНЕЦ СБОРНИКА (дублирует рабочий проект; при правках в коде обновите этот файл)
- ================================================================================
RAW Paste Data
Copied
