1

私が達成しようとしていることを複製する例がここにあります。次のコードが示すように、ビューにバインドされたObservableCollectionプロパティを更新するViewModelがあります。通常、モデルから取得した結果からコレクションを更新しますが、この例で十分であることを願っています。

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Controls;

namespace MVVMWpf.ViewModel
{
    public class ListViewModel
    {

        public ObservableCollection<int> SomeObjectCollection { get; set; }

        public ListViewModel()
        {

            SomeObjectCollection = new ObservableCollection<int>();

        }

        public void Do()
        {
             for (int i = 1; i < 1000000; i++)
             {
                 int i1 = i;
                 SomeObjectCollection.Add(i1);
             }
        }

    }
}

残念ながら、これはこのUIをブロックします。ループが完了するまで実行された場合にのみ、ビューが更新されます。私がそれを解決した方法は、MVVMの概念を破ります。だから私はあなたの助けが必要です。私はこのようにしました。

public class ListViewModel
{
    private delegate void LongRunningProcess();
    public ObservableCollection<int> SomeObjectCollection { get; set; }
    private ListBox listBox;
    public ListViewModel(ListBox listBox)
    {
        this.listBox = listBox;
        SomeObjectCollection = new ObservableCollection<int>();

    }

    public void Do()
    {
        Thread thread = new Thread(() =>
        {
           for (int i = 1; i < int.MaxValue; i++)
           {
               int i1 = i;
               listBox.Dispatcher.Invoke(
                   new LongRunningProcess(() =>
                   SomeObjectCollection.Add(i1);
                 }});

        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
    }

}

ご覧のとおり、ViewModelはUIからlistBox要素を認識しています。また、MVVMダイアグラムを見ると、ビューのみがバインディングを介してViewModelへの参照を持っている必要があります。この問題をどのように克服しますか?ありがとう。

4

2 に答える 2

3

あなたのループが画面への更新を解き放つようにする必要があります - ある種の DoEvents() が行います:

public static void DoEvents() 
{ 
    Application.Current.Dispatcher.Invoke(
    DispatcherPriority.Background,new Action(delegate { })); 
}

それを追加して、ループ内から呼び出します。


タイマーを別のオプションとして使用すると、コードは次のようになります。

private System.Timers.Timer operationsTimer = new System.Timers.Timer();
private int x;

あなたのctorで:

operationsTimer.Elapsed += new System.Timers.ElapsedEventHandler 
(operationsTimer_Elapsed);
operationsTimer.Enabled = true;

あなたのタイマーが過ぎました:

operationsTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{ 
    operationsTimer.Enabled = false;
    //add item to collection code
    x++;
    if(x<100)
        operationsTimer.Enabled = true;
}
于 2012-05-14T21:26:25.210 に答える
0

進行状況と完了したイベントを報告する機能を備えた非同期タスクを実行する簡単な方法であるBackgroundWorkerの使用を検討してください。何よりも、BackgroundWorker の関数は UI スレッドに同期されるため、ディスパッチャーで何も呼び出す必要はありません。

于 2012-05-17T21:38:02.517 に答える