티스토리 뷰

WPF .NET

Kiosk 만들기 - Part8

kaki104 2023. 11. 1. 10:00
반응형

관리자 로그인 화면 작업 합니다.

하나의 윈도우에서 왼쪽이 고객이 메뉴를 선택하는 화면이고, 오른쪽이 기계 뒷쪽에 표시되는 관리자 로그인 화면입니다.

모니터의 해상도를 어떻게 처리하는 것인지는 정확하게 모르지만, 이렇게 사용한다고 하네요

1. 관리자 로그인 작업

ManagerLogin.xaml

<UserControl
    x:Class="PrismKiosk.Views.ManagerLogin"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
    xmlns:behaviors="clr-namespace:PrismKiosk.Behaviors"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:PrismKiosk.Views"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:prism="http://prismlibrary.com/"
    d:DesignHeight="450"
    d:DesignWidth="800"
    prism:ViewModelLocator.AutoWireViewModel="True"
    FontSize="20"
    mc:Ignorable="d">
    <Border>
        <Grid
            Width="400"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Background="LightGray">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <TextBlock Margin="10" Text="관리자 로그인" />
            <TextBlock
                Grid.Row="1"
                Margin="10"
                Text="Id" />
            <TextBlock
                Grid.Row="2"
                Margin="10"
                Text="Password" />
            <TextBox
                Grid.Row="1"
                Grid.Column="1"
                Margin="0,0,10,0"
                Padding="10,10,0,0"
                Text="{Binding Id, Mode=TwoWay}" />
            <PasswordBox
                Grid.Row="2"
                Grid.Column="1"
                Margin="0,0,10,0"
                Padding="10,10,0,0">
                <b:Interaction.Behaviors>
                    <behaviors:PasswordBehavior BindingPassword="{Binding Password, Mode=TwoWay}" />
                </b:Interaction.Behaviors>
            </PasswordBox>
            <Button
                Grid.Row="3"
                Grid.ColumnSpan="2"
                Margin="10"
                Command="{Binding LoginCommand}"
                Content="Login" />
        </Grid>
    </Border>
</UserControl>

* 아이디와 패스워드를 뷰모델에 전달해서 확인 후에 처리합니다.

* PasswordBox의 Password 프로퍼티는 Binding을 사용할 수 없습니다. 왜냐하면, 비밀번호를 바인딩으로 받아서 사용하게되면, 메모리에 비밀번호가 노출되기 때문입니다. 그래서, SecureString을 이용하는 것을 권장합니다.

자세한 PasswordBox에 대한 설명은 여기를 참고하시기 바랍니다.

* 이 포스트에서는 간단하게 PasswordBehavior를 만들어서 바인딩을 할 수 있도록 만들었습니다.

PasswordBehavior.cs

using Microsoft.Xaml.Behaviors;
using System.Windows;
using System.Windows.Controls;

namespace PrismKiosk.Behaviors
{
    /// <summary>
    /// PasswordBox Behavior
    /// </summary>
    public class PasswordBehavior : Behavior<PasswordBox>
    {
        private bool _isWork;

        protected override void OnAttached()
        {
            AssociatedObject.PasswordChanged += AssociatedObject_PasswordChanged;
        }

        private void AssociatedObject_PasswordChanged(object sender, System.Windows.RoutedEventArgs e)
        {
            if (_isWork)
            {
                return;
            }
            _isWork = true;
            BindingPassword = AssociatedObject.Password;
            _isWork = false;
        }
        protected override void OnDetaching()
        {
            AssociatedObject.PasswordChanged -= AssociatedObject_PasswordChanged;
        }

        public string BindingPassword
        {
            get { return (string)GetValue(BindingPasswordProperty); }
            set { SetValue(BindingPasswordProperty, value); }
        }

        /// <summary>
        /// 바인딩 패스워드
        /// </summary>
        public static readonly DependencyProperty BindingPasswordProperty =
            DependencyProperty.Register(nameof(BindingPassword), typeof(string), typeof(PasswordBehavior), new PropertyMetadata(null));
        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            switch (e.Property.Name)
            {
                case nameof(BindingPassword):
                    if (_isWork)
                    {
                        return;
                    }
                    _isWork = true;
                    AssociatedObject.Password = BindingPassword;
                    _isWork = false;
                    break;
            }
            base.OnPropertyChanged(e);
        }
    }
}

* 패스워드를 바인딩 하기 위해서 DependencyProperty를 추가했습니다.

* 패스워드가 뷰모델로 전달되는 과정

가. 사용자가 PasswordBox에 비밀번호 입력

나. PasswordChanged 이벤트 발생 > AssociatedObject_PasswordChanged 메서드 실행

다. _isWork가 true인지 확인하고 false이면 true로 변경

라. BindingPassword에 변경된 비밀번호를 입력

마. ViewModel의 Password 프로퍼티로 값 전달

