0

我的条形码阅读器被编程为添加“,”的前缀和后缀,否则就像键盘一样工作。我有一个在扫描条形码时会打开的 Windows 窗体。

与其编写一堆 KeyDown 代码,Reactive Extensions 似乎非常适合这种工作。我想做的事:

  • 按下逗号时,开始按住按键(不要让屏幕上的任何控件处理它们)
  • 收集所有键,直到按下另一个逗号或 250 毫秒过去。
  • 如果在 250 毫秒内没有按下逗号,则将按键返回到任何处于活动状态的控件。
  • 如果按下逗号,则对从条码扫描进来的字符串值进行处理。

如何在匹配条形码扫描仪的前缀和后缀时使用 System.Reactive 按住按键,如果后缀匹配则处理,但在后缀在时限内不匹配时正常处理按键?

4

2 回答 2

1

有人会出现,可能会给出比这更优雅的答案。但我发现这个具有挑战性。

首先,这个例子没有展示如何为表单上的键做这件事。那里有足够的额外复杂性,需要先尝试自己,或者问另一个问题。在这里需要一个完整的应用程序来回答您问题的所有方面。可以这么说:

  1. 您可以将 KeyEventArgs 转换为可在此框架中使用的 observable
  2. 您可以决定使用SuppressKeyPressKeyEventArgs将其抑制到条形码流中
  3. 您可以决定不抑制KeyEventArgs非条形码流中的任何内容

目前的解决方案涉及定义一个函数,该函数采用 aIObservable<char>并将其转换为 aGroupedObservable标记(用 a bool)底层字符是否在“条形码”流中(真)或不(假):

public IObservable<IGroupedObservable<bool, IObservable<char>>> GroupBySurroundingChars(IObservable<char> source, char ends, TimeSpan within)
{
    var result = 
        source.Buffer(() => 
            source.Select(c => {
                if (c == ends) return source.Where(x => x == ends).Amb(Observable.Timer(within).Select(_ => default(char)));
                else           return Observable.Return(default(char));
            }).Concat())
            .GroupBy(buffer => buffer.Count > 2 && buffer[0] == ends && buffer.Last() == ends, buffer => buffer.ToObservable());                        

    return result;
}

这个函数的作用是:

  1. 用每个字符开始一个缓冲区
  2. 如果该字符不是逗号,则立即返回缓冲区
  3. 如果该字符是逗号,则保持缓冲区打开,直到找到另一个逗号或 250 毫秒过去
  4. 检查缓冲区是否在两端包含逗号(标记为条形码)或不包含(标记为无条形码)

然后是一个完整的用法示例:

var keys1 = "xxx,1234567,xxx,1,xxx".ToCharArray().ToObservable(Scheduler.ThreadPool).Do(_ => Thread.Sleep(100)).Publish().RefCount();
var keys2 = "xxx,1234567,xxx,1,xxx".ToCharArray().ToObservable(Scheduler.ThreadPool).Do(_ => Thread.Sleep(10)).Publish().RefCount();

var result = GroupBySurroundingChars(keys1, ',', TimeSpan.FromMilliseconds(250));

var barcodes = result.Where(x => x.Key);
var others = result.Where(x => !x.Key);

barcodes.Subscribe(groups => groups.Subscribe(x => x.ToList().Dump()));

如果使用keys1,按键速度太慢,无法找到第一个条形码,但它会找到第二个。如果您使用keys2,按键速度足以找到两个条形码。

在任何一种情况下,others流都包含最终未标记为包含条形码的所有键。该barcodes流可以被视为一个字符一个字符的流,或者List像我上面所做的那样与每组条形码一起转换为一个。

于 2012-07-04T06:05:19.910 回答
1

让我们一块一块地拆开它。

首先,您需要将按键作为 Observable:

        var keys =
        Observable.FromEventPattern<KeyEventHandler, KeyEventArgs>(
                a => this.KeyDown += a,
                a => this.KeyDown -= a
            ).Select(ea => ea.EventArgs)
            .Publish();

        var unsubscription = keys.Connect();

您有一个条件来描述缓冲,基于接收到一个按键:

        Func<KeyEventArgs, bool> isDelimiter =
            k => k.KeyCode == Keys.Oemcomma;

现在我们可以在满足缓冲条件时收到通知keys.Where(isDelimiter)

当遇到分隔符或经过一段时间直到没有输入时,我们需要关闭缓冲区:

Observable.Amb(keys.Where(isDelimiter), keys.Throttle(TimeSpan.FromMilliseconds(2000))

将这些放在一起,我们可以创建在这些条件下出现的字符窗口:

        var windows = 
            keys.Window(keys.Where(isDelimiter),
                        first => Observable.Amb(
                                    keys.Where(isDelimiter), 
                                    keys.Throttle(TimeSpan.FromMilliseconds(2000)
                                )
                                .Where(_ => isDelimiter(first))));

现在您需要做的就是保持缓冲直到窗口关闭,并尝试阻止其余控件在缓冲时接收键:

        windows
            .SelectMany(window => window
                                        .Do(ka => ka.SuppressKeyPress = true)
                                        .Buffer(() => Observable.Never<KeyEventArgs>())
                                        )
            .Subscribe(buf => Trace.WriteLine(new string(buf.Select(ka => (char)ka.KeyValue).ToArray())));

SelectMany为您提供缓冲按键的最终流,您最终可以在其中放入程序逻辑。在这里,我只是将列表作为字符串打印到 Trace。

于 2012-07-04T16:04:34.407 回答