【问题标题】:How to pass progress back to UI from callback method如何从回调方法将进度传递回 UI
【发布时间】:2019-09-29 12:00:56
【问题描述】:

我正在开发一个应用程序,该应用程序使用 Jeff Kluge 的 WimgApi 包将 Windows 图像应用到磁盘。

我在获取示例回调方法以更新 UI 组件时遇到问题,特别是表单上的标签(最好是进度条)。

我尝试使用委托来设置值,但这似乎不起作用,因为我不知道如何将委托传递给回调方法。

如果我将回调方法设为非静态,我可以访问表单属性,但是我会遇到一个死锁,即使我禁用了打破死锁,它也会锁定。

我被告知要考虑使用 IProgress 和 async,但是虽然我可以更改代码以异步运行该方法(这有效且 UI 不锁定),但我仍然无法弄清楚如何让 MyCallbackMethod 发送信息返回 ui。

//Apply Image Method
public void ApplyImage()
        {
            using (WimHandle wimHandle = WimgApi.CreateFile(@"C:\osimages\test.wim",
                WimFileAccess.Read,
                WimCreationDisposition.OpenExisting,
                WimCreateFileOptions.None,
                WimCompressionType.None))
            {
                // Always set a temporary path
                WimgApi.SetTemporaryPath(wimHandle, Environment.GetEnvironmentVariable("TEMP"));

                // Register a method to be called while actions are performed by WIMGAPi for this .wim file
                WimgApi.RegisterMessageCallback(wimHandle, MyCallbackMethod);

                try
                {
                    // Get a handle to the first image in the .wim file
                    using (WimHandle imageHandle = WimgApi.LoadImage(wimHandle, 1))
                    {
                        // Apply the image contents to C:\Apply
                        // This call is blocking but WIMGAPI will be calling MyCallbackMethod() during the process
                        WimgApi.ApplyImage(imageHandle, @"X:\", WimApplyImageOptions.None);
                    }
                }
                finally
                {
                    // Be sure to unregister the callback method
                    //
                    WimgApi.UnregisterMessageCallback(wimHandle, MyCallbackMethod);
                }
            }

private static WimMessageResult MyCallbackMethod(WimMessageType messageType, object message, object userData)
        {
            switch (messageType)
            {
                case WimMessageType.Progress:  // Some progress is being sent

                    // Get the message as a WimMessageProgress object
                    //
                    WimMessageProgress progressMessage = (WimMessageProgress)message;

                    // UPDATE UI
                    //THIS IS WHERE I WANT TO SEND BACK PROGRESS INFO

                    break;


                   //REMOVED OTHER MESSAGE CASE STATEMENTS TO CONDENSE CODE


            }

            // Depending on what this method returns, the WIMGAPI will continue or cancel.
            //
            // Return WimMessageResult.Abort to cancel.  In this case we return Success so WIMGAPI keeps going

            return WimMessageResult.Success;
        }

//full example code at Example code is https://github.com/jeffkl/ManagedWimgApi/wiki/Message-Callbacks

如果我尝试在回调方法中访问 label 属性,我会收到“非静态字段、方法或属性 form1.progressLabel.text 需要对象引用”。我尝试创建一个委托,但似乎在访问回调中的方法时遇到了问题。

我观看了几个视频,并试图了解有关委托、回调和异步/后台工作程序之类的 msdn 文档,但我似乎变得更加困惑。

非常感谢我应该关注的任何指针/事情。

【问题讨论】:

  • 底层 API 函数 WIMRegisterMessageCallback 有一个 userData 参数,然后将其传递给回调方法。您可以传递一些允许检索表单的内容(例如窗口句柄)。

标签: c# winforms callback


【解决方案1】:

免责声明:我对 WimgApi 包没有任何经验。 但是WimgApi.RegisterMessageCallback 方法有一个重载,该方法采用将传递给回调的任意对象。 所以请试试这个:

WimgApi.RegisterMessageCallback(wimHandle, MyCallbackMethod, this);

在回调中:

var form = (MyForm)userData;
if (form.InvokeRequired)
{
    form.Invoke((MethodInvoker)(() => UpdateProgressUI(...)));
}
else
{
    form.UpdateProgressUI(...);
}

【讨论】:

  • 感谢克劳斯。我可以使用上面的方法访问表单属性,但我得到一个“System.InvalidOperationException”,我认为这是由于回调尝试在不在同一个线程上更新 UI 造成的?
  • 克劳斯,你的传奇 :) 我将回调从静态更改,然后使用来自 Joe Hopes 答案的 Dispatch.invoke 来运行 UI 更新代码,这有进度条/文本更新工作。
  • 如果您觉得其中一个答案可以解决您的问题,您可以将其标记为“已接受”。
【解决方案2】:

在这里做一些假设,但如果您一次只显示一个进度表,您应该能够避免存储对它的静态引用。即:

class ProgressForm
{
    private static ProgressForm staticRef;

    private void Form_Loaded(object sender, EventArgs e)
    {
        staticRef = this;
    }

    private void InternalCallback(uint m, IntPtr w, IntPtr l, IntPtr u)
    {
        // Ensure we're touching UI on the right thread
        if (Dispatcher.InvokeRequired)
        {
            Dispatcher.Invoke(() => InternalCallback(m, w, l, u));
            return;
        }

        // Update UI components
        // ....
    }

    private static uint StaticCallback(uint m, IntPtr w, IntPtr l, IntPtr u)
    {
        staticRef?.InternalCallback(m, w, l, u);

        return 0;
    }
}

【讨论】:

  • 感谢您的回复,目前我将所有内容都放在一个表单上,同时我掌握了基础知识。
  • 谢谢乔,再加上克劳斯的建议,现在可以工作了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-04
  • 2019-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-29
相关资源
最近更新 更多