0

我正在用 C# 构建一个小型 WinForms 应用程序,该应用程序将与条形码扫描仪交互,并允许我通过扫描条形码、对其执行操作然后将信息发送回以检入/检出标有条形码的项目中央服务器。不过,我想不断检查两件事:

  1. 条码扫描器是否连接到设备?
  2. 有网络连接吗?如果为false,则在本地缓存数据,如果为true,则如果有本地数据,则开始向中央服务器传输数据

GUI 也会有更新(顶部显示“ONLINE”/“OFFLINE”消息,如果没有连接扫描仪则无法使用 GUI)

我可以做到这一点的方法是使用两个连续运行的 BackgroundWorker,轮询设备状态并经常报告。我不确定这种“恒定”使用是否是使用 BackgroundWorker 对象的正确方法。

在 C# WinForms 应用程序中完成这些类型的背景检查是否有更好/更清洁的方法?

4

2 回答 2

1

对于扫描仪接口,最好将逻辑封装在一个或多个类中,并使设备级的东西远离 UI。

网络连接监视器也是如此;这听起来很复杂,可以进入自己的课程。

如果您正在轮询硬件,实际上使用 aSystem.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 更好。

于 2013-05-30T19:55:56.830 回答
1

我已经将 BackgroundWorkers 用于这种类型的应用程序。BGWorkers 非常适合 WinForm 应用程序,它们共享线程池。BGWorker 只是抽象出更细化的线程。当您调用 RunWorkerAsync 时,会在调用线程中引发 DoWork 事件。在您的工作线程中,可以调用 Sleep;如果您正在检查仪器的状态,那么每 1000 或 2000 毫秒之类的东西可能会报告状态,并且不会让 GUI 充满事件。使用 BGWorker 中的 ReportProgress 在 GUI 线程中引发 ProgressChanged 事件;您可以在事件参数中传递数据。当线程完成时,它会引发 RunWorkerCompleted 事件。您可以通过将 CancellationPending 属性设置为 true 来显式停止线程运行。

这就是设备方面。对于可用的网络连接,您可以将网络想象为一个设备并使用相同的通用策略。在我的脑海中,我不清楚我会用什么来“ping”网络。

于 2013-05-30T20:09:43.913 回答