위의 과정에서 _isWork는 BindingPassword에 값이 변경되었을 때 그 값을 다시 AssociatedObject.Password에 입력하는 것을 방지하는 용도로 사용됩니다.

물론 반대로 ViewModel의 Password 프로퍼티에 값을 빈 문자열로 입력하면, 빈 문자열을 AssociatedObject.Password에 입력하고, 이로 인해 발생되는 이벤트를 무시하는 용도로 사용됩니다.

프로퍼티 체인지 이벤트를 이용해서 프로그램을 할 때 연속적으로 발생하는 이벤트를 신경쓰지 않으면 무한루프에 빠져서 스택오버플로우 에러가 발생합니다.
DependencyProperty를 만들때 propdp를 입력하고 Tab키를 두번 눌러서 생성하는 방법을 사용합니다.
AttachedProperty를 만들때는 propa를 입력하고 Tab키를 두번 눌러 줍니다.

ManagerLoginViewModel.cs

using Prism.Commands;
using Prism.Ioc;
using System.Windows.Input;

namespace PrismKiosk.ViewModels
{
    /// <summary>
    /// 관리자 로그인 뷰모델
    /// </summary>
    public class ManagerLoginViewModel : ViewModelBase
    {
        private string _id;
        /// <summary>
        /// 로그인 아이디
        /// </summary>
        public string Id
        {
            get { return _id; }
            set { SetProperty(ref _id, value); }
        }
        private string _password;
        /// <summary>
        /// 비밀번호 - 비밀번호를 문자열로 가지고 있는 것은 좋은 방법은 아닙니다.
        /// </summary>
        public string Password
        {
            get { return _password; }
            set { SetProperty(ref _password, value); }
        }
        /// <summary>
        /// 로그인 커맨드
        /// </summary>
        public ICommand LoginCommand { get; set; }

        public ManagerLoginViewModel()
        {
        }
        public ManagerLoginViewModel(IContainerProvider containerProvider) : base(containerProvider)
        {
            Init();
        }

        private void Init()
        {
            LoginCommand = new DelegateCommand(OnLogin);
        }

        private void OnLogin()
        {
            if (Id?.ToLower() != "admin" || Password != "password")
            {
                return;
            }
            RegionManager.RequestNavigate("ManagerContentRegion", "Deadline");
        }
    }
}

* 관리자가 Login 버튼을 클릭하면 OnLogin() 메서드에서 Id와 Password를 확인해서 마감 화면으로 이동 시켜 줍니다.

로그인이 완료되었을 때 AppContext.IsLogin = true를 넣는 부분은 나중에 다루도록 하겠습니다.
Id.ToLower()에서 Id?.ToLower()로 수정했습니다. 이 수정 사항은 part9 이후에 반영되어 있지 않을 수 있습니다.

2. 마감화면을 위해 결재 완료 데이터 보관

PaymentViewModel.cs

/// <summary>
/// 결제 뷰모델
/// </summary>
public class PaymentViewModel : ViewModelBase
{
    /// <summary>
    /// 결제 완료 커맨드
    /// </summary>
    public ICommand CompleteCommand { get; set; }
    /// <summary>
    /// 기본 생성자
    /// </summary>
    public PaymentViewModel()
    {
    }
    /// <summary>
    /// 런타임 생성자
    /// </summary>
    /// <param name="containerProvider"></param>
    public PaymentViewModel(IContainerProvider containerProvider) : base(containerProvider)
    {
        Init();
    }

    private void Init()
    {
        CompleteCommand = new DelegateCommand(OnComplete);
    }
    /// <summary>
    /// 결제 완료
    /// </summary>
    private void OnComplete()
    {
        //결제 완료 일시 입력
        AppContext.CurrentOrder.OrderDatetime = DateTime.Now;
        //결제 완료 목록에 추가
        AppContext.Orders.Add(AppContext.CurrentOrder);
        AppContext.CurrentOrder = null;
        //처음 화면으로 이동
        ClearAppContextAndGoHome();
    }
}

* Payment.xaml에서 결제 완료 버튼을 추가해서, 버튼 클릭시 OnComplete 메서드에서 몇가지 추가사항을 입력하고, AppContext.Orders에 데이터를 추가합니다.

여기서 설명하지 않은 추가 사항은 소스를 참고하시기 바랍니다.

3. 소스

kaki104/PrismKiosk at Part8/edit-managerlogin (github.com)

 

GitHub - kaki104/PrismKiosk

Contribute to kaki104/PrismKiosk development by creating an account on GitHub.

github.com

 

반응형

'WPF .NET' 카테고리의 다른 글

CommunityToolkit.Mvvm - LayerPopup 빈 공간 클릭으로 닫기  (4) 2024.01.29
Kiosk 만들기 - Part9  (0) 2023.11.07
Kiosk 만들기 - Part7  (0) 2023.10.30
Kiosk 만들기 - Part6  (0) 2023.10.27
Kiosk 만들기 - Part5  (0) 2023.10.25
댓글