🪟

Введение в WPF

Windows Presentation Foundation

ЧАСТЬ 1

4 академических часа (180 минут)

✅ Что такое WPF и зачем он нужен

✅ XAML - язык разметки

✅ Элементы управления и разметка

✅ События и привязка данных

📋 План обучения (4 часа)

09:00 - 10:30
ЧАСТЬ 1 (90 мин)
Введение в WPF, XAML, элементы управления, события
10:30 - 10:45
☕ Перерыв (15 минут)
10:45 - 12:30
ЧАСТЬ 2 (105 мин)
Привязка данных (Binding), стили, шаблоны, MVVM
12:30 - 13:00
❓ Вопросы и ответы, итоговое задание (30 минут)
💡 Формат: Теория → Примеры → Практическое задание → Разбор

🤔 Что такое WPF?

WPF (Windows Presentation Foundation) — это современная технология Microsoft для создания десктопных приложений под Windows.

WinForms (старый способ)

  • ❌ Пиксельная графика
  • ❌ Сложно сделать красивый дизайн
  • ❌ Трудно адаптировать под разные экраны

WPF (новый способ)

  • ✅ Векторная графика (масштабируется без потери качества)
  • ✅ Легко создавать красивый UI
  • ✅ Адаптивный дизайн из коробки
📅 Выход: 2006 год, но до сих пор активно используется!

⚖️ WinForms против WPF

ХарактеристикаWinFormsWPF
ГрафикаПиксельнаяВекторная (DirectX)
РазметкаКод (C#)XAML (как HTML)
ДизайнСложно кастомизироватьЛегко (стили, шаблоны)
РазделениеДизайн и логика вместеДизайн и логика разделены
АнимацияСложноПросто (встроенная)
Привязка данныхРучнаяАвтоматическая (Binding)
🎯 Вывод: WPF — это мощнее, но сложнее в изучении. WinForms — проще, но менее гибкий.

🏛️ Архитектура WPF

┌─────────────────────────────────────────┐ │ Ваше WPF Приложение │ ├─────────────────────────────────────────┤ │ XAML (дизайн) │ C# (логика) │ ├─────────────────────────────────────────┤ │ Presentation Framework │ ├─────────────────────────────────────────┤ │ WPF Core (Media) │ ├─────────────────────────────────────────┤ │ DirectX / MilCore │ └─────────────────────────────────────────┘
1
XAML — описываем внешний вид (как HTML для веба)
2
C# — пишем логику (как JavaScript)
3
WPF — связывает XAML и C# и рисует на экране через DirectX

📝 XAML - eXtensible Application Markup Language

XAML — это язык разметки для создания интерфейсов. Как HTML, только для десктопа!

HTML (веб)

<div>
    <button>Нажми</button>
</div>

XAML (WPF)

<StackPanel>
    <Button Content="Нажми"/>
</StackPanel>
💡 Главное отличие: XAML более строгий и мощный, чем HTML!

🚀 Создаем первое WPF приложение

<!-- MainWindow.xaml -->
<Window x:Class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Мое первое WPF приложение" 
        Height="450" Width="800">
    
    <Grid>
        <Button Content="Нажми меня!" 
                Width="150" Height="50" 
                Click="Button_Click"/>
    </Grid>
</Window>
// MainWindow.xaml.cs
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
    
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Привет, WPF!");
    }
}

✏️ ПРАКТИКА 1 (10 минут)

Задание: Создай новое WPF приложение, добавь кнопку с текстом "Привет" и обработчик, показывающий сообщение "Привет, мир!".

📌 Подсказка: Событие Click добавляется через атрибут Click="Имя_обработчика".

🎮 Основные элементы управления

