代码之家  ›  专栏  ›  技术社区  ›  gakera

如何使用反应式用户界面触发按钮点击与按钮按下(按住)后的不同操作

  •  3
  • gakera  · 技术社区  · 8 年前

    假设我有 Task<Unit> StartMove() , Task<Unit> StopMove() Task<Unit> MoveStep() . 点击按钮应执行 MoveStep()

    true 当用户按下按钮和 false 释放时,该值在常规WPF按钮上被称为IsPressed。小于1秒的真值后跟假值表示单击,大于1秒的真值后跟假值表示保持(该秒值也可以调整为半秒)。

    问题归结为获取以随机间隔到达的真/假值流(想想:猴子随机按下按钮),并从该流中确定按钮是否被单击或按下。基于此,应触发以下操作: MoveStep StartMove StopMove

    我终于找到了一些有用的东西。

    到目前为止,我有主窗口

    public partial class MainWindow : Window, IViewFor<AppViewModel>
    {
        public AppViewModel ViewModel { get; set; }
        object IViewFor.ViewModel { get => ViewModel; set => ViewModel = value as AppViewModel; }
    
        public MainWindow()
        {
            ViewModel = new AppViewModel();
            DataContext = ViewModel;
            InitializeComponent();
    
            this.WhenAnyValue(x => x.MoveLeftButton.IsPressed).InvokeCommand(this, x => x.ViewModel.MoveLeftCommand);
    
        }
    
        protected override void OnClosing(CancelEventArgs e)
        {
            ViewModel.Dispose();
            base.OnClosing(e);
        }
    }
    

    public class AppViewModel : ReactiveObject, IDisposable
    {
        public ReactiveCommand<bool, bool> MoveLeftCommand { get; protected set; }
    
        public AppViewModel()
        {
            MoveLeftCommand = ReactiveCommand.CreateFromTask<bool, bool>(isPressed => _MoveLeft(isPressed));
    
            MoveLeftCommand.Buffer(TimeSpan.FromMilliseconds(500))
                .Do(x => _InterpretCommand(x))
                .Subscribe(x => Console.WriteLine($"{TimeStamp} {string.Join(",", x)}"))
    
        }
    
        private Task<bool> _MoveLeft(bool isPressed)
        {
            return Task.Run(() => isPressed); // Just to set a breakpoint here really
        }
    
        private static void _InterpretCommand(IList<bool> listOfBools)
        {
            if (listOfBools == null || listOfBools.Count == 0)
            {
                return;
            }
    
            if (listOfBools.First() == false)
            {
                Console.WriteLine("Stop move");
                return;
            }
    
            if (listOfBools.Count == 1 && listOfBools.First() == true)
            {
                Console.WriteLine("Start move");
                return;
            }
    
            if (listOfBools.Count >= 2)
            {
                Console.WriteLine("Click move");
                return;
            }
        }
    }
    

    和我的主窗口。xaml真的只是

            <Button x:Name="MoveLeftButton" Content="Left"/>
    

    随机序列示例

            var rands = new Random();
            rands.Next();
    
    
            var better = Observable.Generate(
                true,
                _ => true,
                x => !x,
                x => x,
                _ => TimeSpan.FromMilliseconds(rands.Next(1000)))
                .Take(20);
    
            better.Buffer(TimeSpan.FromMilliseconds(500))
                .Do(x => _InterpretCommand(x))
                .Subscribe(x => Console.WriteLine($"{TimeStamp} {string.Join(",", x)}"));
    
        static string TimeStamp => DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture);
    

    这将产生输出

    2017-10-06 19:11:54.231 
    Start move
    2017-10-06 19:11:54.720 True
    2017-10-06 19:11:55.220 
    Stop move
    2017-10-06 19:11:55.719 False,True
    Stop move
    2017-10-06 19:11:56.221 False
    Start move
    2017-10-06 19:11:56.719 True
    Stop move
    2017-10-06 19:11:57.222 False
    2017-10-06 19:11:57.719 
    Start move
    2017-10-06 19:11:58.220 True
    Stop move
    2017-10-06 19:11:58.720 False
    2017-10-06 19:11:59.219 
    Click move
    2017-10-06 19:11:59.719 True,False
    2017-10-06 19:12:00.217 
    Start move
    2017-10-06 19:12:00.719 True
    Stop move
    2017-10-06 19:12:01.221 False
    Click move
    2017-10-06 19:12:01.722 True,False
    Start move
    2017-10-06 19:12:02.217 True
    2017-10-06 19:12:02.722 
    Stop move
    2017-10-06 19:12:03.220 False
    2017-10-06 19:12:03.720 
    Start move
    2017-10-06 19:12:04.217 True
    Stop move
    2017-10-06 19:12:04.722 False
    Start move
    2017-10-06 19:12:05.220 True
    Stop move
    2017-10-06 19:12:05.516 False
    
    2 回复  |  直到 8 年前
        1
  •  1
  •   gakera    8 年前

    从这个答案中可以看出: https://stackoverflow.com/a/46629909/377562

    BufferWithClosingValue 从链接的答案中:

    public static IObservable<IList<TSource>> BufferWithClosingValue<TSource>(
        this IObservable<TSource> source, 
        TimeSpan maxTime, 
        TSource closingValue)
    {
        return source.GroupByUntil(_ => true,
                                   g => g.Where(i => i.Equals(closingValue)).Select(_ => Unit.Default)
                                         .Merge(Observable.Timer(maxTime).Select(_ => Unit.Default)))
                     .SelectMany(i => i.ToList());
    }
    

    随机序列示例:

    var alternatingTrueFalse = Observable.Generate(
        true,
        _ => true,
        x => !x,
        x => x,
        _ => TimeSpan.FromMilliseconds(new Random().Next(1000)))
        .Take(40).Publish().RefCount();
    
    var bufferedWithTime = alternatingTrueFalse.BufferWithClosingValue(TimeSpan.FromMilliseconds(500), false);
    
    var clicks = bufferedWithTime.Where(x => x.Count() == 2).ThrottleFirst(TimeSpan.FromMilliseconds(500));
    var holdStarts = bufferedWithTime.Where(x => x.Count() == 1 && x.First() == true);
    var holdStops = bufferedWithTime.Where(x => x.Count() == 1 && x.First() == false);
    
    clicks.Select(_ => "Click").DumpTimes("Clicks");
    holdStarts.Select(_ => "Hold Start").DumpTimes("Hold Start");
    holdStops.Select(_ => "Hold Stop").DumpTimes("Hold stop");
    

    ThrottleFirst SampleFirst 此答案的实现: https://stackoverflow.com/a/27160392/377562

    示例输出

    2017-10-08 16:58:14.549 - Hold Start-->Hold Start :: 6
    2017-10-08 16:58:15.032 - Hold stop-->Hold Stop :: 7
    2017-10-08 16:58:15.796 - Clicks-->Click :: 7
    2017-10-08 16:58:16.548 - Clicks-->Click :: 6
    2017-10-08 16:58:17.785 - Hold Start-->Hold Start :: 5
    2017-10-08 16:58:18.254 - Hold stop-->Hold Stop :: 7
    2017-10-08 16:58:19.294 - Hold Start-->Hold Start :: 8
    2017-10-08 16:58:19.728 - Hold stop-->Hold Stop :: 7
    2017-10-08 16:58:20.186 - Clicks-->Click :: 6
    

    这似乎没有任何比赛条件的问题,我已经与其他一些试图解决这个问题,所以我喜欢它!

        2
  •  0
  •   Rodney Littles    8 年前

    根据我有限的经验,我相信您应该能够添加Rx扩展,如 Throttle Buffer 在您的 WhenAnyValue

    this.WhenAnyValue(x => x.MoveLeftButton.IsPressed) .Buffer(TimeSpan.FromSeconds(1)) .InvokeCommand(this, x => x.ViewModel.MoveLeftCommand);

    推荐文章