views:

127

answers:

6

I'm using this to make a web request and download some data:

public partial class MainPage : PhoneApplicationPage
{
    public MainPage()
    {
        InitializeComponent();

        var client = new WebClient();

        client.DownloadStringCompleted += (s, e) => {
            textBlock1.Text = e.Result;
        };

        client.DownloadStringAsync(new Uri("http://example.com"));
    }
}

The text of textBlock1 never changes even though e.Result has the correct data. How do I update that from the callback?

Edit: If I add MessageBox.Show(e.Result); in the callback along with the textBlock1.Text assignment, both the messsage box and the text box show the correct data.

Edit Again: If I add a TextBox and set it's text right after the line textBlock1.Text line, they both show the correct text.

+1  A: 

client.DownloadStringAsync is expecting a Uri like this:

client.DownloadStringAsync(new Uri("http://example.com"));

also, shouldn't you update your TextBlock through a Dispatcher.BeginInvoke like this:

    client.DownloadStringCompleted += (s, e) => 
    { 
        if (null == e.Error) 
            Dispatcher.BeginInvoke(() => UpdateStatus(e.Result)); 
        else 
            Dispatcher.BeginInvoke(() => UpdateStatus("Operation failed: " + e.Error.Message)); 
    };
TheTodd
Updated my incorrect sample code. Dispatcher.BeginInvoke might be what I'm after. I'm a WP7 newb. Got any examples?
John Sheehan
Tried using Dispatcher.BeginInvoke, still didn't work. No update. e.Result still has the correct data.
John Sheehan
I don't think BeginInvoke should be necessary, because the DownloadStringCompleted event gets raised in the UI thread.
Matt Hamilton
+4  A: 

I think, it's a bug.

Sergey Zwezdin
I'm starting to think the same. I just tried it with a WPF Navigation app and it worked fine.
Matt Hamilton
+1  A: 
public partial class MainPage : PhoneApplicationPage
{
    public MainPage()
    {
        InitializeComponent();

        Loaded += MainPage_Loaded;
    }

    void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        var dispatcher = Deployment.Current.Dispatcher;

        var client = new WebClient();

        client.DownloadStringCompleted += (s, e) =>
        {
            var result = e.Result;
            dispatcher.BeginInvoke(
                ()=> textBlock1.Text = result
                );
        };

        client.DownloadStringAsync(new Uri("http://example.com"));
    }
}
Daniel Crenna
I tried a couple different combos with the Dispatcher, but I'll try yours again and figure out what's different. Thanks Daniel.
John Sheehan
Tried it. Same result. I think we're dealing with a bigger bug introduced in the beta.
John Sheehan
+1  A: 

I want to comment but can't yet. Yes, I have a very similar issue. In my case it's my viewmodel that is updating a DownloadStatus property, then when the download is completed I do some more work and continue updating this property.

The view stops updating once the ViewModel code hits the OpenReadCompleted method. I've stepped carefully through the code. PropertyChanged fires, and the view even comes back and retrieves the new property value, but never shows the change.

I was sure it was a bug, but then I created a brand new project to reproduce the issue, and it works fine!

Here's a snippet of my non-reproducing code. The UI textblock bound to "DownloadStatus" happily updates properly all the way through. But the same paradigm doesn't work in my main project. Infuriating!

public void BeginDownload(bool doWorkAfterDownload)
{
    DownloadStatus = "Starting ...";

    _doExtraWork = doWorkAfterDownload;
    var webClient = new WebClient();

    string auth = "Basic " + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes("test:password"));
    webClient.Headers["Authorization"] = auth;

    webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged);
    webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompleted);

    webClient.OpenReadAsync(new Uri("http://www.ben.geek.nz/samsung1.jpg"));
}

void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    if (e.Error != null)
    {
        DownloadStatus = e.Error.Message;
        return;
    }

    DownloadStatus = "Completed. Idle.";

    if(_doExtraWork)
    {
        Thread t = new Thread(DoWork);
        t.Start(e.Result);
    }

}

void DoWork(object param)
{
    InvokeDownloadCompleted(new EventArgs());

    // just do some updating
    for (int i = 1; i <= 10; i++)
    {
        DownloadStatus = string.Format("Doing work {0}/10", i);
        Thread.Sleep(500);
    }
    DownloadStatus = "Completed extra work. Idle.";
    InvokeExtraWorkCompleted(new EventArgs());
}
Ben Gracewood
+1  A: 

I also ran into some problems with updating the UI from different dispatchers. What I finally did was use the TextBlock's (or other UI Element) own dispatcher and that worked for me. I think the phone framework may be using different dispatchers between the app and UI Elements. Notice the change from dispatcher.BeginInvoke to textbox1.Dispatcher...

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    var dispatcher = Deployment.Current.Dispatcher;

    var client = new WebClient();

    client.DownloadStringCompleted += (s, e) =>
    {
        var result = e.Result;
        textBlock1.Dispatcher.BeginInvoke(
            ()=> textBlock1.Text = result
            );
    };

    client.DownloadStringAsync(new Uri("http://example.com"));
}
Jacob
+2  A: 

From browsing through the WP7 forums, a bunch of people were reporting that this was related to a video card driver issue. I've updated my ATI Radeon HD 3400 drivers to the latest version and it appears to work now.

John Sheehan
At least that means it'll most likely work on the device. Frustrating, though!
Matt Hamilton