[WPF] WPF DevExpress.ReactiveUI 실습

목적

  • WPF DevExpress.ReactiveUI을 학습하고 실습합니다.

DevExpress Layout

LayoutControl

  • 자동 Layout 정렬, 사용자 정의 Control 정렬, 크기 제한 설정, 기본 제공 그룹 및 탭 그룹 지원
  • 런타임 사용자 정의 등과 같은 여러 Layout 구성 기능을 사용하여 일관된 Layout의 컨트롤을 만들 수 있다.

LayoutGroup

  • LayoutControl의 LayoutItem 및 기타 그룹의 컨트이너를 나타낸다.
  • LayoutGroup에는 선택 사항으로 숨길 수 있는 테두리와 캡션이 있다.
  • 사용자 정의 모드에서 헤더를 기준으로 그룹을 드래그하면 해당 내용도 드래그된다.

LayoutItem

  • LayoutItem에 속하는 Control의 경우 크기, 도킹 스타일 및 위치를 지정할 수 없다.
  • 이 모든 설정은 레이아웃 항목에 의해 전체적으로 관리된다.

요구사항

  • 기본 요구사항은 프로젝트 2와 동일합니다.
  • 추가 요구사항 : 마우스가 움직이면 마우스 좌표 출력

실습

Model

  • Person.cs
using ReactiveUI;

namespace MVVM
{
    public class Person : ReactiveObject
    {
        private string _name;
        public string Name
        {
            get { return _name; }
            set { this.RaiseAndSetIfChanged(ref _name, value); }
        }

        private bool _gender;

        public bool Gender
        {
            get { return _gender; }
            set { this.RaiseAndSetIfChanged(ref _gender, value); }
        }

        private string _phoneNumber;

        public string PhoneNumber
        {
            get { return _phoneNumber; }
            set { this.RaiseAndSetIfChanged(ref _phoneNumber, value); }
        }

        private string _address;
        public string Address
        {
            get { return _address; }
            set { this.RaiseAndSetIfChanged(ref _address, value); }

        }

        public Person()
        {

        }

        public Person(Person input)
        {
            _address = input.Address;
            _name = input.Name;
            _phoneNumber = input.PhoneNumber;
            _gender = input.Gender;
        }
    }
}

View

  • AddView.xaml
<dx:DXWindow x:Class="MVVM.AddView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MVVM"
        xmlns:dxlc="http://schemas.devexpress.com/winfx/2008/xaml/layoutcontrol"
        xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
        mc:Ignorable="d" Name="AddViewParam"
        d:DataContext="{d:DesignInstance local:AddView}"
        Title="{Binding Path=Caption,
                        UpdateSourceTrigger=PropertyChanged, 
                        Mode=TwoWay}" 
        Height="300" Width="300"
        WindowStartupLocation="CenterScreen"
        WindowStyle="ToolWindow">
    <dxlc:LayoutControl Orientation="Vertical" Padding="4">
        <dxlc:LayoutGroup Orientation="Vertical">
            <dxlc:LayoutItem Label="이름" VerticalAlignment="Top">
                <TextBox Text="{Binding Path=PersonData.Name, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
            </dxlc:LayoutItem>
            <dxlc:LayoutItem Label="성별" VerticalAlignment="Top">
                <TextBox Text="{Binding Path=PersonData.Gender, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
            </dxlc:LayoutItem>
            <dxlc:LayoutItem Label="전화번호" VerticalAlignment="Top">
                <TextBox Text="{Binding Path=PersonData.PhoneNumber, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
            </dxlc:LayoutItem>
            <dxlc:LayoutItem Label="주소" VerticalAlignment="Stretch">
                <TextBox Text="{Binding Path=PersonData.Address, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
            </dxlc:LayoutItem>
        </dxlc:LayoutGroup>

        <dxlc:LayoutGroup VerticalAlignment="Bottom" HorizontalAlignment="Right"
                          Header="20">
            <Button Content="확인"
                    Command="{Binding Path=OkCommand}"
                    CommandParameter="{Binding ElementName=AddViewParam}"
                    Width="75"/>
            <Button Content="취소"
                    Command="{Binding Path=CancelCommand}"
                    CommandParameter="{Binding ElementName=AddViewParam}"
                    Width="75"/>
        </dxlc:LayoutGroup>
    </dxlc:LayoutControl>
</dx:DXWindow>
  • AddView.xaml.cs
