RxUI.NET - 一段时间后隐藏消息

时间:2017-08-16 05:58:43

标签: c# system.reactive reactiveui

我正在玩Reactive UI,我希望在完成一个过程后显示一条消息,并在一段时间(4秒)后隐藏此消息。如果消息的持续时间比隐藏时间快,则应重置超时,以便在显示/更新最后一条消息后4秒后始终隐藏消息。如果最后一条消息与之前的消息相同,也应该延长隐藏时间。

目前我有这个代码,它做了我想要的,但它看起来太麻烦了我。我只是尝试使用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; });      
  }    
  ...
}

1 个答案:

答案 0 :(得分:4)

就个人而言,我这样声明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在用户界面中隐藏消息时,以及string.IsNullOrEmpty(Message) == false显示用户界面时。这与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返回的最新观察结果。如果发送了新消息,则先前的observable将不会发送空字符串
  3. 如果您有多个命令返回消息,您可以添加一个便利功能来减少代码量:

    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);
    

    RunWalkCrawl都是ReactiveCommand s。