0

2 つの異なる Web サイトから 2 種類のデータを取得してリストにバインドしようとしていますが、非同期に問題があります。RSS から情報を取得し、リストに追加してから情報を取得します。別のWebサイトからリストに追加してから、2つをバインドされた監視可能なコレクションに追加します。しかし、DownloadStringAsync は互いにオーバーランしていて、アプリがクラッシュします。助けてください。

私のコードは

private static ObservableCollection<Top> top= new ObservableCollection<Top>();
    private static ObservableCollection<string> place= new ObservableCollection<string>();
    // Constructor
    public MainPage()
    {
        InitializeComponent();
        if (NetworkInterface.GetIsNetworkAvailable())
        {
            LoadSiteContent_A(url1);

           LoadSiteContent_B(url2);


        }
        else
            MessageBox.Show("No Internet Connection, please connect to use this applacation");



        listBox.ItemsSource = top; //trying to bind listbox after web calls
    }





    public void LoadSiteContent_A(string url)
    {

            //create a new WebClient object
            WebClient clientC = new WebClient();


            clientC.DownloadStringCompleted += new DownloadStringCompletedEventHandler(a_DownloadStringCompleted);
            clientC.DownloadStringAsync(new Uri(url));


    }

     public void LoadSiteContent_B(string url)
    {
            //create a new WebClient object
            WebClient clientC = new WebClient();


            clientC.DownloadStringCompleted += new DownloadStringCompletedEventHandler(b_DownloadStringCompleted);
            clientC.DownloadStringAsync(new Uri(url));


    }

     public void a_DownloadStringCompleted(Object sender, DownloadStringCompletedEventArgs e)
     {
         string testString = "";


         if (!e.Cancelled && e.Error == null)
         {
             string str;

             str = (string)e.Result;

             //Various operations and parsing


                     place.Add(testString);

          }


             }


     }
    public void b_DownloadStringCompleted(Object sender, DownloadStringCompletedEventArgs e)
    {

        string testMatch = "";


        if (!e.Cancelled && e.Error == null)
        {
            string str;
            // Size the control to fill the form with a margin
            str = (string)e.Result;

               //Various operations and parsing



                top.Add(new Top(testMatch,(place.Count+1)));


            }



    }


public class TopUsers
{
    public string TopUsername { get; set; }
    public int Place { get; set; }

    public TopUsers(string topusername, int place)
    {
        this.TopUsername = topusername;
        this.Place = place;

    }
}


}
4

4 に答える 4

1

私はそのようにそれらを次々と作ろうとはしません。それらを次々に「積み重ねる」ことにより、そもそも非同期呼び出しのすべての利点が失われます。それだけでなく、Windows Phone のようなモバイル プラットフォームでは、アンテナを効率的に使用するために、ネットワーク コールがキューに入れられることを覚えておく必要があります。両方の呼び出しを同時に行うと、「良いこと」である同じアンテナ接続中に実行される可能性がはるかに高くなります。

次に、各コールバックは実際には完全に独立したコレクションを更新しています。コレクションをA更新し、コレクションを更新しています。したがって、この 2 人が互いに踏み合うことは決して問題ではありません。placeBtop

ここで私が目にする唯一の実際の問題はtoplistBox.ItemsSource. バインドされたデータへの更新を UI スレッド (別名Dispatcherスレッド) にマーシャリングして、それらにバインドされたコントロールが正しいスレッドで更新されるようにする必要があります。

したがって、コードに加えなければならない唯一の変更は、topコレクションへの新しいアイテムの追加をマーシャリングして、コールバックのDispatcherスレッドに戻すことです。Bそれは次のようになります。

public void b_DownloadStringCompleted(Object sender, DownloadStringCompletedEventArgs e) 
{ 
    string testMatch = ""; 

    if(!e.Cancelled && e.Error == null) 
    { 
        string str; 
        // Size the control to fill the form with a margin 
        str = (string)e.Result; 

        //Various operations and parsing 

        Top newTop = new Top(testMatch,(place.Count+1));

        Dispatcher.Invoke(() =>
        {
            top.Add(newTop);
        });
    } 
} 

topこれにより、アイテムをコレクションに追加する小さな部分を除いて、すべての作業は非同期/並行のままになります。

于 2012-04-19T15:57:13.480 に答える
1

これは別の答えです(AlexTheoのソリューションが機能するはずです)。

