views:

367

answers:

1

Hello - I have been stuck on this all weekend and failed miserably!
Please help me to claw back my sanity!!

Your challenge

For my first Silverlight application I thought it would be fun to use the World of Warcraft armoury to list the characters in my guild. This involves making an asyncronous from Silverlight (duh!) to the WoW armoury which is XML based. SIMPLE EH?

Take a look at this link and open the source. You'll see what I mean: http://eu.wowarmory.com/guild-info.xml?r=Eonar&n=Gifted and Talented

Below is code for getting the XML (the call to ShowGuildies will cope with the returned XML - I have tested this locally and I know it works).

I have not managed to get the expected returned XML at all.

Notes:

  • If the browser is capable of transforming the XML it will do so, otherwise HTML will be provided. I think it examines the UserAgent
  • I am a seasoned asp.net web developer C# so go easy if you start talking about native to Windows Forms / WPF
  • I can't seem to set the UserAgent setting in .net 4.0 - doesn't seem to be a property off the HttpWebRequest object for some reason - i think it used to be available.
  • Silverlight 4.0 (created as 3.0 originally before I updated my installation of Silverlight to 4.0)
  • Created using C# 4.0
  • Please explain as if you talking to a web developer and not a proper programming lol!

Below is the code - it should return the XML from the wow armoury.

private void button7_Click(object sender, RoutedEventArgs e)
{
   // URL for armoury lookup
                string url = @"http://eu.wowarmory.com/guild-info.xml?r=Eonar&n=Gifted and Talented";

                // Create the web request
                HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url);

                // Set the user agent so we are returned XML and not HTML
                //httpWebRequest.Headers["User-Agent"] = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)";

                // Not sure about this dispatcher thing - it's late so i have started to guess.
                Dispatcher.BeginInvoke(delegate()
                {
                    // Call asyncronously
                    IAsyncResult asyncResult = httpWebRequest.BeginGetResponse(ReqCallback, httpWebRequest);

                    // End the response and use the result
                    using (HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.EndGetResponse(asyncResult))
                    {
                        // Load an XML document from a stream
                        XDocument x = XDocument.Load(httpWebResponse.GetResponseStream());

                        // Basic function that will use LINQ to XML to get the list of characters.
                        ShowGuildies(x);
                    }
                });
            }

            private void ReqCallback(IAsyncResult asynchronousResult)
            {
                // Not sure what to do here - maybe update the interface?
            }

Really hope someone out there can help me!

Thanks mucho! Dan.

PS Yes, I have noticed the irony in the name of the guild :)

+1  A: 

First, Dispatcher.BeginInvoke is only needed when you're on another thread than the UI thread (where everything silverlight/WPF related happens). On a click event, you're already in the UI thread so there's no need to call it.

Second, BeginGetResponse is an asynchronous operation so when it has finished, it will call a callback function on another thread, here ReqCallback. It's in this method that you can call EndGetResponse. This pattern applies to every BeginX/EndX you'll find in the framework.

However, since you're in another thread, you'll need to use BeginInvoke to dispatch a method back to the UI thread.

The code will look like this:

private void button7_Click(object sender, RoutedEventArgs e) {
    string url = @"http://eu.wowarmory.com/guild-info.xml?r=Eonar&n=Gifted and Talented";
    HttpWebRequest httpWebRequest = (HttpWebRequest) WebRequest.Create(url);
    httpWebRequest.BeginGetResponse(ReqCallback, httpWebRequest);
}

private void ReqCallback(IAsyncResult asyncResult)
{
    HttpWebRequest httpWebRequest = (HttpWebRequest) asyncResult.AsyncState;
    using (HttpWebResponse httpWebResponse = (HttpWebResponse) httpWebRequest.EndGetResponse(asyncResult))
    {
        XDocument x = XDocument.Load(httpWebResponse.GetResponseStream());
        Dispatcher.BeginInvoke((Action) (() => ShowGuildies(x)));
    }
}

Note that you can also process the XML in the thread and use the dispatcher only to send back guildies to the UI, to avoid freezing the UI if the XML is very long to parse (shouldn't be the case).

Edit: Corrected the code. You only have to implement ShowGuildies. Regarding the internet connectivity and delays, since the operation occurs in another thread the UI won't freeze. You might consider showing a loading animation or something though.

Julien Lebosquain
Sorry - that code doesn't compile. Can you post a working sample? I struggling to get it working and more code that doesn't work is going to stress me further :) 1. httpWebRequest doesn't exist in ReqCallBack 2. The part of (Action) () => ShowGuildies(x) I really appreciate your help and that last bit sounds interesting - while there shouldn't be much parsing time I also have to cope with time lag and internet connectivity. Cheers!
Dan B
Also, I've seen many examples which I have tried and just don't work. For such a simple example, using the link above - it would be great to see an expert get it working - all weekend - me now mad :)
Dan B
I need a cookie
Dan B
a chocolate chip one
Dan B
Hi Julien,Thanks for the updated code, I will give it a go and see how far I get. Maybe after sleep I had things will start to make more sense.Either way, I'll let you know how it turns out. I have already implemented ShowGuildies against a local version of the xml file, so hopefully the above will do the trick!
Dan B
We are almost there I think but I do get a security error now (even if I upload to a production server).The most internal error simple says "security error". Not tremendously useful! There is a cross domain policy file in place that may cause this - but not sure how to get round it!? at System.Net.Browser.BrowserHttpWebRequest.InternalEndGetResponse(IAsyncResult asyncResult) at System.Net.Browser.BrowserHttpWebRequest.<>c__DisplayClass5.<EndGetResponse>b__4(Object sendState) at System.Net.Browser.AsyncHelper.<>c__DisplayClass2.<BeginOnUI>b__0(Object sendState)
Dan B
SOLVED - used a WCF service to act as a proxy due to cross domain security and header modification restrictions. Thanks for all the advice.
Dan B