ЭлементНазначениеПример
ButtonКнопка<Button Content="OK"/>
TextBoxВвод текста<TextBox Text="Введите имя"/>
LabelТекстовая метка<Label Content="Имя:"/>
CheckBoxФлажок<CheckBox Content="Согласен"/>
RadioButtonПереключатель<RadioButton Content="Мужской"/>
ListBoxСписок<ListBox><ListBoxItem>Пункт 1</ListBoxItem></ListBox>
ComboBoxВыпадающий список<ComboBox><ComboBoxItem>Выберите</ComboBoxItem></ComboBox>
ImageКартинка<Image Source="image.jpg"/>

📝 Пример - Форма регистрации

<Window ...>
    <StackPanel Margin="20">
        <Label Content="Имя:"/>
        <TextBox x:Name="txtName" Margin="0,0,0,10"/>
        
        <Label Content="Возраст:"/>
        <TextBox x:Name="txtAge" Margin="0,0,0,10"/>
        
        <Label Content="Пол:"/>
        <StackPanel Orientation="Horizontal">
            <RadioButton x:Name="rbMale" Content="Мужской" GroupName="Gender"/>
            <RadioButton x:Name="rbFemale" Content="Женский" GroupName="Gender"/>
        </StackPanel>
        
        <CheckBox x:Name="chkAgree" Content="Согласен с условиями" Margin="0,10,0,10"/>
        
        <Button Content="Зарегистрироваться" Click="Register_Click" 
                Background="#667eea" Foreground="White" Height="40"/>
    </StackPanel>
</Window>

✏️ ПРАКТИКА 2 (15 минут)

Создай форму регистрации с полями: Имя, Email, Пароль, CheckBox "Запомнить меня", кнопка "Войти". При нажатии покажи сообщение с введенными данными.

📐 Панели разметки (Layout)

Панели определяют, как элементы располагаются на экране.

StackPanel

<StackPanel Orientation="Vertical">
    <Button/> <Button/> <Button/>
</StackPanel>

Элементы друг за другом (по вертикали или горизонтали)

Grid

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <Button Grid.Row="0"/>
    <Button Grid.Row="1"/>
</Grid>

Таблица (строки и столбцы) - самая гибкая!

WrapPanel

<WrapPanel>
    <Button/> <Button/> <Button/>
</WrapPanel>

Перенос на новую строку если не влезают

🔲 Grid - самая важная панель

<Grid>
    <!-- Определяем строки и столбцы -->
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>     <!-- Авто (по содержимому) -->
        <RowDefinition Height="*"/>         <!-- Звездочка (пропорционально) -->
        <RowDefinition Height="50"/>        <!-- Фиксированная высота -->
    </Grid.RowDefinitions>
    
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="2*"/>
    </Grid.ColumnDefinitions>
    
    <!-- Элементы -->
    <Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" 
           Content="Заголовок"/>
    <TextBox Grid.Row="1" Grid.Column="0"/>
    <Button Grid.Row="1" Grid.Column="1" Content="OK"/>
    <StatusBar Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"/>
</Grid>
💡 Grid.ColumnSpan и Grid.RowSpan объединяют ячейки (как colspan в HTML)!

🏗️ Создаем калькулятор на Grid

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    
    <!-- Заголовок -->
    <TextBlock Grid.Row="0" Text="Калькулятор" 
               FontSize="24" HorizontalAlignment="Center"/>
    
    <!-- Кнопки (5 строк по 4 столбца) -->
    <Grid Grid.Row="1">
        <Grid.RowDefinitions>
            <RowDefinition/><RowDefinition/>
            <RowDefinition/><RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/><ColumnDefinition/>
            <ColumnDefinition/><ColumnDefinition/>
        </Grid.ColumnDefinitions>
        
        <!-- Цифры -->
        <Button Grid.Row="0" Grid.Column="0" Content="7"/>
        <Button Grid.Row="0" Grid.Column="1" Content="8"/>
        <Button Grid.Row="0" Grid.Column="2" Content="9"/>
        <Button Grid.Row="0" Grid.Column="3" Content="/"/>
        <!-- ... остальные кнопки -->
    </Grid>
    
    <!-- Результат -->
    <TextBox Grid.Row="2" Text="0" FontSize="20" Margin="10"/>
</Grid>

