RxUI.NET - 一段时间后隐藏消息
RxUI.NET - Hiding a message after a period of time
我正在研究 Reactive UI,我想在流程完成后显示一条消息,并在一段时间(4 秒)后隐藏此消息。如果消息持续快于隐藏时间,则应重置超时,因此消息始终在最后一条消息 displayed/updated 后 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; });
}
...
}
就我个人而言,我会这样声明 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);
}
}
- 这将立即 return 新消息,然后在 4 秒后 return 一个空字符串
- 这只会订阅由
Select
编辑的最近观察到的 return。如果发送了一条新消息,之前的 Observable 将不会发送空字符串
如果您有多个命令 returning 消息,您可以添加一个方便的函数来减少代码量:
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);
其中Run
、Walk
和Crawl
都是ReactiveCommand
。
我正在研究 Reactive UI,我想在流程完成后显示一条消息,并在一段时间(4 秒)后隐藏此消息。如果消息持续快于隐藏时间,则应重置超时,因此消息始终在最后一条消息 displayed/updated 后 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; });
}
...
}
就我个人而言,我会这样声明 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);
}
}
- 这将立即 return 新消息,然后在 4 秒后 return 一个空字符串
- 这只会订阅由
Select
编辑的最近观察到的 return。如果发送了一条新消息,之前的 Observable 将不会发送空字符串
如果您有多个命令 returning 消息,您可以添加一个方便的函数来减少代码量:
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);
其中Run
、Walk
和Crawl
都是ReactiveCommand
。