我的条形码阅读器已编程为添加前缀和后缀“,”,否则就像键盘一样工作。我有一个Windows窗体,在扫描条形码时会打开。
Reactive Extensions不是编写一堆KeyDown代码,而是适合这种工作。我想做什么:
如何在匹配条形码扫描器的前缀和后缀时使用System.Reactive来保持按键,如果匹配后缀则进行处理,但是如果后缀在时间限制内不匹配,则处理键正常按下?
答案 0 :(得分:1)
有人会来,可能会给出比这更优雅的答案。但我发现这个具有挑战性。
首先,这个例子没有说明如何为表单上的键做这件事。它有足够的额外复杂性,它首先尝试自己,或提出另一个问题。这需要一个完整的应用程序来回答您问题的所有方面。我只想说:
KeyEventArgs
进入条形码流
KeyEventArgs
现有解决方案涉及定义一个函数,该函数接受IObservable<char>
并将其转换为GroupedObservable
,标记(使用bool
)底层字符是否在&#内39;条形码&#39; stream(true)与否(false):
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;
}
这个功能的作用是:
然后是一个完整的用法示例:
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
。
答案 1 :(得分: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字符串。