using DevExpress.Xpf.Core;

namespace MVVM
{
    /// <summary>
    /// AddView.xaml에 대한 상호 작용 논리
    /// </summary>
    public partial class AddView : DXWindow, IDialogView
    {
        public AddView(AddViewModel viewModel)
        {
            InitializeComponent();
            this.DataContext = viewModel;
        }
    }
}
  • MainView.xaml
<dx:DXWindow x:Class="MVVM.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:dxlc="http://schemas.devexpress.com/winfx/2008/xaml/layoutcontrol"
        xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
        xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"
        xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
        xmlns:local="clr-namespace:MVVM"
        xmlns:cv="clr-namespace:MVVM.Converter"
        mc:Ignorable="d"  
        d:DataContext="{d:DesignInstance local:MainViewModel}"
        Title="주소록 관리 V0.1" Height="328" Width="570" 
        WindowStartupLocation="CenterScreen" Name="MainViewParam">

    <dxlc:LayoutControl Orientation="Vertical" Padding="4">
        <dxg:GridControl EnableSmartColumnsGeneration="True"
                         Name="PersonParam"
                         ItemsSource="{Binding Path=Persons,
                                               UpdateSourceTrigger=PropertyChanged,
                                               Mode=OneWay}">
            <dxmvvm:Interaction.Behaviors>
                <dxmvvm:EventToCommand EventName="MouseDoubleClick"
                                       Command="{Binding Path=ModifyCommand}"
                                       CommandParameter="{Binding ElementName=PersonParam,
                                                                  Path=SelectedItem}"/>
                <dxmvvm:EventToCommand EventName="MouseMove"
                                       Command="{Binding Path=MouseMoveCommand}"
                                       PassEventArgsToCommand="True">
                    <dxmvvm:EventToCommand.EventArgsConverter>
                        <cv:MouseEventConverter/>
                    </dxmvvm:EventToCommand.EventArgsConverter>
                </dxmvvm:EventToCommand>
            </dxmvvm:Interaction.Behaviors>

            <dxg:GridControl.Columns>
                <dxg:GridColumn Header="이름" FieldName="Name" ReadOnly="True"
                                HorizontalHeaderContentAlignment="Center"/>
                <dxg:GridColumn Header="성별" FieldName="Gender" ReadOnly="True"
                                HorizontalHeaderContentAlignment="Center"/>
                <dxg:GridColumn Header="전화번호" FieldName="PhoneNumber" ReadOnly="True"
                                HorizontalHeaderContentAlignment="Center"/>
                <dxg:GridColumn Header="주소" FieldName="Address" ReadOnly="True"
                                HorizontalHeaderContentAlignment="Center"/>
            </dxg:GridControl.Columns>

            <dxg:GridControl.View>
                <dxg:TableView AllowPerPixelScrolling="False"
                               AllowHorizontalScrollingVirtualization="False"
                               AllowCascadeUpdate="False"
                               AllowEditing="False"
                               ShowAutoFilterRow="False"
                               ShowTotalSummary="False"
                               ShowGroupPanel="False"
                               UseAnimationWhenExpanding="False"
                               NewItemRowPosition="None"
                               ScrollingMode="Smart"
                               AutoWidth="True"
                               NavigationStyle="Row"
                               FadeSelectionOnLostFocus="False"/>
            </dxg:GridControl.View>

        </dxg:GridControl>

        <dxlc:LayoutGroup VerticalAlignment="Bottom" Header="24">
            <dxlc:LayoutGroup HorizontalAlignment="Left" VerticalAlignment="Center">
                <dxlc:LayoutItem Label="Mouse X : ">
                    <TextBox Text="{Binding Path=MouseX, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
                             Width="50"/>
                </dxlc:LayoutItem>
                <dxlc:LayoutItem Label="Mouse Y : ">
                    <TextBox Text="{Binding Path=MouseY, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
                             Width="50"/>
                </dxlc:LayoutItem>
            </dxlc:LayoutGroup>

            <dxlc:LayoutGroup HorizontalAlignment="Right">
                <Button Command="{Binding Path=AddCommand}"
                        Content="추가" Width="75"/>
                <Button Command="{Binding Path=ModifyCommand}"
                        CommandParameter="{Binding ElementName=PersonParam, Path=SelectedItem}"
                        Content="변경" Width="75"/>
                <Button Command="{Binding Path=DeleteCommand}"
                        CommandParameter="{Binding ElementName=PersonParam, Path=SelectedItem}"
                        Content="삭제" Width="75"/>
                <Button Command="{Binding Path=ExitCommand}"
                        CommandParameter="{Binding ElementName=MainViewParam}"
                        Content="종료" Width="75"/>
            </dxlc:LayoutGroup>
        </dxlc:LayoutGroup>

    </dxlc:LayoutControl>
