跳至主要内容

ReactiveUI 響應式 MVVM 指南

用途:基於 Rx.NET 的跨平台 MVVM 框架,將響應式程式設計融入 UI 架構

NuGetReactiveUI(核心)、ReactiveUI.WPF(WPF 整合)

版本:23.2.1

授權:MIT(.NET Foundation 成員)

支援:.NET 8+(WPF、WinUI、MAUI、Avalonia、Blazor)


什麼是 ReactiveUI

ReactiveUI 是建立在 Rx.NET(System.Reactive) 之上的 MVVM 框架。它不只是「另一個 MVVM 框架」,而是讓你用 Observable 流 來表達 UI 的所有互動邏輯——屬性變化、命令執行、資料載入、錯誤處理——全部是可組合的響應式管道。

與 Rx.NET 的關係

System.Reactive (Rx.NET)     ← 核心響應式抽象

ReactiveUI ← MVVM 框架層

ReactiveUI.WPF ← WPF 平台綁定

ReactiveUI 不是 Rx.NET 的替代品,而是延伸

  • Rx.NET 提供 IObservable<T>、operators、schedulers
  • ReactiveUI 提供 ReactiveObject(MVVM base class)、ReactiveCommandWhenAnyValue(屬性觀察)、ObservableAsPropertyHelper(Observable → 屬性)

如果你已經在用 Rx.NET 做設備資料流處理,ReactiveUI 讓你把同樣的 Observable 管道直接接到 UI 層。


vs CommunityToolkit.Mvvm(公司目前用法)vs Prism

項目CommunityToolkit.MvvmReactiveUIPrism
核心理念輕量、Source GeneratorRx 原生、響應式綁定模組化、導航
屬性通知[ObservableProperty]RaiseAndSetIfChangedSetProperty
命令[RelayCommand]ReactiveCommandDelegateCommand
Rx.NET 整合無(需手動橋接)原生
學習曲線中高(需要懂 Rx)
程式碼風格屬性 + Source Generator響應式管道傳統 MVVM
CanExecute手動觸發 NotifyCanExecuteChanged自動(Observable 驅動)手動 RaiseCanExecuteChanged
生命週期管理手動 DisposeWhenActivated 自動管理無內建
適合場景簡單 CRUD、設定頁面即時數據、複雜互動大型模組化系統

什麼場景該選哪個

場景推薦理由
簡單設定頁面CommunityToolkit.Mvvm輕量、Source Generator 快速開發
設備即時監控儀表板ReactiveUI大量 Observable 資料流直接綁定 UI
Recipe 編輯器ReactiveUI 或 CommunityToolkit看是否需要響應式驗證
多設備狀態合併顯示ReactiveUICombineLatest + WhenAnyValue
只有按鈕和表單的頁面CommunityToolkit.MvvmReactiveUI 大材小用
資訊

兩者可以共存。你可以在同一個專案中混用——即時監控 ViewModel 用 ReactiveUI,簡單設定頁面用 CommunityToolkit。見遷移指南


核心概念一覽

概念用途對應 CommunityToolkit
ReactiveObjectViewModel 基底類別(INPC)ObservableObject
WhenAnyValue監聽屬性變化 → Observable無直接對應
ReactiveCommand命令(支援 async、CanExecute 自動化)RelayCommand
ObservableAsPropertyHelperObservable → 唯讀屬性無直接對應
WhenActivated自動管理訂閱生命週期無直接對應

響應式綁定流程

ReactiveUI 的精髓是「屬性變更 = Observable 訊號」。一個最常見的情境是:使用者在 TextBox 輸入 IP,透過 WhenAnyValue 推導出 ConnectCommandCanExecute,按鈕啟用狀態自動反映輸入有效性。以最小 WPF 範例的 DeviceMonitorViewModel 為例:

關鍵差異對比 CommunityToolkit.Mvvm:

  • CommunityToolkit:屬性 setter 後,必須呼叫 ConnectCommand.NotifyCanExecuteChanged() 才會刷新按鈕狀態
  • ReactiveUIcanExecute 是 Observable,屬性變更自動走完整條管道,不需要在 setter 裡額外呼叫

WhenAnyValue operator pipeline

WhenAnyValue 只是起點——它把屬性變更轉成 IObservable<T>,之後就能套用任何 Rx operator。下圖以「搜尋去抖動」為例,展示一條典型的 ReactiveUI pipeline:

這條管道等價於以下程式碼:

this.WhenAnyValue(x => x.SearchText)
.Throttle(TimeSpan.FromMilliseconds(300))
.DistinctUntilChanged()
.SelectMany(text => QueryApiAsync(text))
.ObserveOn(RxApp.MainThreadScheduler)
.BindTo(this, vm => vm.Results);

各段 operator 的意圖:

  • WhenAnyValue:訂閱屬性變更,把 setter 訊號轉成 Observable
  • .Throttle + .DistinctUntilChanged:使用者快速打字時合併多次變更,相同字串不重覆查詢
  • .SelectMany:非同步 API 呼叫,SelectMany 會自動取消舊請求
  • .ObserveOn(MainThreadScheduler):把結果 marshal 回 UI 執行緒
  • .BindTo:把 Observable 最終值寫回 ViewModel 屬性,UI 自動更新

安裝

dotnet add package ReactiveUI.WPF                      # WPF 專案
dotnet add package ReactiveUI.SourceGenerators # 推薦:Source Generator 簡化屬性宣告
資訊

ReactiveUI.SourceGenerators 是目前推薦的方式(取代舊的 ReactiveUI.Fody)。它使用 C# Source Generator,支援 [Reactive][ObservableAsProperty][ReactiveCommand] 屬性。

常用 namespace:

using ReactiveUI;
using ReactiveUI.SourceGenerators;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Disposables;

最小 WPF 範例

ViewModel

public class DeviceMonitorViewModel : ReactiveObject
{
private string _deviceIp = "";
public string DeviceIp
{
get => _deviceIp;
set => this.RaiseAndSetIfChanged(ref _deviceIp, value);
}

private string _connectionStatus = "未連線";
public string ConnectionStatus
{
get => _connectionStatus;
set => this.RaiseAndSetIfChanged(ref _connectionStatus, value);
}

public ReactiveCommand<Unit, Unit> ConnectCommand { get; }

public DeviceMonitorViewModel()
{
// CanExecute:IP 不為空時才能點連線
var canConnect = this.WhenAnyValue(x => x.DeviceIp,
ip => !string.IsNullOrWhiteSpace(ip));

ConnectCommand = ReactiveCommand.CreateFromTask(
async () =>
{
ConnectionStatus = "連線中...";
await ConnectToDeviceAsync(DeviceIp);
ConnectionStatus = "已連線";
},
canConnect);
}
}

View(XAML)

<Window x:Class="DeviceControl.MainWindow"
xmlns:rxui="http://reactiveui.net"
Title="設備監控">
<StackPanel Margin="16">
<TextBox Text="{Binding DeviceIp, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="連線" Command="{Binding ConnectCommand}" />
<TextBlock Text="{Binding ConnectionStatus}" />
</StackPanel>
</Window>

本指南結構

頁面內容
概觀(本頁)ReactiveUI 定位、與其他框架比較、安裝
核心概念詳解ReactiveObject、WhenAnyValue、ReactiveCommand、OAPH、WhenActivated
公司場景 PatternDashboard、Recipe Editor、設備狀態管理、搜尋、Polly/FlaUI 整合
遷移指南從 CommunityToolkit.Mvvm 漸進式遷移