2017-08-16 34 views
2

我正在使用反应式UI,我想在完成一个过程后显示一条消息,并在一段时间(4秒)后隐藏此消息。如果消息比隐藏时间快,则应重置超时,以便在最后一条消息显示/更新后的4秒后始终隐藏消息。如果最后一条消息与之前的消息相同,则隐藏时间也应该延长。RxUI.NET - 在一段时间后隐藏消息

目前我有这个代码,这是我想要的,但它看起来太麻烦了。我只是在试用RxUI,所以大部分时间我都不知道自己在做什么。有没有更好的方法来实现这个目标?

public class MainViewModel: ReactiveObject 
{ 
    // Message to be shown. 
    private string message; 
    public string Message { get => message; set => this.RaiseAndSetIfChanged(ref message, value); 

    // Flag for the UI, if the message panel should be visible. 
    private ObservableAsPropertyHelper<bool> isMessageVisible; 
    public bool IsMessageVisible => isMessageVisible.Value; 

    // Command that runs async process, the result is the message to be shown. 
    public ReactiveCommand<Unit, string> Run { get; private set; } 

    public MainViewModel() 
    {   
    var msg = this.WhenAnyValue(x => x.Message, x => !string.IsNullOrEmpty(x)); 

    // If message changes, after 4 seconds return false, causing hiding the message panel. 
    var hide = msg.Select(x => false).Throttle(TimeSpan.FromSeconds(4), RxApp.MainThreadScheduler); 

    // Merge both sequences into one output property. 
    Observable.Merge(msg, hide).ToProperty(this, x => x.IsMessageVisible, out isMessageVisible); 

    Run = ReactiveCommand.CreateFromObservable(() => Observable.StartAsync(Process)); 

    // Merge various message sources and set message property. Set Message = null to force property change. 
    Observable.Merge(Run, Run.ThrownExceptions.Select(x => x.Message)).Subscribe(x => { Message = null; Message = x; });  
    }  
    ... 
} 

回答

3

个人而言,我会宣布isMessageVisible这样的:

isMessageVisible = this 
    .WhenAnyValue(x => x.Message, x => !string.IsNullOrEmpty(x)) 
    .Select(showMessage => Observable.Return(showMessage).Concat(Observable.Return(false).Delay(4, RxApp.MainThreadScheduler))) 
    .Switch() 
    .ToProperty(this, x => x.IsMessageVisible); 

它把所有的逻辑集中在一个管道,我认为这是更具可读性。


除了重写isMessageVisible,我会改变消息如何显示在第一位。

我会掉落isMessageVisible,只有Message属性。当string.IsNullOrEmpty(Message) == true隐藏UI中的消息时,以及string.IsNullOrEmpty(Message) == false显示UI。这将是这样与RxUI绑定:

this.OneWayBind(ViewModel, vm => vm.Message, v => v.Message.Text, message => !string.IsNullOrWhitespace(message)); 

然后我会做到这一点的视图模型:

public class MainViewModel: ReactiveObject 
{ 
    // Message to be shown. 
    private ObservableAsPropertyHelper<string> message; 
    public string Message => message.Value; 

    // Command that runs async process, the result is the message to be shown. 
    public ReactiveCommand<Unit, string> Run { get; private set; } 

    public MainViewModel() 
    { 
     Run = ReactiveCommand.CreateFromObservable(() => Observable.StartAsync(Process)); 

     // Merge various message sources and set message property. 
     message = Observable.Merge(Run, Run.ThrownExceptions.Select(x => x.Message)) 
      .Select(msg => Observable.Return(msg).Concat(Observable.Return("").Delay(4, RxApp.MainThreadScheduler))) // 1 
      .Switch() // 2 
      .ToProperty(this, x => x.Message); 
    } 
} 
  1. 这将立即返回新的消息,然后返回一个空字符串4秒后
  2. 这只会订阅Select返回的最新可观察值。如果有新的消息被发送以前观察到的将不会发送空字符串

如果你有多个命令返回的信息,你可以添加一个方便的功能,以减少代码量:

private IObservable<string> CreateMessageStream(params ReactiveCommand<Unit, string> commands) 
    => Observable.Merge(commands.SelectMany(command => new IObservable<string>[] { command, command.ThrownExceptions.Select(x => x.Message) })) 
     .Select(msg => Observable.Return(msg).Concat(Observable.Return("").Delay(4, RxApp.MainThreadScheduler))) 
     .Switch() 

然后你可以声明message这样的:

message = CreateMessageStream(Run, Walk, Crawl) 
    .ToProperty(this, x => x.Message); 

RunWalkCrawlReactiveCommand s。

+0

很好的答案。似乎人们必须彻底改变心态才能在Rx/UI中发挥作用。谢谢。 –