我正在C#中构建一个小型WinForms应用程序,该应用程序将与条形码扫描器连接,并允许我通过扫描条形码,对它们执行操作然后发送信息来签入/签出标有条形码的项目回到中央服务器。我想继续检查两件事:
还会有GUI的更新(顶部的“ONLINE”/“OFFLINE”消息,如果没有连接扫描仪,则无法使用GUI)
我能够实现这一目标的方法是使用两个持续运行的BackgroundWorkers,轮询设备状态并经常报告。我不确定这种“常量”使用是否是使用BackgroundWorker对象的正确方法。
在C#WinForms应用程序中是否有更好/更清晰的方法来完成这些背景检查?
答案 0 :(得分:1)
对于扫描仪界面,最好将逻辑封装在一个或多个类中,并使设备级别的东西远离UI。
网络连接监视器也是如此;这听起来足够复杂,可以进入自己的班级。
如果您正在轮询硬件,最好使用System.Threading.Timer
来驱动它而不是BackgroundWorker
。
这是一个示例控制台应用程序,它有一个简单的实现,逻辑被分成不同的类。
重要的是Timer
,它驱动它。它将在一个单独的线程上定期回调,并从中调用poll()
。请注意,因为它在一个单独的线程中回调,所以在某些情况下你可能需要使用锁定,而且你不能直接从它调用UI。
对于这个简单的类,bool正在原子操作中更新,因此我们不需要任何同步。
希望你能看到我将多线程逻辑分离成一个单独的类的意思。
using System;
using System.Threading;
namespace Demo
{
// This represents the interface to the scanner hardware.
sealed class BarcodeScannerDevice
{
int counter;
public bool IsConnected()
{
return (++counter%4) != 0; // Make it change every 4 calls.
}
}
// This class has the responsibility of polling the scanner device to determine its status.
sealed class BarcodeScannerMonitor
{
readonly Timer timer;
readonly BarcodeScannerDevice scanner;
bool isConnected;
public BarcodeScannerMonitor(BarcodeScannerDevice scanner)
{
this.scanner = scanner;
timer = new Timer(poll, null, Timeout.Infinite, Timeout.Infinite);
}
public bool IsConnected
{
get
{
return isConnected;
}
}
public void StopPolling()
{
timer.Change(Timeout.Infinite, Timeout.Infinite);
}
public void StartPolling(TimeSpan pollingInterval)
{
timer.Change(TimeSpan.Zero, pollingInterval);
}
void poll(object state)
{
isConnected = scanner.IsConnected();
}
}
static class BarcodeScannerMonitorFactory
{
// You probably want to encapsulate this kind of coupling and
// keep it away from the UI, so we use a factory.
public static BarcodeScannerMonitor Create()
{
var scanner = new BarcodeScannerDevice();
return new BarcodeScannerMonitor(scanner);
}
}
class Program
{
void run()
{
var scannerStatus = BarcodeScannerMonitorFactory.Create();
scannerStatus.StartPolling(TimeSpan.FromSeconds(1));
while (true)
{
Console.WriteLine("Scanner is " + (scannerStatus.IsConnected ? "connected" : "disconnected"));
Thread.Sleep(500);
}
}
static void Main()
{
new Program().run();
}
}
}
另请注意,我没有处理过计时器。正确的代码应该向BarcodeScannerMonitor
类添加一个Dispose()
方法来处理计时器。
您可能还想为类创建接口,而是使用UI中的接口,这有助于单元测试。
最后,您可能会注意到Microsoft建议将Windows窗体计时器用于Windows窗体程序,但我认为对于这种硬件轮询System.Threading.Timer更好。
答案 1 :(得分:1)
我已经将BackgroundWorkers用于这种类型的应用程序。 BGWorkers非常适合WinForm应用程序,他们共享线程池。 BGWorker只是抽象出更细粒度的线程。当您调用RunWorkerAsync时,将在调用线程中引发DoWork事件。在你的工作线程中,允许调用Sleep;如果您正在检查仪器的状态,那么每1000或2000毫秒的某些内容可能会报告状态,并且不会使GUI充满事件。使用BGWorker中的ReportProgress在GUI线程中引发ProgressChanged事件;你可以在事件args中传递数据。当线程完成时,它会引发RunWorkerCompleted事件。您可以通过将CancellationPending属性设置为true来显式停止线程运行。
这就是设备方面。对于可用的网络连接,您可以想象网络是一个设备并使用相同的一般策略。在我的头脑中,我不清楚我用什么来“ping”网络。