从 NFC-Tag-Reader 事件处理程序更新 UI 非常慢

Updating UI from NFC-Tag-Reader event handler is very slow

我有一个 ACR122U NFC reader,我可以在其中读取 MiFare UltraLight NFC 标签。我为 reader 下载了一个框架。但是调用事件处理器时,GUI的更新很慢。

我在线程编程和事件处理方面经验不多。也许我的问题的解决方案很简单。

然而,它只在 UpdateInterface() 方法中更新 'label by label',并且一次全部更新一个。我可以看到每个标签都消失了。所以 UI 的更新非常慢。

我想这与 reader-类 在后台调用的(硬件)事件有关。

该程序通常运行正常,只是速度很慢。

private void StartMonitor()
{
        //Function called when starting the Windows Forms Application
        IMonitorFactory monitorfactory = MonitorFactory.Instance;
        monitor = monitorfactory.Create(SCardScope.System);
        monitor.Start(ReaderNames[0]);
        monitor.StatusChanged += Monitor_StatusChanged;
}

private void Monitor_StatusChanged(object sender, StatusChangeEventArgs e)
{
        strPassportNo = "";
        strPassportNo = lPassportNumberNo.Text;

        if (e.NewState.ToString().ToLower().Contains("empty".ToLower()) == true)
        {
            //Interface should be deleted all by once now as no NFC tag is on the reader.
            UpdateInterface();
        }
}

private void UpdateInterface()
{
        if (InvokeRequired)
        {
            this.BeginInvoke(new Action(() =>
            {
                lPassportNumberNo.Text = "";
                lPassengerName.Text = "";
                lPax.Text = "";
                lTableNo.Text = "";
                lRoomNo.Text = "";
                pbTables.Hide();
                pbPasspic.Hide();
                this.BackgroundImage = BackgroundWelcome;
            }));
        }
        else
        {
            lPassportNumberNo.Text = "";
            lPassengerName.Text = "";
            lPax.Text = "";
            lTableNo.Text = "";
            lRoomNo.Text = "";
            pbTables.Hide();
            pbPasspic.Hide();
            this.BackgroundImage = BackgroundWelcome;
        }
}

预期的输出应该是 UI 的性能比实际快很多。或者至少其中包含数据的标签应该立即出现和消失。

不确定这实际上是什么:this.BackgroundImage = BackgroundWelcome;但是真的有必要每次都分配这样的静态数据吗?

此外,我认为您的更新事件的频率不会太高 - 屏幕上的信息应该是人类可读的,因此它绝对只是在读取整个标签后所需的所有数据的一次更新。如果每个标签都有很多读取事件 - 然后将它们全部存储到临时列表中并在读取端更新表单(整个标签)。 当我说 "tag" 时,我指的是整个 mifare 卡内容。

UI-Update 在我的例子中只是清除标签(参见 UpdateInterface())。但是我找到了 "error"。这是我在每次刷新时加载的“.jpg”文件和 BackgroundImage。虽然是 "just" 1280x1024,但前端更新太多了。

当我清除图像时,立即刷新。

我现在的解决方案是:

this.BackgroundImage = ((System.Drawing.Image(resources.GetObject("$this.BackgroundImage")));
this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom;
this.SetStyle(System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer | System.Windows.Forms.ControlStyles.AllPaintingInWmPaint, true);

在Form1.Designer.cs

并使用以下 class 暂停和恢复布局(我在 Internet 某处找到的)

public static class ControlHelper
{
    #region Redraw Suspend/Resume
    [DllImport("user32.dll", EntryPoint = "SendMessageA", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)]
    private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);
    private const int WM_SETREDRAW = 0xB;

    public static void SuspendDrawing(this Control target)
    {
        SendMessage(target.Handle, WM_SETREDRAW, 0, 0);
    }

    public static void ResumeDrawing(this Control target) { ResumeDrawing(target, true); }
    public static void ResumeDrawing(this Control target, bool redraw)
    {
        SendMessage(target.Handle, WM_SETREDRAW, 1, 0);

        if (redraw)
        {
            target.Refresh();
        }
    }
    #endregion
}

另外,我将“.jpg”文件更改为“.bmp”文件。现在界面流畅快速。

还有@Jesting:

Not sure what is this actually: this.BackgroundImage = BackgroundWelcome; but is it really necessary to assign such static data every time?

不,我本可以找到更好的解决方案。但是由于应用程序非常小而且我只有四个背景图像,所以我发现 "hard code" 它更容易。

最后,标签 (-reader) 并不是真正的问题。

为了更新 UI,我使用了这个非常简单的函数:

private void UpdateInterface(PassportDatabaseReader.Passport _passport)
    {
        if (InvokeRequired)
        {
            this.BeginInvoke(new Action<PassportDatabaseReader.Passport>(UpdateInterface), new object[] { _passport });
            return;
        }
        else
        {
            ControlHelper.SuspendDrawing(this);
            new Thread(() => ChangeBackground(BackgroundPassport)).Start();
            lPassportNumberNo.Text = _passport.PassportNo;
            strPassportNo = _passport.PassportNo;
            lPassengerName.Text = _passport.Name.Replace('$','\n');
            lPax.Text = _passport.Pax;
            lTableNo.Text = _passport.TableNo;
            lRoomNo.Text = _passport.RoomNo;
            pbTables.Hide();
            try
            {
                pbPasspic.Load("..\..\Pics\" + _passport.PassportNo + ".png");
                pbPasspic.Show();
                pbTables.ImageLocation = "..\..\Tischordnung_" + _passport.TableNo + ".png";
                pbTables.Show();
            }
            catch (Exception)
            {
                throw;
            }
            ControlHelper.ResumeDrawing(this);
        }
    }

该应用程序是一个 "wedding passport" reader,它在屏幕上显示姓名、客人房间号、table 号码以及 "passport" 图片。