Software Develop/C# , .NET , WPF

ReactiveUI

jaywapp 2022. 2. 28. 15:38

배경

MVVM 패턴을 자주 사용하는데 주로 Prism.MVVM 프레임워크의 BindableBase를 사용해왔다.

큰 불편함을 느끼지 못해서 그냥 사용해왔는데,

어느 날 회사 동료가 소개해준 ReactiveUI를 알게 된 후부터는 계속 ReactiveUI를 사용하고 있다.

 

Prism.MVVM도 물론 굉장히 좋은 프레임워크임은 분명하나 ReactiveUI는 반응형으로 설계되어 있어 보다 직관적인 UI 작업이 가능하다. 또한 코드도 간결해지고 이해하기 쉬워진다.

 

오늘은 ReactiveUI를 소개해보고자 한다.

ReactiveUI란?

 

An advanced, composable, reactive model-view-viewmodel framework

Declarative Describe what you want, not how to do it & rejoice in the increased readability of your code. Code is communication between people, that also happens to run on a computer. If you optimise for reading by humans, then over a long time your projec

www.reactiveui.net

먼저 ReactiveUI는 이름에서부터 알 수 있듯이 반응형 프로그래밍의 일부라는 것이다.

 

Prism.MVVM에서는 Property가 변경될 때 Setter를 통해 다른 행위를 정의한다면

ReactiveUI에서는 미리 반응의 규칙을 선언해놓고 Property가 변경될 때 규칙대로 행위가 수행되는 방식이다.

예제

먼저 ReactiveUI를 사용하기 위해선 Nuget Package에서 아래 항목을 추가해준다.

Prism 관련 Package는 ReactiveUI내에서 사용되고 있는 듯하다. 없으면 TryLoadExpcetion이 발생함.

  • ReactiveUI
  • ReactiveUI.WPF 
  • System.Reactive
  • Prism.Core
  • Prism.Wpf
  • Prism.Unity

 

 

구현할 내용은 이전에 MVVM 패턴에 대한 포스팅에서 다루었던 내용을 다루고자 한다. Model과 View의 경우는 아래 링크내의 내용과 동일하고, ViewModel에서 ReactiveUI를 적용한다.

 

MVVM 패턴 (Model - View - ViewModel)

MVVM 패턴은 처음 개발자로 입사하여 접했던 패턴이다. 개발을 처음 접했을 때는 전혀 이해가 안 되었지만 5년이 지난 지금은 가장 자신 있게 사용할 수 있는 패턴이기도 하다. 위키에서는 모델-

jaywapp.tistory.com

Model

public class RGBColor
{
    #region Properties
    public byte R { get; set; }
    public byte G { get; set; }
    public byte B { get; set; }
    #endregion
}

View

<UserControl x:Class="Reactive.View.ColorView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <Grid.Resources>
            <Style TargetType="TextBlock">
                <Setter Property="HorizontalAlignment" Value="Center"/>
                <Setter Property="VerticalAlignment" Value="Center"/>
            </Style>
        </Grid.Resources>

        <TextBlock Grid.Row="0" Grid.Column="0" Text="R"/>
        <TextBlock Grid.Row="1" Grid.Column="0" Text="G"/>
        <TextBlock Grid.Row="2" Grid.Column="0" Text="B"/>
        <TextBlock Grid.Row="3" Grid.Column="0" Text="Result"/>

        <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding RValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding GValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding BValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBlock Grid.Row="3" Grid.Column="1" 
               Text="{Binding ColorText, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" 
               Foreground="{Binding ColorBrush,  Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
    </Grid>
</UserControl>

ViewModel

public class ColorViewModel : ReactiveObject
{
    #region Internal Field
    private byte _rValue;
    private byte _gValue;
    private byte _bValue;

    private ObservableAsPropertyHelper<Color> _color;
    private ObservableAsPropertyHelper<string> _colorText;
    private ObservableAsPropertyHelper<SolidColorBrush> _colorBrush;
    #endregion

    #region Properties
    public byte RValue
    {
        get => _rValue;
        set => this.RaiseAndSetIfChanged(ref _rValue, value);
    }
    public byte GValue
    {
        get => _gValue;
        set => this.RaiseAndSetIfChanged(ref _gValue, value);
    }
    public byte BValue
    {
        get => _bValue;
        set => this.RaiseAndSetIfChanged(ref _bValue, value);
    }

    public Color Color => _color.Value;
    public string ColorText => _colorText.Value;
    public SolidColorBrush ColorBrush => _colorBrush.Value;
    #endregion

    #region Constructor
    public ColorViewModel()
    {
        // 1
        this.WhenAnyValue(x => x.RValue, x => x.GValue, x => x.BValue)
            .Select(p => Color.FromRgb(p.Item1, p.Item2, p.Item3))
            .ToProperty(this, x => x.Color, out _color);
        // 2
        this.WhenAnyValue(x => x.Color)
            .Select(c => c.ToString())
            .ToProperty(this, x => x.ColorText, out _colorText);
        // 3
        this.WhenAnyValue(x => x.Color)
            .Select(c => new SolidColorBrush(c))
            .ToProperty(this, x => x.ColorBrush, out _colorBrush);
    }
    #endregion
}

위 ViewModel 코드는 기존에 Prism만 사용한 MVVM 패턴의 코드와 다른 코드이다.

각 항목을 상세 설명을 하기 전에 전체적은 흐름을 보면, 각 Property를 정의하고 Property의 변경에 따른 규칙들을 Constructor내에 정의하고 있다. 상세 내용은 아래와 같다.

 

첫번째 정의.

RValue, GValue, BValue의 값이 변경될 때 Color.FromRgb 함수를 통해 값을 수집하고 이를 Color Property에 할당한다.

 

두번째 정의.

Color 값이 변경될 경우, Color의 ToString() 함수를 통해 값을 수집하고 ColorText Property에 할당한다.

 

세번째 정의.

Color 값이 변경될 경우, Color에 대해 SolidColorBrush를 생성 및 수집하고 ColorBrush Property에 할당한다.

 

결과

 

 

GitHub - jaywapp/Tutorials: Tutorial Codes

Tutorial Codes. Contribute to jaywapp/Tutorials development by creating an account on GitHub.

github.com

추가 내용

이 포스팅에서는 ReactiveUI의 모든 함수나 Extension을 소개하지는 못했지만, 아래 링크에서 참고하여 사용하면 좋을 듯 하다.

 

 

Handbook

 

www.reactiveui.net

 

'Software Develop > C# , .NET , WPF' 카테고리의 다른 글

private, protected method 단위 테스트 코드 작성하기  (0) 2023.02.07
ListBox 사용하기  (0) 2023.02.07
Adonis UI 사용기  (0) 2023.02.07
Lazy<T>  (0) 2023.01.16
Crawling (Selenium)  (0) 2022.02.16