✏️ ПРАКТИКА 3 (20 минут)

Создай интерфейс калькулятора с помощью Grid. Добавь цифры 0-9 и операции +, -, *, /. Пока результат просто выводи в TextBox.

📌 Дополнительно: Добавь кнопку "C" для очистки.

☕

Перерыв 15 минут

Отдохните, разомнитесь, обсудите пройденный материал

✅ XAML - язык разметки

✅ Элементы управления

✅ Панели разметки (StackPanel, Grid)

✅ События и обработчики

💡 Вопросы к себе: Понимаете ли разницу между Grid и StackPanel? Умеете ли обрабатывать события?
🚀

Часть 2

Привязка данных, Стили, MVVM

ЧАСТЬ 2

Сегодня узнаем:

✅ Привязка данных (Data Binding)

✅ Стили и триггеры

✅ Шаблоны элементов

✅ MVVM паттерн

🔗 Data Binding - сердце WPF

┌──────────┐ Binding ┌──────────┐ │ Source │◄─────────►│ Target │ │ (Данные) │ │ (UI) │ └──────────┘ └──────────┘
<!-- XAML -->
<TextBox Text="{Binding UserName}" />
<Label Content="{Binding UserName}" />
// C# (ViewModel)
public class MainViewModel
{
    private string _userName;
    public string UserName
    {
        get => _userName;
        set
        {
            _userName = value;
            OnPropertyChanged(); // Оповещаем UI об изменении!
        }
    }
}
✨ Магия: Изменили данные в коде → UI обновился сам! Не нужно писать textBox1.Text = value!

📢 INotifyPropertyChanged

// Базовый класс для ViewModel
public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    
    // Упрощенный метод для установки значений
    protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
    {
        if (Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }
}

// Использование
public class PersonViewModel : ViewModelBase
{
    private string _name;
    public string Name
    {
        get => _name;
        set => SetProperty(ref _name, value);
    }
}

✏️ Практика: Привязка данных

<Window x:Class="BindingDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        Title="Binding Demo" Height="300" Width="400">
    
    <Window.DataContext>
        <local:PersonViewModel/>
    </Window.DataContext>
    
    <StackPanel Margin="20">
        <Label Content="Имя:"/>
        <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
        
        <Label Content="Возраст:"/>
        <TextBox Text="{Binding Age}"/>
        
        <Label Content="Приветствие:"/>
        <Label Content="{Binding Greeting}" FontWeight="Bold"/>
        
        <Button Content="Обновить" Command="{Binding UpdateCommand}" Margin="0,20,0,0"/>
    </StackPanel>
</Window>

✏️ ПРАКТИКА 4 (20 минут)

Создай приложение с TextBox (ввод имени) и Label (отображает "Привет, [имя]!"). При вводе текста Label обновляется автоматически.

📌 Подсказка: Используй UpdateSourceTrigger=PropertyChanged

⚡ Команды (Commands)

Вместо события Click - используем команды! Так код становится чище.

// RelayCommand - простая реализация ICommand
public class RelayCommand : ICommand
{
    private readonly Action _execute;
    private readonly Func _canExecute;
    
    public RelayCommand(Action execute, Func canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }
    
    public bool CanExecute(object parameter) => _canExecute == null || _canExecute();
    public void Execute(object parameter) => _execute();
    public event EventHandler CanExecuteChanged;
    
    public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

// Использование в ViewModel
public class MainViewModel : ViewModelBase
{
    public ICommand SaveCommand { get; }
    
    public MainViewModel()
    {
        SaveCommand = new RelayCommand(Save, CanSave);
    }
    