</dx:DXWindow>
  • MainView.xaml.cs
using DevExpress.Xpf.Core;

namespace MVVM
{
    /// <summary>
    /// MainView.xaml에 대한 상호 작용 논리
    /// </summary>
    public partial class MainView : DXWindow, IWindowView
    {
        public MainView(MainViewModel viewModel)
        {
            InitializeComponent();
            this.DataContext = viewModel;
        }
    }
}

ViewModel

  • AddViewModel.cs
using ReactiveUI;
using System.Reactive;
using System.Windows.Input;

namespace MVVM
{
    // ReactiveObjext 상속 받는다 : Property 값이 변경될 때 UI에 알려준다.
    public class AddViewModel : ReactiveObject
    {
        public enum ViewType
        {
            Add,
            Modify
        }

        private string _caption;

        public string Caption
        {
            get { return _caption; }
            set { this.RaiseAndSetIfChanged(ref _caption, value); }
        }

        public Person PersonData { get; set; }
        public ICommand OkCommand { get; private set; }
        public ICommand CancelCommand { get; private set; }

        public AddViewModel(Person Data = null, ViewType type = ViewType.Add)
        {
            if (type == ViewType.Add)
            {
                Caption = "추가";
            }
            else if (type == ViewType.Modify)
            {
                Caption = "변경";
            }
            else
            {
                Caption = "추가";
            }

            PersonData = Data;
            OkCommand = ReactiveCommand.Create<IDialogView, Unit>(view => _okCommandAction(view));
            CancelCommand = ReactiveCommand.Create<IDialogView, Unit>(view => _cancelCommandAction(view));

        }

        private Unit _okCommandAction(IDialogView view)
        {
            view.DialogResult = true;
            view.Close();

            return Unit.Default;
        }

        private Unit _cancelCommandAction(IDialogView view)
        {
            view.DialogResult = false;
            view.Close();

            return Unit.Default;
        }
    }
}
  • MainViewModel.cs
using DevExpress.Mvvm;
using ReactiveUI;
using System;
using System.Collections.ObjectModel;
using System.Reactive;
using System.Windows;
using System.Windows.Input;

namespace MVVM
{
    public class MainViewModel : ReactiveObject
    {
        public ObservableCollection<Person> Persons { get; set; }

        public ICommand AddCommand { get; set; }
        public ICommand DeleteCommand { get; set; }
        public ICommand ModifyCommand { get; set; }
        public ICommand ExitCommand { get; set; }
        public ICommand MouseMoveCommand { get; set; }

        private readonly IMessageBoxService _messageBoxService;
        private readonly Func<Person, AddViewModel.ViewType, IDialogView> _createAddView;

        private string _mouseX;
        public string MouseX
        {
            get { return _mouseX; }
            set { this.RaiseAndSetIfChanged(ref _mouseX, value); }
        }

        private string _mouseY;
        public string MouseY
        {
            get { return _mouseY; }
            set { this.RaiseAndSetIfChanged(ref _mouseY, value); }
        }

        public MainViewModel(IMessageBoxService messageBoxService
            , Func<Person, AddViewModel.ViewType, IDialogView> createAddView)
        {
            _messageBoxService = messageBoxService;
            _createAddView = createAddView;
            Persons = new ObservableCollection<Person>();

            _initCommand(); ;
            _initTestData();
        }

        private void _initTestData()
        {
            Persons.Add(new Person() { Address = "test1", Name = "홍길동1", Gender = true, PhoneNumber = "11111" });
            Persons.Add(new Person() { Address = "test2", Name = "홍길동2", Gender = true, PhoneNumber = "22222" });
            Persons.Add(new Person() { Address = "test3", Name = "홍길동3", Gender = true, PhoneNumber = "33333" });
            Persons.Add(new Person() { Address = "test4", Name = "홍길동4", Gender = true, PhoneNumber = "44444" });
        }

