티스토리 뷰

반응형

리플로 문의 주신 내용이 있어서 추가 작업을 진행했습니다.

빈공간이나 OK를 클릭하면 팝업이 닫히도록 수정했습니다.

1. MainViewModel.cs

이 작업의 핵심은 CloseLayerPopupCommand를 호출하고, 클릭한 위치가 빈공간인지 확인해서 빈공간일 때만 닫아주는 것입니다.

  • public ICommand CloseLayerPopupCommand { get; set; }
    • 커맨드를 추가합니다.
  • CloseLayerPopupCommand = new RelayCommand<object>(OnCloseLayerPopup);
    • 커맨드를 생성할 때 CommandParameter를 받을 수 있도록 만들어 줍니다.
  • OnCloseLayerPopup 메서드
    • f (sender is not MouseButtonEventArgs args
                      || args.OriginalSource is not Border border
                      || border.Name != "LayerPopupBorder")
      • 이벤트 발생시 생성된 EventArgs를 분석해서, LayerPopupBorder라는 이름의 border에서 발생한 이벤트가 아니면 무시합니다.
    • OnLayerPopupMessage(null, new LayerPopupMessage(false));
      • 레이어 팝업을 닫기 위해서 호출합니다.
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Input;
using WpfFramework.Bases;
using WpfFramework.Models;

namespace WpfFramework.ViewModels
{
    /// <summary>
    /// 메인 뷰모델 클래스
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        /// <summary>
        /// Busy 목록
        /// </summary>
        private readonly IList<BusyMessage> _busys = new List<BusyMessage>();

        private string _navigationSource;
        /// <summary>
        /// 네비게이션 소스
        /// </summary>
        public string NavigationSource
        {
            get => _navigationSource;
            set => SetProperty(ref _navigationSource, value);
        }
        /// <summary>
        /// 네비게이트 커맨드
        /// </summary>
        public ICommand NavigateCommand { get; set; }

        private bool _isBusy;
        /// <summary>
        /// IsBusy
        /// </summary>
        public bool IsBusy
        {
            get => _isBusy;
            set => SetProperty(ref _isBusy, value);
        }

        private bool _showLayerPopup;
        /// <summary>
        /// 레이어 팝업 출력여부
        /// </summary>
        public bool ShowLayerPopup
        {
            get => _showLayerPopup;
            set => SetProperty(ref _showLayerPopup, value);
        }

        private string _controlName;
        /// <summary>
        /// 레이어 팝업 내부 컨트롤 이름
        /// </summary>
        public string ControlName
        {
            get => _controlName;
            set => SetProperty(ref _controlName, value);
        }
        private object _controlParameter;
        /// <summary>
        /// 컨트롤 파라메터
        /// </summary>
        public object ControlParameter
        {
            get => _controlParameter;
            set => SetProperty(ref _controlParameter, value);
        }
        /// <summary>
        /// 레이어 팝업 닫기 커맨드
        /// </summary>
        public ICommand CloseLayerPopupCommand { get; set; }
        /// <summary>
        /// 생성자
        /// </summary>
        public MainViewModel()
        {
            Title = "Main View";
            Init();
        }

        private void Init()
        {
            //시작 페이지 설정
            NavigationSource = "Views/LoginPage.xaml";
            NavigateCommand = new RelayCommand<string>(OnNavigate);
            CloseLayerPopupCommand = new RelayCommand<object>(OnCloseLayerPopup);
            //네비게이션 메시지 수신 등록
            WeakReferenceMessenger.Default.Register<NavigationMessage>(this, OnNavigationMessage);
            //BusyMessage 수신 등록
            WeakReferenceMessenger.Default.Register<BusyMessage>(this, OnBusyMessage);
            //LayerPopupMessage 수신 등록
            WeakReferenceMessenger.Default.Register<LayerPopupMessage>(this, OnLayerPopupMessage);
        }
        /// <summary>
        /// 레이어 팝업 닫기
        /// </summary>
        private void OnCloseLayerPopup(object sender)
        {
            //sender는 마우스 다운 이벤트 아규먼트가 들어오고, 클릭한 위치가 보더가 아니거나
            //보더의 이름이 지정한 이름과 다르면 닫지 않음
            if (sender is not MouseButtonEventArgs args
                || args.OriginalSource is not Border border
                || border.Name != "LayerPopupBorder")
            {
                return;
            }
            OnLayerPopupMessage(null, new LayerPopupMessage(false));
        }