    private bool CanSave() => !string.IsNullOrEmpty(Name);
    private void Save() => MessageBox.Show("Сохранено!");
}

🎨 Стили (Styles) - как CSS для WPF

<!-- Определение стиля -->
<Window.Resources>
    <Style x:Key="ModernButton" TargetType="Button">
        <Setter Property="Background" Value="#667eea"/>
        <Setter Property="Foreground" Value="White"/>
        <Setter Property="FontSize" Value="16"/>
        <Setter Property="Height" Value="40"/>
        <Setter Property="Width" Value="150"/>
        <Setter Property="Cursor" Value="Hand"/>
        <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Background" Value="#764ba2"/>
            </Trigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

<!-- Применение стиля -->
<Button Style="{StaticResource ModernButton}" Content="Нажми меня!"/>
💡 Глобальные стили: Убери x:Key - стиль применится ко всем кнопкам в окне!

✏️ Практика: Стилизация

✏️ ПРАКТИКА 5 (15 минут)

Создай стили для:

  • Кнопки (синий фон, белый текст, скругленные углы, эффект при наведении)
  • TextBox (серая рамка, внутренние отступы, фокус - синяя рамка)
  • Label (жирный шрифт, цвет #333)

Примени стили к форме входа (Логин, Пароль, Кнопка "Войти").

📌 Подсказка: Используй CornerRadius для скругления углов кнопки (нужно добавить xmlns:sys="clr-namespace:System;assembly=mscorlib").

📦 Шаблоны (Templates)

Стили меняют свойства, а шаблоны полностью меняют внешний вид элемента!

<!-- Шаблон для кнопки -->
<Style TargetType="Button">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Border Background="{TemplateBinding Background}"
                        CornerRadius="10"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="1">
                    <ContentPresenter HorizontalAlignment="Center" 
                                      VerticalAlignment="Center"/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<!-- Шаблон для ListBoxItem -->
<DataTemplate x:Key="PersonTemplate">
    <StackPanel Orientation="Horizontal">
        <Ellipse Width="40" Height="40" Fill="{Binding AvatarColor}"/>
        <StackPanel>
            <TextBlock Text="{Binding Name}" FontWeight="Bold"/>
            <TextBlock Text="{Binding Age}" FontSize="10"/>
        </StackPanel>
    </StackPanel>
</DataTemplate>

🏗️ MVVM - стандарт для WPF

┌──────────┐ Binding ┌──────────────┐ ┌─────────┐ │ VIEW │◄─────────►│ VIEWMODEL │◄────┤ MODEL │ │ (XAML) │ │ (Логика UI) │ │(Данные) │ └──────────┘ └──────────────┘ └─────────┘ ▲ ▲ │ │ └─────────────────────────┘ INotifyPropertyChanged
1
Model — данные и бизнес-логика (User, Product, Order)
2
ViewModel — подготавливает данные для отображения, содержит команды
3
View — XAML с Binding
🎯 Главное правило: View не знает о Model! Только через ViewModel.

📝 Полный пример MVVM

Model:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

ViewModel:

public class MainViewModel : ViewModelBase
{
    private ObservableCollection<Person> _people;
    private Person _selectedPerson;
    
    public MainViewModel()
    {
        LoadCommand = new RelayCommand(LoadPeople);
        AddCommand = new RelayCommand(AddPerson);
        DeleteCommand = new RelayCommand(DeletePerson, CanDelete);
        
        _people = new ObservableCollection<Person>();
    }
    
    public ObservableCollection<Person> People
    {
        get => _people;
        set => SetProperty(ref _people, value);
    }
    
    public Person SelectedPerson
    {
        get => _selectedPerson;
        set => SetProperty(ref _selectedPerson, value);
    }
    
    public ICommand LoadCommand { get; }
    public ICommand AddCommand { get; }
    public ICommand DeleteCommand { get; }
    
    private void LoadPeople()
    {
        People.Clear();
        foreach(var p in Database.GetPeople())
            People.Add(p);
    }
}

📄 XAML для MVVM примера

<Window x:Class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        Title="MVVM Demo" Height="450" Width="600">
    
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        
        <!-- Список людей -->
        <DataGrid Grid.Row="0" 
                  ItemsSource="{Binding People}"
                  SelectedItem="{Binding SelectedPerson}"
                  AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Header="ID" Binding="{Binding Id}" Width="50"/>
                <DataGridTextColumn Header="Имя" Binding="{Binding Name}" Width="150"/>
                <DataGridTextColumn Header="Возраст" Binding="{Binding Age}" Width="100"/>
            </DataGrid.Columns>
        </DataGrid>
        
        <!-- Кнопки управления -->
        <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,10,0,0">
            <Button Content="Загрузить" Command="{Binding LoadCommand}" 
                    Width="100" Margin="5" Padding="8"/>
            <Button Content="Добавить" Command="{Binding AddCommand}" 
                    Width="100" Margin="5" Padding="8"/>
            <Button Content="Удалить" Command="{Binding DeleteCommand}" 
                    Width="100" Margin="5" Padding="8"/>
        </StackPanel>
    </Grid>
</Window>

🏆 ИТОГОВОЕ ЗАДАНИЕ (30 минут)

🎯 Создай "Менеджер задач" (To-Do List) на WPF

Требования:

  • ✅ Использовать MVVM паттерн
  • ✅ TextBox для ввода новой задачи
  • ✅ Кнопка "Добавить" (Команда)
  • ✅ ListBox или ListView для отображения задач
  • ✅ CheckBox для отметки "Выполнено"
  • ✅ Кнопка "Удалить" для каждой задачи
  • ✅ Счетчик "Выполнено / Всего"
  • ✅ Красивый дизайн (стили)
📋 Модель TaskItem (Id, Title, IsCompleted)
📋 ViewModel (ObservableCollection, Commands)
📋 DataTemplate для отображения задачи с CheckBox и кнопкой удаления
📋 Binding для всех элементов
📋 Стили для кнопок, TextBox, ListBox

💡 Подсказка для итогового задания

// TaskItem.cs (Model)
public class TaskItem : ViewModelBase
{
    private bool _isCompleted;
    public int Id { get; set; }
    public string Title { get; set; }
    public bool IsCompleted
    {
        get => _isCompleted;
        set => SetProperty(ref _isCompleted, value);
    }
}

// MainViewModel.cs
public class MainViewModel : ViewModelBase
{
    private ObservableCollection<TaskItem> _tasks;
    private string _newTaskTitle;
    
    public ObservableCollection<TaskItem> Tasks { get; set; }
    public string NewTaskTitle { get; set; }
    public ICommand AddTaskCommand { get; }
    public ICommand DeleteTaskCommand { get; }
    
    public int CompletedCount => Tasks.Count(t => t.IsCompleted);
    public int TotalCount => Tasks.Count;
}
💡 Используй ObservableCollection вместо List - она автоматически обновляет UI при добавлении/удалении!

🚀 Что изучать дальше?

Продвинутые темы WPF

  • 🎨 Анимации и Storyboard
  • 🎭 Конвертеры (IValueConverter)
  • 📌 Прикрепленные свойства
  • 🧩 Пользовательские контролы
  • 📦 Ресурсы и словари ресурсов
  • 🗂️ Навигация и окна

Современные альтернативы

  • 🪟 MAUI — кроссплатформенный наследник
  • 🦊 Avalonia UI — WPF для Linux/macOS
  • ⚡ WinUI 3 — современный Win32
  • 🌐 Blazor Hybrid — веб-технологии в десктопе
📚 Ресурсы: Microsoft Docs, YouTube (канал "WPF для начинающих"), GitHub примеры
🎉

Поздравляю!

Вы освоили основы WPF

Что вы узнали за 4 часа:

  • ✅ Что такое WPF и чем он лучше WinForms
  • ✅ XAML язык разметки
  • ✅ Элементы управления и панели (Grid, StackPanel)
  • ✅ Data Binding и INotifyPropertyChanged
  • ✅ Команды (ICommand)
  • ✅ Стилизация интерфейса
  • ✅ MVVM паттерн
  • ✅ Создание To-Do приложения
💪 Практикуйтесь! Лучший способ закрепить материал — создавать свои проекты!

🙏 Спасибо за внимание! Вопросы?