Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在调度程序上运行异步操作赢得';完成或冻结用户界面_C#_Wpf_Asynchronous_Mvvm_Drag And Drop - Fatal编程技术网

C# 在调度程序上运行异步操作赢得';完成或冻结用户界面

C# 在调度程序上运行异步操作赢得';完成或冻结用户界面,c#,wpf,asynchronous,mvvm,drag-and-drop,C#,Wpf,Asynchronous,Mvvm,Drag And Drop,我只想运行Drop action asynchronous,在移动大量数据时显示一个繁忙的对话框。因为源集合只能由调度程序访问,所以我需要调用它 这样,等待的调用将永远不会完成/对话框将永远不会关闭。 在此处使用Invoke而不是InvokeAsync将导致NotSupportedException,原因与上述相同 public async void Drop(IDropInfo dropInfo) { MainViewModel.Current.ShowBusyDialog();

我只想运行Drop action asynchronous,在移动大量数据时显示一个繁忙的对话框。因为源集合只能由
调度程序访问,所以我需要调用它

这样,等待的调用将永远不会完成/对话框将永远不会关闭。
在此处使用
Invoke
而不是
InvokeAsync
将导致
NotSupportedException
,原因与上述相同

public async void Drop(IDropInfo dropInfo)
{
    MainViewModel.Current.ShowBusyDialog();
    await Task.Run(() =>
    {
        // This would crash:
        // Dispatcher.CurrentDispatcher.Invoke(() =>
        await Dispatcher.CurrentDispatcher.InvokeAsync(() =>
        {
            var data = dropInfo.Data as SomeObject;
            var collection = (ObservableCollection<SomeObject>)
                                   ((ICollectionView) dropInfo.TargetCollection).SourceCollection;
            if (data != null)
            {
                // Operate with collection
            }
            else if (dropInfo.Data is IEnumerable<SomeObject>)
            {
                // Operate with collection
            }
        });
    });
    // Never reaches this point
    MainViewModel.Current.CloseDialog();
}
更新--------------------------------------------------------------------


创建并上载一个示例以显示问题:

您正在UI dispatcher上运行所有这些代码

