背景:
官网只介绍了推荐适用ReactiveUI,没有过多的案例介绍,对于初入桌面应用开发的小白极其不友好。
本文介绍在Avalonia应用中通过ReactiveUI中的MessageBus进行跨组件通信.
假设需求案例:
MainWindowViewModel中发送消息,AnotherViewModel中接收MainWindowViewModel发送的消息。
安装依赖:
Avalonia.ReactiveUI
Microsoft.Extensions.DependencyInjection(DI依赖注入,非此案例必须)
实现思路:
- 使用MessageBus.SendMessage()发送消息;
- 需要接收消息的组件使用MessageBus.Listen()订阅消息;
关键代码:
- 在App.axaml.cs中注入MessageBus(使用DI);
private IServiceProvider _serviceProvider;
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
// 初始化 ReactiveUI
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
}
public override void OnFrameworkInitializationCompleted()
{
ConfigureServices();
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
// Line below is needed to remove Avalonia data validation.
// Without this line you will get duplicate validations from both Avalonia and CT
BindingPlugins.DataValidators.RemoveAt(0);
desktop.MainWindow = _serviceProvider.GetRequiredService<MainWindow>();
}
base.OnFrameworkInitializationCompleted();
}
private void ConfigureServices()
{
var services = new ServiceCollection();
// 注册 MessageBus 为 Singleton
services.AddSingleton(MessageBus.Current);
// 注册 ViewModel 为 Transient
services.AddTransient<MainWindowViewModel>();
services.AddTransient<AnotherViewModel>();
// 注册 MainWindow
services.AddTransient<MainWindow>();
_serviceProvider = services.BuildServiceProvider();
}
- MainWindow.axaml中实例化AnotherViewModel,初始化是为了创建订阅信息监听
// 实例化 AnotherViewModel
_anotherViewModel = serviceProvider.GetRequiredService<AnotherViewModel>();
- 添加Send Message按钮测试;
<Button Content="Send Message" Command="{Binding SendTestMessageCommand}" />
public partial class MainWindowViewModel : ViewModelBase
{
private readonly IMessageBus _messageBus;
public ReactiveCommand<Unit, Unit> SendTestMessageCommand { get; }
public MainWindowViewModel(IMessageBus messageBus)
{
_messageBus = messageBus;
SendTestMessageCommand = ReactiveCommand.Create(SendTestMessage);
}
public void SendTestMessage()
{
var message = new TestMessage { Content = "Hello from MainViewModel" };
_messageBus.SendMessage(message);
}
}
4、在AnotherViewModel中订阅消息
private readonly IMessageBus _messageBus;
public AnotherViewModel(IMessageBus messageBus)
{
_messageBus = messageBus;
// 订阅消息
_messageBus.Listen<TestMessage>()
//.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(Observer.Create<TestMessage>(HandleMessage));
}
private void HandleMessage(TestMessage message)
{
// 处理接收到的消息
Debug.WriteLine($"Message received in AnotherViewModel: {message.Content}");
// 如果需要更新 UI,确保在主线程上执行
Dispatcher.UIThread.InvokeAsync(() =>
{
// 这里可以执行 UI 更新操作
});
}
5、运行测试结果
深潜
如果不使用ReactiveUI的MessageBus,还有什么什么实现方式?
1、通过构造函数注入,如果组件层级深,此方法会使代码耦合性极大增加,建议不超过3级;
2、使用ReactiveUI中ReactiveObject,使用RaiseAndSetIfChanged(),变化后通知;
3、使用ReactiveUI中的ReactiveList<T>处理集合变化,监听change和ItemChange事件;
4、使用.Net中的ObservableCollection<T>配合ReactiveUI的this.WhenAnyValue().BindTo().Subscrble().DisposeWith()使用,个人理解等同于ReactiveList<T>。
总结:
- 使用MessageBus可解耦,省去中间传参处理参数环节且在ReactiveUI中开箱即用,较推荐使用。
- 对于非ReativeUI的MVVM项目可以使用.Net中的ObservableCollection<T>;
- 层级简单较容易实现的,根据实际情况出发,非必要引入消息总线。
- 关于Avalonia+ReactiveUI开发,是在使用中探索,加之网上开发相关资料并不丰富,特此分享,欢迎指正!!!
本文完。