[WPF] WPF DevExpress.ReactiveUI 실습
- C#/WPF
- 2022. 6. 17. 20:46
목적
- 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
'C# > WPF' 카테고리의 다른 글
[WPF] WPF DevExpress 실습 (2) | 2022.06.17 |
---|---|
2장. WPF MVVM 실습 (2) | 2022.06.16 |
1장. WPF Binding 이용하여 프로젝트 실습 (0) | 2022.06.16 |
[WPF] WPF 점선 그리기 (0) | 2022.01.06 |
[WPF 기본 문법] WPF TextBlock, Button 컨트롤 Style 사용하기 (0) | 2021.12.28 |
이 글을 공유하기