1

原文如下。让我尝试更详细地解释我为什么要这样做。

我有一个收听分享魅力请求的页面:

void Page_Loaded(object sender, RoutedEventArgs e)
{
    m_transferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.GetForCurrentView();
    m_transferManager.DataRequested += TransferManager_DataRequested;
}

当事件触发 ( TransferManager_DataRequested) 时,它不会在 UI 线程上触发:

void TransferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
    var data = args.Request.Data;
          // More related stuff omitted - not important.
    data.SetDataProvider(StandardDataFormats.Bitmap, GetImage_DelayRenderer);
}

现在,当GetImage_DelayRender被调用时,它也不会在 UI 线程上被调用。但是,在其中,我需要做一堆与 UI 相关的事情。具体来说,我需要调用一个仅适用于 UI 的方法(这是我在其他地方使用的方法,我想重用它的逻辑)。该方法被调用GetImageAsync并且它需要在 UI 上运行,因为它与 WriteableBitmap 进行了一堆交互。它还执行一堆异步操作(例如写入流等),这就是它是异步的原因。GetImageAsync()我会尽可能短地关闭 UI 。

如下GetImage_DelayRender所示:

private async void GetImage_DelayRenderer(DataProviderRequest request)
{
    var deferral = request.GetDeferral();
    await Dispatcher.RunTask(async () => // RunTask() is an extension method - described in the original question below.
        {
            try
            {
                var bitmapStream = await GetImageAsync();
                request.SetData(RandomAccessStreamReference.CreateFromStream(bitmapStream));
            }
            catch
            {
            }
        });

    deferral.Complete();
}

我想知道的是,实现上述调用的最正确方法是什么Dispatcher.RunTask()(这是我的 hack 扩展方法)。

----- 开始原始消息 --------

假设我有以下任务:

private async Task SomeTask()
{
   await Task.Delay(1000);
   // Do some UI and other stuff that may also be async
}

编辑(澄清):我不想阻止用户界面。我要执行的任务(即使在示例中,如果您阅读它)不会阻塞 UI。我只希望任务在 UI 的上下文中运行,因为它是同步部分。

我想在 UI 线程上的代码上运行它作为异步操作。Dispatcher.RunXXX() 方法执行一个操作,这意味着它们将运行该操作并在完成时通知您。这还不够好。我需要整个任务在 UI 线程上运行(因为如果我从 UI 线程运行它就会执行),然后在完成后通知我。

我能想到的唯一方法是使用 Dispatcher.RunXXX() 方法来执行一个匿名委托,该委托将我的方法中的局部变量设置为任务,然后等待...

public async static Task RunTask(this CoreDispatcher dispatcher, Func<Task> taskGiver)
{
    Task task = null;
    await dispatcher.RunAsync(() => task = taskGiver());
    await task;
}

这看起来非常丑陋。有更好的方法吗?

Edit2:伙计们-阅读此代码-如果我使用我拥有的 RunTask() hack 执行上面的第一个代码块,它不会阻塞 Task.Delay() 上的 UI...

4

3 回答 3

5

我想在 UI 线程上的代码上运行它作为异步操作。

然后运行它:

async void MyEventHandler(object sender, ...)
{
  await SomeTask();
}

更新:

我不确定这是一个“合法”操作,但您可以通过捕获CoreDispatcherUI 处于活动状态并稍后调用来安排该方法在 UI 线程上运行RunAsync

private async void GetImage_DelayRenderer(DataProviderRequest request)
{
  var deferral = request.GetDeferral();
  Task task = null;
  await coreDispatcher.RunAsync(() => { task = SomeTask(); });
  await task;
  deferral.Complete();
}
于 2013-01-08T13:40:30.367 回答
1

我没有时间做一个完整的解决方案,所以希望你仍然会发现这很有用......

首先,正如其他人所指出的那样,您不能在 UI 线程上运行某些东西并且不让它阻塞 UI 线程。讨论完毕。你所说的你需要的是在非 UI 线程上运行的东西,并定期通知 UI 线程有需要处理的更新。

要做到这一点,你需要这样的东西......

public class LongTask
    {
        public event EventHandler MyEvent;

        public void Execute()
        {
            var task = Task.Factory.StartNew(() =>
            {

                while (true)
                {
                    // condition met to notify UI
                    if (MyEvent != null)
                        MyEvent(this, null);
                }
            });
        }
    }

然后在您的 UI 中,执行类似...

private void button_Click(object sender, RoutedEventArgs e)
        {
            var test = new LongTask();
            test.MyEvent += test_MyEvent;
            test.Execute();
        }

        void test_MyEvent(object sender, EventArgs e)
        {
            Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                test.Text += " bang ";
            });

您显然可以使用类似 MVVM 的方式以更简洁的方式实现这一点,但这是基本思想。}

于 2013-01-08T14:51:49.850 回答
0

我已经这样做了:

public static Task<string> GetResultAsync()
{
    return Task<string>.Factory.StartNew(() => GetResultSync());
}

在用户界面中:

private async void test()
{

    string result = await GetResultAsync();

    // update UI no problem  
    textbox.Text = result;  

}
于 2013-01-08T13:37:32.697 回答