await Dispatcher.CurrentDispatcher.InvokeAsync(() =>
你需要做的是

  • 在此处运行所有工作
    等待任务。运行(()=>
  • 完成后,使用dispatcher更新UI
  • 顺便说一句,如果您要更新实现INotifyPropertyChanged的绑定属性,您甚至不必使用dispatcher。从任何线程更新属性。绑定将自动将更新打包到UI线程

    您在代码中使用的Dispatcher是UI Dispatcher。这意味着,当您向其分派方法时,您正在UI线程上执行该方法。仅在最后可能的时刻使用Dispatcher,并且仅在

    • 从其他线程更新UI
    • 在应用程序生命周期事件之后安排UI工作(请参阅DispatcherPriority枚举)
    还有一个问题

    Dispatcher.CurrentDispatcher
    检索…当前Dispatcher。什么是当前Dispatcher?它是当前线程的Dispatcher。在第一个示例中,当前线程是后台线程。您希望从UI使用Dispatcher。这是第一个示例,但经过调整

    public async void Drop(IDropInfo dropInfo)
    {
        // We are in the UI thread here!
        MainViewModel.Current.ShowBusyDialog();
        // as we are on the UI thread, no worries touching the collection here
        var collection = (ObservableCollection<SomeObject>)
             ((ICollectionView)dropInfo.TargetCollection).SourceCollection;
        // as we are on the UI thread, this is the UI dispatcher.
        var dispatcher = Dispatcher.CurrentDispatcher;
        await Task.Run(() =>
        {
            // We are on a background thread here!
            var data = dropInfo.Data as SomeObject;
            YourResults results = null;
            if (data != null)
            {
                results = WhateverProcessingTakesALongTime();
            }
            else if (dropInfo.Data is IEnumerable<SomeObject>)
            {
                results = SameHereIHaveNoIdea(dropInfo.Data);
            }
            // Now, let's update the UI on the UI thread.
            await dispatcher.InvokeAsync(() =>
            {
                UpdateStuffWithStuff(collection, results);
            });
        });
        MainViewModel.Current.CloseDialog();
    }
    
    public异步void Drop(IDropInfo-dropInfo)
    {
    //我们在这里的UI线程中!
    MainViewModel.Current.ShowBusyDialog();
    //因为我们是在UI线程上,所以不必担心这里的集合
    变量集合=(可观测集合)
    ((ICollectionView)dropInfo.TargetCollection).SourceCollection;
    //由于我们在UI线程上,这是UI调度程序。
    var dispatcher=dispatcher.CurrentDispatcher;
    等待任务。运行(()=>
    {
    //我们在这里的背景线程!
    var data=dropInfo.data作为SomeObject;
    YourResults=null;
    如果(数据!=null)
    {
    结果=WhateVerProcessingTakesOngTime();
    }
    else if(dropInfo.Data是IEnumerable)
    {
    结果=SameHereIHaveNoIdea(dropInfo.Data);
    }
    //现在,让我们在UI线程上更新UI。
    等待dispatcher.InvokeAsync(()=>
    {
    UpdateStuffWithStuff(收集、结果);
    });
    });
    MainViewModel.Current.CloseDialog();
    }
    
    我遗漏了什么,或者我如何才能让它像预期的那样工作

    正如其他人所指出的,
    Task.Run
    将在后台线程上执行工作,
    Dispatcher.InvokeAsync
    将在UI线程上执行工作。因此,除了UI线程之外,您的代码实际上没有在任何地方执行任何实际工作,这就是它“阻塞”的原因

    最干净的解决方案是在UI线程上从UI对象复制所有必要的信息,在线程池上执行任何后台工作,最后(如果需要)将任何结果复制回UI对象

    public async void Drop(IDropInfo dropInfo)
    {
      MainViewModel.Current.ShowBusyDialog();
    
      // First, copy the data out of the UI objects.
      List<SomeObject> list;
      var data = dropInfo.Data as SomeObject;
      var collection = (ObservableCollection<SomeObject>)
                                   ((ICollectionView) dropInfo.TargetCollection).SourceCollection;
      if (collection != null)
      {
        list = collection.ToList();
      }
      else if (dropInfo.Data is IEnumerable<SomeObject>)
      {
        list = ((IEnumerable<SomeObject>)dropInfo.Data).ToList();
      }
    
      // Then do the background work.
      await Task.Run(() =>
      {
        // Operate with `list`
      });
    
      // Finally, update the UI objects after the work is complete.
      MainViewModel.Current.CloseDialog();
    }
    
    public异步void Drop(IDropInfo-dropInfo)
    {
    MainViewModel.Current.ShowBusyDialog();
    //首先,从UI对象中复制数据。
    名单;
    var data=dropInfo.data作为SomeObject;
    变量集合=(可观测集合)
    ((ICollectionView)dropInfo.TargetCollection).SourceCollection;
    if(集合!=null)
    {
    list=collection.ToList();
    }
    else if(dropInfo.Data是IEnumerable)
    {
    list=((IEnumerable)dropInfo.Data).ToList();
    }
    //然后做背景工作。
    等待任务。运行(()=>
    {
    //用`列表操作`
    });
    //最后,在工作完成后更新UI对象。
    MainViewModel.Current.CloseDialog();
    }
    
    当我在任务中运行代码时,它也会抛出一个NotSupportedException。该集合是UI上DataGrid的集合,它绑定到一个具有CollectionViewSource的对象,CollectionViewSource只是扩展了一个ObservableCollection。因此UI(DataGrid)要更新集合属性,而不是我的ViewModel或绑定属性吗directly@DanielINotifyPropertyChanged属性会自动封送。很遗憾,INotifyCollectionChanged集合不会自动封送更改。您必须在本地完成任务中的工作,然后使用Dispat更新OCcher.那么,我如何不通过引用调用来完成任务中的工作呢?我正在使用Gong.DragDrop框架通过附加属性绑定Drop处理程序。您需要在后台线程上执行所有长期工作。最后,一旦您的工作完成,您将使用Dispatcher将更新应用到ObservableCollection。您的代码不会显示任何更新这是一项长期的工作,因此我无法告诉您在后台线程上执行的操作和在UI dispatcher上执行的操作之间的界限在哪里。此外,我还为答案添加了一个更新。感谢您的解释和示例代码,我用更新的代码和解释编辑了我的问题。就像标记的副本和许多其他类似的任务一样例如,对于
    Dispatcher.Invoke()
    InvokeAsync()
    要工作,UI线程必须可以自由处理这些调用。如果您正在调用
    Drop()
    在UI线程上,并且不从中返回,UI线程被阻塞,无法处理这些调用。@PeterDuniho我认为这根本不是重复,因为我的问题不仅仅关注调用,而是更多地关注调用
    public async void Drop(IDropInfo dropInfo)
    {
        // We are in the UI thread here!
        MainViewModel.Current.ShowBusyDialog();
        // as we are on the UI thread, no worries touching the collection here
        var collection = (ObservableCollection<SomeObject>)
             ((ICollectionView)dropInfo.TargetCollection).SourceCollection;
        // as we are on the UI thread, this is the UI dispatcher.
        var dispatcher = Dispatcher.CurrentDispatcher;
        await Task.Run(() =>
        {
            // We are on a background thread here!
            var data = dropInfo.Data as SomeObject;
            YourResults results = null;
            if (data != null)
            {
                results = WhateverProcessingTakesALongTime();
            }
            else if (dropInfo.Data is IEnumerable<SomeObject>)
            {
                results = SameHereIHaveNoIdea(dropInfo.Data);
            }
            // Now, let's update the UI on the UI thread.
            await dispatcher.InvokeAsync(() =>
            {
                UpdateStuffWithStuff(collection, results);
            });
        });
        MainViewModel.Current.CloseDialog();
    }
    
    public async void Drop(IDropInfo dropInfo)
    {
      MainViewModel.Current.ShowBusyDialog();
    
      // First, copy the data out of the UI objects.
      List<SomeObject> list;
      var data = dropInfo.Data as SomeObject;
      var collection = (ObservableCollection<SomeObject>)
                                   ((ICollectionView) dropInfo.TargetCollection).SourceCollection;
      if (collection != null)
      {
        list = collection.ToList();
      }
      else if (dropInfo.Data is IEnumerable<SomeObject>)
      {
        list = ((IEnumerable<SomeObject>)dropInfo.Data).ToList();
      }
    
      // Then do the background work.
      await Task.Run(() =>
      {
        // Operate with `list`
      });
    
      // Finally, update the UI objects after the work is complete.
      MainViewModel.Current.CloseDialog();
    }