彼らが私たち (WP 開発者) に新しい Async のものを提供すると、これらすべてがずっと簡単になります。

コードは次のように記述できます。

public async MainPage()
{
    InitializeComponent();
    DoAsyncLoad();
}
private async Task DoAsyncLoad()  // note use of "async" keyword
{
   if (NetworkInterface.GetIsNetworkAvailable())
   {
        await LoadSiteContent_A("");
        await LoadSiteContent_B("");
   }
   else
        MessageBox.Show("No Internet Connection, please connect to use this applacation");

   listBox.ItemsSource = top; //trying to bind listbox after web calls
}

public async Task LoadSiteContent_A(string url)
{
     //create a new WebClient object
     WebClient clientC = new WebClient();

     var result = await clientC.DownloadStringTaskAsync(new Uri(url));
     // No need for a Lambda or setting up an event

     var testString = result; // result is the string you were waiting for (will be empty of canceled or errored) 
}
public async Task LoadSiteContent_B(string url)
{
     //create a new WebClient object
     WebClient clientC = new WebClient();

     var result = await clientC.DownloadStringTaskAsync(new Uri(url));
     // Again, no need for a Lambda or setting up an event (code is simpler as a result)
     top.Add(new Top(testMatch, place.Count + 1));
 }

さらにコードを変更する必要があります (非同期バージョンの Http 呼び出しを使用し、LoadSiteContent_A/B を非同期としてマークし、Task のリターンを設定します)。

ところで、実際に最新の Async-CTP3 をロードして、このように記述された WP コードをリリースできます。ただし、ほとんどの人は CTP を少し怖がっています。

私はこれについてブログ投稿を書きました。ここで確認できます -- http://www.jaykimble.net/metro-nuggets-async-is-your-friend.aspx

于 2012-04-19T15:33:04.567 に答える
0

まず第一に、あなたの場合、lamdbaはコールバックよりも優れていると私は信じています。ダウンロードを同期するには、LoadSiteContent_Aの完全なイベントでLoadSiteContent_Bを呼び出す必要があります。

private static ObservableCollection<Top> top= new ObservableCollection<Top>();
    private static ObservableCollection<string> place= new ObservableCollection<string>();
    private string _url1;
    private string _url2;
    // Constructor
    public MainPage(string url1, string url2)
    {
        InitializeComponent();
        if (NetworkInterface.GetIsNetworkAvailable())
        {
            _url1 = url1;
            _url2 = url2;
            LoadSiteContent_A(url1);
        }
        else
            MessageBox.Show("No Internet Connection, please connect to use this applacation");
        listBox.ItemsSource = top; //trying to bind listbox after web calls
    }

    public void LoadSiteContent_A(string url)
    {
            //create a new WebClient object
            WebClient clientC = new WebClient();
            clientC.DownloadStringCompleted += (sender, e) => { 
             string testString = "";
             if (!e.Cancelled && e.Error == null)
             {
                 string str;
                 str = (string)e.Result;
                 //Various operations and parsing
                place.Add(testString);
                LoadSiteContent_B(_url2);
              }
            };

            clientC.DownloadStringAsync(new Uri(url));
    }

    public void LoadSiteContent_B(string url)
    {
            //create a new WebClient object
            WebClient clientC = new WebClient();
            clientC.DownloadStringCompleted += (sender, e) => {/*do whatever you need*/};
            clientC.DownloadStringAsync(new Uri(url));
    }


public class TopUsers
{
    public string TopUsername { get; set; }
    public int Place { get; set; }

    public TopUsers(string topusername, int place)
    {
        this.TopUsername = topusername;
        this.Place = place;

    }
}


}
于 2012-04-19T14:55:52.230 に答える
0

ラムダを使用しても、はるかに洗練された解決策があります。T がデータ型であるカスタム アクションを使用します。

例えば:

public void LoadSiteContent_A(string url, Action<string> onCompletion)
{
        //create a new WebClient object
        WebClient clientC = new WebClient();

        clientC.DownloadStringCompleted += (s,e) =>
        {
             onCompletion(e.Result);
        };
        clientC.DownloadStringAsync(new Uri(url));
}

このメソッドを呼び出すときは、次のようなアクションを渡すことができます:

LoadSiteContent_a(yourUrlWhatever, data =>
{
   // DO SOMETHING WITH DATA
});
于 2012-04-19T16:16:50.787 に答える