        private void _initCommand()
        {
            AddCommand = ReactiveCommand.Create(_addCommandAction);
            DeleteCommand = ReactiveCommand.Create<Person, Unit>(data => _deleteCommandAction(data));
            ModifyCommand = ReactiveCommand.Create<Person, Unit>(data => _modifyCommandAction(data));
            ExitCommand = ReactiveCommand.Create<IWindowView, Unit>(view => _exitCommandAction(view));
            MouseMoveCommand = ReactiveCommand.Create<Point, Unit>(args => _mouseMoveCommand(args));
        }

        private Unit _mouseMoveCommand(Point currentPoint)
        {
            MouseX = $"{currentPoint.X}";
            MouseY = $"{currentPoint.Y}";

            return Unit.Default;
        }

        private Unit _exitCommandAction(IWindowView view)
        {
            view.Close();
            return Unit.Default;
        }

        private Unit _modifyCommandAction(Person selectedItem)
        {
            if (selectedItem == null)
            {
                _messageBoxService.Show("선택된 데이터가 없습니다.", "주소록 v0.1", MessageBoxButton.OK, MessageBoxImage.Information);
                return Unit.Default;
            }

            var modifyData = new Person(selectedItem);

            IDialogView view = _createAddView(modifyData, AddViewModel.ViewType.Modify);
            if (true == view.ShowDialog())
            {
                var selectedIndex = Persons.IndexOf(selectedItem);
                Persons[selectedIndex] = modifyData;
            }
            return Unit.Default;
        }

        private Unit _deleteCommandAction(Person selectedItem)
        {
            if (selectedItem == null)
            {
                _messageBoxService.Show("선택된 데이터가 없습니다.", "주소록 v0.1", MessageBoxButton.OK, MessageBoxImage.Information);
                return Unit.Default;
            }

            var result = _messageBoxService.Show("선택된 데이터가 삭제 하시겠습니까?", "주소록 v0.1", MessageBoxButton.OKCancel, MessageBoxImage.Question);
            if (result == MessageBoxResult.Cancel)
                return Unit.Default;

            Persons.Remove(selectedItem);
            return Unit.Default;
        }

        private void _addCommandAction()
        {
            var addData = new Person();

            IDialogView view = _createAddView(addData, AddViewModel.ViewType.Add);
            if (true == view.ShowDialog())
            {
                Persons.Add(addData);
            }
        }
    }
}

Converter

  • MouseEventConverter
using DevExpress.Mvvm.UI;
using DevExpress.Xpf.Grid;
using System.Windows.Input;

namespace MVVM.Converter
{
    public class MouseEventConverter : EventArgsConverterBase<MouseEventArgs>
    {
        //MouseEvent를 받을 Convert 를 구현한다.
        protected override object Convert(object sender, MouseEventArgs args)
        {
            var grid = sender as GridControl;
            return args.GetPosition(grid);
        }
    }
}

Interface

  • IDialogView.cs

    namespace MVVM
    {
      public interface IDialogView
      {
          bool? ShowDialog();
          bool? DialogResult { get; set; }
          void Show();
          void Close();
      }
    }
  • IWindowView.cs

using System.Windows;

namespace MVVM
{
    public interface IWindowView
    {
        void Show();
        void Close();
        Visibility Visibility { get; set; }
    }
}

Etc

  • App.xaml
<Application x:Class="MVVM.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:MVVM">
    <Application.Resources>

    </Application.Resources>
</Application>
  • App.xaml.cs
using DevExpress.Mvvm;
using DevExpress.Xpf.Core;
using System.Windows;

namespace MVVM
{
    /// <summary>
    /// App.xaml에 대한 상호 작용 논리
    /// </summary>
    public partial class App : Application
    {

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            ApplicationThemeHelper.ApplicationThemeName = Theme.VS2019DarkName;

            IMessageBoxService messageBoxService = new DXMessageBoxService();
            MainViewModel mainViewModel = new MainViewModel(messageBoxService, _createAddView);
            MainView mainView = new MainView(mainViewModel);
            mainView.Show();
        }

        protected override void OnExit(ExitEventArgs e)
        {
            base.OnExit(e);
        }

        private IDialogView _createAddView(Person modifyData, AddViewModel.ViewType type = AddViewModel.ViewType.Add)
        {
            IMessageBoxService messageBoxService = new DXMessageBoxService();
            var viewModel = new AddViewModel(modifyData, type);
            return new AddView(viewModel);
        }
    }
}
  • 실행 결과

728x90

이 글을 공유하기

댓글

Designed by JB FACTORY