2

私の疑似ソリューションについては、投稿の下部を参照してください。

もう一度、私は完全にそして完全にこれに固執しています。私は理解しようとして何時間も費やしました-そしてそうです、コードビハインドのスレッド化について何もせずに、単一のcollectionviewsourceを美しく動作させることができます。

ページに2つのcollectionviewsourcesを追加するだけで、スレッドの問題が発生することに気付いたときのショックを想像してみてください。私は昨夜、C#5のAsyncとMSDNのものを読んで数時間を過ごしましたが、今日仕事に取り掛かり、これを実現する方法を理解できません。

以下のコードは、私が燃やしたので助けを求めて泣き言を言う前に私が行った最後の試みです。おそらく、これを行う方法を理解しようとするのに少し時間がかかりすぎます。1つのcollectionviewsourceを完了してから別のコレクションを開始する必要があることを理解したので、AwaitTask.ContinueWithなどを試して次々にチェーンを試みました。

スレッド内の両方のタスクセットを正しく並べるのは非常に難しいようです。または、私はまだ根本的な何かを誤解しています。

誰かがWPFUIのいくつかのコントロールに非同期でデータを入力する方法をアドバイスできれば、私は非常に感謝しています。

アプリケーション自体は使い捨てのアプリケーションであり、Accessデータベースにリンクされており、適切なコードベースに実装するためにスレッド化を十分に流暢に行えるようにするために使用しています。私はそれから遠く離れています!

より完全なコードサンプルと回答に従って行われた調整で更新されました:

Private Async Sub MainWindowLoaded(sender As Object, e As RoutedEventArgs) Handles MyBase.Loaded
InitializeComponent()

Dim personSetViewSource As System.Windows.Data.CollectionViewSource = CType(Me.FindResource("personSetViewSource"), System.Windows.Data.CollectionViewSource)
Dim contactSetViewSource As System.Windows.Data.CollectionViewSource = CType(Me.FindResource("contactSetViewSource"), System.Windows.Data.CollectionViewSource)

Dim personList = Await Task.Run(Function() personSet.personList)
personSetViewSource.Source = personList

Dim contactList = Await Task.Run(Function() contactSet.contactList)
contactSetViewSource.Source = contactList

End Sub`

ObservableCollectionExクラス:

public class ObservableCollectionEx<T> : ObservableCollection<T>
{
public override event NotifyCollectionChangedEventHandler CollectionChanged;

protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
    using (BlockReentrancy())
    {
        NotifyCollectionChangedEventHandler collectionChanged = this.CollectionChanged;
        if (collectionChanged != null)
            foreach (NotifyCollectionChangedEventHandler nh in collectionChanged.GetInvocationList())
            {
                DispatcherObject dispObj = nh.Target as DispatcherObject;
                if (dispObj != null)
                {
                    Dispatcher dispatcher = dispObj.Dispatcher;
                    if (dispatcher != null && !dispatcher.CheckAccess())
                    {
                        NotifyCollectionChangedEventHandler nh1 = nh;
                        dispatcher.BeginInvoke(
                            (Action) (() => nh1.Invoke(this,
                                                       new NotifyCollectionChangedEventArgs(
                                                           NotifyCollectionChangedAction.Reset))),
                            DispatcherPriority.DataBind);
                        continue;
                    }
                }
                nh.Invoke(this, e);
            }
    }
}
}

イベントオーバーライドが必要なため、このクラスをVBに変換できないことに注意してください。私が試した別のバリエーションですが、スレッドの所有権に再び反することになります。2つのcollectionviewsの問題は解決策になりません。それが、基になるコレクションがそのコレクションに適していないためなのか、それとも実際にはそのように機能することを意図していないためなのかはわかりません。私は近づきますが葉巻はありません。

 Dim CarePlanList = Task.Run(Function() CarePlanSet.CarePlanList)
    Dim rcpdList = Task.Run(Function() rcpdSet.rcpdList)

    Dim tasks() As Task = {CarePlanList, rcpdList}
    Dim t = New TaskFactory
    Await t.ContinueWhenAll(tasks, Sub()
                                       carePlanSetViewSource.Source = CarePlanList
                                       rcpdSetViewSource.Source = rcpdList
                                   End Sub)

今朝のフィードバックと調査の組み合わせに基づいて、それを行う方法を見つけました。WPFのSTAThreadモデルを考えると、2つのコレクションビューを非同期で構築すること自体はやや非現実的です。ただし、1つのHASが完了していることを確認し、非同期の一部を1つのエンティティクラスからシフトするだけで、これはもっともらしいものになります。

代わりに、基礎となるクラスが独自のAsyncメソッドを使用してデータを構築する最初のタスクを実行します。次に、2番目のコレクションビューを起動する前に、完了したかどうかをテストします。このようにして、コンテキストやディスパッチャオブジェクトについて心配する必要はありません。2番目のコレクションは非同期を使用しません。

    Dim personList = Task(Of List(Of person)).Run(Function() personSet.personList)
    Dim contactList = Task(Of ObservableCollectionEx(Of contact)).Run(Function() contactSet.contactList)

    contactSetViewSource.Source = contactList.Result
    If contactList.IsCompleted Then personSetViewSource.Source = personList.Result

これは本当にコンセプト研究のための実験的なプロジェクトです。たまたま、このような2つのリストをこのように作成したいという考えは、それほど有用ではありませんが、データ量の多いインターフェイスを非同期で作成できると便利な場合があります。

4

1 に答える 1

1

2つのコードサンプルにはそれぞれ、すぐに飛び出す問題があります。

最初にあなたが待っているのはtask1、次のコードが多いと思いますが、task1基本的にはファイアアンドフォーゲット操作を開始してUIスレッド(Dispatcher.BeginInvoke)に戻すだけなので、実際には非同期で待機するものは生成されません。

2番目の主な問題は、インスタンスのセットアップを大量に実行し、Taskそれらを継続してチェーンしているが、action2 Taskチェーン全体のルートであると思われるを開始しないため、アクティビティがまったく発生しないことです。BackgroundWorkerこれは、これまでに呼び出されたことのないもので得られるものと似ていますRunWorkerAsync

これを適切に機能させ、頭を回転させないようにするには、非同期なしでこのブロック全体を記述し、すべてが期待どおりに読み込まれることを確認することから始めることをお勧めしますが、UIのロックアップは避けたいと思います。Async / Awaitは、最小限の構造変更でそのようなコードに追加されるように設計されています。Task.Runasync and awaitと一緒に使用すると、コードを非同期にすることができます。

非同期を開始せずに、基本パターンの擬似コードを次に示します。

PersonSetList = LoadData1()
CVS1.Source = PersonSetList
ContactList = LoadData2()
CVS2.Source = ContactList

そして今非同期を追加します:

PersonSetList = await Task.Run(LoadData1())
CVS1.Source = PersonSetList
ContactList = await Task.Run(LoadData2())
CVS2.Source = ContactList

これにより、個人データをロードするタスクを開始し、すぐにWindowLoadedメソッドから戻って、UIがレンダリングを続行できるようにします。そのデータがロードされると、元のスレッドの次の行に進み、データをUIにプッシュします(レンダリング中にUIの速度が低下する可能性があります)。その後、連絡先データに対して同じことを行います。継続を完了するためにawaitがUIスレッドに戻っているため、Dispatcherは明示的に必要ないことに注意してください。

于 2013-01-17T15:34:35.213 に答える