        private void OnLayerPopupMessage(object recipient, LayerPopupMessage message)
        {
            ShowLayerPopup = message.Value;
            //순서에 주의
            ControlParameter = message.Parameter;
            ControlName = message.ControlName;
        }

        /// <summary>
        /// 비지 메시지 수신 처리
        /// </summary>
        /// <param name="recipient"></param>
        /// <param name="message"></param>
        private void OnBusyMessage(object recipient, BusyMessage message)
        {
            if (message.Value)
            {
                BusyMessage existBusy = _busys.FirstOrDefault(b => b.BusyId == message.BusyId);
                if (existBusy != null)
                {
                    //이미 추가된 녀석이기 때문에 추가하지 않음
                    return;
                }
                _busys.Add(message);
            }
            else
            {
                BusyMessage existBusy = _busys.FirstOrDefault(b => b.BusyId == message.BusyId);
                if (existBusy == null)
                {
                    //없기 때문에 나감
                    return;
                }
                _ = _busys.Remove(existBusy);
            }
            //_busys에 아이템이 있으면 true, 없으면 false
            IsBusy = _busys.Any();
        }

        /// <summary>
        /// 네비게이션 메시지 수신 처리
        /// </summary>
        /// <param name="recipient"></param>
        /// <param name="message"></param>
        private void OnNavigationMessage(object recipient, NavigationMessage message)
        {
            NavigationSource = message.Value;
        }

        private void OnNavigate(string pageUri)
        {
            NavigationSource = pageUri;
        }
    }
}

2. MainWindow.xaml

CloseLayerPopupCommand는 Border에 EventTrigger를 추가하고, MouseLeftButtonDown 이벤트가 발생했을 때 실행합니다.

  • <b:InvokeCommandAction Command="{Binding CloseLayerPopupCommand}" PassEventArgsToCommand="True" />
    • InvokeCommandAction에는 이벤트의 아규먼트를 CommandParameter로 전달하도록 해주는 프로퍼티가 있습니다. 그 프로퍼티를 True로 설정하면 됩니다.

여기까지 진행하면 딤처리된 보더를 클릭하면 커맨드가 실행되는 것을 확인 할 수 있습니다.

<!--  클릭한 위치가 LayerPopupBorder인 경우에만 닫기 위해서 이름 추가  -->
<Border
    x:Name="LayerPopupBorder"
    Background="#66000000"
    Visibility="{Binding ShowLayerPopup, Converter={StaticResource BoolToVisibilityConverter}}">
    <b:Interaction.Triggers>
        <!--  보더에 마우스 다운 이벤트를 트리거로 걸어서 커맨드 실행  -->
        <b:EventTrigger EventName="MouseLeftButtonDown">
            <!--  MouseLeftButtonDown 이벤트가 발생할 때 생성된 아규먼트를 CommandParameter로 전달  -->
            <b:InvokeCommandAction Command="{Binding CloseLayerPopupCommand}" PassEventArgsToCommand="True" />
        </b:EventTrigger>
    </b:Interaction.Triggers>
    <Border
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Background="White"
        CornerRadius="5">
        <Border.Effect>
            <DropShadowEffect />
        </Border.Effect>
        <ContentControl Width="400" MinHeight="200">
            <b:Interaction.Behaviors>
                <behaviors:ContentControlBehavior
                    ControlName="{Binding ControlName}"
                    ControlParameter="{Binding ControlParameter}"
                    ShowLayerPopup="{Binding ShowLayerPopup}" />
            </b:Interaction.Behaviors>
        </ContentControl>
    </Border>
</Border>

3. AboutControl.xaml

그런데 여기서 문제가 기존에 팝업 출력시 사용했던 컨트롤의 Background 컬러가 지정되어 있지 않으면, 팝업에서 클릭시에도 팝업 닫기 커맨드가 실행이 된다는 점입니다.

이 문제는 Grid의 Background 컬러를 지정하는 것으로 간단하게 해결할 수 있습니다.

4. 소스

kaki104/WpfFramework at part11/edit-close-layerpopup (github.com)

 

GitHub - kaki104/WpfFramework

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

github.com

 

반응형

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

ViewModel을 싱글톤으로 만들어서 계속 사용하기  (0) 2024.01.30
Kiosk 만들기 - Part9  (0) 2023.11.07
Kiosk 만들기 - Part8  (2) 2023.11.01
Kiosk 만들기 - Part7  (0) 2023.10.30
Kiosk 만들기 - Part6  (0) 2023.10.27
댓글