0

我正在尝试从两个不同的网站获取两种类型的数据并将其绑定到一个列表,但我遇到了异步问题,我想要做的是从 rss 获取信息将其添加到列表中,然后获取信息从另一个网站将其添加到列表中,然后将两者添加到绑定的可观察集合中。但是 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

这更像是一个替代答案(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 标记为异步——并设置任务的返回)。

顺便说一句,您实际上可以加载最新的 Async-CTP3 并发布以这种方式编写的 WP 代码。大多数人都对 CTP 有点害怕。

我为此写了一篇博文,你可以在这里查看——http: //www.jaykimble.net/metro-nuggets-async-is-your-friend.aspx

于 2012-04-19T15:33:04.567 回答
1

我不会尝试像那样一个接一个地制作它们。通过像这样一个接一个地“堆叠”它们,您首先失去了异步调用的所有优势。不仅如此,在像 Windows Phone 这样的移动平台上,您必须记住网络呼叫需要排队才能有效使用天线。当您同时拨打这两个电话时,它们在同一天线连接期间被执行的机会要高得多,这是一件“好事”。

接下来,您的每个回调实际上都在更新完全独立的集合。A正在更新place集合并且B正在更新top集合。所以这不是这两个人以任何方式互相踩踏的问题。

我在这里看到的唯一真正的问题是您正在更新top设置为listBox.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 回答
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

即使使用 lambda,也有一个更优雅的解决方案 - 使用自定义 Action,其中 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 回答