The need is to get the possibility to differentiate between a Barcode-Scanner (which behaves like a keyboard) from a human pressing keys in a...let's say a Textbox (the use case would be all over everything, wherever I expect a barcode scanner).
The basic idea was to create a buffer which holds the typed string (converted from the Keys-Enum, which I already have a function for) and "releases" it to a callback which then handles it. The problem with those scanners is that they behave like normal keyboards, there's no way to differentiate between them and the user except for the amount of time spend between keypresses. To minimize on-screen flickering, it is shoveled into a buffer instead of the control.
Despite that I mostly need it to hold Strings, I wrote it as a generic method because I can imagine to stuff pretty much everything in there when I need it in a certain amount of time back.
So, let's cut to the case:
public class TimeoutBuffer<T>
{
private Thread backgroundThread;
private T buffer;
private DateTime lastChange = DateTime.MaxValue;
private Object syncObject = new Object();
public Action<T> Callback { get; set; }
public int Timeout { get; set; }
public TimeoutBuffer(int timeout)
{
this.Timeout = timeout;
}
/// <summary>
/// Retrieves the value from the buffer (and clears the buffer). Resets the timer.
/// </summary>
/// <returns></returns>
public T Get()
{
lock (syncObject)
{
T temp = buffer;
buffer = default(T);
lastChange = DateTime.MaxValue;
return temp;
}
}
/// <summary>
/// Stores the value into the buffer. Resets the timer.
/// </summary>
/// <param name="buffer"></param>
public void Store(T buffer)
{
lock (syncObject)
{
this.buffer = buffer;
lastChange = DateTime.Now;
if (backgroundThread == null || backgroundThread.ThreadState == ThreadState.Stopped)
{
backgroundThread = new Thread(Check);
backgroundThread.IsBackground = true;
backgroundThread.Start();
}
}
}
private void Check()
{
while (syncObject != null && buffer != null) // Just to be sure...I think...
{
lock (syncObject)
{
if ((DateTime.Now - lastChange).TotalMilliseconds >= Timeout)
{
if (Callback != null)
{
Callback.Invoke(Get());
}
else
{
// Clear it anyway...their loss.
buffer = default(T);
lastChange = DateTime.MaxValue;
}
return;
}
}
Thread.Sleep(1);
}
}
}
What you're looking at here is a threaded-beast:
- It gets created
- Something gets stored in the buffer
- The background-thread is started
- [Optional]: Go back to 2
- The background-thread finds that it is time and calls the callback
- The buffer is cleared and the background-thread terminates
- Go back to 2
The original design had a constantly running background-thread (with an if(buffer != null) right inside the lock), which I'm not very fond of...I'm not very fond of creating a thread every keypress, either.
Example usage:
private TimeoutBuffer<String> buffer;
// In the form constructor
buffer = new TimeoutBuffer<String>(20);
buffer.Callback = new Action<String>(buffer_TimedOut);
// Keypress-Event
buffer.Store((buffer.Get() ?? "") + keyData.ToString());
void buffer_TimedOut(String value)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new Action<String>(buffer_TimedOut), new Object[] { value });
}
else
{
if (value.Length == 13)
{
// Barcode!
}
else
{
// Typed text...append to Textbox f.e.
}
}
}
What I feel odd about:
- The locking
- The handling of T
- The thread