Добавлены основные классы для MVVM, частично реализована страница входа.

This commit is contained in:
mikhail "synzr" 2025-11-20 21:08:33 +05:00
parent a0e66de850
commit afabd343f5
8 changed files with 218 additions and 25 deletions

View file

@ -18,6 +18,7 @@ using SimpleInjector;
using Intersvyaz.Views;
using Intersvyaz.Net;
using Intersvyaz.Core.Services;
using Intersvyaz.ViewModels;
namespace Intersvyaz
{
@ -121,6 +122,7 @@ namespace Intersvyaz
var container = new Container();
container.Register<IntersvyazClient>(Lifestyle.Singleton);
container.Register<ISessionService, SessionService>(Lifestyle.Transient);
container.Register<LoginViewModel>(Lifestyle.Transient);
return container;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View file

@ -0,0 +1,41 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace Intersvyaz.Common
{
public class BindableBase : INotifyPropertyChanged
{
/// <summary>
/// Событие об изменении значения свойства.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Установка значения в заднее поля свойства с вызовом события.
/// </summary>
/// <typeparam name="T">Тип значения.</typeparam>
/// <param name="back">Заднее поле свойства.</param>
/// <param name="value">Новое значение.</param>
/// <param name="propertyName">Имя свойства. (Необязательный параметр)</param>
/// <remarks>При компилиции, имя свойства будет автоматически заполнено.</remarks>
protected void SetProperty<T>(ref T back, T value, [CallerMemberName] string propertyName = null)
{
if (!Equals(back, value))
{
back = value;
OnPropertyChanged(propertyName);
}
}
/// <summary>
/// Вызов события об изменении
/// </summary>
/// <param name="propertyName">Имя свойства. (Необязательный параметр)</param>
/// <remarks>При компилиции, имя свойства будет автоматически заполнено.</remarks>
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View file

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Intersvyaz.Common
{
public class RelayCommand : ICommand
{
/// <summary>
/// Действие команды.
/// </summary>
private readonly Action<object> _execute;
/// <summary>
/// Действие проверки «можно ли исполнить команду?».
/// </summary>
private readonly Predicate<object> _canExecute = null;
/// <inheritdoc />
public event EventHandler CanExecuteChanged;
public RelayCommand(Action<object> execute)
{
_execute = execute;
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
/// <inheritdoc />
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
/// <inheritdoc />
public void Execute(object parameter)
{
_execute(parameter);
}
/// <summary>
/// Вызов события изменения состояния.
/// </summary>
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
}

View file

@ -119,7 +119,10 @@
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="Common\BindableBase.cs" />
<Compile Include="Common\RelayCommand.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ViewModels\LoginViewModel.cs" />
<Compile Include="Views\LoginPage.xaml.cs">
<DependentUpon>LoginPage.xaml</DependentUpon>
</Compile>
@ -131,6 +134,9 @@
<None Include="Intersvyaz_TemporaryKey.pfx" />
</ItemGroup>
<ItemGroup>
<Content Include="Assets\IntersvyazLogo.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Properties\Default.rd.xml" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\SplashScreen.scale-200.png" />

View file

@ -0,0 +1,75 @@
using Intersvyaz.Common;
using Intersvyaz.Core.Services;
using System.Diagnostics;
namespace Intersvyaz.ViewModels
{
public class LoginViewModel : BindableBase
{
/// <summary>
/// Сервис сессии пользователя.
/// </summary>
private readonly ISessionService _sessionService;
/// <summary>
/// Заднее поле имени пользователя.
/// </summary>
private string _username;
/// <summary>
/// Заднее поле пароля пользователя.
/// </summary>
private string _password;
/// <summary>
/// Имя пользователя.
/// </summary>
public string Username
{
get => _username;
set
{
SetProperty(ref _username, value);
LoginCommand.RaiseCanExecuteChanged();
}
}
/// <summary>
/// Пароль пользователя.
/// </summary>
public string Password
{
get => _password;
set
{
SetProperty(ref _password, value);
LoginCommand.RaiseCanExecuteChanged();
}
}
/// <summary>
/// Команда входа.
/// </summary>
public RelayCommand LoginCommand { get; }
public LoginViewModel(ISessionService sessionService)
{
_sessionService = sessionService;
LoginCommand = new RelayCommand(ExecuteLoginCommand, CanExecuteLoginCommand);
Username = Password = string.Empty;
}
private async void ExecuteLoginCommand(object _)
{
var sessionData = await _sessionService.GetSessionAsync(Username, Password);
// TODO: Обработка данных об сессии
}
private bool CanExecuteLoginCommand(object _)
{
return !string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password);
}
}
}

View file

@ -6,9 +6,37 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
DataContext="{x:Bind ViewModel}">
<Grid>
<TextBlock Text="LoginPage" />
<Grid VerticalAlignment="Center"
Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Source="ms-appx:///Assets/IntersvyazLogo.png"
VerticalAlignment="Center"
Margin="0,0,0,10"
Height="64"
Grid.Row="0" />
<TextBox PlaceholderText="логин"
Text="{Binding Username,Mode=TwoWay}"
Margin="0,0,0,10"
Grid.Row="1" />
<PasswordBox PlaceholderText="пароль"
Password="{Binding Password,Mode=TwoWay}"
Grid.Row="2" />
<Button Content="войти"
Command="{Binding LoginCommand}"
HorizontalAlignment="Stretch"
Margin="0,10,0,0"
Grid.Row="3" />
</Grid>
</Page>

View file

@ -1,34 +1,19 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Intersvyaz.Core.Services;
using Windows.UI.Xaml.Controls;
using Intersvyaz.ViewModels;
namespace Intersvyaz.Views
{
/// <summary>
/// Страница входа.
/// </summary>
public sealed partial class LoginPage : Page
{
private readonly ISessionService _sessionService;
/// <summary>
/// Модель представления страницы входа.
/// </summary>
public LoginViewModel ViewModel { get; }
public LoginPage()
{
InitializeComponent();
// NOTE: Получение сервисов
_sessionService = App.Current.Services.GetInstance<ISessionService>();
ViewModel = App.Current.Services.GetInstance<LoginViewModel>();
}
}
}